diff --git a/compiler/rustc_const_eval/src/const_eval/comptime_fs.rs b/compiler/rustc_const_eval/src/const_eval/comptime_fs.rs new file mode 100644 index 0000000000000..5c579d66298c1 --- /dev/null +++ b/compiler/rustc_const_eval/src/const_eval/comptime_fs.rs @@ -0,0 +1,57 @@ +use std::io::ErrorKind; + +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::comptime::ReadFileError; +use rustc_middle::query::LocalCrate; +use rustc_middle::ty::TyCtxt; +use rustc_span::Symbol; +use tracing::instrument; + +#[instrument(skip(tcx), level = "debug")] +pub fn experimental_comptime_list_files_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> &[Symbol] { + let filelist = tcx + .sess + .opts + .unstable_opts + .experimental_comptime_files + .iter() + .map(|p| Symbol::intern(p)) + .collect::>(); + + tcx.arena.alloc_slice(&filelist) +} + +#[instrument(skip(tcx), level = "debug")] +pub fn experimental_comptime_fetch_file_provider<'tcx>( + tcx: TyCtxt<'tcx>, + key: Symbol, +) -> Result, ReadFileError<'tcx>> { + // In case we are in a context without a crate root, just return that + // every file is whitelisted but does not exist. Is this code path + // taken by rustdoc when checking a comment code block? + let Some(crate_root_path) = tcx.sess.io.input.opt_path() else { + return Ok(None); + }; + + let requested_path = key.as_str(); + + // Always resolve path from the crate root to provide consistent behavior. + let resolved_path = crate_root_path.with_file_name(requested_path); + + // Very simple implementation where we whitelist paths directly. We don't even attempt + // to canonicalize for now. + if tcx + .experimental_comptime_list_files(LOCAL_CRATE) + .iter() + .find(|s| requested_path == s.as_str()) + .is_some() + { + match std::fs::read(resolved_path) { + Ok(content) => Ok(Some(tcx.arena.alloc_slice(&content))), + Err(e) if e.kind() == ErrorKind::NotFound => Ok(None), + Err(e) => Err(ReadFileError::IO(tcx.arena.alloc_str(&e.to_string()))), + } + } else { + Err(ReadFileError::NotWhitelisted) + } +} diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index cb9432f9832e5..f2d8a8fc6db23 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -10,6 +10,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{CRATE_HIR_ID, HirId}; use rustc_lint_defs::builtin::LONG_RUNNING_CONST_EVAL; +use rustc_middle::comptime::ReadFileError; use rustc_middle::mir::AssertMessage; use rustc_middle::mir::interpret::ReportedErrorInfo; use rustc_middle::query::TyCtxtAt; @@ -24,10 +25,10 @@ use super::error::*; use crate::diagnostics::{LongRunning, LongRunningWarn}; use crate::interpret::{ self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, - GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, - RetagMode, Scalar, compile_time_machine, ensure_monomorphic_enough, err_inval, interp_ok, - throw_exhaust, throw_inval, throw_ub, throw_ub_format, throw_unsup, throw_unsup_format, - type_implements_dyn_trait, + GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, Projectable, + RangeSet, RetagMode, Scalar, compile_time_machine, ensure_monomorphic_enough, err_inval, + interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_format, throw_unsup, + throw_unsup_format, type_implements_dyn_trait, }; /// When hitting this many interpreted terminators we emit a deny by default lint @@ -598,6 +599,112 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { } } + sym::comptime_fetch_file => { + let arg_place = ecx.deref_pointer(&args[0])?; + let path = ecx.read_str(&arg_place)?; + let path_sym = Symbol::intern(path); + + let krate = ecx + .stack() + .first() + .expect("should have a call frame if we are here") + .instance() + .def_id() + .krate; + + let content_maybe = match ecx + .tcx + .experimental_comptime_fetch_file((krate, path_sym)) + { + Ok(content) => content, + Err(ReadFileError::IO(err)) => { + let guard = ecx + .tcx + .dcx() + .struct_err("error while reading file in constant evaluation") + .with_span(ecx.cur_span()) + .with_span_help(ecx.tcx.span, "while evaluating this constant") + .with_note(format!("filename: {path_sym}")) + .with_note(format!("error: {err}")) + .emit_err(); + throw_inval!(AlreadyReported(ReportedErrorInfo::allowed_in_infallible( + guard + ))); + } + Err(ReadFileError::NotWhitelisted) => { + let guard = ecx + .tcx + .dcx() + .struct_err("this file cannot be accessed during const-evaluation") + .with_span(ecx.cur_span()) + .with_span_help(ecx.tcx.span, "while evaluating this constant") + .with_note(format!("filename: {path_sym}")) + .with_help("pass -Zexperimental-comptime-files= to the compiler to allow reading this file") + .emit_err(); + throw_inval!(AlreadyReported(ReportedErrorInfo::allowed_in_infallible( + guard + ))); + } + }; + + let variant_index = if let Some(value) = content_maybe { + let ptr = ecx.allocate_bytes_dedup(value)?; + + let (variant_idx, variant_place) = + ecx.project_downcast_named(dest, sym::Some)?; + let field_place = ecx.project_field(&variant_place, FieldIdx::ZERO)?; + ecx.write_immediate( + Immediate::ScalarPair( + Scalar::from_pointer(ptr, ecx), + Scalar::from_target_usize(value.len() as u64, ecx), + ), + &field_place, + )?; + + variant_idx + } else { + ecx.project_downcast_named(dest, sym::None)?.0 + }; + + ecx.write_discriminant(variant_index, dest)?; + } + + sym::comptime_list_files => { + let krate = ecx + .stack() + .first() + .expect("should have a call frame if we are here") + .instance() + .def_id() + .krate; + + let filelist = ecx.tcx().experimental_comptime_list_files(krate); + + let element_type = + dest.layout().ty.builtin_deref(false).unwrap().sequence_element_type(ecx.tcx()); + + // type and layout of [&str, filelist.len()]; + let array_ty = + ecx.layout_of(Ty::new_array(ecx.tcx(), element_type, filelist.len() as _))?; + + // actual allocated array + let array_place = ecx.allocate(array_ty, interpret::MemoryKind::Stack)?; + + let mut array_iter = ecx.project_array_fields(&array_place)?; + while let Some((idx, entry_place)) = array_iter.next(ecx)? { + let file = filelist[idx as usize]; + let file_place = ecx.allocate_str_dedup(file.as_str())?; + let imm = file_place.to_ref(ecx); + ecx.write_immediate(imm, &entry_place)?; + } + + let array_place = array_place.map_provenance(CtfeProvenance::as_immutable); + ecx.write_immediate( + Immediate::new_slice(array_place.ptr(), filelist.len() as _, ecx), + dest, + )?; + } + sym::type_id_vtable => { let tp_ty = ecx.read_type_id(&args[0])?; let result_ty = ecx.read_type_id(&args[1])?; diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 58b8418707eff..aca2c085c157c 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -8,6 +8,7 @@ use tracing::instrument; use crate::interpret::InterpCx; +pub(crate) mod comptime_fs; mod dummy_machine; mod dyn_trait; mod error; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 84cf61c06e790..4c39f23d3596b 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -714,10 +714,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.read_scalar(op)?.to_target_isize(self) } - /// Turn the wide MPlace into a string (must already be dereferenced!) - pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, &str> { + /// Turn the wide MPlace into a slice of bytes (must already be dereferenced!) + pub fn read_bytes(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, &[u8]> { let len = mplace.len(self)?; let bytes = self.read_bytes_ptr_strip_provenance(mplace.ptr(), Size::from_bytes(len))?; + interp_ok(bytes) + } + + /// Turn the wide MPlace into a string (must already be dereferenced!) + pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, &str> { + let bytes = self.read_bytes(mplace)?; let s = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?; interp_ok(s) } diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 091f0f71cc50d..3d6ff03f0c8fd 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -61,6 +61,10 @@ pub fn provide(providers: &mut Providers) { }; providers.hooks.validate_scalar_in_layout = |tcx, scalar, layout| util::validate_scalar_in_layout(tcx, scalar, layout); + providers.queries.experimental_comptime_fetch_file = + const_eval::comptime_fs::experimental_comptime_fetch_file_provider; + providers.queries.experimental_comptime_list_files = + const_eval::comptime_fs::experimental_comptime_list_files_provider; } /// `rustc_driver::main` installs a handler that will set this to `true` if diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index f30e3c235cbc1..85657ccea8470 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -85,6 +85,8 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::ceil | sym::codeview_annotation | sym::cold_path + | sym::comptime_fetch_file + | sym::comptime_list_files | sym::const_eval_select | sym::contract_check_ensures | sym::contract_check_requires @@ -288,6 +290,23 @@ pub(crate) fn check_intrinsic_type( } sym::needs_drop => (1, 0, vec![], tcx.types.bool), + sym::comptime_fetch_file => { + let slice_u8 = Ty::new_slice(tcx, tcx.types.u8); + let slice_u8_static: Ty<'_> = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, slice_u8); + + let br = ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon }; + let var_name = + Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), tcx.types.str_); + + (0, 0, vec![var_name], Ty::new_option(tcx, slice_u8_static)) + } + + sym::comptime_list_files => { + let slice = Ty::new_slice(tcx, Ty::new_static_str(tcx)); + let slice_static: Ty<'_> = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, slice); + (0, 0, vec![], slice_static) + } + sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)), sym::type_id => (1, 0, vec![], type_id_ty()), sym::type_id_array_len => (0, 0, vec![type_id_ty()], tcx.types.usize), diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 73bebb22bc508..5fc7ff915563d 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -657,6 +657,15 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P ); files.extend(extra_tracked_files); + let comptime_tracked_files = hash_iter_files( + tcx.experimental_comptime_list_files(LOCAL_CRATE) + .iter() + .map(|s| PathBuf::from(s.as_str())) + .map(normalize_path), + checksum_hash_algo, + ); + files.extend(comptime_tracked_files); + // We also need to track used PGO profile files if let Some(ref profile_instr) = sess.opts.cg.profile_use { files.extend(hash_iter_files( diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 154cf4f060a9e..0179b507cb8ea 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -26,6 +26,7 @@ use rustc_span::{Span, Symbol, bug, kw}; use super::{Decodable, DecodeIterator}; use crate::creader::{CStore, LoadedMacro}; use crate::rmeta::AttrFlags; +use crate::rmeta::decoder::MetaBlob; use crate::rmeta::table::IsDefault; use crate::{eii, foreign_modules, native_libs}; @@ -624,6 +625,28 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { ..providers.queries }; provide_extern(&mut providers.extern_queries); + provide_extern_comptime(&mut providers.extern_queries); +} + +fn provide_extern_comptime(extern_queries: &mut ExternProviders) { + extern_queries.experimental_comptime_list_files = |tcx, cnum| { + let cstore = CStore::from_tcx(tcx); + let crate_data = cstore.get_crate_data(cnum); + let filelist = crate_data.root.comptime_files.keys().copied().collect::>(); + tcx.arena.alloc_slice(&filelist) + }; + + extern_queries.experimental_comptime_fetch_file = |tcx, (cnum, path)| { + let cstore = CStore::from_tcx(tcx); + let crate_data = cstore.get_crate_data(cnum); + let data = crate_data + .root + .comptime_files + .get(&path) + .map(|lv| lv.decode(crate_data.blob())) + .and_then(|v| v.map(|d| &*tcx.arena.alloc_slice(&d))); + Ok(data) + }; } impl CStore { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index b107b1fe13c7f..72ff4814c1a26 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -771,6 +771,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let denied_partial_mitigations = stat!("denied-partial-mitigations", || self .encode_enabled_denied_partial_mitigations()); + let comptime_files = stat!("comptime-files", || self.encode_comptime_files(tcx)); + let root = stat!("crate-root", || { let attrs = tcx.hir_krate_attrs(); self.lazy(CrateRoot { @@ -829,6 +831,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { expn_hashes, def_path_hash_map, specialization_enabled_in: tcx.specialization_enabled_in(LOCAL_CRATE), + comptime_files, }) }); @@ -2465,6 +2468,24 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } LazyArray::default() } + + fn encode_comptime_files( + &mut self, + tcx: TyCtxt<'_>, + ) -> IndexMap>>> { + let mut comptime_files = IndexMap::default(); + for &path in tcx.experimental_comptime_list_files(LOCAL_CRATE) { + // Must encode None too, as we depend on the keys of the IndexMap to store the list + // of files that we export from this crate. + if let Ok(file_data) = tcx.experimental_comptime_fetch_file((LOCAL_CRATE, path)) { + comptime_files.insert_full(path, self.lazy(file_data.map(|d| d.to_vec()))); + } else { + bug!("a comptile time file read error should fail crate compilation") + } + } + + comptime_files + } } /// Used to prefetch queries which will be needed later by metadata encoding. diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index c389a5ea6afcf..a930684e1eb72 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -41,6 +41,7 @@ use rustc_middle::middle::resolve::{AmbigModChild, DocLinkResMap, ModChild}; use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::mir; use rustc_middle::mir::ConstValue; +use rustc_middle::ty::data_structures::IndexMap; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::util::Providers; @@ -331,6 +332,8 @@ pub(crate) struct CrateRoot { symbol_mangling_version: SymbolManglingVersion, specialization_enabled_in: bool, + + comptime_files: IndexMap>>>, } /// A separate struct for extra metadata that must be encoded *after* @@ -653,6 +656,7 @@ define_tables! { defaultness: Table, impl_is_fully_generic_for_reflection: Table, + - optional: opt_rpitit_info: Table>, attributes: Table>, diff --git a/compiler/rustc_metadata/src/rmeta/parameterized.rs b/compiler/rustc_metadata/src/rmeta/parameterized.rs index f7a4661430916..1c631b5838739 100644 --- a/compiler/rustc_metadata/src/rmeta/parameterized.rs +++ b/compiler/rustc_metadata/src/rmeta/parameterized.rs @@ -70,6 +70,7 @@ macro_rules! trivially_parameterized_over_tcx { trivially_parameterized_over_tcx! { bool, + u8, u32, u64, usize, diff --git a/compiler/rustc_middle/src/comptime.rs b/compiler/rustc_middle/src/comptime.rs new file mode 100644 index 0000000000000..209789ce697b5 --- /dev/null +++ b/compiler/rustc_middle/src/comptime.rs @@ -0,0 +1,12 @@ +use rustc_macros::StableHash; + +/// This error is emitted by comptime files queries when a file cannot be read. +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[derive(StableHash)] +pub enum ReadFileError<'tcx> { + /// Another IO error happened while trying to read the file + IO(&'tcx str), + + /// The file being accessed at comptime has not been whitelisted + NotWhitelisted, +} diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 695eca6225858..67b31ba4fec37 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -69,6 +69,7 @@ mod macros; #[macro_use] pub mod arena; +pub mod comptime; pub mod dep_graph; pub mod diagnostics; pub mod hir; diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 56889f84f38d6..074a4eb0b1e8b 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -79,6 +79,7 @@ use rustc_span::def_id::{LOCAL_CRATE, ModId}; use rustc_span::{DUMMY_SP, LocalExpnId, Span, Spanned, Symbol}; use rustc_target::spec::PanicStrategy; +use crate::comptime::ReadFileError; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, SanitizerFnAttrs}; @@ -2852,6 +2853,18 @@ rustc_queries! { desc { "calculating all fake doc items" } } + /// Query the content of a file during const evaluation + query experimental_comptime_fetch_file(key: (CrateNum, Symbol)) -> Result, ReadFileError<'tcx>> { + desc { "fetch file {} from crate {}", key.1, key.0 } + separate_provide_extern + } + + /// Query list of files available during const evaluation + query experimental_comptime_list_files(key: CrateNum) -> &'tcx [Symbol] { + desc { "fetch const eval file list from crate {}", key } + separate_provide_extern + } + //----------------------------------------------------------------------------- // "Non-queries" are special dep kinds that are not queries. //----------------------------------------------------------------------------- diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index b0281a70eeb73..2597d1abf1f11 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -17,6 +17,7 @@ use rustc_index::{Idx, IndexSlice}; use rustc_span::def_id::ModId; use rustc_span::{ErrorGuaranteed, Spanned}; +use crate::comptime::ReadFileError; use crate::mono::{MonoItem, NormalizationErrorInMono}; use crate::ty::{self, Ty, TyCtxt}; use crate::{mir, thir, traits}; @@ -211,6 +212,7 @@ impl_erasable_for_types_with_no_type_params! { Result<(&'_ Steal>, thir::ExprId), ErrorGuaranteed>, Result<(&'_ [Spanned>], &'_ [Spanned>]), NormalizationErrorInMono>, Result<(), ErrorGuaranteed>, + Result, ReadFileError<'_>>, Result>>, ErrorGuaranteed>, Result>, ErrorGuaranteed>, Result>, diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 2bfe80f444090..e81957a568948 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -420,6 +420,19 @@ impl<'tcx> QueryKey for (ty::Instance<'tcx>, CollectionMode) { } } +impl QueryKey for (CrateNum, Symbol) { + type LocalQueryKey = Symbol; + + #[inline(always)] + fn as_local_key(&self) -> Option { + (self.0 == LOCAL_CRATE).then(|| self.1) + } + + fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + /// Gets a `DefId` associated with a type /// /// Visited set is needed to avoid full iteration over diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 2e71a7c72603d..9b126aea0f1f0 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -21,7 +21,7 @@ use rustc_attr_ir::find_attr; use rustc_attr_ir::lang_items::LangItem; use rustc_crate_store::{CrateStoreDyn, Untracked}; use rustc_data_structures::defer; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::intern::Interned; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; @@ -711,6 +711,9 @@ pub struct GlobalCaches<'tcx> { /// Caches the instantiation of a canonical binder given a set of args. pub clauses_cache: Lock, &'tcx [ty::GenericArg<'tcx>]), ty::Clauses<'tcx>>>, + + /// Cache of files that have been read by the const evaluator + pub experimental_comptime_files: Lock>, } /// The central data structure of the compiler. It stores references diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 9ac479e8abe51..088e68eea682b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2448,7 +2448,7 @@ options! { codegen_emit_retag: Option = (None, parse_codegen_retag_options, [TRACKED], "emit retag function calls in generated code"), codegen_source_order: bool = (false, parse_bool, [UNTRACKED], - "emit mono items in the order of spans in source files (default: no)"), + "emit mono items in the order of spans in source files (default: no)"), contract_checks: Option = (None, parse_opt_bool, [TRACKED], "emit runtime checks for contract pre- and post-conditions (default: no)"), coverage_options: CoverageOptions = (CoverageOptions::default(), parse_coverage_options, [TRACKED], @@ -2527,6 +2527,8 @@ options! { "emit a section containing stack size metadata (default: no)"), enforce_type_length_limit: bool = (false, parse_bool, [TRACKED], "enforce the type length limit when monomorphizing instances in codegen"), + experimental_comptime_files: Vec = (Vec::new(), parse_string_push, [UNTRACKED], + "enable access to a file in comptime"), experimental_default_bounds: bool = (false, parse_bool, [TRACKED], "enable default bounds for experimental group of auto traits"), export_executable_symbols: bool = (false, parse_bool, [TRACKED], diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 95197e6c98a9f..9e568e2aaf30d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -680,6 +680,8 @@ symbols! { compiler_fence, compiler_move, complex, + comptime_fetch_file, + comptime_list_files, concat, concat_bytes, concat_str, diff --git a/library/core/src/intrinsics/comptime.rs b/library/core/src/intrinsics/comptime.rs new file mode 100644 index 0000000000000..c9b77a5411fc2 --- /dev/null +++ b/library/core/src/intrinsics/comptime.rs @@ -0,0 +1,18 @@ +//! Intrinsics for compile time operations + +/// Read a file from the system at comptime time, and returns its bytes. +/// +/// Returns [Option::None] if the file cannot be read. This includes when the file does not exist, or hasn't been +/// made available at comptile time by the compiler. +#[rustc_nounwind] +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn comptime_fetch_file(file_path: &str) -> Option<&'static [u8]>; + +/// Returns the list of files that can be read at compile time +#[rustc_nounwind] +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn comptime_list_files() -> &'static [&'static str]; diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index d12543e6b35e2..ea6df76b5acf7 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -84,6 +84,7 @@ use crate::num::imp::libm; use crate::{mem, ptr}; mod bounds; +pub mod comptime; pub mod fallback; pub mod gpu; mod macros; diff --git a/library/core/src/mem/comptime.rs b/library/core/src/mem/comptime.rs new file mode 100644 index 0000000000000..53aa33cfe3120 --- /dev/null +++ b/library/core/src/mem/comptime.rs @@ -0,0 +1,17 @@ +//! MVP for compile-type only operations like reading files + +/// Read a file at compile time +#[unstable(feature = "type_info", issue = "146922")] +#[rustc_const_unstable(feature = "type_info", issue = "146922")] +#[rustc_comptime] +pub fn read_file(path: &str) -> Option<&'static [u8]> { + crate::intrinsics::comptime::comptime_fetch_file(path) +} + +/// Read a file at compile time +#[unstable(feature = "type_info", issue = "146922")] +#[rustc_const_unstable(feature = "type_info", issue = "146922")] +#[rustc_comptime] +pub fn list_files() -> &'static [&'static str] { + crate::intrinsics::comptime::comptime_list_files() +} diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index a22e52f3a3bc7..8c693a2d9f0e1 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -39,6 +39,7 @@ use crate::ub_checks::assert_unsafe_precondition; use crate::{clone, cmp, fmt, hash, intrinsics, ptr}; mod alignment; + #[unstable(feature = "ptr_alignment_type", issue = "102070")] pub use alignment::Alignment; @@ -71,6 +72,9 @@ pub use crate::intrinsics::transmute; #[unstable(feature = "type_info", issue = "146922")] pub mod type_info; +#[unstable(feature = "type_info", issue = "146922")] +pub mod comptime; + /// Takes ownership and "forgets" about the value **without running its destructor**. /// /// Any resources the value manages, such as heap memory or a file handle, will linger