diff --git a/src/librustc/arena.rs b/src/librustc/arena.rs index 5a5919d786638..70ac6b91b2ba2 100644 --- a/src/librustc/arena.rs +++ b/src/librustc/arena.rs @@ -23,9 +23,9 @@ macro_rules! arena_types { [] generics: rustc::ty::Generics, [] trait_def: rustc::ty::TraitDef, [] adt_def: rustc::ty::AdtDef, - [] steal_mir: rustc::ty::steal::Steal>, + [] steal_mir: rustc_data_structures::steal::Steal>, [] mir: rustc::mir::Body<$tcx>, - [] steal_promoted: rustc::ty::steal::Steal< + [] steal_promoted: rustc_data_structures::steal::Steal< rustc_index::vec::IndexVec< rustc::mir::Promoted, rustc::mir::Body<$tcx> diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 2238a56b29d04..7c362657b8612 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2079,6 +2079,7 @@ impl<'a> LoweringContext<'a> { span: l.span, attrs: l.attrs.clone(), source: hir::LocalSource::Normal, + interp_tag: l.interp_tag.clone(), }, ids) } @@ -3048,6 +3049,7 @@ impl<'a> LoweringContext<'a> { source, span, ty: None, + interp_tag: None, }; self.stmt(span, hir::StmtKind::Local(P(local))) } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 9ae661f0952a4..bcb7ee11aab97 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1273,6 +1273,8 @@ pub struct Local { /// Can be `ForLoopDesugar` if the `let` statement is part of a `for` loop /// desugaring. Otherwise will be `Normal`. pub source: LocalSource, + /// See comment on `syntax::ast::Local`. + pub interp_tag: Option, } /// Represents a single arm of a `match` expression, e.g. diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index c0255e5b8a481..82b5532bd62b8 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -392,3 +392,14 @@ impl<'hir> HashStable> for attr::OptimizeAttr { mem::discriminant(self).hash_stable(hcx, hasher); } } + +impl_stable_hash_for!(enum ast::LocalInterpState { + Uninitialized, + Set, + Moved, +}); + +impl_stable_hash_for!(struct ast::LocalInterpTag { + id, + state, +}); diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index c643baf11254c..b562d96bed786 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -1,14 +1,15 @@ //! This module contains `HashStable` implementations for various data types -//! from rustc::ty in no particular order. +//! from `rustc::ty` in no particular order. use crate::ich::{Fingerprint, StableHashingContext, NodeIdHashingMode}; +use crate::middle::region; +use crate::mir; +use crate::ty; + use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, ToStableHashKey, StableHasher}; use std::cell::RefCell; use std::mem; -use crate::middle::region; -use crate::ty; -use crate::mir; impl<'a, 'tcx, T> HashStable> for &'tcx ty::List where @@ -197,15 +198,6 @@ impl<'a> HashStable> for ty::FloatVid { } } -impl<'a, T> HashStable> for ty::steal::Steal -where - T: HashStable>, -{ - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - self.borrow().hash_stable(hcx, hasher); - } -} - impl<'a> HashStable> for crate::middle::privacy::AccessLevels { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index e463810b7af5f..03aa5a9809f2e 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs @@ -1120,8 +1120,7 @@ rustc_queries! { } // Get an estimate of the size of an InstanceDef based on its MIR for CGU partitioning. - query instance_def_size_estimate(def: ty::InstanceDef<'tcx>) - -> usize { + query instance_def_size_estimate(def: ty::InstanceDef<'tcx>) -> usize { no_force desc { |tcx| "estimating size for `{}`", tcx.def_path_str(def.def_id()) } } @@ -1130,5 +1129,10 @@ rustc_queries! { eval_always desc { "looking up enabled feature gates" } } + + query interp_user_fn(_: CrateNum) -> DefId { + eval_always + desc { "locating interpreter user fn in HIR" } + } } } diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 50aa036f723a0..8f2e4d975b33b 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -391,6 +391,7 @@ top_level_options!( // can influence whether overflow checks are done or not. debug_assertions: bool [TRACKED], debuginfo: DebugInfo [TRACKED], + interp_mode: bool [TRACKED], lint_opts: Vec<(String, lint::Level)> [TRACKED], lint_cap: Option [TRACKED], describe_lints: bool [UNTRACKED], @@ -599,6 +600,7 @@ impl Default for Options { crate_types: Vec::new(), optimize: OptLevel::No, debuginfo: DebugInfo::None, + interp_mode: false, lint_opts: Vec::new(), lint_cap: None, describe_lints: false, @@ -2467,6 +2469,7 @@ pub fn build_session_options_and_crate_config( crate_types, optimize: opt_level, debuginfo, + interp_mode: false, lint_opts, lint_cap, describe_lints, diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index ffe1a11e81a11..31704d6c5fec3 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -38,7 +38,6 @@ use crate::ty::{InferConst, ParamConst}; use crate::ty::GenericParamDefKind; use crate::ty::layout::{LayoutDetails, TargetDataLayout, VariantIdx}; use crate::ty::query; -use crate::ty::steal::Steal; use crate::ty::subst::{UserSubsts, GenericArgKind}; use crate::ty::{BoundVar, BindingMode}; use crate::ty::CanonicalPolyFnSig; @@ -49,12 +48,13 @@ use crate::util::nodemap::{FxHashMap, FxHashSet}; use errors::DiagnosticBuilder; use arena::SyncDroplessArena; use smallvec::SmallVec; +use rustc_index::vec::{Idx, IndexVec}; use rustc_data_structures::stable_hasher::{ HashStable, StableHasher, StableVec, hash_stable_hashmap, }; -use rustc_index::vec::{Idx, IndexVec}; use rustc_data_structures::sharded::ShardedHashMap; use rustc_data_structures::sync::{Lrc, Lock, WorkerLocal}; +use rustc_data_structures::steal::Steal; use std::any::Any; use std::borrow::Borrow; use std::cmp::Ordering; @@ -2894,6 +2894,44 @@ fn ptr_eq(t: *const T, u: *const U) -> bool { t as *const () == u as *const () } +/// In interpreter mode, locates the `DefId` of the user fn (a closure marked by an attribute named +/// `rustc_interp_user_fn`) by visiting the local crate's HIR. +fn find_interp_user_fn(tcx: TyCtxt<'_>) -> DefId { + use hir::intravisit::{self, Visitor, NestedVisitorMap}; + + struct InterpUserFnVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + def_id: Option, + } + + impl<'tcx> Visitor<'tcx> for InterpUserFnVisitor<'tcx> { + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { + NestedVisitorMap::OnlyBodies(&self.tcx.hir()) + } + + fn visit_expr(&mut self, ex: &'tcx hir::Expr) { + if syntax::attr::contains_name(&ex.attrs, sym::rustc_interp_user_fn) { + self.def_id = Some(self.tcx.hir().local_def_id(ex.hir_id)); + return; + } + + intravisit::walk_expr(self, ex); + } + } + + let mut visitor = InterpUserFnVisitor { + tcx, + def_id: None, + }; + tcx.hir().krate().visit_all_item_likes(&mut visitor.as_deep_visitor()); + visitor.def_id + .unwrap_or_else(|| tcx.sess.fatal(&format!( + "could not find interpreter user fn in HIR; it should be a closure expression \ + marked with the `#[{}]` attribute", + sym::rustc_interp_user_fn + ))) +} + pub fn provide(providers: &mut ty::query::Providers<'_>) { providers.in_scope_traits_map = |tcx, id| tcx.gcx.trait_map.get(&id); providers.module_exports = |tcx, id| tcx.gcx.export_map.get(&id).map(|v| &v[..]); @@ -2971,4 +3009,8 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) { assert_eq!(cnum, LOCAL_CRATE); attr::contains_name(tcx.hir().krate_attrs(), sym::compiler_builtins) }; + providers.interp_user_fn = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + find_interp_user_fn(tcx) + }; } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 4b9117f71be5c..58e99a8434612 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -105,7 +105,6 @@ pub mod outlives; pub mod print; pub mod query; pub mod relate; -pub mod steal; pub mod subst; pub mod trait_def; pub mod walk; diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs index 863721a5b4b79..9622f57c57cea 100644 --- a/src/librustc/ty/query/mod.rs +++ b/src/librustc/ty/query/mod.rs @@ -34,7 +34,7 @@ use crate::traits::query::outlives_bounds::OutlivesBound; use crate::traits::specialization_graph; use crate::traits::Clauses; use crate::ty::{self, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt, AdtSizedConstraint}; -use crate::ty::steal::Steal; +use rustc_data_structures::steal::Steal; use crate::ty::util::NeedsDrop; use crate::ty::subst::SubstsRef; use crate::util::nodemap::{DefIdSet, DefIdMap, ItemLocalSet}; diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 474a42644d915..7b19755422050 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -94,6 +94,7 @@ pub use ena::unify; pub mod vec_linked_list; pub mod work_queue; pub mod fingerprint; +pub mod steal; pub struct OnDrop(pub F); diff --git a/src/librustc/ty/steal.rs b/src/librustc_data_structures/steal.rs similarity index 74% rename from src/librustc/ty/steal.rs rename to src/librustc_data_structures/steal.rs index 711e59dbcc9d2..39530ea48e8b5 100644 --- a/src/librustc/ty/steal.rs +++ b/src/librustc_data_structures/steal.rs @@ -1,7 +1,8 @@ -use rustc_data_structures::sync::{RwLock, ReadGuard, MappedReadGuard}; +use crate::stable_hasher::{StableHasher, HashStable}; +use crate::sync::{RwLock, ReadGuard, MappedReadGuard}; /// The `Steal` struct is intended to used as the value for a query. -/// Specifically, we sometimes have queries (*cough* MIR *cough*) +/// Specifically, we sometimes have queries (in particular, for MIR) /// where we create a large, complex value that we want to iteratively /// update (e.g., optimize). We could clone the value for each /// optimization, but that'd be expensive. And yet we don't just want @@ -26,21 +27,28 @@ pub struct Steal { impl Steal { pub fn new(value: T) -> Self { - Steal { + Self { value: RwLock::new(Some(value)) } } pub fn borrow(&self) -> MappedReadGuard<'_, T> { ReadGuard::map(self.value.borrow(), |opt| match *opt { - None => bug!("attempted to read from stolen value"), + None => panic!("attempted to read from stolen value"), Some(ref v) => v }) } pub fn steal(&self) -> T { - let value_ref = &mut *self.value.try_write().expect("stealing value which is locked"); + let value_ref = &mut *self.value.try_write() + .expect("stealing value that is locked"); let value = value_ref.take(); value.expect("attempt to read from stolen value") } } + +impl, CTX> HashStable for Steal { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + self.borrow().hash_stable(hcx, hasher); + } +} diff --git a/src/librustc_interface/interface.rs b/src/librustc_interface/interface.rs index fef60a47dc4e7..2edb07c5852a1 100644 --- a/src/librustc_interface/interface.rs +++ b/src/librustc_interface/interface.rs @@ -9,13 +9,15 @@ use rustc::session::{DiagnosticOutput, Session}; use rustc::util::common::ErrorReported; use rustc_codegen_utils::codegen_backend::CodegenBackend; use rustc_data_structures::OnDrop; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; -use rustc_data_structures::fx::{FxHashSet, FxHashMap}; +use rustc_data_structures::steal::Steal; use rustc_metadata::cstore::CStore; use std::path::PathBuf; use std::result; use std::sync::{Arc, Mutex}; use syntax; +use syntax::parse::parser::InterpUserFn; use syntax::source_map::{FileLoader, SourceMap}; use syntax_pos::edition; @@ -35,9 +37,19 @@ pub struct Compiler { pub(crate) queries: Queries, pub(crate) cstore: Lrc, pub(crate) crate_name: Option, + /// In interpreter mode, the user fn to use when parsing. + pub(crate) interp_user_fn: Option>, } impl Compiler { + pub fn set_interp_user_fn( + &mut self, + interp_user_fn: Option, + ) -> &mut Self { + self.interp_user_fn = interp_user_fn.map(|user_fn| Steal::new(user_fn)); + self + } + pub fn session(&self) -> &Lrc { &self.sess } @@ -61,12 +73,12 @@ impl Compiler { } } -/// The compiler configuration +/// The compiler configuration. pub struct Config { - /// Command line options + /// The command-line options. pub opts: config::Options, - /// cfg! configuration in addition to the default ones + /// `cfg!` configuration in addition to the default ones. pub crate_cfg: FxHashSet<(String, Option)>, pub input: Input, @@ -76,17 +88,14 @@ pub struct Config { pub file_loader: Option>, pub diagnostic_output: DiagnosticOutput, - /// Set to capture stderr output during compiler execution + /// `Some` to capture stderr output during compiler execution. pub stderr: Option>>>, pub crate_name: Option, pub lint_caps: FxHashMap, } -pub fn run_compiler_in_existing_thread_pool(config: Config, f: F) -> R -where - F: FnOnce(&Compiler) -> R, -{ +pub fn create_compiler(config: Config) -> Compiler { let (sess, codegen_backend, source_map) = util::create_session( config.opts, config.crate_cfg, @@ -98,7 +107,7 @@ where let cstore = Lrc::new(CStore::new(codegen_backend.metadata_loader())); - let compiler = Compiler { + Compiler { sess, codegen_backend, source_map, @@ -109,7 +118,15 @@ where output_file: config.output_file, queries: Default::default(), crate_name: config.crate_name, - }; + interp_user_fn: None, + } +} + +pub fn run_compiler_in_existing_thread_pool( + config: Config, + f: impl FnOnce(&Compiler) -> R, +) -> R { + let compiler = create_compiler(config); let _sess_abort_error = OnDrop(|| { compiler.sess.diagnostic().print_error_count(&util::diagnostics_registry()); diff --git a/src/librustc_interface/lib.rs b/src/librustc_interface/lib.rs index 2e593d441553a..a4bcf02a11e19 100644 --- a/src/librustc_interface/lib.rs +++ b/src/librustc_interface/lib.rs @@ -19,3 +19,4 @@ mod proc_macro_decls; mod profile; pub use interface::{run_compiler, Config}; +pub use passes::BoxedGlobalCtxt; diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 9874c0673cfc9..e4b860d380ba8 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -11,7 +11,7 @@ use rustc::lint; use rustc::middle::{self, reachable, resolve_lifetime, stability}; use rustc::middle::cstore::CrateStore; use rustc::ty::{self, AllArenas, Resolutions, TyCtxt, GlobalCtxt}; -use rustc::ty::steal::Steal; +use rustc_data_structures::steal::Steal; use rustc::traits; use rustc::util::common::{time, ErrorReported}; use rustc::session::Session; @@ -38,6 +38,7 @@ use syntax::early_buffered_lints::BufferedEarlyLint; use syntax::ext::base::{NamedSyntaxExtension, ExtCtxt}; use syntax::mut_visit::MutVisitor; use syntax::parse::{self, PResult}; +use syntax::parse::parser::InterpUserFn; use syntax::util::node_count::NodeCounter; use syntax::symbol::Symbol; use syntax_pos::FileName; @@ -56,16 +57,28 @@ use std::path::PathBuf; use std::cell::RefCell; use std::rc::Rc; -pub fn parse<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::Crate> { +pub fn parse<'a>( + sess: &'a Session, + input: &Input, + interp_user_fn: Option, +) -> PResult<'a, ast::Crate> { sess.diagnostic() .set_continue_after_error(sess.opts.debugging_opts.continue_parse_after_error); sess.profiler(|p| p.start_activity("parsing")); let krate = time(sess, "parsing", || match *input { - Input::File(ref file) => parse::parse_crate_from_file(file, &sess.parse_sess), + Input::File(ref file) => parse::parse_crate_from_file( + file, + &sess.parse_sess, + ), Input::Str { ref input, ref name, - } => parse::parse_crate_from_source_str(name.clone(), input.clone(), &sess.parse_sess), + } => parse::parse_crate_from_source_str( + name.clone(), + input.clone(), + &sess.parse_sess, + interp_user_fn, + ), })?; sess.profiler(|p| p.end_activity("parsing")); @@ -112,7 +125,7 @@ declare_box_region_type!( /// harness if one is to be provided, injection of a dependency on the /// standard library and prelude, and name resolution. /// -/// Returns `None` if we're aborting after handling -W help. +/// Returns `None` if we're aborting after handling `-W help`. pub fn configure_and_expand( sess: Lrc, cstore: Lrc, @@ -236,7 +249,7 @@ pub fn register_plugins<'a>( sess.edition(), &sess.opts.debugging_opts.allow_features, ); - // these need to be set "early" so that expansion sees `quote` if enabled. + // These need to be set "early" so that expansion sees `quote` if enabled. sess.init_features(features); let crate_types = util::collect_crate_types(sess, &krate.attrs); @@ -354,13 +367,13 @@ fn configure_and_expand_inner<'a>( &mut krate, &mut resolver, plugin_info.syntax_exts, sess.edition() ); - // Expand all macros + // Expand all macros. sess.profiler(|p| p.start_activity("macro expansion")); krate = time(sess, "expansion", || { - // Windows dlls do not have rpaths, so they don't know how to find their + // Windows DLLs do not have rpaths, so they don't know how to find their // dependencies. It's up to us to tell the system where to find all the - // dependent dlls. Note that this uses cfg!(windows) as opposed to - // targ_cfg because syntax extensions are always loaded for the host + // dependent DLLs. Note that this uses `cfg!(windows)` as opposed to + // `targ_cfg` because syntax extensions are always loaded for the host // compiler, not for the target. // // This is somewhat of an inherently racy operation, however, as @@ -389,7 +402,7 @@ fn configure_and_expand_inner<'a>( ); } - // Create the config for macro expansion + // Create the config for macro expansion. let features = sess.features_untracked(); let cfg = syntax::ext::expand::ExpansionConfig { features: Some(&features), @@ -406,7 +419,7 @@ fn configure_and_expand_inner<'a>( ecx.monotonic_expander().expand_crate(krate) }); - // The rest is error reporting + // The rest is error reporting. time(sess, "check unused macros", || { ecx.check_unused_macros(); @@ -447,7 +460,7 @@ fn configure_and_expand_inner<'a>( }); // If we're actually rustdoc then there's no need to actually compile - // anything, so switch everything to just looping + // anything, so switch everything to just looping. if sess.opts.actually_rustdoc { util::ReplaceBodyWithLoop::new(sess).visit_crate(&mut krate); } @@ -582,7 +595,7 @@ fn generated_output_paths( out_filenames.push(p); }, OutputType::DepInfo if sess.opts.debugging_opts.dep_info_omit_d_target => { - // Don't add the dep-info output when omitting it from dep-info targets + // Don't add the dep-info output when omitting it from dep-info targets. } _ => { out_filenames.push(file); @@ -640,7 +653,7 @@ fn escape_dep_filename(filename: &FileName) -> String { fn write_out_deps(compiler: &Compiler, outputs: &OutputFilenames, out_filenames: &[PathBuf]) { let sess = &compiler.sess; - // Write out dependency rules to the dep-info file if requested + // Write out dependency rules to the dep-info file if requested. if !sess.opts.output_types.contains_key(&OutputType::DepInfo) { return; } @@ -648,7 +661,7 @@ fn write_out_deps(compiler: &Compiler, outputs: &OutputFilenames, out_filenames: let result = (|| -> io::Result<()> { // Build a list of files used to compile the output and - // write Makefile-compatible dependency rules + // write Makefile-compatible dependency rules. let mut files: Vec = sess.source_map() .files() .iter() @@ -680,7 +693,7 @@ fn write_out_deps(compiler: &Compiler, outputs: &OutputFilenames, out_filenames: // Emit a fake target for each input file to the compilation. This // prevents `make` from spitting out an error if a file is later - // deleted. For more info see #28735 + // deleted. For more info see #28735. for path in files { writeln!(file, "{}:", path)?; } @@ -710,7 +723,7 @@ pub fn prepare_outputs( krate: &ast::Crate, crate_name: &str ) -> Result { - // FIXME: rustdoc passes &[] instead of &krate.attrs here + // FIXME: rustdoc passes `&[]` instead of `&krate.attrs` here. let outputs = util::build_output_filenames( &compiler.input, &compiler.output_dir, @@ -731,8 +744,7 @@ pub fn prepare_outputs( if sess.opts.will_create_output_file() { if output_contains_path(&output_paths, input_path) { sess.err(&format!( - "the input file \"{}\" would be overwritten by the generated \ - executable", + "the input file \"{}\" would be overwritten by the generated executable", input_path.display() )); return Err(ErrorReported); @@ -740,7 +752,7 @@ pub fn prepare_outputs( if let Some(dir_path) = output_conflicts_with_dir(&output_paths) { sess.err(&format!( "the generated executable for the input file \"{}\" conflicts with the \ - existing directory \"{}\"", + existing directory \"{}\"", input_path.display(), dir_path.display() )); @@ -857,14 +869,14 @@ pub fn create_global_ctxt( hir_map, query_result_on_disk_cache, &crate_name, - &outputs + &outputs, ); global_ctxt = Some(gcx); let gcx = global_ctxt.as_ref().unwrap(); ty::tls::enter_global(gcx, |tcx| { - // Do some initialization of the DepGraph that can only be done with the + // Do some initialization of the `DepGraph` that can only be done with the // tcx available. time(tcx.sess, "dep graph tcx init", || rustc_incremental::dep_graph_tcx_init(tcx)); }); @@ -911,7 +923,7 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> { }); }); - // passes are timed inside typeck + // Passes are timed inside typeck. typeck::check_crate(tcx)?; time(sess, "misc checking 2", || { @@ -925,12 +937,11 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> { }, { time(sess, "liveness checking + intrinsic checking", || { par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| { - // this must run before MIR dump, because + // This must run before MIR dump, because // "not all control paths return a value" is reported here. // - // maybe move the check to a MIR pass? + // FIXME: maybe move the check to a MIR pass? let local_def_id = tcx.hir().local_def_id(module); - tcx.ensure().check_mod_liveness(local_def_id); tcx.ensure().check_mod_intrinsics(local_def_id); }); @@ -1055,8 +1066,7 @@ fn encode_and_write_metadata( (metadata, need_metadata_module) } -/// Runs the codegen backend, after which the AST and analysis can -/// be discarded. +/// Runs the codegen backend, after which the AST and analysis can be discarded. pub fn start_codegen<'tcx>( codegen_backend: &dyn CodegenBackend, tcx: TyCtxt<'tcx>, diff --git a/src/librustc_interface/queries.rs b/src/librustc_interface/queries.rs index cd72dc9453c7e..5dcc9b4117ed4 100644 --- a/src/librustc_interface/queries.rs +++ b/src/librustc_interface/queries.rs @@ -6,15 +6,16 @@ use rustc::session::config::{OutputFilenames, OutputType}; use rustc::util::common::{time, ErrorReported}; use rustc::hir; use rustc::hir::def_id::LOCAL_CRATE; -use rustc::ty::steal::Steal; +use rustc_data_structures::steal::Steal; use rustc::dep_graph::DepGraph; +use syntax::{self, ast}; + use std::cell::{Ref, RefMut, RefCell}; use std::rc::Rc; use std::any::Any; use std::mem; -use syntax::{self, ast}; -/// Represent the result of a query. +/// Represents the result of a query. /// This result can be stolen with the `take` method and returned with the `give` method. pub struct Query { result: RefCell>>, @@ -46,14 +47,14 @@ impl Query { *result = Some(Ok(value)); } - /// Borrows the query result using the RefCell. Panics if the result is stolen. + /// Borrows the query result using the `RefCell`. Panics if the result is stolen. pub fn peek(&self) -> Ref<'_, T> { Ref::map(self.result.borrow(), |r| { r.as_ref().unwrap().as_ref().expect("missing query result") }) } - /// Mutably borrows the query result using the RefCell. Panics if the result is stolen. + /// Mutably borrows the query result using the `RefCell`. Panics if the result is stolen. pub fn peek_mut(&self) -> RefMut<'_, T> { RefMut::map(self.result.borrow_mut(), |r| { r.as_mut().unwrap().as_mut().expect("missing query result") @@ -97,11 +98,18 @@ impl Compiler { pub fn parse(&self) -> Result<&Query> { self.queries.parse.compute(|| { - passes::parse(self.session(), &self.input).map_err( + passes::parse( + self.session(), + &self.input, + // This is only run once, so the steal here is fine (unfortunately, the current + // architechture of rustc_interface is not great at exposing the nature of these + // queries as "linear"). + self.interp_user_fn.as_ref().map(|s| s.steal()), + ).map_err( |mut parse_error| { parse_error.emit(); ErrorReported - }, + } ) }) } @@ -221,7 +229,8 @@ impl Compiler { expansion.defs.steal(), expansion.resolutions.steal(), outputs, - &crate_name)) + &crate_name, + )) }) } @@ -231,7 +240,7 @@ impl Compiler { self.global_ctxt()?.peek_mut().enter(|tcx| { tcx.analysis(LOCAL_CRATE).ok(); - // Don't do code generation if there were any errors + // Don't do code generation if there were any errors. self.session().compile_status()?; Ok(passes::start_codegen( @@ -276,12 +285,12 @@ impl Compiler { self.global_ctxt()?; - // Drop AST after creating GlobalCtxt to free memory. + // Drop AST after creating `GlobalCtxt` to free memory. mem::drop(self.expansion()?.take()); self.ongoing_codegen()?; - // Drop GlobalCtxt after starting codegen to free memory. + // Drop `GlobalCtxt` after starting codegen to free memory. mem::drop(self.global_ctxt()?.take()); self.link().map(|_| ()) diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 7e06729c2c742..6c5a44e13f224 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -1,10 +1,11 @@ use crate::{build, shim}; + use rustc_index::vec::IndexVec; +use rustc_data_structures::steal::Steal; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc::mir::{Body, MirPhase, Promoted}; use rustc::ty::{TyCtxt, InstanceDef}; use rustc::ty::query::Providers; -use rustc::ty::steal::Steal; use rustc::hir; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::util::nodemap::DefIdSet; diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 023952042e6d4..d3c572c3a4f41 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -905,7 +905,27 @@ pub enum MacStmtStyle { NoBraces, } -/// Local represents a `let` statement, e.g., `let : = ;`. +/// The interpreter state of a `Local`. +#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug)] +pub enum LocalInterpState { + /// The local was not uninitialized in any previous evaluation session. + Uninitialized, + /// The local was set to a value in a previous evaluation session. + Set, + /// The local was moved during a previous evaluation session. + Moved, +} + +/// Extra information about a `Local` for the interpreter. +#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug)] +pub struct LocalInterpTag { + /// The ID of this local, for use by the interpreter. + pub id: usize, + /// The state of the local. + pub state: LocalInterpState, +} + +/// Represents a `let` statement, e.g., `let : = ;`. #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct Local { pub id: NodeId, @@ -915,6 +935,8 @@ pub struct Local { pub init: Option>, pub span: Span, pub attrs: ThinVec, + /// In interpreter mode, extra information about the local. + pub interp_tag: Option, } /// An arm of a 'match'. diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 8c5289671c98e..9d826d1070251 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -190,6 +190,7 @@ impl<'a> ExtCtxt<'a> { id: ast::DUMMY_NODE_ID, span: sp, attrs: ThinVec::new(), + interp_tag: None, }); ast::Stmt { id: ast::DUMMY_NODE_ID, @@ -207,6 +208,7 @@ impl<'a> ExtCtxt<'a> { id: ast::DUMMY_NODE_ID, span, attrs: ThinVec::new(), + interp_tag: None, }); ast::Stmt { id: ast::DUMMY_NODE_ID, diff --git a/src/libsyntax/feature_gate/builtin_attrs.rs b/src/libsyntax/feature_gate/builtin_attrs.rs index d14afc6deaa69..eb8175143ee00 100644 --- a/src/libsyntax/feature_gate/builtin_attrs.rs +++ b/src/libsyntax/feature_gate/builtin_attrs.rs @@ -337,7 +337,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ gated!(allow_internal_unsafe, Normal, template!(Word), EXPLAIN_ALLOW_INTERNAL_UNSAFE), // ========================================================================== - // Internal attributes: Type system related: + // Internal attributes: Type system: // ========================================================================== gated!(fundamental, Whitelisted, template!(Word), experimental!(fundamental)), @@ -352,7 +352,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // ========================================================================== - // Internal attributes: Runtime related: + // Internal attributes: Runtime: // ========================================================================== rustc_attr!(rustc_allocator, Whitelisted, template!(Word), IMPL_DETAIL), @@ -389,7 +389,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // ========================================================================== - // Internal attributes, Linkage: + // Internal attributes: Linkage: // ========================================================================== gated!( @@ -399,7 +399,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(rustc_std_internal_symbol, Whitelisted, template!(Word), INTERAL_UNSTABLE), // ========================================================================== - // Internal attributes, Macro related: + // Internal attributes: Macros: // ========================================================================== rustc_attr!(rustc_builtin_macro, Whitelisted, template!(Word), IMPL_DETAIL), @@ -411,7 +411,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // ========================================================================== - // Internal attributes, Diagnostics related: + // Internal attributes: Diagnostics: // ========================================================================== gated!( @@ -427,7 +427,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(rustc_conversion_suggestion, Whitelisted, template!(Word), INTERAL_UNSTABLE), // ========================================================================== - // Internal attributes, Const related: + // Internal attributes: Const: // ========================================================================== rustc_attr!(rustc_promotable, Whitelisted, template!(Word), IMPL_DETAIL), @@ -435,7 +435,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(rustc_args_required_const, Whitelisted, template!(List: "N"), INTERAL_UNSTABLE), // ========================================================================== - // Internal attributes, Layout related: + // Internal attributes: Layout related: // ========================================================================== rustc_attr!( @@ -455,7 +455,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // ========================================================================== - // Internal attributes, Misc: + // Internal attributes: Miscellaneous: // ========================================================================== gated!( lang, Normal, template!(NameValueStr: "name"), lang_items, @@ -489,7 +489,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), gated!( rustc_paren_sugar, Normal, template!(Word), unboxed_closures, - "unboxed_closures are still evolving", + "the `unboxed_closures` feature is still evolving", ), rustc_attr!( rustc_inherit_overflow_checks, Whitelisted, template!(Word), @@ -505,9 +505,14 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_test_marker, Normal, template!(Word), "the `#[rustc_test_marker]` attribute is used internally to track tests", ), + rustc_attr!( + // Used by interpreters: + rustc_interp_user_fn, Normal, template!(Word), + "`#[rustc_interp_user_fn]` is for use by interpreters only" + ), // ========================================================================== - // Internal attributes, Testing: + // Internal attributes: Testing: // ========================================================================== rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)), @@ -545,7 +550,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(TEST, rustc_dump_program_clauses, Whitelisted, template!(Word)), rustc_attr!(TEST, rustc_dump_env_program_clauses, Whitelisted, template!(Word)), rustc_attr!(TEST, rustc_object_lifetime_default, Whitelisted, template!(Word)), - rustc_attr!(TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/)), + rustc_attr!(TEST, rustc_dummy, Normal, template!(Word /* doesn't matter */)), gated!( omit_gdb_pretty_printer_section, Whitelisted, template!(Word), "the `#[omit_gdb_pretty_printer_section]` attribute is just used for the Rust test suite", diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs index 3923b9f297b9f..841449d6e4648 100644 --- a/src/libsyntax/mut_visit.rs +++ b/src/libsyntax/mut_visit.rs @@ -540,7 +540,7 @@ pub fn noop_visit_parenthesized_parameter_data(args: &mut Parenth } pub fn noop_visit_local(local: &mut P, vis: &mut T) { - let Local { id, pat, ty, init, span, attrs } = local.deref_mut(); + let Local { id, pat, ty, init, span, attrs, interp_tag: _ } = local.deref_mut(); vis.visit_id(id); vis.visit_pat(pat); visit_opt(ty, |ty| vis.visit_ty(ty)); diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs index ec5d00e0952d7..247ee9b5f5673 100644 --- a/src/libsyntax/parse/diagnostics.rs +++ b/src/libsyntax/parse/diagnostics.rs @@ -1087,7 +1087,9 @@ impl<'a> Parser<'a> { /// statement. This is something of a best-effort heuristic. /// /// We terminate when we find an unmatched `}` (without consuming it). - crate fn recover_stmt(&mut self) { + // + // NOTE: needs to be `pub` for interpreter. + pub fn recover_stmt(&mut self) { self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore) } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index fa4c10431228a..e39ce36a885cc 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -170,25 +170,40 @@ pub enum DirectoryOwnership { // uses a HOF to parse anything, and includes file and // `source_str`. -pub fn parse_crate_from_file<'a>(input: &Path, sess: &'a ParseSess) -> PResult<'a, ast::Crate> { - let mut parser = new_parser_from_file(sess, input); - parser.parse_crate_mod() +pub fn parse_crate_from_file<'a>( + input: &Path, + sess: &'a ParseSess, +) -> PResult<'a, ast::Crate> { + new_parser_from_file(sess, input) + .parse_crate_mod() } -pub fn parse_crate_attrs_from_file<'a>(input: &Path, sess: &'a ParseSess) - -> PResult<'a, Vec> { - let mut parser = new_parser_from_file(sess, input); - parser.parse_inner_attributes() +pub fn parse_crate_attrs_from_file<'a>( + input: &Path, + sess: &'a ParseSess, +) -> PResult<'a, Vec> { + new_parser_from_file(sess, input) + .parse_inner_attributes() } -pub fn parse_crate_from_source_str(name: FileName, source: String, sess: &ParseSess) - -> PResult<'_, ast::Crate> { - new_parser_from_source_str(sess, name, source).parse_crate_mod() +pub fn parse_crate_from_source_str<'a>( + name: FileName, + source: String, + sess: &'a ParseSess, + interp_user_fn: Option, +) -> PResult<'a, ast::Crate> { + new_parser_from_source_str(sess, name, source) + .set_interp_user_fn(interp_user_fn) + .parse_crate_mod() } -pub fn parse_crate_attrs_from_source_str(name: FileName, source: String, sess: &ParseSess) - -> PResult<'_, Vec> { - new_parser_from_source_str(sess, name, source).parse_inner_attributes() +pub fn parse_crate_attrs_from_source_str<'a>( + name: FileName, + source: String, + sess: &'a ParseSess, +) -> PResult<'a, Vec> { + new_parser_from_source_str(sess, name, source) + .parse_inner_attributes() } pub fn parse_stream_from_source_str( diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 2fb6f197dad7c..7c2d62c4ea082 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -107,6 +107,15 @@ enum PrevTokenKind { // NOTE: `Ident`s are handled by `common.rs`. +/// In interpreter mode, information about the user fn (where the user-input code goes). +#[derive(Clone)] +pub struct InterpUserFn { + /// The placeholder name. The parser looks for this as a single ident within a block. + pub placeholder: Symbol, + /// The body to insert in place of the placeholder ident. + pub body: Option>, +} + #[derive(Clone)] pub struct Parser<'a> { pub sess: &'a ParseSess, @@ -151,6 +160,8 @@ pub struct Parser<'a> { crate last_type_ascription: Option<(Span, bool /* likely path typo */)>, /// If present, this `Parser` is not parsing Rust code but rather a macro call. crate subparser_name: Option<&'static str>, + /// In interpreter mode, the user fn to use. + pub interp_user_fn: Option, } impl<'a> Drop for Parser<'a> { @@ -368,6 +379,7 @@ impl<'a> Parser<'a> { last_unexpected_token_span: None, last_type_ascription: None, subparser_name, + interp_user_fn: None, }; parser.token = parser.next_tok(); @@ -387,6 +399,11 @@ impl<'a> Parser<'a> { parser } + pub fn set_interp_user_fn(&mut self, interp_user_fn: Option) -> &mut Self { + self.interp_user_fn = interp_user_fn; + self + } + fn next_tok(&mut self) -> Token { let mut next = if self.desugar_doc_comments { self.token_cursor.next_desugared() diff --git a/src/libsyntax/parse/parser/expr.rs b/src/libsyntax/parse/parser/expr.rs index 23674ad589dc5..6d6474a3043e7 100644 --- a/src/libsyntax/parse/parser/expr.rs +++ b/src/libsyntax/parse/parser/expr.rs @@ -5,7 +5,7 @@ use super::{ use super::pat::{GateOr, PARAM_EXPECTED}; use crate::ast::{ - self, DUMMY_NODE_ID, Attribute, AttrStyle, Ident, CaptureBy, BlockCheckMode, + self, DUMMY_NODE_ID, Attribute, AttrStyle, Ident, CaptureBy, Block, BlockCheckMode, Expr, ExprKind, RangeLimits, Label, Movability, IsAsync, Arm, Ty, TyKind, FunctionRetTy, Param, FnDecl, BinOpKind, BinOp, UnOp, Mac, AnonConst, Field, }; @@ -631,13 +631,13 @@ impl<'a> Parser<'a> { let mut e = e0; let mut hi; loop { - // expr? + // `expr?` while self.eat(&token::Question) { let hi = self.prev_span; e = self.mk_expr(lo.to(hi), ExprKind::Try(e), ThinVec::new()); } - // expr.f + // `expr.f` if self.eat(&token::Dot) { match self.token.kind { token::Ident(..) => { @@ -691,7 +691,7 @@ impl<'a> Parser<'a> { } if self.expr_is_complete(&e) { break; } match self.token.kind { - // expr(...) + // `expr(...)` token::OpenDelim(token::Paren) => { let seq = self.parse_paren_expr_seq().map(|es| { let nd = self.mk_call(e, es); @@ -701,7 +701,7 @@ impl<'a> Parser<'a> { e = self.recover_seq_parse_error(token::Paren, lo, seq); } - // expr[...] + // `expr[...]` // Could be either an index expression or a slicing expression. token::OpenDelim(token::Bracket) => { self.bump(); @@ -1100,10 +1100,42 @@ impl<'a> Parser<'a> { let mut attrs = outer_attrs; attrs.extend(self.parse_inner_attributes()?); - let blk = self.parse_block_tail(lo, blk_mode)?; + let blk = if let Some(interp_blk) = self.parse_interp_user_fn_block()? { + interp_blk + } else { + self.parse_block_tail(lo, blk_mode)? + }; Ok(self.mk_expr(blk.span, ExprKind::Block(blk, opt_label), attrs)) } + /// In interpreter mode, parses the placeholder for the user fn body as the user-provided + /// block (not from the source). + /// Outside of interpreter mode, simply returns `Ok(None)`. + fn parse_interp_user_fn_block(&mut self) -> PResult<'a, Option>> { + if let Some(placeholder) = self.interp_user_fn.as_ref().map(|f| f.placeholder) { + if let token::Ident(name, _) = self.token.kind { + if name == placeholder { + // We are in interpreter mode, and have found the block to use as the + // "user fn" body. Insert the given body for the user fn instead of + // parsing one. + + self.bump(); + self.expect(&token::CloseDelim(token::Brace))?; + + let blk = self.interp_user_fn.as_mut().and_then(|f| f.body.take()) + .ok_or_else(|| { + self.diagnostic().struct_span_err(self.token.span, &format!( + "encountered interpreter user fn placeholder `{}` more than once", + placeholder + )) + })?; + return Ok(Some(blk)); + } + } + } + Ok(None) + } + /// Parses a closure expression (e.g., `move |args| expr`). fn parse_closure_expr(&mut self, attrs: ThinVec) -> PResult<'a, P> { let lo = self.token.span; diff --git a/src/libsyntax/parse/parser/stmt.rs b/src/libsyntax/parse/parser/stmt.rs index 855b03ddd6f6b..cd936d3168fd8 100644 --- a/src/libsyntax/parse/parser/stmt.rs +++ b/src/libsyntax/parse/parser/stmt.rs @@ -275,6 +275,7 @@ impl<'a> Parser<'a> { id: DUMMY_NODE_ID, span: lo.to(hi), attrs, + interp_tag: None, })) } @@ -422,7 +423,9 @@ impl<'a> Parser<'a> { } /// Parses a statement, including the trailing semicolon. - crate fn parse_full_stmt(&mut self, macro_legacy_warnings: bool) -> PResult<'a, Option> { + // + // NOTE: needs to be `pub` for interpreter. + pub fn parse_full_stmt(&mut self, macro_legacy_warnings: bool) -> PResult<'a, Option> { // Skip looking for a trailing semicolon when we have an interpolated statement. maybe_whole!(self, NtStmt, |x| Some(x)); diff --git a/src/libsyntax_ext/deriving/debug.rs b/src/libsyntax_ext/deriving/debug.rs index 003c2423576eb..0b7eda7cce713 100644 --- a/src/libsyntax_ext/deriving/debug.rs +++ b/src/libsyntax_ext/deriving/debug.rs @@ -128,6 +128,7 @@ fn stmt_let_undescore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P) -> ast id: ast::DUMMY_NODE_ID, span: sp, attrs: ThinVec::new(), + interp_tag: None, }); ast::Stmt { id: ast::DUMMY_NODE_ID, diff --git a/src/libsyntax_pos/span_encoding.rs b/src/libsyntax_pos/span_encoding.rs index 525ec13623289..f9f22302e36e8 100644 --- a/src/libsyntax_pos/span_encoding.rs +++ b/src/libsyntax_pos/span_encoding.rs @@ -68,7 +68,7 @@ const LEN_TAG: u16 = 0b1000_0000_0000_0000; const MAX_LEN: u32 = 0b0111_1111_1111_1111; const MAX_CTXT: u32 = 0b1111_1111_1111_1111; -/// Dummy span, both position and length are zero, syntax context is zero as well. +/// Dummy span: both position and length are zero; syntax context is zero (empty) as well. pub const DUMMY_SP: Span = Span { base_or_index: 0, len_or_tag: 0, ctxt_or_zero: 0 }; impl Span { diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 1769135e7f21a..4e14596ff9b63 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -101,7 +101,7 @@ symbols! { } // Symbols that can be referred to with syntax_pos::sym::*. The symbol is - // the stringified identifier unless otherwise specified (e.g. + // the stringified identifier unless otherwise specified (e.g., // `proc_dash_macro` represents "proc-macro"). // // As well as the symbols listed, there are symbols for the the strings @@ -581,6 +581,7 @@ symbols! { rustc_expected_cgu_reuse, rustc_if_this_changed, rustc_inherit_overflow_checks, + rustc_interp_user_fn, rustc_layout, rustc_layout_scalar_valid_range_end, rustc_layout_scalar_valid_range_start, diff --git a/src/test/ui/interp-user-fn.rs b/src/test/ui/interp-user-fn.rs new file mode 100644 index 0000000000000..a9a1b28c0ea45 --- /dev/null +++ b/src/test/ui/interp-user-fn.rs @@ -0,0 +1,7 @@ +#![feature(stmt_expr_attributes)] + +fn main() { + let user_body = #[rustc_interp_user_fn] || {}; + //~^ ERROR `#[rustc_interp_user_fn]` is for use by interpreters only [E0658] + user_body(); +} diff --git a/src/test/ui/interp-user-fn.stderr b/src/test/ui/interp-user-fn.stderr new file mode 100644 index 0000000000000..bb2275fd1fba3 --- /dev/null +++ b/src/test/ui/interp-user-fn.stderr @@ -0,0 +1,12 @@ +error[E0658]: `#[rustc_interp_user_fn]` is for use by interpreters only + --> $DIR/interp-user-fn.rs:4:21 + | +LL | let user_body = #[rustc_interp_user_fn] || {}; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: for more information, see https://github.com/rust-lang/rust/issues/29642 + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`.