diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2b5699dcd098d..3f052eca48e61 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ the Zulip stream is the best place to *ask* for help. Documentation for contributing to the compiler or tooling is located in the [Guide to Rustc Development][rustc-dev-guide], commonly known as the [rustc-dev-guide]. Documentation for the -standard library in the [Standard library developers Guide][std-dev-guide], commonly known as the [std-dev-guide]. +standard library is in the [Standard library developers Guide][std-dev-guide], commonly known as the [std-dev-guide]. ## Making changes to subtrees and submodules diff --git a/Cargo.lock b/Cargo.lock index 1a930b64f4581..116f45e68e58b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4492,6 +4492,7 @@ name = "rustc_query_impl" version = "0.0.0" dependencies = [ "measureme", + "rustc_abi", "rustc_data_structures", "rustc_errors", "rustc_hashes", @@ -4501,7 +4502,9 @@ dependencies = [ "rustc_middle", "rustc_query_system", "rustc_serialize", + "rustc_session", "rustc_span", + "rustc_thread_pool", "tracing", ] diff --git a/INSTALL.md b/INSTALL.md index dd6c64b3df5f6..0e998101fcd1d 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -233,7 +233,7 @@ itself back on after some time). ### MSVC -MSVC builds of Rust additionally requires an installation of: +MSVC builds of Rust additionally require an installation of: - Visual Studio 2022 (or later) build tools so `rustc` can use its linker. Older Visual Studio versions such as 2019 *may* work but aren't actively tested. diff --git a/RELEASES.md b/RELEASES.md index 424e12ceec054..3c0a95f8462b8 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1546,7 +1546,7 @@ Compatibility Notes - [Check well-formedness of the source type's signature in fn pointer casts.](https://github.com/rust-lang/rust/pull/129021) This partly closes a soundness hole that comes when casting a function item to function pointer - [Use equality instead of subtyping when resolving type dependent paths.](https://github.com/rust-lang/rust/pull/129073) - Linking on macOS now correctly includes Rust's default deployment target. Due to a linker bug, you might have to pass `MACOSX_DEPLOYMENT_TARGET` or fix your `#[link]` attributes to point to the correct frameworks. See . -- [Rust will now correctly raise an error for `repr(Rust)` written on non-`struct`/`enum`/`union` items, since it previous did not have any effect.](https://github.com/rust-lang/rust/pull/129422) +- [Rust will now correctly raise an error for `repr(Rust)` written on non-`struct`/`enum`/`union` items, since it previously did not have any effect.](https://github.com/rust-lang/rust/pull/129422) - The future incompatibility lint `deprecated_cfg_attr_crate_type_name` [has been made into a hard error](https://github.com/rust-lang/rust/pull/129670). It was used to deny usage of `#![crate_type]` and `#![crate_name]` attributes in `#![cfg_attr]`, which required a hack in the compiler to be able to change the used crate type and crate name after cfg expansion. Users can use `--crate-type` instead of `#![cfg_attr(..., crate_type = "...")]` and `--crate-name` instead of `#![cfg_attr(..., crate_name = "...")]` when running `rustc`/`cargo rustc` on the command line. Use of those two attributes outside of `#![cfg_attr]` continue to be fully supported. @@ -1722,7 +1722,7 @@ Cargo Compatibility Notes ------------------- - We now [disallow setting some built-in cfgs via the command-line](https://github.com/rust-lang/rust/pull/126158) with the newly added [`explicit_builtin_cfgs_in_flags`](https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#explicit-builtin-cfgs-in-flags) lint in order to prevent incoherent state, eg. `windows` cfg active but target is Linux based. The appropriate [`rustc` flag](https://doc.rust-lang.org/rustc/command-line-arguments.html) should be used instead. -- The standard library has a new implementation of `binary_search` which is significantly improves performance ([#128254](https://github.com/rust-lang/rust/pull/128254)). However when a sorted slice has multiple values which compare equal, the new implementation may select a different value among the equal ones than the old implementation. +- The standard library has a new implementation of `binary_search` which significantly improves performance ([#128254](https://github.com/rust-lang/rust/pull/128254)). However when a sorted slice has multiple values which compare equal, the new implementation may select a different value among the equal ones than the old implementation. - [illumos/Solaris now sets `MSG_NOSIGNAL` when writing to sockets](https://github.com/rust-lang/rust/pull/128259). This avoids killing the process with SIGPIPE when writing to a closed socket, which matches the existing behavior on other UNIX targets. - [Removes a problematic hack that always passed the --whole-archive linker flag for tests, which may cause linker errors for code accidentally relying on it.](https://github.com/rust-lang/rust/pull/128400) - The WebAssembly target features `multivalue` and `reference-types` are now @@ -1872,7 +1872,7 @@ These changes do not affect any public interfaces of Rust, but they represent significant improvements to the performance or internals of rustc and related tools. -- [Add a Rust-for Linux `auto` CI job to check kernel builds.](https://github.com/rust-lang/rust/pull/125209/) +- [Add a Rust-for-Linux `auto` CI job to check kernel builds.](https://github.com/rust-lang/rust/pull/125209/) Version 1.80.1 (2024-08-08) =========================== @@ -4510,7 +4510,7 @@ Compatibility Notes saturating to `0` instead][89926]. In the real world the panic happened mostly on platforms with buggy monotonic clock implementations rather than catching programming errors like reversing the start and end times. Such programming - errors will now results in `0` rather than a panic. + errors will now result in `0` rather than a panic. - In a future release we're planning to increase the baseline requirements for the Linux kernel to version 3.2, and for glibc to version 2.17. We'd love your feedback in [PR #95026][95026]. diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 1f01cadcb43ef..a2be2d42b3e1a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -244,7 +244,20 @@ impl AttributeParser for ConstStabilityParser { this.promotable = true; }), ]; - const ALLOWED_TARGETS: AllowedTargets = ALLOWED_TARGETS; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Impl { of_trait: false }), + Allow(Target::Impl { of_trait: true }), + Allow(Target::Use), // FIXME I don't think this does anything? + Allow(Target::Const), + Allow(Target::AssocConst), + Allow(Target::Trait), + Allow(Target::Static), + Allow(Target::Crate), + ]); fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option { if self.promotable { diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 6b3c7fe979327..91f98d475f00a 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -962,15 +962,6 @@ impl SyntaxExtension { let stability = find_attr!(attrs, AttributeKind::Stability { stability, .. } => *stability); - // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem - if let Some(sp) = - find_attr!(attrs, AttributeKind::RustcConstStability { span, .. } => *span) - { - sess.dcx().emit_err(errors::MacroConstStability { - span: sp, - head_span: sess.source_map().guess_head_span(span), - }); - } if let Some(sp) = find_attr!(attrs, AttributeKind::RustcBodyStability{ span, .. } => *span) { sess.dcx().emit_err(errors::MacroBodyStability { diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index e64bfc136d16c..b6fcc13321ee7 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -80,16 +80,6 @@ pub(crate) struct ResolveRelativePath { pub path: String, } -#[derive(Diagnostic)] -#[diag("macros cannot have const stability attributes")] -pub(crate) struct MacroConstStability { - #[primary_span] - #[label("invalid const stability attribute")] - pub span: Span, - #[label("const stability attribute affects this macro")] - pub head_span: Span, -} - #[derive(Diagnostic)] #[diag("macros cannot have body stability attributes")] pub(crate) struct MacroBodyStability { diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index ceb0e138a0dae..32af13b88a1ab 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1569,11 +1569,40 @@ pub(super) fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, def_id: let predicates = predicates.instantiate_identity(tcx); + let assoc_const_obligations: Vec<_> = predicates + .predicates + .iter() + .copied() + .zip(predicates.spans.iter().copied()) + .filter_map(|(clause, sp)| { + let proj = clause.as_projection_clause()?; + let pred_binder = proj + .map_bound(|pred| { + pred.term.as_const().map(|ct| { + let assoc_const_ty = tcx + .type_of(pred.projection_term.def_id) + .instantiate(tcx, pred.projection_term.args); + ty::ClauseKind::ConstArgHasType(ct, assoc_const_ty) + }) + }) + .transpose(); + pred_binder.map(|pred_binder| { + let cause = traits::ObligationCause::new( + sp, + wfcx.body_def_id, + ObligationCauseCode::WhereClause(def_id.to_def_id(), sp), + ); + Obligation::new(tcx, cause, wfcx.param_env, pred_binder) + }) + }) + .collect(); + assert_eq!(predicates.predicates.len(), predicates.spans.len()); let wf_obligations = predicates.into_iter().flat_map(|(p, sp)| { traits::wf::clause_obligations(infcx, wfcx.param_env, wfcx.body_def_id, p, sp) }); - let obligations: Vec<_> = wf_obligations.chain(default_obligations).collect(); + let obligations: Vec<_> = + wf_obligations.chain(default_obligations).chain(assoc_const_obligations).collect(); wfcx.register_obligations(obligations); } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 72080c5f53696..6cbc184e8f1b7 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -16,8 +16,7 @@ use rustc_parse::lexer::StripTokens; use rustc_parse::new_parser_from_source_str; use rustc_parse::parser::Recovery; use rustc_parse::parser::attr::AllowLeadingUnsafe; -use rustc_query_impl::QueryCtxt; -use rustc_query_system::query::print_query_stack; +use rustc_query_impl::{QueryCtxt, print_query_stack}; use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName}; use rustc_session::parse::ParseSess; use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, lint}; diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 458e7ad321433..59f0b67d67299 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -184,8 +184,7 @@ pub(crate) fn run_in_thread_pool_with_globals< use rustc_data_structures::defer; use rustc_data_structures::sync::FromDyn; use rustc_middle::ty::tls; - use rustc_query_impl::QueryCtxt; - use rustc_query_system::query::{QueryContext, break_query_cycles}; + use rustc_query_impl::{QueryCtxt, break_query_cycles}; let thread_stack_size = init_stack_size(thread_builder_diag); diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index a7707cf48c496..24a38e70ff6f2 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -13,7 +13,6 @@ mod keys; pub mod on_disk_cache; #[macro_use] pub mod plumbing; -pub mod values; pub fn describe_as_module(def_id: impl Into, tcx: TyCtxt<'_>) -> String { let def_id = def_id.into(); diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 003841f3af3fd..dd012a5146e61 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -687,6 +687,9 @@ where /// /// because these impls overlap, and I'd rather not build a coherence hack for /// this harmless overlap. + /// + /// This trait is indirectly exposed on stable, so do *not* extend the set of types that + /// implement the trait without FCP! fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index d611629671a0a..a8edc11294818 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start measureme = "12.0.1" +rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hashes = { path = "../rustc_hashes" } @@ -15,6 +16,8 @@ rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } +rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_thread_pool = { path = "../rustc_thread_pool" } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_query_impl/src/error.rs b/compiler/rustc_query_impl/src/error.rs index b109172d8e495..6d3eb29509826 100644 --- a/compiler/rustc_query_impl/src/error.rs +++ b/compiler/rustc_query_impl/src/error.rs @@ -1,3 +1,4 @@ +use rustc_errors::codes::*; use rustc_hir::limit::Limit; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; @@ -22,3 +23,59 @@ pub(crate) struct QueryOverflowNote { pub desc: String, pub depth: usize, } + +#[derive(Subdiagnostic)] +#[note("...which requires {$desc}...")] +pub(crate) struct CycleStack { + #[primary_span] + pub span: Span, + pub desc: String, +} + +#[derive(Subdiagnostic)] +pub(crate) enum StackCount { + #[note("...which immediately requires {$stack_bottom} again")] + Single, + #[note("...which again requires {$stack_bottom}, completing the cycle")] + Multiple, +} + +#[derive(Subdiagnostic)] +pub(crate) enum Alias { + #[note("type aliases cannot be recursive")] + #[help("consider using a struct, enum, or union instead to break the cycle")] + #[help( + "see for more information" + )] + Ty, + #[note("trait aliases cannot be recursive")] + Trait, +} + +#[derive(Subdiagnostic)] +#[note("cycle used when {$usage}")] +pub(crate) struct CycleUsage { + #[primary_span] + pub span: Span, + pub usage: String, +} + +#[derive(Diagnostic)] +#[diag("cycle detected when {$stack_bottom}", code = E0391)] +pub(crate) struct Cycle { + #[primary_span] + pub span: Span, + pub stack_bottom: String, + #[subdiagnostic] + pub cycle_stack: Vec, + #[subdiagnostic] + pub stack_count: StackCount, + #[subdiagnostic] + pub alias: Option, + #[subdiagnostic] + pub cycle_usage: Option, + #[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" + )] + pub note_span: (), +} diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 88604c91d0259..d7e95d43ecaad 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -9,13 +9,13 @@ use rustc_middle::dep_graph::DepsType; use rustc_middle::ty::TyCtxt; use rustc_query_system::dep_graph::{DepGraphData, DepNodeKey, HasDepContext}; use rustc_query_system::query::{ - ActiveKeyStatus, CycleError, CycleErrorHandling, QueryCache, QueryContext, QueryJob, - QueryJobId, QueryJobInfo, QueryLatch, QueryMap, QueryMode, QueryStackDeferred, QueryStackFrame, - QueryState, incremental_verify_ich, report_cycle, + ActiveKeyStatus, CycleError, CycleErrorHandling, QueryCache, QueryJob, QueryJobId, QueryLatch, + QueryMode, QueryStackDeferred, QueryStackFrame, QueryState, incremental_verify_ich, }; use rustc_span::{DUMMY_SP, Span}; use crate::dep_graph::{DepContext, DepNode, DepNodeIndex}; +use crate::job::{QueryJobInfo, QueryMap, find_cycle_in_stack, report_cycle}; use crate::{QueryCtxt, QueryFlags, SemiDynamicQueryDispatcher}; #[inline] @@ -218,7 +218,7 @@ fn cycle_error<'tcx, C: QueryCache, const FLAGS: QueryFlags>( .ok() .expect("failed to collect active queries"); - let error = try_execute.find_cycle_in_stack(query_map, &qcx.current_query_job(), span); + let error = find_cycle_in_stack(try_execute, query_map, &qcx.current_query_job(), span); (mk_cycle(query, qcx, error.lift()), None) } diff --git a/compiler/rustc_query_impl/src/job.rs b/compiler/rustc_query_impl/src/job.rs new file mode 100644 index 0000000000000..f1eba0f76d171 --- /dev/null +++ b/compiler/rustc_query_impl/src/job.rs @@ -0,0 +1,500 @@ +use std::io::Write; +use std::iter; +use std::sync::Arc; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::{Diag, DiagCtxtHandle}; +use rustc_hir::def::DefKind; +use rustc_query_system::query::{ + CycleError, QueryInfo, QueryJob, QueryJobId, QueryLatch, QueryStackDeferred, QueryStackFrame, + QueryWaiter, +}; +use rustc_session::Session; +use rustc_span::{DUMMY_SP, Span}; + +use crate::QueryCtxt; +use crate::dep_graph::DepContext; + +/// Map from query job IDs to job information collected by +/// `collect_active_jobs_from_all_queries`. +pub type QueryMap<'tcx> = FxHashMap>; + +fn query_job_id_frame<'a, 'tcx>( + id: QueryJobId, + map: &'a QueryMap<'tcx>, +) -> QueryStackFrame> { + map.get(&id).unwrap().frame.clone() +} + +fn query_job_id_span<'a, 'tcx>(id: QueryJobId, map: &'a QueryMap<'tcx>) -> Span { + map.get(&id).unwrap().job.span +} + +fn query_job_id_parent<'a, 'tcx>(id: QueryJobId, map: &'a QueryMap<'tcx>) -> Option { + map.get(&id).unwrap().job.parent +} + +fn query_job_id_latch<'a, 'tcx>( + id: QueryJobId, + map: &'a QueryMap<'tcx>, +) -> Option<&'a QueryLatch<'tcx>> { + map.get(&id).unwrap().job.latch.as_ref() +} + +#[derive(Clone, Debug)] +pub struct QueryJobInfo<'tcx> { + pub frame: QueryStackFrame>, + pub job: QueryJob<'tcx>, +} + +pub(crate) fn find_cycle_in_stack<'tcx>( + id: QueryJobId, + query_map: QueryMap<'tcx>, + current_job: &Option, + span: Span, +) -> CycleError> { + // Find the waitee amongst `current_job` parents + let mut cycle = Vec::new(); + let mut current_job = Option::clone(current_job); + + while let Some(job) = current_job { + let info = query_map.get(&job).unwrap(); + cycle.push(QueryInfo { span: info.job.span, frame: info.frame.clone() }); + + if job == id { + cycle.reverse(); + + // This is the end of the cycle + // The span entry we included was for the usage + // of the cycle itself, and not part of the cycle + // Replace it with the span which caused the cycle to form + cycle[0].span = span; + // Find out why the cycle itself was used + let usage = info + .job + .parent + .as_ref() + .map(|parent| (info.job.span, query_job_id_frame(*parent, &query_map))); + return CycleError { usage, cycle }; + } + + current_job = info.job.parent; + } + + panic!("did not find a cycle") +} + +#[cold] +#[inline(never)] +pub(crate) fn find_dep_kind_root<'tcx>( + id: QueryJobId, + query_map: QueryMap<'tcx>, +) -> (QueryJobInfo<'tcx>, usize) { + let mut depth = 1; + let info = query_map.get(&id).unwrap(); + let dep_kind = info.frame.dep_kind; + let mut current_id = info.job.parent; + let mut last_layout = (info.clone(), depth); + + while let Some(id) = current_id { + let info = query_map.get(&id).unwrap(); + if info.frame.dep_kind == dep_kind { + depth += 1; + last_layout = (info.clone(), depth); + } + current_id = info.job.parent; + } + last_layout +} + +/// A resumable waiter of a query. The usize is the index into waiters in the query's latch +type Waiter = (QueryJobId, usize); + +/// Visits all the non-resumable and resumable waiters of a query. +/// Only waiters in a query are visited. +/// `visit` is called for every waiter and is passed a query waiting on `query_ref` +/// and a span indicating the reason the query waited on `query_ref`. +/// If `visit` returns Some, this function returns. +/// For visits of non-resumable waiters it returns the return value of `visit`. +/// For visits of resumable waiters it returns Some(Some(Waiter)) which has the +/// required information to resume the waiter. +/// If all `visit` calls returns None, this function also returns None. +fn visit_waiters<'tcx, F>( + query_map: &QueryMap<'tcx>, + query: QueryJobId, + mut visit: F, +) -> Option> +where + F: FnMut(Span, QueryJobId) -> Option>, +{ + // Visit the parent query which is a non-resumable waiter since it's on the same stack + if let Some(parent) = query_job_id_parent(query, query_map) + && let Some(cycle) = visit(query_job_id_span(query, query_map), parent) + { + return Some(cycle); + } + + // Visit the explicit waiters which use condvars and are resumable + if let Some(latch) = query_job_id_latch(query, query_map) { + for (i, waiter) in latch.info.lock().waiters.iter().enumerate() { + if let Some(waiter_query) = waiter.query { + if visit(waiter.span, waiter_query).is_some() { + // Return a value which indicates that this waiter can be resumed + return Some(Some((query, i))); + } + } + } + } + + None +} + +/// Look for query cycles by doing a depth first search starting at `query`. +/// `span` is the reason for the `query` to execute. This is initially DUMMY_SP. +/// If a cycle is detected, this initial value is replaced with the span causing +/// the cycle. +fn cycle_check<'tcx>( + query_map: &QueryMap<'tcx>, + query: QueryJobId, + span: Span, + stack: &mut Vec<(Span, QueryJobId)>, + visited: &mut FxHashSet, +) -> Option> { + if !visited.insert(query) { + return if let Some(p) = stack.iter().position(|q| q.1 == query) { + // We detected a query cycle, fix up the initial span and return Some + + // Remove previous stack entries + stack.drain(0..p); + // Replace the span for the first query with the cycle cause + stack[0].0 = span; + Some(None) + } else { + None + }; + } + + // Query marked as visited is added it to the stack + stack.push((span, query)); + + // Visit all the waiters + let r = visit_waiters(query_map, query, |span, successor| { + cycle_check(query_map, successor, span, stack, visited) + }); + + // Remove the entry in our stack if we didn't find a cycle + if r.is_none() { + stack.pop(); + } + + r +} + +/// Finds out if there's a path to the compiler root (aka. code which isn't in a query) +/// from `query` without going through any of the queries in `visited`. +/// This is achieved with a depth first search. +fn connected_to_root<'tcx>( + query_map: &QueryMap<'tcx>, + query: QueryJobId, + visited: &mut FxHashSet, +) -> bool { + // We already visited this or we're deliberately ignoring it + if !visited.insert(query) { + return false; + } + + // This query is connected to the root (it has no query parent), return true + if query_job_id_parent(query, query_map).is_none() { + return true; + } + + visit_waiters(query_map, query, |_, successor| { + connected_to_root(query_map, successor, visited).then_some(None) + }) + .is_some() +} + +// Deterministically pick an query from a list +fn pick_query<'a, 'tcx, T, F>(query_map: &QueryMap<'tcx>, queries: &'a [T], f: F) -> &'a T +where + F: Fn(&T) -> (Span, QueryJobId), +{ + // Deterministically pick an entry point + // FIXME: Sort this instead + queries + .iter() + .min_by_key(|v| { + let (span, query) = f(v); + let hash = query_job_id_frame(query, query_map).hash; + // Prefer entry points which have valid spans for nicer error messages + // We add an integer to the tuple ensuring that entry points + // with valid spans are picked first + let span_cmp = if span == DUMMY_SP { 1 } else { 0 }; + (span_cmp, hash) + }) + .unwrap() +} + +/// Looks for query cycles starting from the last query in `jobs`. +/// If a cycle is found, all queries in the cycle is removed from `jobs` and +/// the function return true. +/// If a cycle was not found, the starting query is removed from `jobs` and +/// the function returns false. +fn remove_cycle<'tcx>( + query_map: &QueryMap<'tcx>, + jobs: &mut Vec, + wakelist: &mut Vec>>, +) -> bool { + let mut visited = FxHashSet::default(); + let mut stack = Vec::new(); + // Look for a cycle starting with the last query in `jobs` + if let Some(waiter) = + cycle_check(query_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited) + { + // The stack is a vector of pairs of spans and queries; reverse it so that + // the earlier entries require later entries + let (mut spans, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip(); + + // Shift the spans so that queries are matched with the span for their waitee + spans.rotate_right(1); + + // Zip them back together + let mut stack: Vec<_> = iter::zip(spans, queries).collect(); + + // Remove the queries in our cycle from the list of jobs to look at + for r in &stack { + if let Some(pos) = jobs.iter().position(|j| j == &r.1) { + jobs.remove(pos); + } + } + + // Find the queries in the cycle which are + // connected to queries outside the cycle + let entry_points = stack + .iter() + .filter_map(|&(span, query)| { + if query_job_id_parent(query, query_map).is_none() { + // This query is connected to the root (it has no query parent) + Some((span, query, None)) + } else { + let mut waiters = Vec::new(); + // Find all the direct waiters who lead to the root + visit_waiters(query_map, query, |span, waiter| { + // Mark all the other queries in the cycle as already visited + let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1)); + + if connected_to_root(query_map, waiter, &mut visited) { + waiters.push((span, waiter)); + } + + None + }); + if waiters.is_empty() { + None + } else { + // Deterministically pick one of the waiters to show to the user + let waiter = *pick_query(query_map, &waiters, |s| *s); + Some((span, query, Some(waiter))) + } + } + }) + .collect::)>>(); + + // Deterministically pick an entry point + let (_, entry_point, usage) = pick_query(query_map, &entry_points, |e| (e.0, e.1)); + + // Shift the stack so that our entry point is first + let entry_point_pos = stack.iter().position(|(_, query)| query == entry_point); + if let Some(pos) = entry_point_pos { + stack.rotate_left(pos); + } + + let usage = + usage.as_ref().map(|(span, query)| (*span, query_job_id_frame(*query, query_map))); + + // Create the cycle error + let error = CycleError { + usage, + cycle: stack + .iter() + .map(|&(s, ref q)| QueryInfo { span: s, frame: query_job_id_frame(*q, query_map) }) + .collect(), + }; + + // We unwrap `waiter` here since there must always be one + // edge which is resumable / waited using a query latch + let (waitee_query, waiter_idx) = waiter.unwrap(); + + // Extract the waiter we want to resume + let waiter = + query_job_id_latch(waitee_query, query_map).unwrap().extract_waiter(waiter_idx); + + // Set the cycle error so it will be picked up when resumed + *waiter.cycle.lock() = Some(error); + + // Put the waiter on the list of things to resume + wakelist.push(waiter); + + true + } else { + false + } +} + +/// Detects query cycles by using depth first search over all active query jobs. +/// If a query cycle is found it will break the cycle by finding an edge which +/// uses a query latch and then resuming that waiter. +/// There may be multiple cycles involved in a deadlock, so this searches +/// all active queries for cycles before finally resuming all the waiters at once. +pub fn break_query_cycles<'tcx>(query_map: QueryMap<'tcx>, registry: &rustc_thread_pool::Registry) { + let mut wakelist = Vec::new(); + // It is OK per the comments: + // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798854932 + // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798866392 + #[allow(rustc::potential_query_instability)] + let mut jobs: Vec = query_map.keys().cloned().collect(); + + let mut found_cycle = false; + + while jobs.len() > 0 { + if remove_cycle(&query_map, &mut jobs, &mut wakelist) { + found_cycle = true; + } + } + + // Check that a cycle was found. It is possible for a deadlock to occur without + // a query cycle if a query which can be waited on uses Rayon to do multithreading + // internally. Such a query (X) may be executing on 2 threads (A and B) and A may + // wait using Rayon on B. Rayon may then switch to executing another query (Y) + // which in turn will wait on X causing a deadlock. We have a false dependency from + // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here + // only considers the true dependency and won't detect a cycle. + if !found_cycle { + panic!( + "deadlock detected as we're unable to find a query cycle to break\n\ + current query map:\n{:#?}", + query_map + ); + } + + // Mark all the thread we're about to wake up as unblocked. This needs to be done before + // we wake the threads up as otherwise Rayon could detect a deadlock if a thread we + // resumed fell asleep and this thread had yet to mark the remaining threads as unblocked. + for _ in 0..wakelist.len() { + rustc_thread_pool::mark_unblocked(registry); + } + + for waiter in wakelist.into_iter() { + waiter.condvar.notify_one(); + } +} + +pub fn print_query_stack<'tcx>( + qcx: QueryCtxt<'tcx>, + mut current_query: Option, + dcx: DiagCtxtHandle<'_>, + limit_frames: Option, + mut file: Option, +) -> usize { + // Be careful relying on global state here: this code is called from + // a panic hook, which means that the global `DiagCtxt` may be in a weird + // state if it was responsible for triggering the panic. + let mut count_printed = 0; + let mut count_total = 0; + + // Make use of a partial query map if we fail to take locks collecting active queries. + let query_map = match qcx.collect_active_jobs_from_all_queries(false) { + Ok(query_map) => query_map, + Err(query_map) => query_map, + }; + + if let Some(ref mut file) = file { + let _ = writeln!(file, "\n\nquery stack during panic:"); + } + while let Some(query) = current_query { + let Some(query_info) = query_map.get(&query) else { + break; + }; + let query_extra = query_info.frame.info.extract(); + if Some(count_printed) < limit_frames || limit_frames.is_none() { + // Only print to stderr as many stack frames as `num_frames` when present. + dcx.struct_failure_note(format!( + "#{} [{:?}] {}", + count_printed, query_info.frame.dep_kind, query_extra.description + )) + .with_span(query_info.job.span) + .emit(); + count_printed += 1; + } + + if let Some(ref mut file) = file { + let _ = writeln!( + file, + "#{} [{}] {}", + count_total, + qcx.tcx.dep_kind_vtable(query_info.frame.dep_kind).name, + query_extra.description + ); + } + + current_query = query_info.job.parent; + count_total += 1; + } + + if let Some(ref mut file) = file { + let _ = writeln!(file, "end of query stack"); + } + count_total +} + +#[inline(never)] +#[cold] +pub(crate) fn report_cycle<'a>( + sess: &'a Session, + CycleError { usage, cycle: stack }: &CycleError, +) -> Diag<'a> { + assert!(!stack.is_empty()); + + let span = stack[0].frame.info.default_span(stack[1 % stack.len()].span); + + let mut cycle_stack = Vec::new(); + + use crate::error::StackCount; + let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple }; + + for i in 1..stack.len() { + let frame = &stack[i].frame; + let span = frame.info.default_span(stack[(i + 1) % stack.len()].span); + cycle_stack + .push(crate::error::CycleStack { span, desc: frame.info.description.to_owned() }); + } + + let mut cycle_usage = None; + if let Some((span, ref query)) = *usage { + cycle_usage = Some(crate::error::CycleUsage { + span: query.info.default_span(span), + usage: query.info.description.to_string(), + }); + } + + let alias = + if stack.iter().all(|entry| matches!(entry.frame.info.def_kind, Some(DefKind::TyAlias))) { + Some(crate::error::Alias::Ty) + } else if stack.iter().all(|entry| entry.frame.info.def_kind == Some(DefKind::TraitAlias)) { + Some(crate::error::Alias::Trait) + } else { + None + }; + + let cycle_diag = crate::error::Cycle { + span, + cycle_stack, + stack_bottom: stack[0].frame.info.description.to_owned(), + alias, + cycle_usage, + stack_count, + note_span: (), + }; + + sess.dcx().create_err(cycle_diag) +} diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index a33bdd22a7970..2241c3f440874 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -19,24 +19,27 @@ use rustc_middle::queries::{ use rustc_middle::query::AsLocalKey; use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; use rustc_middle::query::plumbing::{HashResult, QuerySystem, QuerySystemFns, QueryVTable}; -use rustc_middle::query::values::Value; use rustc_middle::ty::TyCtxt; use rustc_query_system::dep_graph::SerializedDepNodeIndex; use rustc_query_system::query::{ - CycleError, CycleErrorHandling, QueryCache, QueryMap, QueryMode, QueryState, + CycleError, CycleErrorHandling, QueryCache, QueryMode, QueryState, }; use rustc_span::{ErrorGuaranteed, Span}; +pub use crate::job::{QueryMap, break_query_cycles, print_query_stack}; pub use crate::plumbing::{QueryCtxt, query_key_hash_verify_all}; use crate::plumbing::{encode_all_query_results, try_mark_green}; use crate::profiling_support::QueryKeyStringCache; pub use crate::profiling_support::alloc_self_profile_query_strings; +use crate::values::Value; mod error; mod execution; +mod job; #[macro_use] mod plumbing; mod profiling_support; +mod values; #[derive(ConstParamTy)] // Allow this struct to be used for const-generic values. #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index eb195550b2e50..62401fd04015b 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -28,14 +28,15 @@ use rustc_middle::ty::tls::{self, ImplicitCtxt}; use rustc_middle::ty::{self, TyCtxt}; use rustc_query_system::dep_graph::{DepNodeKey, FingerprintStyle, HasDepContext}; use rustc_query_system::query::{ - QueryCache, QueryContext, QueryJobId, QueryMap, QuerySideEffect, QueryStackDeferred, - QueryStackFrame, QueryStackFrameExtra, + QueryCache, QueryContext, QueryJobId, QuerySideEffect, QueryStackDeferred, QueryStackFrame, + QueryStackFrameExtra, }; use rustc_serialize::{Decodable, Encodable}; use rustc_span::def_id::LOCAL_CRATE; use crate::error::{QueryOverflow, QueryOverflowNote}; use crate::execution::{all_inactive, force_query}; +use crate::job::{QueryMap, find_dep_kind_root}; use crate::{QueryDispatcherUnerased, QueryFlags, SemiDynamicQueryDispatcher}; /// Implements [`QueryContext`] for use by [`rustc_query_system`], since that @@ -55,7 +56,7 @@ impl<'tcx> QueryCtxt<'tcx> { let query_map = self .collect_active_jobs_from_all_queries(true) .expect("failed to collect active queries"); - let (info, depth) = job.find_dep_kind_root(query_map); + let (info, depth) = find_dep_kind_root(job, query_map); let suggested_limit = match self.tcx.recursion_limit() { Limit(0) => Limit(2), @@ -116,23 +117,6 @@ impl<'tcx> QueryCtxt<'tcx> { tls::enter_context(&new_icx, compute) }) } -} - -impl<'tcx> HasDepContext for QueryCtxt<'tcx> { - type Deps = DepsType; - type DepContext = TyCtxt<'tcx>; - - #[inline] - fn dep_context(&self) -> &Self::DepContext { - &self.tcx - } -} - -impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { - #[inline] - fn jobserver_proxy(&self) -> &Proxy { - &self.tcx.jobserver_proxy - } /// Returns a map of currently active query jobs, collected from all queries. /// @@ -144,7 +128,7 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { /// Prefer passing `false` to `require_complete` to avoid potential deadlocks, /// especially when called from within a deadlock handler, unless a /// complete map is needed and no deadlock is possible at this call site. - fn collect_active_jobs_from_all_queries( + pub fn collect_active_jobs_from_all_queries( self, require_complete: bool, ) -> Result, QueryMap<'tcx>> { @@ -159,6 +143,23 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { if complete { Ok(jobs) } else { Err(jobs) } } +} + +impl<'tcx> HasDepContext for QueryCtxt<'tcx> { + type Deps = DepsType; + type DepContext = TyCtxt<'tcx>; + + #[inline] + fn dep_context(&self) -> &Self::DepContext { + &self.tcx + } +} + +impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { + #[inline] + fn jobserver_proxy(&self) -> &Proxy { + &self.tcx.jobserver_proxy + } // Interactions with on_disk_cache fn load_side_effect( diff --git a/compiler/rustc_middle/src/query/values.rs b/compiler/rustc_query_impl/src/values.rs similarity index 98% rename from compiler/rustc_middle/src/query/values.rs rename to compiler/rustc_query_impl/src/values.rs index 0828758cc94ef..783e7a10d12ac 100644 --- a/compiler/rustc_middle/src/query/values.rs +++ b/compiler/rustc_query_impl/src/values.rs @@ -7,15 +7,17 @@ use rustc_errors::codes::*; use rustc_errors::{Applicability, MultiSpan, pluralize, struct_span_code_err}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_query_system::query::{CycleError, report_cycle}; +use rustc_middle::dep_graph::dep_kinds; +use rustc_middle::query::plumbing::CyclePlaceholder; +use rustc_middle::ty::{self, Representability, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug}; +use rustc_query_system::query::CycleError; use rustc_span::def_id::LocalDefId; use rustc_span::{ErrorGuaranteed, Span}; -use crate::dep_graph::dep_kinds; -use crate::query::plumbing::CyclePlaceholder; -use crate::ty::{self, Representability, Ty, TyCtxt}; +use crate::job::report_cycle; -pub trait Value<'tcx>: Sized { +pub(crate) trait Value<'tcx>: Sized { fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed) -> Self; } diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs index adce85bf31be2..f48afe6f75fa7 100644 --- a/compiler/rustc_query_system/src/error.rs +++ b/compiler/rustc_query_system/src/error.rs @@ -1,62 +1,4 @@ -use rustc_errors::codes::*; -use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_span::Span; - -#[derive(Subdiagnostic)] -#[note("...which requires {$desc}...")] -pub(crate) struct CycleStack { - #[primary_span] - pub span: Span, - pub desc: String, -} - -#[derive(Subdiagnostic)] -pub(crate) enum StackCount { - #[note("...which immediately requires {$stack_bottom} again")] - Single, - #[note("...which again requires {$stack_bottom}, completing the cycle")] - Multiple, -} - -#[derive(Subdiagnostic)] -pub(crate) enum Alias { - #[note("type aliases cannot be recursive")] - #[help("consider using a struct, enum, or union instead to break the cycle")] - #[help( - "see for more information" - )] - Ty, - #[note("trait aliases cannot be recursive")] - Trait, -} - -#[derive(Subdiagnostic)] -#[note("cycle used when {$usage}")] -pub(crate) struct CycleUsage { - #[primary_span] - pub span: Span, - pub usage: String, -} - -#[derive(Diagnostic)] -#[diag("cycle detected when {$stack_bottom}", code = E0391)] -pub(crate) struct Cycle { - #[primary_span] - pub span: Span, - pub stack_bottom: String, - #[subdiagnostic] - pub cycle_stack: Vec, - #[subdiagnostic] - pub stack_count: StackCount, - #[subdiagnostic] - pub alias: Option, - #[subdiagnostic] - pub cycle_usage: Option, - #[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" - )] - pub note_span: (), -} +use rustc_macros::Diagnostic; #[derive(Diagnostic)] #[diag("internal compiler error: reentrant incremental verify failure, suppressing message")] diff --git a/compiler/rustc_query_system/src/query/caches.rs b/compiler/rustc_query_system/src/query/caches.rs index eea1c5872290d..c1f5e5b670856 100644 --- a/compiler/rustc_query_system/src/query/caches.rs +++ b/compiler/rustc_query_system/src/query/caches.rs @@ -21,9 +21,6 @@ pub trait QueryCacheKey = Hash + Eq + Copy + Debug + for<'a> HashStable QueryInfo> { } } -/// Map from query job IDs to job information collected by -/// [`QueryContext::collect_active_jobs_from_all_queries`]. -pub type QueryMap<'tcx> = FxHashMap>; - /// A value uniquely identifying an active query job. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct QueryJobId(pub NonZero); -impl QueryJobId { - fn frame<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> QueryStackFrame> { - map.get(&self).unwrap().frame.clone() - } - - fn span<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Span { - map.get(&self).unwrap().job.span - } - - fn parent<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Option { - map.get(&self).unwrap().job.parent - } - - fn latch<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Option<&'a QueryLatch<'tcx>> { - map.get(&self).unwrap().job.latch.as_ref() - } -} - -#[derive(Clone, Debug)] -pub struct QueryJobInfo<'tcx> { - pub frame: QueryStackFrame>, - pub job: QueryJob<'tcx>, -} - /// Represents an active query job. #[derive(Clone, Debug)] pub struct QueryJob<'tcx> { @@ -76,7 +40,7 @@ pub struct QueryJob<'tcx> { pub parent: Option, /// The latch that is used to wait on this job. - latch: Option>, + pub latch: Option>, } impl<'tcx> QueryJob<'tcx> { @@ -105,91 +69,23 @@ impl<'tcx> QueryJob<'tcx> { } } -impl QueryJobId { - pub fn find_cycle_in_stack<'tcx>( - &self, - query_map: QueryMap<'tcx>, - current_job: &Option, - span: Span, - ) -> CycleError> { - // Find the waitee amongst `current_job` parents - let mut cycle = Vec::new(); - let mut current_job = Option::clone(current_job); - - while let Some(job) = current_job { - let info = query_map.get(&job).unwrap(); - cycle.push(QueryInfo { span: info.job.span, frame: info.frame.clone() }); - - if job == *self { - cycle.reverse(); - - // This is the end of the cycle - // The span entry we included was for the usage - // of the cycle itself, and not part of the cycle - // Replace it with the span which caused the cycle to form - cycle[0].span = span; - // Find out why the cycle itself was used - let usage = info - .job - .parent - .as_ref() - .map(|parent| (info.job.span, parent.frame(&query_map))); - return CycleError { usage, cycle }; - } - - current_job = info.job.parent; - } - - panic!("did not find a cycle") - } - - #[cold] - #[inline(never)] - pub fn find_dep_kind_root<'tcx>( - &self, - query_map: QueryMap<'tcx>, - ) -> (QueryJobInfo<'tcx>, usize) { - let mut depth = 1; - let info = query_map.get(&self).unwrap(); - let dep_kind = info.frame.dep_kind; - let mut current_id = info.job.parent; - let mut last_layout = (info.clone(), depth); - - while let Some(id) = current_id { - let info = query_map.get(&id).unwrap(); - if info.frame.dep_kind == dep_kind { - depth += 1; - last_layout = (info.clone(), depth); - } - current_id = info.job.parent; - } - last_layout - } -} - #[derive(Debug)] -struct QueryWaiter<'tcx> { - query: Option, - condvar: Condvar, - span: Span, - cycle: Mutex>>>, +pub struct QueryWaiter<'tcx> { + pub query: Option, + pub condvar: Condvar, + pub span: Span, + pub cycle: Mutex>>>, } #[derive(Debug)] -struct QueryLatchInfo<'tcx> { - complete: bool, - waiters: Vec>>, +pub struct QueryLatchInfo<'tcx> { + pub complete: bool, + pub waiters: Vec>>, } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct QueryLatch<'tcx> { - info: Arc>>, -} - -impl<'tcx> Clone for QueryLatch<'tcx> { - fn clone(&self) -> Self { - Self { info: Arc::clone(&self.info) } - } + pub info: Arc>>, } impl<'tcx> QueryLatch<'tcx> { @@ -256,399 +152,10 @@ impl<'tcx> QueryLatch<'tcx> { /// Removes a single waiter from the list of waiters. /// This is used to break query cycles. - fn extract_waiter(&self, waiter: usize) -> Arc> { + pub fn extract_waiter(&self, waiter: usize) -> Arc> { let mut info = self.info.lock(); debug_assert!(!info.complete); // Remove the waiter from the list of waiters info.waiters.remove(waiter) } } - -/// A resumable waiter of a query. The usize is the index into waiters in the query's latch -type Waiter = (QueryJobId, usize); - -/// Visits all the non-resumable and resumable waiters of a query. -/// Only waiters in a query are visited. -/// `visit` is called for every waiter and is passed a query waiting on `query_ref` -/// and a span indicating the reason the query waited on `query_ref`. -/// If `visit` returns Some, this function returns. -/// For visits of non-resumable waiters it returns the return value of `visit`. -/// For visits of resumable waiters it returns Some(Some(Waiter)) which has the -/// required information to resume the waiter. -/// If all `visit` calls returns None, this function also returns None. -fn visit_waiters<'tcx, F>( - query_map: &QueryMap<'tcx>, - query: QueryJobId, - mut visit: F, -) -> Option> -where - F: FnMut(Span, QueryJobId) -> Option>, -{ - // Visit the parent query which is a non-resumable waiter since it's on the same stack - if let Some(parent) = query.parent(query_map) - && let Some(cycle) = visit(query.span(query_map), parent) - { - return Some(cycle); - } - - // Visit the explicit waiters which use condvars and are resumable - if let Some(latch) = query.latch(query_map) { - for (i, waiter) in latch.info.lock().waiters.iter().enumerate() { - if let Some(waiter_query) = waiter.query { - if visit(waiter.span, waiter_query).is_some() { - // Return a value which indicates that this waiter can be resumed - return Some(Some((query, i))); - } - } - } - } - - None -} - -/// Look for query cycles by doing a depth first search starting at `query`. -/// `span` is the reason for the `query` to execute. This is initially DUMMY_SP. -/// If a cycle is detected, this initial value is replaced with the span causing -/// the cycle. -fn cycle_check<'tcx>( - query_map: &QueryMap<'tcx>, - query: QueryJobId, - span: Span, - stack: &mut Vec<(Span, QueryJobId)>, - visited: &mut FxHashSet, -) -> Option> { - if !visited.insert(query) { - return if let Some(p) = stack.iter().position(|q| q.1 == query) { - // We detected a query cycle, fix up the initial span and return Some - - // Remove previous stack entries - stack.drain(0..p); - // Replace the span for the first query with the cycle cause - stack[0].0 = span; - Some(None) - } else { - None - }; - } - - // Query marked as visited is added it to the stack - stack.push((span, query)); - - // Visit all the waiters - let r = visit_waiters(query_map, query, |span, successor| { - cycle_check(query_map, successor, span, stack, visited) - }); - - // Remove the entry in our stack if we didn't find a cycle - if r.is_none() { - stack.pop(); - } - - r -} - -/// Finds out if there's a path to the compiler root (aka. code which isn't in a query) -/// from `query` without going through any of the queries in `visited`. -/// This is achieved with a depth first search. -fn connected_to_root<'tcx>( - query_map: &QueryMap<'tcx>, - query: QueryJobId, - visited: &mut FxHashSet, -) -> bool { - // We already visited this or we're deliberately ignoring it - if !visited.insert(query) { - return false; - } - - // This query is connected to the root (it has no query parent), return true - if query.parent(query_map).is_none() { - return true; - } - - visit_waiters(query_map, query, |_, successor| { - connected_to_root(query_map, successor, visited).then_some(None) - }) - .is_some() -} - -// Deterministically pick an query from a list -fn pick_query<'a, 'tcx, T, F>(query_map: &QueryMap<'tcx>, queries: &'a [T], f: F) -> &'a T -where - F: Fn(&T) -> (Span, QueryJobId), -{ - // Deterministically pick an entry point - // FIXME: Sort this instead - queries - .iter() - .min_by_key(|v| { - let (span, query) = f(v); - let hash = query.frame(query_map).hash; - // Prefer entry points which have valid spans for nicer error messages - // We add an integer to the tuple ensuring that entry points - // with valid spans are picked first - let span_cmp = if span == DUMMY_SP { 1 } else { 0 }; - (span_cmp, hash) - }) - .unwrap() -} - -/// Looks for query cycles starting from the last query in `jobs`. -/// If a cycle is found, all queries in the cycle is removed from `jobs` and -/// the function return true. -/// If a cycle was not found, the starting query is removed from `jobs` and -/// the function returns false. -fn remove_cycle<'tcx>( - query_map: &QueryMap<'tcx>, - jobs: &mut Vec, - wakelist: &mut Vec>>, -) -> bool { - let mut visited = FxHashSet::default(); - let mut stack = Vec::new(); - // Look for a cycle starting with the last query in `jobs` - if let Some(waiter) = - cycle_check(query_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited) - { - // The stack is a vector of pairs of spans and queries; reverse it so that - // the earlier entries require later entries - let (mut spans, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip(); - - // Shift the spans so that queries are matched with the span for their waitee - spans.rotate_right(1); - - // Zip them back together - let mut stack: Vec<_> = iter::zip(spans, queries).collect(); - - // Remove the queries in our cycle from the list of jobs to look at - for r in &stack { - if let Some(pos) = jobs.iter().position(|j| j == &r.1) { - jobs.remove(pos); - } - } - - // Find the queries in the cycle which are - // connected to queries outside the cycle - let entry_points = stack - .iter() - .filter_map(|&(span, query)| { - if query.parent(query_map).is_none() { - // This query is connected to the root (it has no query parent) - Some((span, query, None)) - } else { - let mut waiters = Vec::new(); - // Find all the direct waiters who lead to the root - visit_waiters(query_map, query, |span, waiter| { - // Mark all the other queries in the cycle as already visited - let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1)); - - if connected_to_root(query_map, waiter, &mut visited) { - waiters.push((span, waiter)); - } - - None - }); - if waiters.is_empty() { - None - } else { - // Deterministically pick one of the waiters to show to the user - let waiter = *pick_query(query_map, &waiters, |s| *s); - Some((span, query, Some(waiter))) - } - } - }) - .collect::)>>(); - - // Deterministically pick an entry point - let (_, entry_point, usage) = pick_query(query_map, &entry_points, |e| (e.0, e.1)); - - // Shift the stack so that our entry point is first - let entry_point_pos = stack.iter().position(|(_, query)| query == entry_point); - if let Some(pos) = entry_point_pos { - stack.rotate_left(pos); - } - - let usage = usage.as_ref().map(|(span, query)| (*span, query.frame(query_map))); - - // Create the cycle error - let error = CycleError { - usage, - cycle: stack - .iter() - .map(|&(s, ref q)| QueryInfo { span: s, frame: q.frame(query_map) }) - .collect(), - }; - - // We unwrap `waiter` here since there must always be one - // edge which is resumable / waited using a query latch - let (waitee_query, waiter_idx) = waiter.unwrap(); - - // Extract the waiter we want to resume - let waiter = waitee_query.latch(query_map).unwrap().extract_waiter(waiter_idx); - - // Set the cycle error so it will be picked up when resumed - *waiter.cycle.lock() = Some(error); - - // Put the waiter on the list of things to resume - wakelist.push(waiter); - - true - } else { - false - } -} - -/// Detects query cycles by using depth first search over all active query jobs. -/// If a query cycle is found it will break the cycle by finding an edge which -/// uses a query latch and then resuming that waiter. -/// There may be multiple cycles involved in a deadlock, so this searches -/// all active queries for cycles before finally resuming all the waiters at once. -pub fn break_query_cycles<'tcx>(query_map: QueryMap<'tcx>, registry: &rustc_thread_pool::Registry) { - let mut wakelist = Vec::new(); - // It is OK per the comments: - // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798854932 - // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798866392 - #[allow(rustc::potential_query_instability)] - let mut jobs: Vec = query_map.keys().cloned().collect(); - - let mut found_cycle = false; - - while jobs.len() > 0 { - if remove_cycle(&query_map, &mut jobs, &mut wakelist) { - found_cycle = true; - } - } - - // Check that a cycle was found. It is possible for a deadlock to occur without - // a query cycle if a query which can be waited on uses Rayon to do multithreading - // internally. Such a query (X) may be executing on 2 threads (A and B) and A may - // wait using Rayon on B. Rayon may then switch to executing another query (Y) - // which in turn will wait on X causing a deadlock. We have a false dependency from - // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here - // only considers the true dependency and won't detect a cycle. - if !found_cycle { - panic!( - "deadlock detected as we're unable to find a query cycle to break\n\ - current query map:\n{:#?}", - query_map - ); - } - - // Mark all the thread we're about to wake up as unblocked. This needs to be done before - // we wake the threads up as otherwise Rayon could detect a deadlock if a thread we - // resumed fell asleep and this thread had yet to mark the remaining threads as unblocked. - for _ in 0..wakelist.len() { - rustc_thread_pool::mark_unblocked(registry); - } - - for waiter in wakelist.into_iter() { - waiter.condvar.notify_one(); - } -} - -#[inline(never)] -#[cold] -pub fn report_cycle<'a>( - sess: &'a Session, - CycleError { usage, cycle: stack }: &CycleError, -) -> Diag<'a> { - assert!(!stack.is_empty()); - - let span = stack[0].frame.info.default_span(stack[1 % stack.len()].span); - - let mut cycle_stack = Vec::new(); - - use crate::error::StackCount; - let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple }; - - for i in 1..stack.len() { - let frame = &stack[i].frame; - let span = frame.info.default_span(stack[(i + 1) % stack.len()].span); - cycle_stack.push(CycleStack { span, desc: frame.info.description.to_owned() }); - } - - let mut cycle_usage = None; - if let Some((span, ref query)) = *usage { - cycle_usage = Some(crate::error::CycleUsage { - span: query.info.default_span(span), - usage: query.info.description.to_string(), - }); - } - - let alias = - if stack.iter().all(|entry| matches!(entry.frame.info.def_kind, Some(DefKind::TyAlias))) { - Some(crate::error::Alias::Ty) - } else if stack.iter().all(|entry| entry.frame.info.def_kind == Some(DefKind::TraitAlias)) { - Some(crate::error::Alias::Trait) - } else { - None - }; - - let cycle_diag = crate::error::Cycle { - span, - cycle_stack, - stack_bottom: stack[0].frame.info.description.to_owned(), - alias, - cycle_usage, - stack_count, - note_span: (), - }; - - sess.dcx().create_err(cycle_diag) -} - -pub fn print_query_stack<'tcx, Qcx: QueryContext<'tcx>>( - qcx: Qcx, - mut current_query: Option, - dcx: DiagCtxtHandle<'_>, - limit_frames: Option, - mut file: Option, -) -> usize { - // Be careful relying on global state here: this code is called from - // a panic hook, which means that the global `DiagCtxt` may be in a weird - // state if it was responsible for triggering the panic. - let mut count_printed = 0; - let mut count_total = 0; - - // Make use of a partial query map if we fail to take locks collecting active queries. - let query_map = match qcx.collect_active_jobs_from_all_queries(false) { - Ok(query_map) => query_map, - Err(query_map) => query_map, - }; - - if let Some(ref mut file) = file { - let _ = writeln!(file, "\n\nquery stack during panic:"); - } - while let Some(query) = current_query { - let Some(query_info) = query_map.get(&query) else { - break; - }; - let query_extra = query_info.frame.info.extract(); - if Some(count_printed) < limit_frames || limit_frames.is_none() { - // Only print to stderr as many stack frames as `num_frames` when present. - dcx.struct_failure_note(format!( - "#{} [{:?}] {}", - count_printed, query_info.frame.dep_kind, query_extra.description - )) - .with_span(query_info.job.span) - .emit(); - count_printed += 1; - } - - if let Some(ref mut file) = file { - let _ = writeln!( - file, - "#{} [{}] {}", - count_total, - qcx.dep_context().dep_kind_vtable(query_info.frame.dep_kind).name, - query_extra.description - ); - } - - current_query = query_info.job.parent; - count_total += 1; - } - - if let Some(ref mut file) = file { - let _ = writeln!(file, "end of query stack"); - } - count_total -} diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index f4a3fda7e3727..c33af941f8026 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -15,10 +15,7 @@ use rustc_span::def_id::DefId; pub use self::caches::{ DefIdCache, DefaultCache, QueryCache, QueryCacheKey, SingleCache, VecCache, }; -pub use self::job::{ - QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryLatch, QueryMap, break_query_cycles, - print_query_stack, report_cycle, -}; +pub use self::job::{QueryInfo, QueryJob, QueryJobId, QueryLatch, QueryWaiter}; pub use self::plumbing::*; use crate::dep_graph::{DepKind, DepNodeIndex, HasDepContext, SerializedDepNodeIndex}; @@ -52,7 +49,7 @@ pub struct QueryStackFrame { pub dep_kind: DepKind, /// This hash is used to deterministically pick /// a query to remove cycles in the parallel compiler. - hash: Hash64, + pub hash: Hash64, pub def_id: Option, /// A def-id that is extracted from a `Ty` in a query key pub def_id_for_ty_in_cycle: Option, @@ -161,11 +158,6 @@ pub trait QueryContext<'tcx>: HasDepContext { /// a token while waiting on a query. fn jobserver_proxy(&self) -> &Proxy; - fn collect_active_jobs_from_all_queries( - self, - require_complete: bool, - ) -> Result, QueryMap<'tcx>>; - /// Load a side effect associated to the node in the previous session. fn load_side_effect( self, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs index 0916864ac4cc9..513a022adec15 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { llvm_target, metadata: TargetMetadata { description: Some("ARM64 Apple tvOS".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(true), }, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs index 2e18abdef3991..0f48a36d5d777 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { llvm_target, metadata: TargetMetadata { description: Some("ARM64 Apple tvOS Simulator".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(true), }, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs index b771b3ff10b99..453809fb3bb34 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { llvm_target, metadata: TargetMetadata { description: Some("ARM64 Apple visionOS".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(true), }, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs index d4b4d94115b40..1aacb41ffeaac 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { llvm_target, metadata: TargetMetadata { description: Some("ARM64 Apple visionOS simulator".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(true), }, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs index 9f0a24ddd334d..82c986a0f8fe7 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { llvm_target, metadata: TargetMetadata { description: Some("ARM64 Apple watchOS".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(true), }, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs index fabf01d641c37..77b03c1de7223 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { llvm_target, metadata: TargetMetadata { description: Some("ARM64 Apple watchOS Simulator".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(true), }, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs index 8fe4ffebd8655..7d061e65df80b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs @@ -190,8 +190,8 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { } Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)) => { - debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index,); - debug!("id={:?}", id); + debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {debruijn_index:?}"); + debug!("id={id:?}"); if debruijn_index == self.current_index && id.to_def_id() == self.region_def_id { return ControlFlow::Break(()); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 3ee6e6b739c03..3ed1f7c3481f9 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -239,14 +239,14 @@ pub fn suggest_new_region_bound( }; spans_suggs.push((fn_return.span.shrink_to_hi(), format!(" + {name} "))); err.multipart_suggestion_verbose( - format!("{declare} `{ty}` {captures}, {use_lt}",), + format!("{declare} `{ty}` {captures}, {use_lt}"), spans_suggs, Applicability::MaybeIncorrect, ); } else { err.span_suggestion_verbose( fn_return.span.shrink_to_hi(), - format!("{declare} `{ty}` {captures}, {explicit}",), + format!("{declare} `{ty}` {captures}, {explicit}"), &plus_lt, Applicability::MaybeIncorrect, ); @@ -257,7 +257,7 @@ pub fn suggest_new_region_bound( if let LifetimeKind::ImplicitObjectLifetimeDefault = lt.kind { err.span_suggestion_verbose( fn_return.span.shrink_to_hi(), - format!("{declare} the trait object {captures}, {explicit}",), + format!("{declare} the trait object {captures}, {explicit}"), &plus_lt, Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index eb72f71382ef9..16c4ac68cdf0c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -710,7 +710,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { predicate ); let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { - format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::>().join("\n"),) + format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::>().join("\n")) } else if post.len() == 1 { format!(": `{}`", post[0]) } else { @@ -722,7 +722,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.note(format!("cannot satisfy `{predicate}`")); } (0, _, 1) => { - err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,)); + err.note(format!("{msg} in the `{}` crate{post}", crates[0])); } (0, _, _) => { err.note(format!( @@ -739,7 +739,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { (_, 1, 1) => { let span: MultiSpan = spans.into(); err.span_note(span, msg); - err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,)); + err.note(format!("and another `impl` found in the `{}` crate{post}", crates[0])); } _ => { let span: MultiSpan = spans.into(); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 1de088f19cc7f..68240f5ea599d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -2263,7 +2263,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.highlighted_span_help( self.tcx.def_span(def_id), vec![ - StringPart::normal(format!("the trait `{trait_}` ",)), + StringPart::normal(format!("the trait `{trait_}` ")), StringPart::highlighted("is"), StringPart::normal(desc), StringPart::highlighted(self_ty), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 5eff7bba7c633..a98f952d55a38 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -888,7 +888,7 @@ impl<'tcx> OnUnimplementedFormatString { } } else { let reported = - struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,) + struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description) .emit(); result = Err(reported); } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index f5bf74a799192..935834b832f24 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -312,7 +312,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // `async`/`gen` constructs get lowered to a special kind of coroutine that // should *not* `impl Coroutine`. ty::Coroutine(did, ..) if self.tcx().is_general_coroutine(*did) => { - debug!(?self_ty, ?obligation, "assemble_coroutine_candidates",); + debug!(?self_ty, ?obligation, "assemble_coroutine_candidates"); candidates.vec.push(CoroutineCandidate); } @@ -334,7 +334,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // async constructs get lowered to a special kind of coroutine that // should directly `impl Future`. if self.tcx().coroutine_is_async(*did) { - debug!(?self_ty, ?obligation, "assemble_future_candidates",); + debug!(?self_ty, ?obligation, "assemble_future_candidates"); candidates.vec.push(FutureCandidate); } @@ -352,7 +352,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let ty::Coroutine(did, ..) = self_ty.kind() && self.tcx().coroutine_is_gen(*did) { - debug!(?self_ty, ?obligation, "assemble_iterator_candidates",); + debug!(?self_ty, ?obligation, "assemble_iterator_candidates"); candidates.vec.push(IteratorCandidate); } @@ -378,7 +378,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // gen constructs get lowered to a special kind of coroutine that // should directly `impl AsyncIterator`. if self.tcx().coroutine_is_async_gen(did) { - debug!(?self_ty, ?obligation, "assemble_iterator_candidates",); + debug!(?self_ty, ?obligation, "assemble_iterator_candidates"); // Can only confirm this candidate if we have constrained // the `Yield` type to at least `Poll>`.. diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 4f65b30775ed0..b0e045274d0d8 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -1246,6 +1246,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }) } + /// This trait is indirectly exposed on stable, so do *not* extend the set of types that + /// implement the trait without FCP! fn confirm_bikeshed_guaranteed_no_drop_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4469afc3a4ebf..3c3160bc533e2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1223,7 +1223,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { && self.match_fresh_trait_preds(stack.fresh_trait_pred, prev.fresh_trait_pred) }) { - debug!("evaluate_stack --> unbound argument, recursive --> giving up",); + debug!("evaluate_stack --> unbound argument, recursive --> giving up"); return Ok(EvaluatedToAmbigStackDependent); } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index d383cb9d91dd4..358f14221a0dc 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -951,6 +951,34 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { ty::Binder::dummy(ty::PredicateKind::DynCompatible(principal)), )); } + + if !t.has_escaping_bound_vars() { + for projection in data.projection_bounds() { + let pred_binder = projection + .with_self_ty(tcx, t) + .map_bound(|p| { + p.term.as_const().map(|ct| { + let assoc_const_ty = tcx + .type_of(p.projection_term.def_id) + .instantiate(tcx, p.projection_term.args); + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType( + ct, + assoc_const_ty, + )) + }) + }) + .transpose(); + if let Some(pred_binder) = pred_binder { + self.out.push(traits::Obligation::with_depth( + tcx, + self.cause(ObligationCauseCode::WellFormed(None)), + self.recursion_depth, + self.param_env, + pred_binder, + )); + } + } + } } // Inference variables are the complicated case, since we don't diff --git a/library/core/src/array/drain.rs b/library/core/src/array/drain.rs index 1c6137191324c..17792dca583d2 100644 --- a/library/core/src/array/drain.rs +++ b/library/core/src/array/drain.rs @@ -31,7 +31,6 @@ impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> { } /// See [`Drain::new`]; this is our fake iterator. -#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] #[unstable(feature = "array_try_map", issue = "79711")] pub(super) struct Drain<'l, 'f, T, const N: usize, F> { // FIXME(const-hack): This is essentially a slice::IterMut<'static>, replace when possible. diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index fafac482206e5..2300904f22c10 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -466,10 +466,34 @@ auto: - name: dist-apple-various env: - SCRIPT: ./x.py dist bootstrap --include-default-paths --host='' --target=aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim,aarch64-apple-ios-macabi,x86_64-apple-ios-macabi + # Build and distribute the standard library for these targets. + TARGETS: "aarch64-apple-ios,\ + aarch64-apple-ios-sim,\ + x86_64-apple-ios,\ + aarch64-apple-ios-macabi,\ + x86_64-apple-ios-macabi,\ + aarch64-apple-tvos,\ + aarch64-apple-tvos-sim,\ + aarch64-apple-visionos,\ + aarch64-apple-visionos-sim,\ + aarch64-apple-watchos,\ + aarch64-apple-watchos-sim" + SCRIPT: ./x.py dist bootstrap --include-default-paths --host='' --target=$TARGETS # Mac Catalyst cannot currently compile the sanitizer: # https://github.com/rust-lang/rust/issues/129069 - RUST_CONFIGURE_ARGS: --enable-sanitizers --enable-profiler --set rust.jemalloc --set target.aarch64-apple-ios-macabi.sanitizers=false --set target.x86_64-apple-ios-macabi.sanitizers=false + # + # And tvOS and watchOS don't currently support the profiler runtime: + # https://github.com/rust-lang/rust/issues/152426 + RUST_CONFIGURE_ARGS: >- + --enable-sanitizers + --enable-profiler + --set rust.jemalloc + --set target.aarch64-apple-ios-macabi.sanitizers=false + --set target.x86_64-apple-ios-macabi.sanitizers=false + --set target.aarch64-apple-tvos.profiler=false + --set target.aarch64-apple-tvos-sim.profiler=false + --set target.aarch64-apple-watchos.profiler=false + --set target.aarch64-apple-watchos-sim.profiler=false # Ensure that host tooling is built to support our minimum support macOS version. # FIXME(madsmtm): This might be redundant, as we're not building host tooling here (?) MACOSX_DEPLOYMENT_TARGET: 10.12 diff --git a/src/doc/book b/src/doc/book index 39aeceaa3aeab..05d114287b7d6 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 39aeceaa3aeab845bc4517e7a44e48727d3b9dbe +Subproject commit 05d114287b7d6f6c9253d5242540f00fbd6172ab diff --git a/src/doc/index.md b/src/doc/index.md index 892057a8f4db3..4189393539751 100644 --- a/src/doc/index.md +++ b/src/doc/index.md @@ -194,9 +194,8 @@ resources maintained by the [Embedded Working Group] useful. #### The Embedded Rust Book -[The Embedded Rust Book] is targeted at developers familiar with embedded -development and familiar with Rust, but have not used Rust for embedded -development. +[The Embedded Rust Book] is targeted at developers who are familiar with embedded +development and Rust, but who have not used Rust for embedded development. [The Embedded Rust Book]: embedded-book/index.html [Rust project]: https://www.rust-lang.org diff --git a/src/doc/nomicon b/src/doc/nomicon index 050c002a360fa..b8f254a991b8b 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 050c002a360fa45b701ea34feed7a860dc8a41bf +Subproject commit b8f254a991b8b7e8f704527f0d4f343a4697dfa9 diff --git a/src/doc/not_found.md b/src/doc/not_found.md index 9552759d2b8b3..69e6ae3e2d036 100644 --- a/src/doc/not_found.md +++ b/src/doc/not_found.md @@ -41,7 +41,7 @@ Some things that might be helpful to you though: