diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index bacdad25c50b3..e81f0133e3b6b 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -18,7 +18,7 @@ use rustc_data_structures::sync; use rustc_metadata::{DylibError, EncodedMetadata, load_symbol_from_dylib}; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::{CurrentGcx, TyCtxt}; -use rustc_query_impl::collect_active_jobs_from_all_queries; +use rustc_query_impl::{CollectActiveJobsKind, collect_active_jobs_from_all_queries}; use rustc_session::config::{ Cfg, CrateType, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple, }; @@ -253,9 +253,11 @@ internal compiler error: query cycle handler thread panicked, aborting process"; unsafe { &*(session_globals as *const SessionGlobals) }, || { // Ensure there were no errors collecting all active jobs. - // We need the complete map to ensure we find a cycle to break. - collect_active_jobs_from_all_queries(tcx, false).expect( - "failed to collect active queries in deadlock handler", + // We need the complete map to ensure we find a cycle to + // break. + collect_active_jobs_from_all_queries( + tcx, + CollectActiveJobsKind::FullNoContention, ) }, ); diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 9c2907cfacf72..752c2220d4147 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -516,7 +516,7 @@ pub trait LintContext { /// /// [`emit_lint_base`]: rustc_middle::lint::emit_lint_base#decorate-signature #[track_caller] - fn opt_span_diag_lint>( + fn opt_span_lint>( &self, lint: &'static Lint, span: Option, @@ -532,13 +532,7 @@ pub trait LintContext { span: S, decorator: impl for<'a> Diagnostic<'a, ()>, ) { - self.opt_span_diag_lint(lint, Some(span), decorator); - } - - /// Emit a lint from a lint struct (some type that implements `Diagnostic`, typically - /// generated by `#[derive(Diagnostic)]`). - fn emit_diag_lint(&self, lint: &'static Lint, decorator: impl for<'a> Diagnostic<'a, ()>) { - self.opt_span_diag_lint(lint, None as Option, decorator); + self.opt_span_lint(lint, Some(span), decorator); } /// This returns the lint level for the given lint at the current location. @@ -594,7 +588,7 @@ impl<'tcx> LintContext for LateContext<'tcx> { self.tcx.sess } - fn opt_span_diag_lint>( + fn opt_span_lint>( &self, lint: &'static Lint, span: Option, @@ -619,13 +613,13 @@ impl LintContext for EarlyContext<'_> { self.builder.sess() } - fn opt_span_diag_lint>( + fn opt_span_lint>( &self, lint: &'static Lint, span: Option, decorator: impl for<'a> Diagnostic<'a, ()>, ) { - self.builder.opt_span_diag_lint(lint, span.map(|s| s.into()), decorator) + self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorator) } fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource { diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 92232c7d230bc..c0eca3b5197b6 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -39,7 +39,7 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> { let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint; match diagnostic { DecorateDiagCompat::Builtin(b) => { - self.context.opt_span_diag_lint( + self.context.opt_span_lint( lint_id.lint, span, DecorateBuiltinLint { @@ -50,7 +50,7 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> { ); } DecorateDiagCompat::Dynamic(d) => { - self.context.opt_span_diag_lint(lint_id.lint, span, d); + self.context.opt_span_lint(lint_id.lint, span, d); } } } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 5536bfc0176ab..2b859b65c9f8f 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1001,7 +1001,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { /// Used to emit a lint-related diagnostic based on the current state of /// this lint context. #[track_caller] - pub(crate) fn opt_span_diag_lint( + pub(crate) fn opt_span_lint( &self, lint: &'static Lint, span: Option, diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index ed5951fe1c034..a0c5b38d01362 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -143,7 +143,6 @@ struct QueryModifiers { anon: Option, arena_cache: Option, cache_on_disk_if: Option, - cycle_delay_bug: Option, depth_limit: Option, desc: Desc, eval_always: Option, @@ -157,7 +156,6 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { let mut arena_cache = None; let mut cache_on_disk_if = None; let mut desc = None; - let mut cycle_delay_bug = None; let mut no_hash = None; let mut anon = None; let mut eval_always = None; @@ -191,8 +189,6 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { try_insert!(cache_on_disk_if = CacheOnDiskIf { modifier, block }); } else if modifier == "arena_cache" { try_insert!(arena_cache = modifier); - } else if modifier == "cycle_delay_bug" { - try_insert!(cycle_delay_bug = modifier); } else if modifier == "no_hash" { try_insert!(no_hash = modifier); } else if modifier == "anon" { @@ -216,7 +212,6 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { arena_cache, cache_on_disk_if, desc, - cycle_delay_bug, no_hash, anon, eval_always, @@ -251,7 +246,6 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { anon, arena_cache, cache_on_disk_if, - cycle_delay_bug, depth_limit, desc: _, eval_always, @@ -264,13 +258,6 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { let anon = anon.is_some(); let arena_cache = arena_cache.is_some(); let cache_on_disk = cache_on_disk_if.is_some(); - - let cycle_error_handling = if cycle_delay_bug.is_some() { - quote! { DelayBug } - } else { - quote! { Error } - }; - let depth_limit = depth_limit.is_some(); let eval_always = eval_always.is_some(); let feedable = feedable.is_some(); @@ -289,7 +276,6 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { anon: #anon, arena_cache: #arena_cache, cache_on_disk: #cache_on_disk, - cycle_error_handling: #cycle_error_handling, depth_limit: #depth_limit, eval_always: #eval_always, feedable: #feedable, @@ -402,7 +388,6 @@ fn add_to_analyzer_stream(query: &Query, analyzer_stream: &mut proc_macro2::Toke doc_link!( arena_cache, - cycle_delay_bug, no_hash, anon, eval_always, diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index d4d9b9c0189d9..6c30762725b77 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -564,13 +564,13 @@ rustc_queries! { } /// Checks whether a type is representable or infinitely sized - query check_representability(key: LocalDefId) -> rustc_middle::ty::Representability { + // + // Infinitely sized types will cause a cycle. The `value_from_cycle_error` impl will print + // a custom error about the infinite size and then abort compilation. (In the past we + // recovered and continued, but in practice that leads to confusing subsequent error + // messages about cycles that then abort.) + query check_representability(key: LocalDefId) { desc { "checking if `{}` is representable", tcx.def_path_str(key) } - // Infinitely sized types will cause a cycle. The custom `FromCycleError` impl for - // `Representability` will print a custom error about the infinite size and then abort - // compilation. (In the past we recovered and continued, but in practice that leads to - // confusing subsequent error messages about cycles that then abort.) - cycle_delay_bug // We don't want recursive representability calls to be forced with // incremental compilation because, if a cycle occurs, we need the // entire cycle to be in memory for diagnostics. This means we can't @@ -580,9 +580,8 @@ rustc_queries! { /// An implementation detail for the `check_representability` query. See that query for more /// details, particularly on the modifiers. - query check_representability_adt_ty(key: Ty<'tcx>) -> rustc_middle::ty::Representability { + query check_representability_adt_ty(key: Ty<'tcx>) { desc { "checking if `{}` is representable", key } - cycle_delay_bug anon } @@ -1027,7 +1026,6 @@ rustc_queries! { desc { "computing the variances of `{}`", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } separate_provide_extern - cycle_delay_bug } /// Gets a map with the inferred outlives-predicates of every item in the local crate. @@ -1160,7 +1158,6 @@ rustc_queries! { desc { "computing function signature of `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern - cycle_delay_bug } /// Performs lint checking for the module. @@ -1751,8 +1748,6 @@ rustc_queries! { ) -> Result, &'tcx ty::layout::LayoutError<'tcx>> { depth_limit desc { "computing layout of `{}`", key.value } - // we emit our own error during query cycle handling - cycle_delay_bug } /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 65c6b7551bc63..d15e0b2a122b9 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -383,7 +383,6 @@ impl_erasable_for_simple_types! { rustc_middle::ty::Destructor, rustc_middle::ty::fast_reject::SimplifiedType, rustc_middle::ty::ImplPolarity, - rustc_middle::ty::Representability, rustc_middle::ty::UnusedGenericParams, rustc_middle::ty::util::AlwaysRequiresDrop, rustc_middle::ty::Visibility, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 9876441f5218d..b8fd88f2af17d 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -4,8 +4,8 @@ pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCac pub use self::job::{QueryInfo, QueryJob, QueryJobId, QueryLatch, QueryWaiter}; pub use self::keys::{AsLocalQueryKey, LocalCrate, QueryKey}; pub use self::plumbing::{ - ActiveKeyStatus, CycleError, CycleErrorHandling, EnsureMode, IntoQueryParam, QueryMode, - QueryState, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk, TyCtxtEnsureResult, + ActiveKeyStatus, CycleError, EnsureMode, IntoQueryParam, QueryMode, QueryState, TyCtxtAt, + TyCtxtEnsureDone, TyCtxtEnsureOk, TyCtxtEnsureResult, }; pub use self::stack::QueryStackFrame; pub use crate::queries::Providers; diff --git a/compiler/rustc_middle/src/query/modifiers.rs b/compiler/rustc_middle/src/query/modifiers.rs index 100d1ac527693..ad9b9de119d55 100644 --- a/compiler/rustc_middle/src/query/modifiers.rs +++ b/compiler/rustc_middle/src/query/modifiers.rs @@ -29,11 +29,6 @@ pub(crate) struct arena_cache; /// identifier is available for use within the block, as is `tcx`. pub(crate) struct cache_on_disk_if; -/// # `cycle_delay_bug` query modifier -/// -/// If a dependency cycle is detected, emit a delayed bug instead of a normal error. -pub(crate) struct cycle_delay_bug; - /// # `depth_limit` query modifier /// /// Impose a recursion call depth limit on the query to prevent stack overflow. diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index a85412158830b..f388898465246 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -5,9 +5,10 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::hash_table::HashTable; use rustc_data_structures::sharded::Sharded; use rustc_data_structures::sync::{AtomicU64, WorkerLocal}; +use rustc_errors::Diag; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::hir_id::OwnerId; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::Span; pub use sealed::IntoQueryParam; use crate::dep_graph::{DepKind, DepNodeIndex, SerializedDepNodeIndex}; @@ -49,16 +50,6 @@ pub enum ActiveKeyStatus<'tcx> { Poisoned, } -/// How a particular query deals with query cycle errors. -/// -/// Inspected by the code that actually handles cycle errors, to decide what -/// approach to use. -#[derive(Copy, Clone)] -pub enum CycleErrorHandling { - Error, - DelayBug, -} - #[derive(Clone, Debug)] pub struct CycleError<'tcx> { /// The query and related span that uses the cycle. @@ -98,8 +89,6 @@ pub struct QueryVTable<'tcx, C: QueryCache> { pub feedable: bool, pub dep_kind: DepKind, - /// How this query deals with query cycle errors. - pub cycle_error_handling: CycleErrorHandling, pub state: QueryState<'tcx, C::Key>, pub cache: C, @@ -127,12 +116,16 @@ pub struct QueryVTable<'tcx, C: QueryCache> { /// For `no_hash` queries, this function pointer is None. pub hash_value_fn: Option, &C::Value) -> Fingerprint>, + /// Function pointer that handles a cycle error. `error` must be consumed, e.g. with `emit` (if + /// it should be emitted) or `delay_as_bug` (if it need not be emitted because an alternative + /// error is created and emitted). pub value_from_cycle_error: fn( tcx: TyCtxt<'tcx>, key: C::Key, cycle_error: CycleError<'tcx>, - guar: ErrorGuaranteed, + error: Diag<'_>, ) -> C::Value, + pub format_value: fn(&C::Value) -> String, pub create_tagged_key: fn(C::Key) -> TaggedQueryKey<'tcx>, @@ -295,7 +288,6 @@ macro_rules! define_callbacks { anon: $anon:literal, arena_cache: $arena_cache:literal, cache_on_disk: $cache_on_disk:literal, - cycle_error_handling: $cycle_error_handling:ident, depth_limit: $depth_limit:literal, eval_always: $eval_always:literal, feedable: $feedable:literal, diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index ac51e5bd2289a..ae3c45877900b 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -751,8 +751,3 @@ impl<'tcx> AdtDef<'tcx> { if self.is_struct() { tcx.adt_sizedness_constraint((self.did(), sizedness)) } else { None } } } - -/// This type exists just so a `FromCycleError` impl can be made for the `check_representability` -/// query. -#[derive(Clone, Copy, Debug, HashStable)] -pub struct Representability; diff --git a/compiler/rustc_query_impl/src/dep_kind_vtables.rs b/compiler/rustc_query_impl/src/dep_kind_vtables.rs index 3bc2e35b02bcd..34ef70f141b94 100644 --- a/compiler/rustc_query_impl/src/dep_kind_vtables.rs +++ b/compiler/rustc_query_impl/src/dep_kind_vtables.rs @@ -136,7 +136,6 @@ macro_rules! define_dep_kind_vtables { anon: $anon:literal, arena_cache: $arena_cache:literal, cache_on_disk: $cache_on_disk:literal, - cycle_error_handling: $cycle_error_handling:ident, depth_limit: $depth_limit:literal, eval_always: $eval_always:literal, feedable: $feedable:literal, diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 9cb6eababbce8..2b535d64cf706 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -9,12 +9,13 @@ use rustc_errors::FatalError; use rustc_middle::dep_graph::{DepGraphData, DepNodeKey, SerializedDepNodeIndex}; use rustc_middle::query::plumbing::QueryVTable; use rustc_middle::query::{ - ActiveKeyStatus, CycleError, CycleErrorHandling, EnsureMode, QueryCache, QueryJob, QueryJobId, - QueryKey, QueryLatch, QueryMode, QueryState, + ActiveKeyStatus, CycleError, EnsureMode, QueryCache, QueryJob, QueryJobId, QueryKey, + QueryLatch, QueryMode, QueryState, }; use rustc_middle::ty::TyCtxt; use rustc_middle::verify_ich::incremental_verify_ich; use rustc_span::{DUMMY_SP, Span}; +use tracing::warn; use crate::dep_graph::{DepNode, DepNodeIndex}; use crate::for_each_query_vtable; @@ -30,6 +31,21 @@ pub(crate) fn all_inactive<'tcx, K>(state: &QueryState<'tcx, K>) -> bool { state.active.lock_shards().all(|shard| shard.is_empty()) } +#[derive(Clone, Copy)] +pub enum CollectActiveJobsKind { + /// We need the full query job map, and we are willing to wait to obtain the query state + /// shard lock(s). + Full, + + /// We need the full query job map, and we shouldn't need to wait to obtain the shard lock(s), + /// because we are in a place where nothing else could hold the shard lock(s). + FullNoContention, + + /// We can get by without the full query job map, so we won't bother waiting to obtain the + /// shard lock(s) if they're not already unlocked. + PartialAllowed, +} + /// Returns a map of currently active query jobs, collected from all queries. /// /// If `require_complete` is `true`, this function locks all shards of the @@ -42,19 +58,15 @@ pub(crate) fn all_inactive<'tcx, K>(state: &QueryState<'tcx, K>) -> bool { /// complete map is needed and no deadlock is possible at this call site. pub fn collect_active_jobs_from_all_queries<'tcx>( tcx: TyCtxt<'tcx>, - require_complete: bool, -) -> Result, QueryJobMap<'tcx>> { - let mut job_map_out = QueryJobMap::default(); - let mut complete = true; + collect_kind: CollectActiveJobsKind, +) -> QueryJobMap<'tcx> { + let mut job_map = QueryJobMap::default(); for_each_query_vtable!(ALL, tcx, |query| { - let res = gather_active_jobs(query, require_complete, &mut job_map_out); - if res.is_none() { - complete = false; - } + gather_active_jobs(query, collect_kind, &mut job_map); }); - if complete { Ok(job_map_out) } else { Err(job_map_out) } + job_map } /// Internal plumbing for collecting the set of active jobs for this query. @@ -64,59 +76,49 @@ pub fn collect_active_jobs_from_all_queries<'tcx>( /// (We arbitrarily use the word "gather" when collecting the jobs for /// each individual query, so that we have distinct function names to /// grep for.) +/// +/// Aborts if jobs can't be gathered as specified by `collect_kind`. fn gather_active_jobs<'tcx, C>( query: &'tcx QueryVTable<'tcx, C>, - require_complete: bool, - job_map_out: &mut QueryJobMap<'tcx>, // Out-param; job info is gathered into this map -) -> Option<()> -where + collect_kind: CollectActiveJobsKind, + job_map: &mut QueryJobMap<'tcx>, +) where C: QueryCache, QueryVTable<'tcx, C>: DynSync, { - let mut active = Vec::new(); - - // Helper to gather active jobs from a single shard. let mut gather_shard_jobs = |shard: &HashTable<(C::Key, ActiveKeyStatus<'tcx>)>| { - for (k, v) in shard.iter() { - if let ActiveKeyStatus::Started(ref job) = *v { - active.push((*k, job.clone())); + for (key, status) in shard.iter() { + if let ActiveKeyStatus::Started(job) = status { + // This function is safe to call with the shard locked because it is very simple. + let frame = crate::plumbing::create_query_stack_frame(query, *key); + job_map.insert(job.id, QueryJobInfo { frame, job: job.clone() }); } } }; - // Lock shards and gather jobs from each shard. - if require_complete { - for shard in query.state.active.lock_shards() { - gather_shard_jobs(&shard); + match collect_kind { + CollectActiveJobsKind::Full => { + for shard in query.state.active.lock_shards() { + gather_shard_jobs(&shard); + } } - } else { - // We use try_lock_shards here since we are called from the - // deadlock handler, and this shouldn't be locked. - for shard in query.state.active.try_lock_shards() { - // This can be called during unwinding, and the function has a `try_`-prefix, so - // don't `unwrap()` here, just manually check for `None` and do best-effort error - // reporting. - match shard { - None => { - tracing::warn!( - "Failed to collect active jobs for query with name `{}`!", - query.name - ); - return None; + CollectActiveJobsKind::FullNoContention => { + for shard in query.state.active.try_lock_shards() { + match shard { + Some(shard) => gather_shard_jobs(&shard), + None => panic!("Failed to collect active jobs for query `{}`!", query.name), + } + } + } + CollectActiveJobsKind::PartialAllowed => { + for shard in query.state.active.try_lock_shards() { + match shard { + Some(shard) => gather_shard_jobs(&shard), + None => warn!("Failed to collect active jobs for query `{}`!", query.name), } - Some(shard) => gather_shard_jobs(&shard), } } } - - // Call `make_frame` while we're not holding a `state.active` lock as `make_frame` may call - // queries leading to a deadlock. - for (key, job) in active { - let frame = crate::plumbing::create_query_stack_frame(query, key); - job_map_out.insert(job.id, QueryJobInfo { frame, job }); - } - - Some(()) } #[cold] @@ -128,16 +130,7 @@ fn mk_cycle<'tcx, C: QueryCache>( cycle_error: CycleError<'tcx>, ) -> C::Value { let error = report_cycle(tcx, &cycle_error); - match query.cycle_error_handling { - CycleErrorHandling::Error => { - let guar = error.emit(); - (query.value_from_cycle_error)(tcx, key, cycle_error, guar) - } - CycleErrorHandling::DelayBug => { - let guar = error.delay_as_bug(); - (query.value_from_cycle_error)(tcx, key, cycle_error, guar) - } - } + (query.value_from_cycle_error)(tcx, key, cycle_error, error) } /// Guard object representing the responsibility to execute a query job and @@ -223,11 +216,10 @@ fn cycle_error<'tcx, C: QueryCache>( try_execute: QueryJobId, span: Span, ) -> (C::Value, Option) { - // Ensure there was no errors collecting all active jobs. + // Ensure there were no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - let job_map = collect_active_jobs_from_all_queries(tcx, false) - .ok() - .expect("failed to collect active queries"); + let job_map = + collect_active_jobs_from_all_queries(tcx, CollectActiveJobsKind::FullNoContention); let error = find_cycle_in_stack(try_execute, job_map, ¤t_query_job(), span); (mk_cycle(query, tcx, key, error), None) diff --git a/compiler/rustc_query_impl/src/from_cycle_error.rs b/compiler/rustc_query_impl/src/from_cycle_error.rs index 10baaa9209d01..6a3155c02949e 100644 --- a/compiler/rustc_query_impl/src/from_cycle_error.rs +++ b/compiler/rustc_query_impl/src/from_cycle_error.rs @@ -5,14 +5,14 @@ use std::ops::ControlFlow; use rustc_data_structures::fx::FxHashSet; use rustc_errors::codes::*; -use rustc_errors::{Applicability, MultiSpan, pluralize, struct_span_code_err}; +use rustc_errors::{Applicability, Diag, MultiSpan, pluralize, struct_span_code_err}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_middle::dep_graph::DepKind; use rustc_middle::queries::QueryVTables; use rustc_middle::query::CycleError; use rustc_middle::query::erase::erase_val; -use rustc_middle::ty::layout::{LayoutError, TyAndLayout}; +use rustc_middle::ty::layout::LayoutError; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::{DefId, LocalDefId}; @@ -21,41 +21,45 @@ use rustc_span::{ErrorGuaranteed, Span}; use crate::job::report_cycle; pub(crate) fn specialize_query_vtables<'tcx>(vtables: &mut QueryVTables<'tcx>) { - vtables.type_of.value_from_cycle_error = - |tcx, _, _, guar| erase_val(ty::EarlyBinder::bind(Ty::new_error(tcx, guar))); + vtables.type_of.value_from_cycle_error = |tcx, _, _, err| { + let guar = err.emit(); + erase_val(ty::EarlyBinder::bind(Ty::new_error(tcx, guar))) + }; - vtables.type_of_opaque_hir_typeck.value_from_cycle_error = - |tcx, _, _, guar| erase_val(ty::EarlyBinder::bind(Ty::new_error(tcx, guar))); + vtables.type_of_opaque_hir_typeck.value_from_cycle_error = |tcx, _, _, err| { + let guar = err.emit(); + erase_val(ty::EarlyBinder::bind(Ty::new_error(tcx, guar))) + }; - vtables.erase_and_anonymize_regions_ty.value_from_cycle_error = - |tcx, _, _, guar| erase_val(Ty::new_error(tcx, guar)); + vtables.erase_and_anonymize_regions_ty.value_from_cycle_error = |tcx, _, _, err| { + let guar = err.emit(); + erase_val(Ty::new_error(tcx, guar)) + }; - vtables.fn_sig.value_from_cycle_error = |tcx, key, _, guar| erase_val(fn_sig(tcx, key, guar)); + vtables.fn_sig.value_from_cycle_error = |tcx, key, _, err| { + let guar = err.delay_as_bug(); + erase_val(fn_sig(tcx, key, guar)) + }; vtables.check_representability.value_from_cycle_error = - |tcx, _, cycle, guar| check_representability(tcx, cycle, guar); + |tcx, _, cycle, _err| check_representability(tcx, cycle); vtables.check_representability_adt_ty.value_from_cycle_error = - |tcx, _, cycle, guar| check_representability(tcx, cycle, guar); + |tcx, _, cycle, _err| check_representability(tcx, cycle); - vtables.variances_of.value_from_cycle_error = - |tcx, _, cycle, guar| erase_val(variances_of(tcx, cycle, guar)); + vtables.variances_of.value_from_cycle_error = |tcx, _, cycle, err| { + let _guar = err.delay_as_bug(); + erase_val(variances_of(tcx, cycle)) + }; - vtables.layout_of.value_from_cycle_error = - |tcx, _, cycle, guar| erase_val(layout_of(tcx, cycle, guar)); + vtables.layout_of.value_from_cycle_error = |tcx, _, cycle, err| { + let _guar = err.delay_as_bug(); + erase_val(Err(layout_of(tcx, cycle))) + } } -pub(crate) fn default<'tcx>( - tcx: TyCtxt<'tcx>, - cycle_error: CycleError<'tcx>, - query_name: &str, -) -> ! { - let Some(guar) = tcx.sess.dcx().has_errors() else { - bug!( - "`from_cycle_error_default` on query `{query_name}` called without errors: {:#?}", - cycle_error.cycle, - ); - }; +pub(crate) fn default(err: Diag<'_>) -> ! { + let guar = err.emit(); guar.raise_fatal() } @@ -84,11 +88,7 @@ fn fn_sig<'tcx>( ))) } -fn check_representability<'tcx>( - tcx: TyCtxt<'tcx>, - cycle_error: CycleError<'tcx>, - _guar: ErrorGuaranteed, -) -> ! { +fn check_representability<'tcx>(tcx: TyCtxt<'tcx>, cycle_error: CycleError<'tcx>) -> ! { let mut item_and_field_ids = Vec::new(); let mut representable_ids = FxHashSet::default(); for info in &cycle_error.cycle { @@ -120,11 +120,7 @@ fn check_representability<'tcx>( guar.raise_fatal() } -fn variances_of<'tcx>( - tcx: TyCtxt<'tcx>, - cycle_error: CycleError<'tcx>, - _guar: ErrorGuaranteed, -) -> &'tcx [ty::Variance] { +fn variances_of<'tcx>(tcx: TyCtxt<'tcx>, cycle_error: CycleError<'tcx>) -> &'tcx [ty::Variance] { search_for_cycle_permutation( &cycle_error.cycle, |cycle| { @@ -169,8 +165,7 @@ fn search_for_cycle_permutation( fn layout_of<'tcx>( tcx: TyCtxt<'tcx>, cycle_error: CycleError<'tcx>, - _guar: ErrorGuaranteed, -) -> Result, &'tcx ty::layout::LayoutError<'tcx>> { +) -> &'tcx ty::layout::LayoutError<'tcx> { let diag = search_for_cycle_permutation( &cycle_error.cycle, |cycle| { @@ -247,7 +242,7 @@ fn layout_of<'tcx>( ); let guar = diag.emit(); - Err(tcx.arena.alloc(LayoutError::Cycle(guar))) + tcx.arena.alloc(LayoutError::Cycle(guar)) } // item_and_field_ids should form a cycle where each field contains the diff --git a/compiler/rustc_query_impl/src/job.rs b/compiler/rustc_query_impl/src/job.rs index ae32ad01b1578..64ed9b6b51bf2 100644 --- a/compiler/rustc_query_impl/src/job.rs +++ b/compiler/rustc_query_impl/src/job.rs @@ -12,7 +12,7 @@ use rustc_middle::query::{ use rustc_middle::ty::TyCtxt; use rustc_span::{DUMMY_SP, Span}; -use crate::execution::collect_active_jobs_from_all_queries; +use crate::{CollectActiveJobsKind, collect_active_jobs_from_all_queries}; /// Map from query job IDs to job information collected by /// `collect_active_jobs_from_all_queries`. @@ -383,8 +383,7 @@ pub fn print_query_stack<'tcx>( let mut count_total = 0; // Make use of a partial query job map if we fail to take locks collecting active queries. - let job_map: QueryJobMap<'_> = collect_active_jobs_from_all_queries(tcx, false) - .unwrap_or_else(|partial_job_map| partial_job_map); + let job_map = collect_active_jobs_from_all_queries(tcx, CollectActiveJobsKind::PartialAllowed); if let Some(ref mut file) = file { let _ = writeln!(file, "\n\nquery stack during panic:"); diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index aaf9c78feb7f3..44c97d19914c9 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::TyCtxt; use rustc_span::Span; pub use crate::dep_kind_vtables::make_dep_kind_vtables; -pub use crate::execution::collect_active_jobs_from_all_queries; +pub use crate::execution::{CollectActiveJobsKind, collect_active_jobs_from_all_queries}; pub use crate::job::{QueryJobMap, break_query_cycles, print_query_stack}; #[macro_use] diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 0a84a8b23c076..bece2e56c0a86 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -29,11 +29,13 @@ use rustc_span::def_id::LOCAL_CRATE; use crate::error::{QueryOverflow, QueryOverflowNote}; use crate::execution::{all_inactive, force_query}; use crate::job::find_dep_kind_root; -use crate::{GetQueryVTable, collect_active_jobs_from_all_queries, for_each_query_vtable}; +use crate::{ + CollectActiveJobsKind, GetQueryVTable, collect_active_jobs_from_all_queries, + for_each_query_vtable, +}; fn depth_limit_error<'tcx>(tcx: TyCtxt<'tcx>, job: QueryJobId) { - let job_map = - collect_active_jobs_from_all_queries(tcx, true).expect("failed to collect active queries"); + let job_map = collect_active_jobs_from_all_queries(tcx, CollectActiveJobsKind::Full); let (info, depth) = find_dep_kind_root(job, job_map); let suggested_limit = match tcx.recursion_limit() { @@ -295,7 +297,6 @@ macro_rules! define_queries { anon: $anon:literal, arena_cache: $arena_cache:literal, cache_on_disk: $cache_on_disk:literal, - cycle_error_handling: $cycle_error_handling:ident, depth_limit: $depth_limit:literal, eval_always: $eval_always:literal, feedable: $feedable:literal, @@ -409,8 +410,6 @@ macro_rules! define_queries { depth_limit: $depth_limit, feedable: $feedable, dep_kind: dep_graph::DepKind::$name, - cycle_error_handling: - rustc_middle::query::CycleErrorHandling::$cycle_error_handling, state: Default::default(), cache: Default::default(), @@ -446,8 +445,11 @@ macro_rules! define_queries { #[cfg(not($cache_on_disk))] is_loadable_from_disk_fn: |_tcx, _key, _index| false, - value_from_cycle_error: |tcx, _, cycle, _| { - $crate::from_cycle_error::default(tcx, cycle, stringify!($name)) + // The default just emits `err` and then aborts. + // `from_cycle_error::specialize_query_vtables` overwrites this default for + // certain queries. + value_from_cycle_error: |_tcx, _key, _cycle, err| { + $crate::from_cycle_error::default(err) }, #[cfg($no_hash)] diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs index 1814e7604a2d1..cef964ab139ae 100644 --- a/compiler/rustc_ty_utils/src/representability.rs +++ b/compiler/rustc_ty_utils/src/representability.rs @@ -2,7 +2,7 @@ use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Representability, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; pub(crate) fn provide(providers: &mut Providers) { @@ -14,7 +14,7 @@ pub(crate) fn provide(providers: &mut Providers) { }; } -fn check_representability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Representability { +fn check_representability(tcx: TyCtxt<'_>, def_id: LocalDefId) { match tcx.def_kind(def_id) { DefKind::Struct | DefKind::Union | DefKind::Enum => { for variant in tcx.adt_def(def_id).variants() { @@ -28,7 +28,6 @@ fn check_representability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Representabili } def_kind => bug!("unexpected {def_kind:?}"), } - Representability } fn check_representability_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) { @@ -67,7 +66,7 @@ fn check_representability_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) { // Looking at the query cycle above, we know that `Bar` is representable // because `check_representability_adt_ty(Bar<..>)` is in the cycle and // `check_representability(Bar)` is *not* in the cycle. -fn check_representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representability { +fn check_representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) { let ty::Adt(adt, args) = ty.kind() else { bug!("expected adt") }; if let Some(def_id) = adt.did().as_local() { let _ = tcx.check_representability(def_id); @@ -82,7 +81,6 @@ fn check_representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Repre } } } - Representability } fn params_in_repr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> DenseBitSet { diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index fba967c04895a..b2f4277458f1c 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -321,6 +321,10 @@ impl CString { /// assertion is made that `v` contains no 0 bytes, and it requires an /// actual byte vector, not anything that can be converted to one with Into. /// + /// # Safety + /// + /// The caller must ensure `v` contains no nul bytes in its contents. + /// /// # Examples /// /// ``` diff --git a/library/core/src/future/async_drop.rs b/library/core/src/future/async_drop.rs index c48c3f2ba281e..106493fd3e815 100644 --- a/library/core/src/future/async_drop.rs +++ b/library/core/src/future/async_drop.rs @@ -41,6 +41,15 @@ pub trait AsyncDrop { } /// Async drop. +/// +/// # Safety +/// +/// The pointer `_to_drop` must be valid for both reads and writes, +/// not only for the duration of this function call, +/// but also until the returned future has completed. +/// See [ptr::drop_in_place] for additional safety concerns. +/// +/// [ptr::drop_in_place]: crate::ptr::drop_in_place() #[unstable(feature = "async_drop", issue = "126482")] #[lang = "async_drop_in_place"] pub async unsafe fn async_drop_in_place(_to_drop: *mut T) { diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index fce3269b149a0..69a9e04afe1ca 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2718,18 +2718,19 @@ impl<'test> TestCx<'test> { // Wrapper tools set by `runner` might provide extra output on failure, // for example a WebAssembly runtime might print the stack trace of an // `unreachable` instruction by default. - // + let compare_output_by_lines_subset = self.config.runner.is_some(); + // Also, some tests like `ui/parallel-rustc` have non-deterministic // orders of output, so we need to compare by lines. - let compare_output_by_lines = - self.props.compare_output_by_lines || self.config.runner.is_some(); + let compare_output_by_lines = self.props.compare_output_by_lines; let tmp; - let (expected, actual): (&str, &str) = if compare_output_by_lines { + let (expected, actual): (&str, &str) = if compare_output_by_lines_subset { let actual_lines: HashSet<_> = actual.lines().collect(); let expected_lines: Vec<_> = expected.lines().collect(); let mut used = expected_lines.clone(); used.retain(|line| actual_lines.contains(line)); + // check if `expected` contains a subset of the lines of `actual` if used.len() == expected_lines.len() && (expected.is_empty() == actual.is_empty()) { return CompareOutcome::Same; @@ -2738,9 +2739,20 @@ impl<'test> TestCx<'test> { // if we have no lines to check, force a full overwite ("", actual) } else { + // this prints/blesses the subset, not the actual tmp = (expected_lines.join("\n"), used.join("\n")); (&tmp.0, &tmp.1) } + } else if compare_output_by_lines { + let mut actual_lines: Vec<&str> = actual.lines().collect(); + let mut expected_lines: Vec<&str> = expected.lines().collect(); + actual_lines.sort_unstable(); + expected_lines.sort_unstable(); + if actual_lines == expected_lines { + return CompareOutcome::Same; + } else { + (expected, actual) + } } else { (expected, actual) }; diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs index 9d6edaddc1b74..6534902e0940e 100644 --- a/src/tools/compiletest/src/runtest/debuginfo.rs +++ b/src/tools/compiletest/src/runtest/debuginfo.rs @@ -310,12 +310,7 @@ impl TestCx<'_> { let mut gdb = Command::new(self.config.gdb.as_ref().unwrap()); - // FIXME: we are propagating `PYTHONPATH` from the environment, not a compiletest flag! - let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") { - format!("{pp}:{rust_pp_module_abs_path}") - } else { - rust_pp_module_abs_path.to_string() - }; + let pythonpath = with_pythonpath_prepended(&rust_pp_module_abs_path); gdb.args(debugger_opts).env("PYTHONPATH", pythonpath); debugger_run_result = @@ -458,7 +453,8 @@ impl TestCx<'_> { debugger_script: &Utf8Path, ) -> ProcRes { // Path containing `lldb_batchmode.py`, so that the `script` command can import it. - let pythonpath = self.config.src_root.join("src/etc"); + let rust_pp_module_abs_path = self.config.src_root.join("src/etc"); + let pythonpath = with_pythonpath_prepended(&rust_pp_module_abs_path); let mut cmd = Command::new(lldb); cmd.arg("--one-line") @@ -471,3 +467,12 @@ impl TestCx<'_> { self.run_command_to_procres(&mut cmd) } } + +fn with_pythonpath_prepended(some_path: &Utf8Path) -> String { + // FIXME: we are propagating `PYTHONPATH` from the environment, not a compiletest flag! + if let Ok(pp) = std::env::var("PYTHONPATH") { + format!("{pp}:{some_path}") + } else { + some_path.to_string() + } +} diff --git a/tests/ui/delegation/ice-non-fn-target-in-trait-impl.rs b/tests/ui/delegation/ice-non-fn-target-in-trait-impl.rs new file mode 100644 index 0000000000000..ebb826f832bc9 --- /dev/null +++ b/tests/ui/delegation/ice-non-fn-target-in-trait-impl.rs @@ -0,0 +1,20 @@ +// Regression test for #153743 and #153744. +// Delegation to a module or crate root inside a trait impl +// should emit a resolution error, not ICE. + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn bar(); + fn bar2(); +} + +impl Trait for () { + reuse std::path::<> as bar; + //~^ ERROR expected function, found module `std::path` + reuse core::<> as bar2; + //~^ ERROR expected function, found crate `core` +} + +fn main() {} diff --git a/tests/ui/delegation/ice-non-fn-target-in-trait-impl.stderr b/tests/ui/delegation/ice-non-fn-target-in-trait-impl.stderr new file mode 100644 index 0000000000000..bed13b0a3a9b1 --- /dev/null +++ b/tests/ui/delegation/ice-non-fn-target-in-trait-impl.stderr @@ -0,0 +1,15 @@ +error[E0423]: expected function, found module `std::path` + --> $DIR/ice-non-fn-target-in-trait-impl.rs:14:11 + | +LL | reuse std::path::<> as bar; + | ^^^^^^^^^^^^^ not a function + +error[E0423]: expected function, found crate `core` + --> $DIR/ice-non-fn-target-in-trait-impl.rs:16:11 + | +LL | reuse core::<> as bar2; + | ^^^^^^^^ not a function + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0423`. diff --git a/tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr b/tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr index b80d0f92fcfa4..a5c6ad5e90ba2 100644 --- a/tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr +++ b/tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr @@ -1,12 +1,22 @@ +error[E0391]: cycle detected when checking if `FOO` is a trivial const --> $DIR/cycle_crash-issue-135870.rs:6:1 | LL | const FOO: usize = FOO; | ^^^^^^^^^^^^^^^^ | +note: ...which requires building MIR for `FOO`... + --> $DIR/cycle_crash-issue-135870.rs:6:1 + | +LL | const FOO: usize = FOO; + | ^^^^^^^^^^^^^^^^ + = note: ...which again requires checking if `FOO` is a trivial const, completing the cycle +note: cycle used when simplifying constant for the type system `FOO` + --> $DIR/cycle_crash-issue-135870.rs:6:1 | LL | const FOO: usize = FOO; + | ^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0391`. \ No newline at end of file +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/parallel-rustc/ty-variance-issue-124423.stderr b/tests/ui/parallel-rustc/ty-variance-issue-124423.stderr index bf9ad85dfc5c6..631f43eea906f 100644 --- a/tests/ui/parallel-rustc/ty-variance-issue-124423.stderr +++ b/tests/ui/parallel-rustc/ty-variance-issue-124423.stderr @@ -244,10 +244,10 @@ LL | fn elided4(_: &impl Copy + 'a) -> new { x(x) } | ^^^ not found in this scope error[E0224]: at least one trait is required for an object type - --> $DIR/ty-variance-issue-124423.rs:35:39 + --> $DIR/ty-variance-issue-124423.rs:55:40 | -LL | fn elided3(_: &impl Copy + 'a) -> Box { Box::new(x) } - | ^^^^^^ +LL | impl<'a> LifetimeTrait<'a> for &'a Box {} + | ^^^^^^ error[E0224]: at least one trait is required for an object type --> $DIR/ty-variance-issue-124423.rs:41:40 @@ -256,10 +256,10 @@ LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } | ^^^^^^ error[E0224]: at least one trait is required for an object type - --> $DIR/ty-variance-issue-124423.rs:55:40 + --> $DIR/ty-variance-issue-124423.rs:35:39 | -LL | impl<'a> LifetimeTrait<'a> for &'a Box {} - | ^^^^^^ +LL | fn elided3(_: &impl Copy + 'a) -> Box { Box::new(x) } + | ^^^^^^ error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/ty-variance-issue-124423.rs:8:34 @@ -278,6 +278,7 @@ note: if you're trying to build a new `Box<_, _>` consider using one of the foll Box::::new_uninit Box::::new_zeroed Box::::try_new + and 27 others --> $SRC_DIR/alloc/src/boxed.rs:LL:COL error: aborting due to 30 previous errors diff --git a/tests/ui/parallel-rustc/ty-variance-issue-127971.stderr b/tests/ui/parallel-rustc/ty-variance-issue-127971.stderr index 55d52d35f4a8d..dce4acfc4ca74 100644 --- a/tests/ui/parallel-rustc/ty-variance-issue-127971.stderr +++ b/tests/ui/parallel-rustc/ty-variance-issue-127971.stderr @@ -104,9 +104,10 @@ note: if you're trying to build a new `Box<_, _>` consider using one of the foll Box::::new_uninit Box::::new_zeroed Box::::try_new + and 27 others --> $SRC_DIR/alloc/src/boxed.rs:LL:COL error: aborting due to 11 previous errors Some errors have detailed explanations: E0121, E0224, E0261, E0599. -For more information about an error, try `rustc --explain E0121`. \ No newline at end of file +For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/parallel-rustc/unexpected-type-issue-120601.stderr b/tests/ui/parallel-rustc/unexpected-type-issue-120601.stderr index da225ad82bc7b..70471719a70db 100644 --- a/tests/ui/parallel-rustc/unexpected-type-issue-120601.stderr +++ b/tests/ui/parallel-rustc/unexpected-type-issue-120601.stderr @@ -1,4 +1,5 @@ error[E0670]: `async fn` is not permitted in Rust 2015 + --> $DIR/unexpected-type-issue-120601.rs:10:1 | LL | async fn foo() -> Result<(), ()> { | ^^^^^ to use `async fn`, switch to Rust 2018 or later @@ -7,6 +8,7 @@ LL | async fn foo() -> Result<(), ()> { = note: for more on editions, read https://doc.rust-lang.org/edition-guide error[E0670]: `async fn` is not permitted in Rust 2015 + --> $DIR/unexpected-type-issue-120601.rs:16:1 | LL | async fn tuple() -> Tuple { | ^^^^^ to use `async fn`, switch to Rust 2018 or later @@ -15,6 +17,7 @@ LL | async fn tuple() -> Tuple { = note: for more on editions, read https://doc.rust-lang.org/edition-guide error[E0670]: `async fn` is not permitted in Rust 2015 + --> $DIR/unexpected-type-issue-120601.rs:21:1 | LL | async fn match_() { | ^^^^^ to use `async fn`, switch to Rust 2018 or later @@ -22,12 +25,8 @@ LL | async fn match_() { = help: pass `--edition 2024` to `rustc` = note: for more on editions, read https://doc.rust-lang.org/edition-guide -error[E0425]: cannot find function, tuple struct or tuple variant `Unstable2` in this scope - | -LL | Unstable2(()) - | ^^^^^^^^^ not found in this scope - error[E0308]: mismatched types + --> $DIR/unexpected-type-issue-120601.rs:23:9 | LL | match tuple() { | ------- this expression has type `impl Future` @@ -41,7 +40,13 @@ help: consider `await`ing on the `Future` LL | match tuple().await { | ++++++ +error[E0425]: cannot find function, tuple struct or tuple variant `Unstable2` in this scope + --> $DIR/unexpected-type-issue-120601.rs:11:5 + | +LL | Unstable2(()) + | ^^^^^^^^^ not found in this scope + error: aborting due to 5 previous errors Some errors have detailed explanations: E0308, E0425, E0670. -For more information about an error, try `rustc --explain E0308`. \ No newline at end of file +For more information about an error, try `rustc --explain E0308`.