authorbors <bors@rust-lang.org> 2026-02-11 22:07:19 UTC
committerbors <bors@rust-lang.org> 2026-02-11 22:07:19 UTC
log5fdff787e6ef7cd4458f81e69cbb4d2a39439a98
treef888669c10137de98355c9cf9d1786a8f5e166e0
parent7057231bd78d6c7893f905ea1832365d4c5efe17
parentf1956632f68a13d0af7c3b352b118a615d3146b9

Auto merge of #152484 - matthiaskrgr:rollup-h4u26eb, r=matthiaskrgr

Rollup of 9 pull requests Successful merges: - rust-lang/rust#152419 (Move more query system code) - rust-lang/rust#152431 (Restrict the set of things that const stability can be applied to) - rust-lang/rust#152436 (Reenable a GCI+mGCA+GCPT test case) - rust-lang/rust#152021 (Bump tvOS, visionOS and watchOS Aarch64 targets to tier 2) - rust-lang/rust#152146 (mGCA: Add associated const type check) - rust-lang/rust#152372 (style: remove unneeded trailing commas) - rust-lang/rust#152383 (BikeshedGuaranteedNoDrop trait: add comments indicating that it can be observed on stable) - rust-lang/rust#152397 (Update books) - rust-lang/rust#152441 (Fix typos and grammar in top-level and src/doc documentation)

62 files changed, 1330 insertions(+), 1152 deletions(-)

CONTRIBUTING.md+1-1
......@@ -10,7 +10,7 @@ the Zulip stream is the best place to *ask* for help.
1010
1111Documentation for contributing to the compiler or tooling is located in the [Guide to Rustc
1212Development][rustc-dev-guide], commonly known as the [rustc-dev-guide]. Documentation for the
13standard library in the [Standard library developers Guide][std-dev-guide], commonly known as the [std-dev-guide].
13standard library is in the [Standard library developers Guide][std-dev-guide], commonly known as the [std-dev-guide].
1414
1515## Making changes to subtrees and submodules
1616
Cargo.lock+3
......@@ -4492,6 +4492,7 @@ name = "rustc_query_impl"
44924492version = "0.0.0"
44934493dependencies = [
44944494 "measureme",
4495 "rustc_abi",
44954496 "rustc_data_structures",
44964497 "rustc_errors",
44974498 "rustc_hashes",
......@@ -4501,7 +4502,9 @@ dependencies = [
45014502 "rustc_middle",
45024503 "rustc_query_system",
45034504 "rustc_serialize",
4505 "rustc_session",
45044506 "rustc_span",
4507 "rustc_thread_pool",
45054508 "tracing",
45064509]
45074510
INSTALL.md+1-1
......@@ -233,7 +233,7 @@ itself back on after some time).
233233
234234### MSVC
235235
236MSVC builds of Rust additionally requires an installation of:
236MSVC builds of Rust additionally require an installation of:
237237
238238- Visual Studio 2022 (or later) build tools so `rustc` can use its linker. Older
239239 Visual Studio versions such as 2019 *may* work but aren't actively tested.
RELEASES.md+4-4
......@@ -1546,7 +1546,7 @@ Compatibility Notes
15461546- [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
15471547- [Use equality instead of subtyping when resolving type dependent paths.](https://github.com/rust-lang/rust/pull/129073)
15481548- 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 <https://github.com/rust-lang/rust/pull/129369>.
1549- [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)
1549- [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)
15501550- 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.
15511551 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.
15521552 Use of those two attributes outside of `#![cfg_attr]` continue to be fully supported.
......@@ -1722,7 +1722,7 @@ Cargo
17221722Compatibility Notes
17231723-------------------
17241724 - 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.
1725- 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.
1725- 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.
17261726- [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.
17271727- [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)
17281728- 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
18721872significant improvements to the performance or internals of rustc and related
18731873tools.
18741874
1875- [Add a Rust-for Linux `auto` CI job to check kernel builds.](https://github.com/rust-lang/rust/pull/125209/)
1875- [Add a Rust-for-Linux `auto` CI job to check kernel builds.](https://github.com/rust-lang/rust/pull/125209/)
18761876
18771877Version 1.80.1 (2024-08-08)
18781878===========================
......@@ -4510,7 +4510,7 @@ Compatibility Notes
45104510 saturating to `0` instead][89926]. In the real world the panic happened mostly
45114511 on platforms with buggy monotonic clock implementations rather than catching
45124512 programming errors like reversing the start and end times. Such programming
4513 errors will now results in `0` rather than a panic.
4513 errors will now result in `0` rather than a panic.
45144514- In a future release we're planning to increase the baseline requirements for
45154515 the Linux kernel to version 3.2, and for glibc to version 2.17. We'd love
45164516 your feedback in [PR #95026][95026].
compiler/rustc_attr_parsing/src/attributes/stability.rs+14-1
......@@ -244,7 +244,20 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
244244 this.promotable = true;
245245 }),
246246 ];
247 const ALLOWED_TARGETS: AllowedTargets = ALLOWED_TARGETS;
247 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
248 Allow(Target::Fn),
249 Allow(Target::Method(MethodKind::Inherent)),
250 Allow(Target::Method(MethodKind::TraitImpl)),
251 Allow(Target::Method(MethodKind::Trait { body: true })),
252 Allow(Target::Impl { of_trait: false }),
253 Allow(Target::Impl { of_trait: true }),
254 Allow(Target::Use), // FIXME I don't think this does anything?
255 Allow(Target::Const),
256 Allow(Target::AssocConst),
257 Allow(Target::Trait),
258 Allow(Target::Static),
259 Allow(Target::Crate),
260 ]);
248261
249262 fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
250263 if self.promotable {
compiler/rustc_expand/src/base.rs-9
......@@ -962,15 +962,6 @@ impl SyntaxExtension {
962962
963963 let stability = find_attr!(attrs, AttributeKind::Stability { stability, .. } => *stability);
964964
965 // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem
966 if let Some(sp) =
967 find_attr!(attrs, AttributeKind::RustcConstStability { span, .. } => *span)
968 {
969 sess.dcx().emit_err(errors::MacroConstStability {
970 span: sp,
971 head_span: sess.source_map().guess_head_span(span),
972 });
973 }
974965 if let Some(sp) = find_attr!(attrs, AttributeKind::RustcBodyStability{ span, .. } => *span)
975966 {
976967 sess.dcx().emit_err(errors::MacroBodyStability {
compiler/rustc_expand/src/errors.rs-10
......@@ -80,16 +80,6 @@ pub(crate) struct ResolveRelativePath {
8080 pub path: String,
8181}
8282
83#[derive(Diagnostic)]
84#[diag("macros cannot have const stability attributes")]
85pub(crate) struct MacroConstStability {
86 #[primary_span]
87 #[label("invalid const stability attribute")]
88 pub span: Span,
89 #[label("const stability attribute affects this macro")]
90 pub head_span: Span,
91}
92
9383#[derive(Diagnostic)]
9484#[diag("macros cannot have body stability attributes")]
9585pub(crate) struct MacroBodyStability {
compiler/rustc_hir_analysis/src/check/wfcheck.rs+30-1
......@@ -1569,11 +1569,40 @@ pub(super) fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, def_id:
15691569
15701570 let predicates = predicates.instantiate_identity(tcx);
15711571
1572 let assoc_const_obligations: Vec<_> = predicates
1573 .predicates
1574 .iter()
1575 .copied()
1576 .zip(predicates.spans.iter().copied())
1577 .filter_map(|(clause, sp)| {
1578 let proj = clause.as_projection_clause()?;
1579 let pred_binder = proj
1580 .map_bound(|pred| {
1581 pred.term.as_const().map(|ct| {
1582 let assoc_const_ty = tcx
1583 .type_of(pred.projection_term.def_id)
1584 .instantiate(tcx, pred.projection_term.args);
1585 ty::ClauseKind::ConstArgHasType(ct, assoc_const_ty)
1586 })
1587 })
1588 .transpose();
1589 pred_binder.map(|pred_binder| {
1590 let cause = traits::ObligationCause::new(
1591 sp,
1592 wfcx.body_def_id,
1593 ObligationCauseCode::WhereClause(def_id.to_def_id(), sp),
1594 );
1595 Obligation::new(tcx, cause, wfcx.param_env, pred_binder)
1596 })
1597 })
1598 .collect();
1599
15721600 assert_eq!(predicates.predicates.len(), predicates.spans.len());
15731601 let wf_obligations = predicates.into_iter().flat_map(|(p, sp)| {
15741602 traits::wf::clause_obligations(infcx, wfcx.param_env, wfcx.body_def_id, p, sp)
15751603 });
1576 let obligations: Vec<_> = wf_obligations.chain(default_obligations).collect();
1604 let obligations: Vec<_> =
1605 wf_obligations.chain(default_obligations).chain(assoc_const_obligations).collect();
15771606 wfcx.register_obligations(obligations);
15781607}
15791608
compiler/rustc_interface/src/interface.rs+1-2
......@@ -16,8 +16,7 @@ use rustc_parse::lexer::StripTokens;
1616use rustc_parse::new_parser_from_source_str;
1717use rustc_parse::parser::Recovery;
1818use rustc_parse::parser::attr::AllowLeadingUnsafe;
19use rustc_query_impl::QueryCtxt;
20use rustc_query_system::query::print_query_stack;
19use rustc_query_impl::{QueryCtxt, print_query_stack};
2120use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName};
2221use rustc_session::parse::ParseSess;
2322use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, lint};
compiler/rustc_interface/src/util.rs+1-2
......@@ -184,8 +184,7 @@ pub(crate) fn run_in_thread_pool_with_globals<
184184 use rustc_data_structures::defer;
185185 use rustc_data_structures::sync::FromDyn;
186186 use rustc_middle::ty::tls;
187 use rustc_query_impl::QueryCtxt;
188 use rustc_query_system::query::{QueryContext, break_query_cycles};
187 use rustc_query_impl::{QueryCtxt, break_query_cycles};
189188
190189 let thread_stack_size = init_stack_size(thread_builder_diag);
191190
compiler/rustc_middle/src/query/mod.rs-1
......@@ -13,7 +13,6 @@ mod keys;
1313pub mod on_disk_cache;
1414#[macro_use]
1515pub mod plumbing;
16pub mod values;
1716
1817pub fn describe_as_module(def_id: impl Into<LocalDefId>, tcx: TyCtxt<'_>) -> String {
1918 let def_id = def_id.into();
compiler/rustc_middle/src/query/values.rs deleted-422
......@@ -1,422 +0,0 @@
1use std::collections::VecDeque;
2use std::fmt::Write;
3use std::ops::ControlFlow;
4
5use rustc_data_structures::fx::FxHashSet;
6use rustc_errors::codes::*;
7use rustc_errors::{Applicability, MultiSpan, pluralize, struct_span_code_err};
8use rustc_hir as hir;
9use rustc_hir::def::{DefKind, Res};
10use rustc_query_system::query::{CycleError, report_cycle};
11use rustc_span::def_id::LocalDefId;
12use rustc_span::{ErrorGuaranteed, Span};
13
14use crate::dep_graph::dep_kinds;
15use crate::query::plumbing::CyclePlaceholder;
16use crate::ty::{self, Representability, Ty, TyCtxt};
17
18pub trait Value<'tcx>: Sized {
19 fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed)
20 -> Self;
21}
22
23impl<'tcx, T> Value<'tcx> for T {
24 default fn from_cycle_error(
25 tcx: TyCtxt<'tcx>,
26 cycle_error: &CycleError,
27 _guar: ErrorGuaranteed,
28 ) -> T {
29 tcx.sess.dcx().abort_if_errors();
30 bug!(
31 "<{} as Value>::from_cycle_error called without errors: {:#?}",
32 std::any::type_name::<T>(),
33 cycle_error.cycle,
34 );
35 }
36}
37
38impl<'tcx> Value<'tcx> for Ty<'_> {
39 fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self {
40 // SAFETY: This is never called when `Self` is not `Ty<'tcx>`.
41 // FIXME: Represent the above fact in the trait system somehow.
42 unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(Ty::new_error(tcx, guar)) }
43 }
44}
45
46impl<'tcx> Value<'tcx> for Result<ty::EarlyBinder<'_, Ty<'_>>, CyclePlaceholder> {
47 fn from_cycle_error(_tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self {
48 Err(CyclePlaceholder(guar))
49 }
50}
51
52impl<'tcx> Value<'tcx> for ty::SymbolName<'_> {
53 fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, _guar: ErrorGuaranteed) -> Self {
54 // SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`.
55 // FIXME: Represent the above fact in the trait system somehow.
56 unsafe {
57 std::mem::transmute::<ty::SymbolName<'tcx>, ty::SymbolName<'_>>(ty::SymbolName::new(
58 tcx, "<error>",
59 ))
60 }
61 }
62}
63
64impl<'tcx> Value<'tcx> for ty::Binder<'_, ty::FnSig<'_>> {
65 fn from_cycle_error(
66 tcx: TyCtxt<'tcx>,
67 cycle_error: &CycleError,
68 guar: ErrorGuaranteed,
69 ) -> Self {
70 let err = Ty::new_error(tcx, guar);
71
72 let arity = if let Some(info) = cycle_error.cycle.get(0)
73 && info.frame.dep_kind == dep_kinds::fn_sig
74 && let Some(def_id) = info.frame.def_id
75 && let Some(node) = tcx.hir_get_if_local(def_id)
76 && let Some(sig) = node.fn_sig()
77 {
78 sig.decl.inputs.len()
79 } else {
80 tcx.dcx().abort_if_errors();
81 unreachable!()
82 };
83
84 let fn_sig = ty::Binder::dummy(tcx.mk_fn_sig(
85 std::iter::repeat_n(err, arity),
86 err,
87 false,
88 rustc_hir::Safety::Safe,
89 rustc_abi::ExternAbi::Rust,
90 ));
91
92 // SAFETY: This is never called when `Self` is not `ty::Binder<'tcx, ty::FnSig<'tcx>>`.
93 // FIXME: Represent the above fact in the trait system somehow.
94 unsafe { std::mem::transmute::<ty::PolyFnSig<'tcx>, ty::Binder<'_, ty::FnSig<'_>>>(fn_sig) }
95 }
96}
97
98impl<'tcx> Value<'tcx> for Representability {
99 fn from_cycle_error(
100 tcx: TyCtxt<'tcx>,
101 cycle_error: &CycleError,
102 _guar: ErrorGuaranteed,
103 ) -> Self {
104 let mut item_and_field_ids = Vec::new();
105 let mut representable_ids = FxHashSet::default();
106 for info in &cycle_error.cycle {
107 if info.frame.dep_kind == dep_kinds::representability
108 && let Some(field_id) = info.frame.def_id
109 && let Some(field_id) = field_id.as_local()
110 && let Some(DefKind::Field) = info.frame.info.def_kind
111 {
112 let parent_id = tcx.parent(field_id.to_def_id());
113 let item_id = match tcx.def_kind(parent_id) {
114 DefKind::Variant => tcx.parent(parent_id),
115 _ => parent_id,
116 };
117 item_and_field_ids.push((item_id.expect_local(), field_id));
118 }
119 }
120 for info in &cycle_error.cycle {
121 if info.frame.dep_kind == dep_kinds::representability_adt_ty
122 && let Some(def_id) = info.frame.def_id_for_ty_in_cycle
123 && let Some(def_id) = def_id.as_local()
124 && !item_and_field_ids.iter().any(|&(id, _)| id == def_id)
125 {
126 representable_ids.insert(def_id);
127 }
128 }
129 let guar = recursive_type_error(tcx, item_and_field_ids, &representable_ids);
130 Representability::Infinite(guar)
131 }
132}
133
134impl<'tcx> Value<'tcx> for ty::EarlyBinder<'_, Ty<'_>> {
135 fn from_cycle_error(
136 tcx: TyCtxt<'tcx>,
137 cycle_error: &CycleError,
138 guar: ErrorGuaranteed,
139 ) -> Self {
140 ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle_error, guar))
141 }
142}
143
144impl<'tcx> Value<'tcx> for ty::EarlyBinder<'_, ty::Binder<'_, ty::FnSig<'_>>> {
145 fn from_cycle_error(
146 tcx: TyCtxt<'tcx>,
147 cycle_error: &CycleError,
148 guar: ErrorGuaranteed,
149 ) -> Self {
150 ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle_error, guar))
151 }
152}
153
154impl<'tcx> Value<'tcx> for &[ty::Variance] {
155 fn from_cycle_error(
156 tcx: TyCtxt<'tcx>,
157 cycle_error: &CycleError,
158 _guar: ErrorGuaranteed,
159 ) -> Self {
160 search_for_cycle_permutation(
161 &cycle_error.cycle,
162 |cycle| {
163 if let Some(info) = cycle.get(0)
164 && info.frame.dep_kind == dep_kinds::variances_of
165 && let Some(def_id) = info.frame.def_id
166 {
167 let n = tcx.generics_of(def_id).own_params.len();
168 ControlFlow::Break(vec![ty::Bivariant; n].leak())
169 } else {
170 ControlFlow::Continue(())
171 }
172 },
173 || {
174 span_bug!(
175 cycle_error.usage.as_ref().unwrap().0,
176 "only `variances_of` returns `&[ty::Variance]`"
177 )
178 },
179 )
180 }
181}
182
183// Take a cycle of `Q` and try `try_cycle` on every permutation, falling back to `otherwise`.
184fn search_for_cycle_permutation<Q, T>(
185 cycle: &[Q],
186 try_cycle: impl Fn(&mut VecDeque<&Q>) -> ControlFlow<T, ()>,
187 otherwise: impl FnOnce() -> T,
188) -> T {
189 let mut cycle: VecDeque<_> = cycle.iter().collect();
190 for _ in 0..cycle.len() {
191 match try_cycle(&mut cycle) {
192 ControlFlow::Continue(_) => {
193 cycle.rotate_left(1);
194 }
195 ControlFlow::Break(t) => return t,
196 }
197 }
198
199 otherwise()
200}
201
202impl<'tcx, T> Value<'tcx> for Result<T, &'_ ty::layout::LayoutError<'_>> {
203 fn from_cycle_error(
204 tcx: TyCtxt<'tcx>,
205 cycle_error: &CycleError,
206 _guar: ErrorGuaranteed,
207 ) -> Self {
208 let diag = search_for_cycle_permutation(
209 &cycle_error.cycle,
210 |cycle| {
211 if cycle[0].frame.dep_kind == dep_kinds::layout_of
212 && let Some(def_id) = cycle[0].frame.def_id_for_ty_in_cycle
213 && let Some(def_id) = def_id.as_local()
214 && let def_kind = tcx.def_kind(def_id)
215 && matches!(def_kind, DefKind::Closure)
216 && let Some(coroutine_kind) = tcx.coroutine_kind(def_id)
217 {
218 // FIXME: `def_span` for an fn-like coroutine will point to the fn's body
219 // due to interactions between the desugaring into a closure expr and the
220 // def_span code. I'm not motivated to fix it, because I tried and it was
221 // not working, so just hack around it by grabbing the parent fn's span.
222 let span = if coroutine_kind.is_fn_like() {
223 tcx.def_span(tcx.local_parent(def_id))
224 } else {
225 tcx.def_span(def_id)
226 };
227 let mut diag = struct_span_code_err!(
228 tcx.sess.dcx(),
229 span,
230 E0733,
231 "recursion in {} {} requires boxing",
232 tcx.def_kind_descr_article(def_kind, def_id.to_def_id()),
233 tcx.def_kind_descr(def_kind, def_id.to_def_id()),
234 );
235 for (i, info) in cycle.iter().enumerate() {
236 if info.frame.dep_kind != dep_kinds::layout_of {
237 continue;
238 }
239 let Some(frame_def_id) = info.frame.def_id_for_ty_in_cycle else {
240 continue;
241 };
242 let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else {
243 continue;
244 };
245 let frame_span =
246 info.frame.info.default_span(cycle[(i + 1) % cycle.len()].span);
247 if frame_span.is_dummy() {
248 continue;
249 }
250 if i == 0 {
251 diag.span_label(frame_span, "recursive call here");
252 } else {
253 let coroutine_span: Span = if frame_coroutine_kind.is_fn_like() {
254 tcx.def_span(tcx.parent(frame_def_id))
255 } else {
256 tcx.def_span(frame_def_id)
257 };
258 let mut multispan = MultiSpan::from_span(coroutine_span);
259 multispan
260 .push_span_label(frame_span, "...leading to this recursive call");
261 diag.span_note(
262 multispan,
263 format!("which leads to this {}", tcx.def_descr(frame_def_id)),
264 );
265 }
266 }
267 // FIXME: We could report a structured suggestion if we had
268 // enough info here... Maybe we can use a hacky HIR walker.
269 if matches!(
270 coroutine_kind,
271 hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)
272 ) {
273 diag.note("a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future");
274 }
275
276 ControlFlow::Break(diag)
277 } else {
278 ControlFlow::Continue(())
279 }
280 },
281 || report_cycle(tcx.sess, cycle_error),
282 );
283
284 let guar = diag.emit();
285
286 // tcx.arena.alloc cannot be used because we are not allowed to use &'tcx LayoutError under
287 // min_specialization. Since this is an error path anyways, leaking doesn't matter (and really,
288 // tcx.arena.alloc is pretty much equal to leaking).
289 Err(Box::leak(Box::new(ty::layout::LayoutError::Cycle(guar))))
290 }
291}
292
293// item_and_field_ids should form a cycle where each field contains the
294// type in the next element in the list
295fn recursive_type_error(
296 tcx: TyCtxt<'_>,
297 mut item_and_field_ids: Vec<(LocalDefId, LocalDefId)>,
298 representable_ids: &FxHashSet<LocalDefId>,
299) -> ErrorGuaranteed {
300 const ITEM_LIMIT: usize = 5;
301
302 // Rotate the cycle so that the item with the lowest span is first
303 let start_index = item_and_field_ids
304 .iter()
305 .enumerate()
306 .min_by_key(|&(_, &(id, _))| tcx.def_span(id))
307 .unwrap()
308 .0;
309 item_and_field_ids.rotate_left(start_index);
310
311 let cycle_len = item_and_field_ids.len();
312 let show_cycle_len = cycle_len.min(ITEM_LIMIT);
313
314 let mut err_span = MultiSpan::from_spans(
315 item_and_field_ids[..show_cycle_len]
316 .iter()
317 .map(|(id, _)| tcx.def_span(id.to_def_id()))
318 .collect(),
319 );
320 let mut suggestion = Vec::with_capacity(show_cycle_len * 2);
321 for i in 0..show_cycle_len {
322 let (_, field_id) = item_and_field_ids[i];
323 let (next_item_id, _) = item_and_field_ids[(i + 1) % cycle_len];
324 // Find the span(s) that contain the next item in the cycle
325 let hir::Node::Field(field) = tcx.hir_node_by_def_id(field_id) else {
326 bug!("expected field")
327 };
328 let mut found = Vec::new();
329 find_item_ty_spans(tcx, field.ty, next_item_id, &mut found, representable_ids);
330
331 // Couldn't find the type. Maybe it's behind a type alias?
332 // In any case, we'll just suggest boxing the whole field.
333 if found.is_empty() {
334 found.push(field.ty.span);
335 }
336
337 for span in found {
338 err_span.push_span_label(span, "recursive without indirection");
339 // FIXME(compiler-errors): This suggestion might be erroneous if Box is shadowed
340 suggestion.push((span.shrink_to_lo(), "Box<".to_string()));
341 suggestion.push((span.shrink_to_hi(), ">".to_string()));
342 }
343 }
344 let items_list = {
345 let mut s = String::new();
346 for (i, &(item_id, _)) in item_and_field_ids.iter().enumerate() {
347 let path = tcx.def_path_str(item_id);
348 write!(&mut s, "`{path}`").unwrap();
349 if i == (ITEM_LIMIT - 1) && cycle_len > ITEM_LIMIT {
350 write!(&mut s, " and {} more", cycle_len - 5).unwrap();
351 break;
352 }
353 if cycle_len > 1 && i < cycle_len - 2 {
354 s.push_str(", ");
355 } else if cycle_len > 1 && i == cycle_len - 2 {
356 s.push_str(" and ")
357 }
358 }
359 s
360 };
361 struct_span_code_err!(
362 tcx.dcx(),
363 err_span,
364 E0072,
365 "recursive type{} {} {} infinite size",
366 pluralize!(cycle_len),
367 items_list,
368 pluralize!("has", cycle_len),
369 )
370 .with_multipart_suggestion(
371 "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle",
372 suggestion,
373 Applicability::HasPlaceholders,
374 )
375 .emit()
376}
377
378fn find_item_ty_spans(
379 tcx: TyCtxt<'_>,
380 ty: &hir::Ty<'_>,
381 needle: LocalDefId,
382 spans: &mut Vec<Span>,
383 seen_representable: &FxHashSet<LocalDefId>,
384) {
385 match ty.kind {
386 hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
387 if let Res::Def(kind, def_id) = path.res
388 && matches!(kind, DefKind::Enum | DefKind::Struct | DefKind::Union)
389 {
390 let check_params = def_id.as_local().is_none_or(|def_id| {
391 if def_id == needle {
392 spans.push(ty.span);
393 }
394 seen_representable.contains(&def_id)
395 });
396 if check_params && let Some(args) = path.segments.last().unwrap().args {
397 let params_in_repr = tcx.params_in_repr(def_id);
398 // the domain size check is needed because the HIR may not be well-formed at this point
399 for (i, arg) in args.args.iter().enumerate().take(params_in_repr.domain_size())
400 {
401 if let hir::GenericArg::Type(ty) = arg
402 && params_in_repr.contains(i as u32)
403 {
404 find_item_ty_spans(
405 tcx,
406 ty.as_unambig_ty(),
407 needle,
408 spans,
409 seen_representable,
410 );
411 }
412 }
413 }
414 }
415 }
416 hir::TyKind::Array(ty, _) => find_item_ty_spans(tcx, ty, needle, spans, seen_representable),
417 hir::TyKind::Tup(tys) => {
418 tys.iter().for_each(|ty| find_item_ty_spans(tcx, ty, needle, spans, seen_representable))
419 }
420 _ => {}
421 }
422}
compiler/rustc_next_trait_solver/src/solve/trait_goals.rs+3
......@@ -687,6 +687,9 @@ where
687687 ///
688688 /// because these impls overlap, and I'd rather not build a coherence hack for
689689 /// this harmless overlap.
690 ///
691 /// This trait is indirectly exposed on stable, so do *not* extend the set of types that
692 /// implement the trait without FCP!
690693 fn consider_builtin_bikeshed_guaranteed_no_drop_candidate(
691694 ecx: &mut EvalCtxt<'_, D>,
692695 goal: Goal<I, Self>,
compiler/rustc_query_impl/Cargo.toml+3
......@@ -6,6 +6,7 @@ edition = "2024"
66[dependencies]
77# tidy-alphabetical-start
88measureme = "12.0.1"
9rustc_abi = { path = "../rustc_abi" }
910rustc_data_structures = { path = "../rustc_data_structures" }
1011rustc_errors = { path = "../rustc_errors" }
1112rustc_hashes = { path = "../rustc_hashes" }
......@@ -15,6 +16,8 @@ rustc_macros = { path = "../rustc_macros" }
1516rustc_middle = { path = "../rustc_middle" }
1617rustc_query_system = { path = "../rustc_query_system" }
1718rustc_serialize = { path = "../rustc_serialize" }
19rustc_session = { path = "../rustc_session" }
1820rustc_span = { path = "../rustc_span" }
21rustc_thread_pool = { path = "../rustc_thread_pool" }
1922tracing = "0.1"
2023# tidy-alphabetical-end
compiler/rustc_query_impl/src/error.rs+57
......@@ -1,3 +1,4 @@
1use rustc_errors::codes::*;
12use rustc_hir::limit::Limit;
23use rustc_macros::{Diagnostic, Subdiagnostic};
34use rustc_span::{Span, Symbol};
......@@ -22,3 +23,59 @@ pub(crate) struct QueryOverflowNote {
2223 pub desc: String,
2324 pub depth: usize,
2425}
26
27#[derive(Subdiagnostic)]
28#[note("...which requires {$desc}...")]
29pub(crate) struct CycleStack {
30 #[primary_span]
31 pub span: Span,
32 pub desc: String,
33}
34
35#[derive(Subdiagnostic)]
36pub(crate) enum StackCount {
37 #[note("...which immediately requires {$stack_bottom} again")]
38 Single,
39 #[note("...which again requires {$stack_bottom}, completing the cycle")]
40 Multiple,
41}
42
43#[derive(Subdiagnostic)]
44pub(crate) enum Alias {
45 #[note("type aliases cannot be recursive")]
46 #[help("consider using a struct, enum, or union instead to break the cycle")]
47 #[help(
48 "see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information"
49 )]
50 Ty,
51 #[note("trait aliases cannot be recursive")]
52 Trait,
53}
54
55#[derive(Subdiagnostic)]
56#[note("cycle used when {$usage}")]
57pub(crate) struct CycleUsage {
58 #[primary_span]
59 pub span: Span,
60 pub usage: String,
61}
62
63#[derive(Diagnostic)]
64#[diag("cycle detected when {$stack_bottom}", code = E0391)]
65pub(crate) struct Cycle {
66 #[primary_span]
67 pub span: Span,
68 pub stack_bottom: String,
69 #[subdiagnostic]
70 pub cycle_stack: Vec<CycleStack>,
71 #[subdiagnostic]
72 pub stack_count: StackCount,
73 #[subdiagnostic]
74 pub alias: Option<Alias>,
75 #[subdiagnostic]
76 pub cycle_usage: Option<CycleUsage>,
77 #[note(
78 "see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information"
79 )]
80 pub note_span: (),
81}
compiler/rustc_query_impl/src/execution.rs+4-4
......@@ -9,13 +9,13 @@ use rustc_middle::dep_graph::DepsType;
99use rustc_middle::ty::TyCtxt;
1010use rustc_query_system::dep_graph::{DepGraphData, DepNodeKey, HasDepContext};
1111use rustc_query_system::query::{
12 ActiveKeyStatus, CycleError, CycleErrorHandling, QueryCache, QueryContext, QueryJob,
13 QueryJobId, QueryJobInfo, QueryLatch, QueryMap, QueryMode, QueryStackDeferred, QueryStackFrame,
14 QueryState, incremental_verify_ich, report_cycle,
12 ActiveKeyStatus, CycleError, CycleErrorHandling, QueryCache, QueryJob, QueryJobId, QueryLatch,
13 QueryMode, QueryStackDeferred, QueryStackFrame, QueryState, incremental_verify_ich,
1514};
1615use rustc_span::{DUMMY_SP, Span};
1716
1817use crate::dep_graph::{DepContext, DepNode, DepNodeIndex};
18use crate::job::{QueryJobInfo, QueryMap, find_cycle_in_stack, report_cycle};
1919use crate::{QueryCtxt, QueryFlags, SemiDynamicQueryDispatcher};
2020
2121#[inline]
......@@ -218,7 +218,7 @@ fn cycle_error<'tcx, C: QueryCache, const FLAGS: QueryFlags>(
218218 .ok()
219219 .expect("failed to collect active queries");
220220
221 let error = try_execute.find_cycle_in_stack(query_map, &qcx.current_query_job(), span);
221 let error = find_cycle_in_stack(try_execute, query_map, &qcx.current_query_job(), span);
222222 (mk_cycle(query, qcx, error.lift()), None)
223223}
224224
compiler/rustc_query_impl/src/job.rs created+500
......@@ -0,0 +1,500 @@
1use std::io::Write;
2use std::iter;
3use std::sync::Arc;
4
5use rustc_data_structures::fx::{FxHashMap, FxHashSet};
6use rustc_errors::{Diag, DiagCtxtHandle};
7use rustc_hir::def::DefKind;
8use rustc_query_system::query::{
9 CycleError, QueryInfo, QueryJob, QueryJobId, QueryLatch, QueryStackDeferred, QueryStackFrame,
10 QueryWaiter,
11};
12use rustc_session::Session;
13use rustc_span::{DUMMY_SP, Span};
14
15use crate::QueryCtxt;
16use crate::dep_graph::DepContext;
17
18/// Map from query job IDs to job information collected by
19/// `collect_active_jobs_from_all_queries`.
20pub type QueryMap<'tcx> = FxHashMap<QueryJobId, QueryJobInfo<'tcx>>;
21
22fn query_job_id_frame<'a, 'tcx>(
23 id: QueryJobId,
24 map: &'a QueryMap<'tcx>,
25) -> QueryStackFrame<QueryStackDeferred<'tcx>> {
26 map.get(&id).unwrap().frame.clone()
27}
28
29fn query_job_id_span<'a, 'tcx>(id: QueryJobId, map: &'a QueryMap<'tcx>) -> Span {
30 map.get(&id).unwrap().job.span
31}
32
33fn query_job_id_parent<'a, 'tcx>(id: QueryJobId, map: &'a QueryMap<'tcx>) -> Option<QueryJobId> {
34 map.get(&id).unwrap().job.parent
35}
36
37fn query_job_id_latch<'a, 'tcx>(
38 id: QueryJobId,
39 map: &'a QueryMap<'tcx>,
40) -> Option<&'a QueryLatch<'tcx>> {
41 map.get(&id).unwrap().job.latch.as_ref()
42}
43
44#[derive(Clone, Debug)]
45pub struct QueryJobInfo<'tcx> {
46 pub frame: QueryStackFrame<QueryStackDeferred<'tcx>>,
47 pub job: QueryJob<'tcx>,
48}
49
50pub(crate) fn find_cycle_in_stack<'tcx>(
51 id: QueryJobId,
52 query_map: QueryMap<'tcx>,
53 current_job: &Option<QueryJobId>,
54 span: Span,
55) -> CycleError<QueryStackDeferred<'tcx>> {
56 // Find the waitee amongst `current_job` parents
57 let mut cycle = Vec::new();
58 let mut current_job = Option::clone(current_job);
59
60 while let Some(job) = current_job {
61 let info = query_map.get(&job).unwrap();
62 cycle.push(QueryInfo { span: info.job.span, frame: info.frame.clone() });
63
64 if job == id {
65 cycle.reverse();
66
67 // This is the end of the cycle
68 // The span entry we included was for the usage
69 // of the cycle itself, and not part of the cycle
70 // Replace it with the span which caused the cycle to form
71 cycle[0].span = span;
72 // Find out why the cycle itself was used
73 let usage = info
74 .job
75 .parent
76 .as_ref()
77 .map(|parent| (info.job.span, query_job_id_frame(*parent, &query_map)));
78 return CycleError { usage, cycle };
79 }
80
81 current_job = info.job.parent;
82 }
83
84 panic!("did not find a cycle")
85}
86
87#[cold]
88#[inline(never)]
89pub(crate) fn find_dep_kind_root<'tcx>(
90 id: QueryJobId,
91 query_map: QueryMap<'tcx>,
92) -> (QueryJobInfo<'tcx>, usize) {
93 let mut depth = 1;
94 let info = query_map.get(&id).unwrap();
95 let dep_kind = info.frame.dep_kind;
96 let mut current_id = info.job.parent;
97 let mut last_layout = (info.clone(), depth);
98
99 while let Some(id) = current_id {
100 let info = query_map.get(&id).unwrap();
101 if info.frame.dep_kind == dep_kind {
102 depth += 1;
103 last_layout = (info.clone(), depth);
104 }
105 current_id = info.job.parent;
106 }
107 last_layout
108}
109
110/// A resumable waiter of a query. The usize is the index into waiters in the query's latch
111type Waiter = (QueryJobId, usize);
112
113/// Visits all the non-resumable and resumable waiters of a query.
114/// Only waiters in a query are visited.
115/// `visit` is called for every waiter and is passed a query waiting on `query_ref`
116/// and a span indicating the reason the query waited on `query_ref`.
117/// If `visit` returns Some, this function returns.
118/// For visits of non-resumable waiters it returns the return value of `visit`.
119/// For visits of resumable waiters it returns Some(Some(Waiter)) which has the
120/// required information to resume the waiter.
121/// If all `visit` calls returns None, this function also returns None.
122fn visit_waiters<'tcx, F>(
123 query_map: &QueryMap<'tcx>,
124 query: QueryJobId,
125 mut visit: F,
126) -> Option<Option<Waiter>>
127where
128 F: FnMut(Span, QueryJobId) -> Option<Option<Waiter>>,
129{
130 // Visit the parent query which is a non-resumable waiter since it's on the same stack
131 if let Some(parent) = query_job_id_parent(query, query_map)
132 && let Some(cycle) = visit(query_job_id_span(query, query_map), parent)
133 {
134 return Some(cycle);
135 }
136
137 // Visit the explicit waiters which use condvars and are resumable
138 if let Some(latch) = query_job_id_latch(query, query_map) {
139 for (i, waiter) in latch.info.lock().waiters.iter().enumerate() {
140 if let Some(waiter_query) = waiter.query {
141 if visit(waiter.span, waiter_query).is_some() {
142 // Return a value which indicates that this waiter can be resumed
143 return Some(Some((query, i)));
144 }
145 }
146 }
147 }
148
149 None
150}
151
152/// Look for query cycles by doing a depth first search starting at `query`.
153/// `span` is the reason for the `query` to execute. This is initially DUMMY_SP.
154/// If a cycle is detected, this initial value is replaced with the span causing
155/// the cycle.
156fn cycle_check<'tcx>(
157 query_map: &QueryMap<'tcx>,
158 query: QueryJobId,
159 span: Span,
160 stack: &mut Vec<(Span, QueryJobId)>,
161 visited: &mut FxHashSet<QueryJobId>,
162) -> Option<Option<Waiter>> {
163 if !visited.insert(query) {
164 return if let Some(p) = stack.iter().position(|q| q.1 == query) {
165 // We detected a query cycle, fix up the initial span and return Some
166
167 // Remove previous stack entries
168 stack.drain(0..p);
169 // Replace the span for the first query with the cycle cause
170 stack[0].0 = span;
171 Some(None)
172 } else {
173 None
174 };
175 }
176
177 // Query marked as visited is added it to the stack
178 stack.push((span, query));
179
180 // Visit all the waiters
181 let r = visit_waiters(query_map, query, |span, successor| {
182 cycle_check(query_map, successor, span, stack, visited)
183 });
184
185 // Remove the entry in our stack if we didn't find a cycle
186 if r.is_none() {
187 stack.pop();
188 }
189
190 r
191}
192
193/// Finds out if there's a path to the compiler root (aka. code which isn't in a query)
194/// from `query` without going through any of the queries in `visited`.
195/// This is achieved with a depth first search.
196fn connected_to_root<'tcx>(
197 query_map: &QueryMap<'tcx>,
198 query: QueryJobId,
199 visited: &mut FxHashSet<QueryJobId>,
200) -> bool {
201 // We already visited this or we're deliberately ignoring it
202 if !visited.insert(query) {
203 return false;
204 }
205
206 // This query is connected to the root (it has no query parent), return true
207 if query_job_id_parent(query, query_map).is_none() {
208 return true;
209 }
210
211 visit_waiters(query_map, query, |_, successor| {
212 connected_to_root(query_map, successor, visited).then_some(None)
213 })
214 .is_some()
215}
216
217// Deterministically pick an query from a list
218fn pick_query<'a, 'tcx, T, F>(query_map: &QueryMap<'tcx>, queries: &'a [T], f: F) -> &'a T
219where
220 F: Fn(&T) -> (Span, QueryJobId),
221{
222 // Deterministically pick an entry point
223 // FIXME: Sort this instead
224 queries
225 .iter()
226 .min_by_key(|v| {
227 let (span, query) = f(v);
228 let hash = query_job_id_frame(query, query_map).hash;
229 // Prefer entry points which have valid spans for nicer error messages
230 // We add an integer to the tuple ensuring that entry points
231 // with valid spans are picked first
232 let span_cmp = if span == DUMMY_SP { 1 } else { 0 };
233 (span_cmp, hash)
234 })
235 .unwrap()
236}
237
238/// Looks for query cycles starting from the last query in `jobs`.
239/// If a cycle is found, all queries in the cycle is removed from `jobs` and
240/// the function return true.
241/// If a cycle was not found, the starting query is removed from `jobs` and
242/// the function returns false.
243fn remove_cycle<'tcx>(
244 query_map: &QueryMap<'tcx>,
245 jobs: &mut Vec<QueryJobId>,
246 wakelist: &mut Vec<Arc<QueryWaiter<'tcx>>>,
247) -> bool {
248 let mut visited = FxHashSet::default();
249 let mut stack = Vec::new();
250 // Look for a cycle starting with the last query in `jobs`
251 if let Some(waiter) =
252 cycle_check(query_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited)
253 {
254 // The stack is a vector of pairs of spans and queries; reverse it so that
255 // the earlier entries require later entries
256 let (mut spans, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip();
257
258 // Shift the spans so that queries are matched with the span for their waitee
259 spans.rotate_right(1);
260
261 // Zip them back together
262 let mut stack: Vec<_> = iter::zip(spans, queries).collect();
263
264 // Remove the queries in our cycle from the list of jobs to look at
265 for r in &stack {
266 if let Some(pos) = jobs.iter().position(|j| j == &r.1) {
267 jobs.remove(pos);
268 }
269 }
270
271 // Find the queries in the cycle which are
272 // connected to queries outside the cycle
273 let entry_points = stack
274 .iter()
275 .filter_map(|&(span, query)| {
276 if query_job_id_parent(query, query_map).is_none() {
277 // This query is connected to the root (it has no query parent)
278 Some((span, query, None))
279 } else {
280 let mut waiters = Vec::new();
281 // Find all the direct waiters who lead to the root
282 visit_waiters(query_map, query, |span, waiter| {
283 // Mark all the other queries in the cycle as already visited
284 let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1));
285
286 if connected_to_root(query_map, waiter, &mut visited) {
287 waiters.push((span, waiter));
288 }
289
290 None
291 });
292 if waiters.is_empty() {
293 None
294 } else {
295 // Deterministically pick one of the waiters to show to the user
296 let waiter = *pick_query(query_map, &waiters, |s| *s);
297 Some((span, query, Some(waiter)))
298 }
299 }
300 })
301 .collect::<Vec<(Span, QueryJobId, Option<(Span, QueryJobId)>)>>();
302
303 // Deterministically pick an entry point
304 let (_, entry_point, usage) = pick_query(query_map, &entry_points, |e| (e.0, e.1));
305
306 // Shift the stack so that our entry point is first
307 let entry_point_pos = stack.iter().position(|(_, query)| query == entry_point);
308 if let Some(pos) = entry_point_pos {
309 stack.rotate_left(pos);
310 }
311
312 let usage =
313 usage.as_ref().map(|(span, query)| (*span, query_job_id_frame(*query, query_map)));
314
315 // Create the cycle error
316 let error = CycleError {
317 usage,
318 cycle: stack
319 .iter()
320 .map(|&(s, ref q)| QueryInfo { span: s, frame: query_job_id_frame(*q, query_map) })
321 .collect(),
322 };
323
324 // We unwrap `waiter` here since there must always be one
325 // edge which is resumable / waited using a query latch
326 let (waitee_query, waiter_idx) = waiter.unwrap();
327
328 // Extract the waiter we want to resume
329 let waiter =
330 query_job_id_latch(waitee_query, query_map).unwrap().extract_waiter(waiter_idx);
331
332 // Set the cycle error so it will be picked up when resumed
333 *waiter.cycle.lock() = Some(error);
334
335 // Put the waiter on the list of things to resume
336 wakelist.push(waiter);
337
338 true
339 } else {
340 false
341 }
342}
343
344/// Detects query cycles by using depth first search over all active query jobs.
345/// If a query cycle is found it will break the cycle by finding an edge which
346/// uses a query latch and then resuming that waiter.
347/// There may be multiple cycles involved in a deadlock, so this searches
348/// all active queries for cycles before finally resuming all the waiters at once.
349pub fn break_query_cycles<'tcx>(query_map: QueryMap<'tcx>, registry: &rustc_thread_pool::Registry) {
350 let mut wakelist = Vec::new();
351 // It is OK per the comments:
352 // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798854932
353 // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798866392
354 #[allow(rustc::potential_query_instability)]
355 let mut jobs: Vec<QueryJobId> = query_map.keys().cloned().collect();
356
357 let mut found_cycle = false;
358
359 while jobs.len() > 0 {
360 if remove_cycle(&query_map, &mut jobs, &mut wakelist) {
361 found_cycle = true;
362 }
363 }
364
365 // Check that a cycle was found. It is possible for a deadlock to occur without
366 // a query cycle if a query which can be waited on uses Rayon to do multithreading
367 // internally. Such a query (X) may be executing on 2 threads (A and B) and A may
368 // wait using Rayon on B. Rayon may then switch to executing another query (Y)
369 // which in turn will wait on X causing a deadlock. We have a false dependency from
370 // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here
371 // only considers the true dependency and won't detect a cycle.
372 if !found_cycle {
373 panic!(
374 "deadlock detected as we're unable to find a query cycle to break\n\
375 current query map:\n{:#?}",
376 query_map
377 );
378 }
379
380 // Mark all the thread we're about to wake up as unblocked. This needs to be done before
381 // we wake the threads up as otherwise Rayon could detect a deadlock if a thread we
382 // resumed fell asleep and this thread had yet to mark the remaining threads as unblocked.
383 for _ in 0..wakelist.len() {
384 rustc_thread_pool::mark_unblocked(registry);
385 }
386
387 for waiter in wakelist.into_iter() {
388 waiter.condvar.notify_one();
389 }
390}
391
392pub fn print_query_stack<'tcx>(
393 qcx: QueryCtxt<'tcx>,
394 mut current_query: Option<QueryJobId>,
395 dcx: DiagCtxtHandle<'_>,
396 limit_frames: Option<usize>,
397 mut file: Option<std::fs::File>,
398) -> usize {
399 // Be careful relying on global state here: this code is called from
400 // a panic hook, which means that the global `DiagCtxt` may be in a weird
401 // state if it was responsible for triggering the panic.
402 let mut count_printed = 0;
403 let mut count_total = 0;
404
405 // Make use of a partial query map if we fail to take locks collecting active queries.
406 let query_map = match qcx.collect_active_jobs_from_all_queries(false) {
407 Ok(query_map) => query_map,
408 Err(query_map) => query_map,
409 };
410
411 if let Some(ref mut file) = file {
412 let _ = writeln!(file, "\n\nquery stack during panic:");
413 }
414 while let Some(query) = current_query {
415 let Some(query_info) = query_map.get(&query) else {
416 break;
417 };
418 let query_extra = query_info.frame.info.extract();
419 if Some(count_printed) < limit_frames || limit_frames.is_none() {
420 // Only print to stderr as many stack frames as `num_frames` when present.
421 dcx.struct_failure_note(format!(
422 "#{} [{:?}] {}",
423 count_printed, query_info.frame.dep_kind, query_extra.description
424 ))
425 .with_span(query_info.job.span)
426 .emit();
427 count_printed += 1;
428 }
429
430 if let Some(ref mut file) = file {
431 let _ = writeln!(
432 file,
433 "#{} [{}] {}",
434 count_total,
435 qcx.tcx.dep_kind_vtable(query_info.frame.dep_kind).name,
436 query_extra.description
437 );
438 }
439
440 current_query = query_info.job.parent;
441 count_total += 1;
442 }
443
444 if let Some(ref mut file) = file {
445 let _ = writeln!(file, "end of query stack");
446 }
447 count_total
448}
449
450#[inline(never)]
451#[cold]
452pub(crate) fn report_cycle<'a>(
453 sess: &'a Session,
454 CycleError { usage, cycle: stack }: &CycleError,
455) -> Diag<'a> {
456 assert!(!stack.is_empty());
457
458 let span = stack[0].frame.info.default_span(stack[1 % stack.len()].span);
459
460 let mut cycle_stack = Vec::new();
461
462 use crate::error::StackCount;
463 let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple };
464
465 for i in 1..stack.len() {
466 let frame = &stack[i].frame;
467 let span = frame.info.default_span(stack[(i + 1) % stack.len()].span);
468 cycle_stack
469 .push(crate::error::CycleStack { span, desc: frame.info.description.to_owned() });
470 }
471
472 let mut cycle_usage = None;
473 if let Some((span, ref query)) = *usage {
474 cycle_usage = Some(crate::error::CycleUsage {
475 span: query.info.default_span(span),
476 usage: query.info.description.to_string(),
477 });
478 }
479
480 let alias =
481 if stack.iter().all(|entry| matches!(entry.frame.info.def_kind, Some(DefKind::TyAlias))) {
482 Some(crate::error::Alias::Ty)
483 } else if stack.iter().all(|entry| entry.frame.info.def_kind == Some(DefKind::TraitAlias)) {
484 Some(crate::error::Alias::Trait)
485 } else {
486 None
487 };
488
489 let cycle_diag = crate::error::Cycle {
490 span,
491 cycle_stack,
492 stack_bottom: stack[0].frame.info.description.to_owned(),
493 alias,
494 cycle_usage,
495 stack_count,
496 note_span: (),
497 };
498
499 sess.dcx().create_err(cycle_diag)
500}
compiler/rustc_query_impl/src/lib.rs+5-2
......@@ -19,24 +19,27 @@ use rustc_middle::queries::{
1919use rustc_middle::query::AsLocalKey;
2020use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache};
2121use rustc_middle::query::plumbing::{HashResult, QuerySystem, QuerySystemFns, QueryVTable};
22use rustc_middle::query::values::Value;
2322use rustc_middle::ty::TyCtxt;
2423use rustc_query_system::dep_graph::SerializedDepNodeIndex;
2524use rustc_query_system::query::{
26 CycleError, CycleErrorHandling, QueryCache, QueryMap, QueryMode, QueryState,
25 CycleError, CycleErrorHandling, QueryCache, QueryMode, QueryState,
2726};
2827use rustc_span::{ErrorGuaranteed, Span};
2928
29pub use crate::job::{QueryMap, break_query_cycles, print_query_stack};
3030pub use crate::plumbing::{QueryCtxt, query_key_hash_verify_all};
3131use crate::plumbing::{encode_all_query_results, try_mark_green};
3232use crate::profiling_support::QueryKeyStringCache;
3333pub use crate::profiling_support::alloc_self_profile_query_strings;
34use crate::values::Value;
3435
3536mod error;
3637mod execution;
38mod job;
3739#[macro_use]
3840mod plumbing;
3941mod profiling_support;
42mod values;
4043
4144#[derive(ConstParamTy)] // Allow this struct to be used for const-generic values.
4245#[derive(Clone, Copy, Debug, PartialEq, Eq)]
compiler/rustc_query_impl/src/plumbing.rs+22-21
......@@ -28,14 +28,15 @@ use rustc_middle::ty::tls::{self, ImplicitCtxt};
2828use rustc_middle::ty::{self, TyCtxt};
2929use rustc_query_system::dep_graph::{DepNodeKey, FingerprintStyle, HasDepContext};
3030use rustc_query_system::query::{
31 QueryCache, QueryContext, QueryJobId, QueryMap, QuerySideEffect, QueryStackDeferred,
32 QueryStackFrame, QueryStackFrameExtra,
31 QueryCache, QueryContext, QueryJobId, QuerySideEffect, QueryStackDeferred, QueryStackFrame,
32 QueryStackFrameExtra,
3333};
3434use rustc_serialize::{Decodable, Encodable};
3535use rustc_span::def_id::LOCAL_CRATE;
3636
3737use crate::error::{QueryOverflow, QueryOverflowNote};
3838use crate::execution::{all_inactive, force_query};
39use crate::job::{QueryMap, find_dep_kind_root};
3940use crate::{QueryDispatcherUnerased, QueryFlags, SemiDynamicQueryDispatcher};
4041
4142/// Implements [`QueryContext`] for use by [`rustc_query_system`], since that
......@@ -55,7 +56,7 @@ impl<'tcx> QueryCtxt<'tcx> {
5556 let query_map = self
5657 .collect_active_jobs_from_all_queries(true)
5758 .expect("failed to collect active queries");
58 let (info, depth) = job.find_dep_kind_root(query_map);
59 let (info, depth) = find_dep_kind_root(job, query_map);
5960
6061 let suggested_limit = match self.tcx.recursion_limit() {
6162 Limit(0) => Limit(2),
......@@ -116,23 +117,6 @@ impl<'tcx> QueryCtxt<'tcx> {
116117 tls::enter_context(&new_icx, compute)
117118 })
118119 }
119}
120
121impl<'tcx> HasDepContext for QueryCtxt<'tcx> {
122 type Deps = DepsType;
123 type DepContext = TyCtxt<'tcx>;
124
125 #[inline]
126 fn dep_context(&self) -> &Self::DepContext {
127 &self.tcx
128 }
129}
130
131impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> {
132 #[inline]
133 fn jobserver_proxy(&self) -> &Proxy {
134 &self.tcx.jobserver_proxy
135 }
136120
137121 /// Returns a map of currently active query jobs, collected from all queries.
138122 ///
......@@ -144,7 +128,7 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> {
144128 /// Prefer passing `false` to `require_complete` to avoid potential deadlocks,
145129 /// especially when called from within a deadlock handler, unless a
146130 /// complete map is needed and no deadlock is possible at this call site.
147 fn collect_active_jobs_from_all_queries(
131 pub fn collect_active_jobs_from_all_queries(
148132 self,
149133 require_complete: bool,
150134 ) -> Result<QueryMap<'tcx>, QueryMap<'tcx>> {
......@@ -159,6 +143,23 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> {
159143
160144 if complete { Ok(jobs) } else { Err(jobs) }
161145 }
146}
147
148impl<'tcx> HasDepContext for QueryCtxt<'tcx> {
149 type Deps = DepsType;
150 type DepContext = TyCtxt<'tcx>;
151
152 #[inline]
153 fn dep_context(&self) -> &Self::DepContext {
154 &self.tcx
155 }
156}
157
158impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> {
159 #[inline]
160 fn jobserver_proxy(&self) -> &Proxy {
161 &self.tcx.jobserver_proxy
162 }
162163
163164 // Interactions with on_disk_cache
164165 fn load_side_effect(
compiler/rustc_query_impl/src/values.rs created+424
......@@ -0,0 +1,424 @@
1use std::collections::VecDeque;
2use std::fmt::Write;
3use std::ops::ControlFlow;
4
5use rustc_data_structures::fx::FxHashSet;
6use rustc_errors::codes::*;
7use rustc_errors::{Applicability, MultiSpan, pluralize, struct_span_code_err};
8use rustc_hir as hir;
9use rustc_hir::def::{DefKind, Res};
10use rustc_middle::dep_graph::dep_kinds;
11use rustc_middle::query::plumbing::CyclePlaceholder;
12use rustc_middle::ty::{self, Representability, Ty, TyCtxt};
13use rustc_middle::{bug, span_bug};
14use rustc_query_system::query::CycleError;
15use rustc_span::def_id::LocalDefId;
16use rustc_span::{ErrorGuaranteed, Span};
17
18use crate::job::report_cycle;
19
20pub(crate) trait Value<'tcx>: Sized {
21 fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed)
22 -> Self;
23}
24
25impl<'tcx, T> Value<'tcx> for T {
26 default fn from_cycle_error(
27 tcx: TyCtxt<'tcx>,
28 cycle_error: &CycleError,
29 _guar: ErrorGuaranteed,
30 ) -> T {
31 tcx.sess.dcx().abort_if_errors();
32 bug!(
33 "<{} as Value>::from_cycle_error called without errors: {:#?}",
34 std::any::type_name::<T>(),
35 cycle_error.cycle,
36 );
37 }
38}
39
40impl<'tcx> Value<'tcx> for Ty<'_> {
41 fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self {
42 // SAFETY: This is never called when `Self` is not `Ty<'tcx>`.
43 // FIXME: Represent the above fact in the trait system somehow.
44 unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(Ty::new_error(tcx, guar)) }
45 }
46}
47
48impl<'tcx> Value<'tcx> for Result<ty::EarlyBinder<'_, Ty<'_>>, CyclePlaceholder> {
49 fn from_cycle_error(_tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self {
50 Err(CyclePlaceholder(guar))
51 }
52}
53
54impl<'tcx> Value<'tcx> for ty::SymbolName<'_> {
55 fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, _guar: ErrorGuaranteed) -> Self {
56 // SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`.
57 // FIXME: Represent the above fact in the trait system somehow.
58 unsafe {
59 std::mem::transmute::<ty::SymbolName<'tcx>, ty::SymbolName<'_>>(ty::SymbolName::new(
60 tcx, "<error>",
61 ))
62 }
63 }
64}
65
66impl<'tcx> Value<'tcx> for ty::Binder<'_, ty::FnSig<'_>> {
67 fn from_cycle_error(
68 tcx: TyCtxt<'tcx>,
69 cycle_error: &CycleError,
70 guar: ErrorGuaranteed,
71 ) -> Self {
72 let err = Ty::new_error(tcx, guar);
73
74 let arity = if let Some(info) = cycle_error.cycle.get(0)
75 && info.frame.dep_kind == dep_kinds::fn_sig
76 && let Some(def_id) = info.frame.def_id
77 && let Some(node) = tcx.hir_get_if_local(def_id)
78 && let Some(sig) = node.fn_sig()
79 {
80 sig.decl.inputs.len()
81 } else {
82 tcx.dcx().abort_if_errors();
83 unreachable!()
84 };
85
86 let fn_sig = ty::Binder::dummy(tcx.mk_fn_sig(
87 std::iter::repeat_n(err, arity),
88 err,
89 false,
90 rustc_hir::Safety::Safe,
91 rustc_abi::ExternAbi::Rust,
92 ));
93
94 // SAFETY: This is never called when `Self` is not `ty::Binder<'tcx, ty::FnSig<'tcx>>`.
95 // FIXME: Represent the above fact in the trait system somehow.
96 unsafe { std::mem::transmute::<ty::PolyFnSig<'tcx>, ty::Binder<'_, ty::FnSig<'_>>>(fn_sig) }
97 }
98}
99
100impl<'tcx> Value<'tcx> for Representability {
101 fn from_cycle_error(
102 tcx: TyCtxt<'tcx>,
103 cycle_error: &CycleError,
104 _guar: ErrorGuaranteed,
105 ) -> Self {
106 let mut item_and_field_ids = Vec::new();
107 let mut representable_ids = FxHashSet::default();
108 for info in &cycle_error.cycle {
109 if info.frame.dep_kind == dep_kinds::representability
110 && let Some(field_id) = info.frame.def_id
111 && let Some(field_id) = field_id.as_local()
112 && let Some(DefKind::Field) = info.frame.info.def_kind
113 {
114 let parent_id = tcx.parent(field_id.to_def_id());
115 let item_id = match tcx.def_kind(parent_id) {
116 DefKind::Variant => tcx.parent(parent_id),
117 _ => parent_id,
118 };
119 item_and_field_ids.push((item_id.expect_local(), field_id));
120 }
121 }
122 for info in &cycle_error.cycle {
123 if info.frame.dep_kind == dep_kinds::representability_adt_ty
124 && let Some(def_id) = info.frame.def_id_for_ty_in_cycle
125 && let Some(def_id) = def_id.as_local()
126 && !item_and_field_ids.iter().any(|&(id, _)| id == def_id)
127 {
128 representable_ids.insert(def_id);
129 }
130 }
131 let guar = recursive_type_error(tcx, item_and_field_ids, &representable_ids);
132 Representability::Infinite(guar)
133 }
134}
135
136impl<'tcx> Value<'tcx> for ty::EarlyBinder<'_, Ty<'_>> {
137 fn from_cycle_error(
138 tcx: TyCtxt<'tcx>,
139 cycle_error: &CycleError,
140 guar: ErrorGuaranteed,
141 ) -> Self {
142 ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle_error, guar))
143 }
144}
145
146impl<'tcx> Value<'tcx> for ty::EarlyBinder<'_, ty::Binder<'_, ty::FnSig<'_>>> {
147 fn from_cycle_error(
148 tcx: TyCtxt<'tcx>,
149 cycle_error: &CycleError,
150 guar: ErrorGuaranteed,
151 ) -> Self {
152 ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle_error, guar))
153 }
154}
155
156impl<'tcx> Value<'tcx> for &[ty::Variance] {
157 fn from_cycle_error(
158 tcx: TyCtxt<'tcx>,
159 cycle_error: &CycleError,
160 _guar: ErrorGuaranteed,
161 ) -> Self {
162 search_for_cycle_permutation(
163 &cycle_error.cycle,
164 |cycle| {
165 if let Some(info) = cycle.get(0)
166 && info.frame.dep_kind == dep_kinds::variances_of
167 && let Some(def_id) = info.frame.def_id
168 {
169 let n = tcx.generics_of(def_id).own_params.len();
170 ControlFlow::Break(vec![ty::Bivariant; n].leak())
171 } else {
172 ControlFlow::Continue(())
173 }
174 },
175 || {
176 span_bug!(
177 cycle_error.usage.as_ref().unwrap().0,
178 "only `variances_of` returns `&[ty::Variance]`"
179 )
180 },
181 )
182 }
183}
184
185// Take a cycle of `Q` and try `try_cycle` on every permutation, falling back to `otherwise`.
186fn search_for_cycle_permutation<Q, T>(
187 cycle: &[Q],
188 try_cycle: impl Fn(&mut VecDeque<&Q>) -> ControlFlow<T, ()>,
189 otherwise: impl FnOnce() -> T,
190) -> T {
191 let mut cycle: VecDeque<_> = cycle.iter().collect();
192 for _ in 0..cycle.len() {
193 match try_cycle(&mut cycle) {
194 ControlFlow::Continue(_) => {
195 cycle.rotate_left(1);
196 }
197 ControlFlow::Break(t) => return t,
198 }
199 }
200
201 otherwise()
202}
203
204impl<'tcx, T> Value<'tcx> for Result<T, &'_ ty::layout::LayoutError<'_>> {
205 fn from_cycle_error(
206 tcx: TyCtxt<'tcx>,
207 cycle_error: &CycleError,
208 _guar: ErrorGuaranteed,
209 ) -> Self {
210 let diag = search_for_cycle_permutation(
211 &cycle_error.cycle,
212 |cycle| {
213 if cycle[0].frame.dep_kind == dep_kinds::layout_of
214 && let Some(def_id) = cycle[0].frame.def_id_for_ty_in_cycle
215 && let Some(def_id) = def_id.as_local()
216 && let def_kind = tcx.def_kind(def_id)
217 && matches!(def_kind, DefKind::Closure)
218 && let Some(coroutine_kind) = tcx.coroutine_kind(def_id)
219 {
220 // FIXME: `def_span` for an fn-like coroutine will point to the fn's body
221 // due to interactions between the desugaring into a closure expr and the
222 // def_span code. I'm not motivated to fix it, because I tried and it was
223 // not working, so just hack around it by grabbing the parent fn's span.
224 let span = if coroutine_kind.is_fn_like() {
225 tcx.def_span(tcx.local_parent(def_id))
226 } else {
227 tcx.def_span(def_id)
228 };
229 let mut diag = struct_span_code_err!(
230 tcx.sess.dcx(),
231 span,
232 E0733,
233 "recursion in {} {} requires boxing",
234 tcx.def_kind_descr_article(def_kind, def_id.to_def_id()),
235 tcx.def_kind_descr(def_kind, def_id.to_def_id()),
236 );
237 for (i, info) in cycle.iter().enumerate() {
238 if info.frame.dep_kind != dep_kinds::layout_of {
239 continue;
240 }
241 let Some(frame_def_id) = info.frame.def_id_for_ty_in_cycle else {
242 continue;
243 };
244 let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else {
245 continue;
246 };
247 let frame_span =
248 info.frame.info.default_span(cycle[(i + 1) % cycle.len()].span);
249 if frame_span.is_dummy() {
250 continue;
251 }
252 if i == 0 {
253 diag.span_label(frame_span, "recursive call here");
254 } else {
255 let coroutine_span: Span = if frame_coroutine_kind.is_fn_like() {
256 tcx.def_span(tcx.parent(frame_def_id))
257 } else {
258 tcx.def_span(frame_def_id)
259 };
260 let mut multispan = MultiSpan::from_span(coroutine_span);
261 multispan
262 .push_span_label(frame_span, "...leading to this recursive call");
263 diag.span_note(
264 multispan,
265 format!("which leads to this {}", tcx.def_descr(frame_def_id)),
266 );
267 }
268 }
269 // FIXME: We could report a structured suggestion if we had
270 // enough info here... Maybe we can use a hacky HIR walker.
271 if matches!(
272 coroutine_kind,
273 hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)
274 ) {
275 diag.note("a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future");
276 }
277
278 ControlFlow::Break(diag)
279 } else {
280 ControlFlow::Continue(())
281 }
282 },
283 || report_cycle(tcx.sess, cycle_error),
284 );
285
286 let guar = diag.emit();
287
288 // tcx.arena.alloc cannot be used because we are not allowed to use &'tcx LayoutError under
289 // min_specialization. Since this is an error path anyways, leaking doesn't matter (and really,
290 // tcx.arena.alloc is pretty much equal to leaking).
291 Err(Box::leak(Box::new(ty::layout::LayoutError::Cycle(guar))))
292 }
293}
294
295// item_and_field_ids should form a cycle where each field contains the
296// type in the next element in the list
297fn recursive_type_error(
298 tcx: TyCtxt<'_>,
299 mut item_and_field_ids: Vec<(LocalDefId, LocalDefId)>,
300 representable_ids: &FxHashSet<LocalDefId>,
301) -> ErrorGuaranteed {
302 const ITEM_LIMIT: usize = 5;
303
304 // Rotate the cycle so that the item with the lowest span is first
305 let start_index = item_and_field_ids
306 .iter()
307 .enumerate()
308 .min_by_key(|&(_, &(id, _))| tcx.def_span(id))
309 .unwrap()
310 .0;
311 item_and_field_ids.rotate_left(start_index);
312
313 let cycle_len = item_and_field_ids.len();
314 let show_cycle_len = cycle_len.min(ITEM_LIMIT);
315
316 let mut err_span = MultiSpan::from_spans(
317 item_and_field_ids[..show_cycle_len]
318 .iter()
319 .map(|(id, _)| tcx.def_span(id.to_def_id()))
320 .collect(),
321 );
322 let mut suggestion = Vec::with_capacity(show_cycle_len * 2);
323 for i in 0..show_cycle_len {
324 let (_, field_id) = item_and_field_ids[i];
325 let (next_item_id, _) = item_and_field_ids[(i + 1) % cycle_len];
326 // Find the span(s) that contain the next item in the cycle
327 let hir::Node::Field(field) = tcx.hir_node_by_def_id(field_id) else {
328 bug!("expected field")
329 };
330 let mut found = Vec::new();
331 find_item_ty_spans(tcx, field.ty, next_item_id, &mut found, representable_ids);
332
333 // Couldn't find the type. Maybe it's behind a type alias?
334 // In any case, we'll just suggest boxing the whole field.
335 if found.is_empty() {
336 found.push(field.ty.span);
337 }
338
339 for span in found {
340 err_span.push_span_label(span, "recursive without indirection");
341 // FIXME(compiler-errors): This suggestion might be erroneous if Box is shadowed
342 suggestion.push((span.shrink_to_lo(), "Box<".to_string()));
343 suggestion.push((span.shrink_to_hi(), ">".to_string()));
344 }
345 }
346 let items_list = {
347 let mut s = String::new();
348 for (i, &(item_id, _)) in item_and_field_ids.iter().enumerate() {
349 let path = tcx.def_path_str(item_id);
350 write!(&mut s, "`{path}`").unwrap();
351 if i == (ITEM_LIMIT - 1) && cycle_len > ITEM_LIMIT {
352 write!(&mut s, " and {} more", cycle_len - 5).unwrap();
353 break;
354 }
355 if cycle_len > 1 && i < cycle_len - 2 {
356 s.push_str(", ");
357 } else if cycle_len > 1 && i == cycle_len - 2 {
358 s.push_str(" and ")
359 }
360 }
361 s
362 };
363 struct_span_code_err!(
364 tcx.dcx(),
365 err_span,
366 E0072,
367 "recursive type{} {} {} infinite size",
368 pluralize!(cycle_len),
369 items_list,
370 pluralize!("has", cycle_len),
371 )
372 .with_multipart_suggestion(
373 "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle",
374 suggestion,
375 Applicability::HasPlaceholders,
376 )
377 .emit()
378}
379
380fn find_item_ty_spans(
381 tcx: TyCtxt<'_>,
382 ty: &hir::Ty<'_>,
383 needle: LocalDefId,
384 spans: &mut Vec<Span>,
385 seen_representable: &FxHashSet<LocalDefId>,
386) {
387 match ty.kind {
388 hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
389 if let Res::Def(kind, def_id) = path.res
390 && matches!(kind, DefKind::Enum | DefKind::Struct | DefKind::Union)
391 {
392 let check_params = def_id.as_local().is_none_or(|def_id| {
393 if def_id == needle {
394 spans.push(ty.span);
395 }
396 seen_representable.contains(&def_id)
397 });
398 if check_params && let Some(args) = path.segments.last().unwrap().args {
399 let params_in_repr = tcx.params_in_repr(def_id);
400 // the domain size check is needed because the HIR may not be well-formed at this point
401 for (i, arg) in args.args.iter().enumerate().take(params_in_repr.domain_size())
402 {
403 if let hir::GenericArg::Type(ty) = arg
404 && params_in_repr.contains(i as u32)
405 {
406 find_item_ty_spans(
407 tcx,
408 ty.as_unambig_ty(),
409 needle,
410 spans,
411 seen_representable,
412 );
413 }
414 }
415 }
416 }
417 }
418 hir::TyKind::Array(ty, _) => find_item_ty_spans(tcx, ty, needle, spans, seen_representable),
419 hir::TyKind::Tup(tys) => {
420 tys.iter().for_each(|ty| find_item_ty_spans(tcx, ty, needle, spans, seen_representable))
421 }
422 _ => {}
423 }
424}
compiler/rustc_query_system/src/error.rs+1-59
......@@ -1,62 +1,4 @@
1use rustc_errors::codes::*;
2use rustc_macros::{Diagnostic, Subdiagnostic};
3use rustc_span::Span;
4
5#[derive(Subdiagnostic)]
6#[note("...which requires {$desc}...")]
7pub(crate) struct CycleStack {
8 #[primary_span]
9 pub span: Span,
10 pub desc: String,
11}
12
13#[derive(Subdiagnostic)]
14pub(crate) enum StackCount {
15 #[note("...which immediately requires {$stack_bottom} again")]
16 Single,
17 #[note("...which again requires {$stack_bottom}, completing the cycle")]
18 Multiple,
19}
20
21#[derive(Subdiagnostic)]
22pub(crate) enum Alias {
23 #[note("type aliases cannot be recursive")]
24 #[help("consider using a struct, enum, or union instead to break the cycle")]
25 #[help(
26 "see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information"
27 )]
28 Ty,
29 #[note("trait aliases cannot be recursive")]
30 Trait,
31}
32
33#[derive(Subdiagnostic)]
34#[note("cycle used when {$usage}")]
35pub(crate) struct CycleUsage {
36 #[primary_span]
37 pub span: Span,
38 pub usage: String,
39}
40
41#[derive(Diagnostic)]
42#[diag("cycle detected when {$stack_bottom}", code = E0391)]
43pub(crate) struct Cycle {
44 #[primary_span]
45 pub span: Span,
46 pub stack_bottom: String,
47 #[subdiagnostic]
48 pub cycle_stack: Vec<CycleStack>,
49 #[subdiagnostic]
50 pub stack_count: StackCount,
51 #[subdiagnostic]
52 pub alias: Option<Alias>,
53 #[subdiagnostic]
54 pub cycle_usage: Option<CycleUsage>,
55 #[note(
56 "see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information"
57 )]
58 pub note_span: (),
59}
1use rustc_macros::Diagnostic;
602
613#[derive(Diagnostic)]
624#[diag("internal compiler error: reentrant incremental verify failure, suppressing message")]
compiler/rustc_query_system/src/query/caches.rs-3
......@@ -21,9 +21,6 @@ pub trait QueryCacheKey = Hash + Eq + Copy + Debug + for<'a> HashStable<StableHa
2121/// Types implementing this trait are associated with actual key/value types
2222/// by the `Cache` associated type of the `rustc_middle::query::Key` trait.
2323pub trait QueryCache: Sized {
24 // `Key` and `Value` are `Copy` instead of `Clone` to ensure copying them stays cheap,
25 // but it isn't strictly necessary.
26 // FIXME: Is that comment still true?
2724 type Key: QueryCacheKey;
2825 type Value: Copy;
2926
compiler/rustc_query_system/src/query/job.rs+13-506
......@@ -1,20 +1,12 @@
11use std::fmt::Debug;
22use std::hash::Hash;
3use std::io::Write;
4use std::iter;
53use std::num::NonZero;
64use std::sync::Arc;
75
86use parking_lot::{Condvar, Mutex};
9use rustc_data_structures::fx::{FxHashMap, FxHashSet};
10use rustc_errors::{Diag, DiagCtxtHandle};
11use rustc_hir::def::DefKind;
12use rustc_session::Session;
13use rustc_span::{DUMMY_SP, Span};
7use rustc_span::Span;
148
159use super::{QueryStackDeferred, QueryStackFrameExtra};
16use crate::dep_graph::DepContext;
17use crate::error::CycleStack;
1810use crate::query::plumbing::CycleError;
1911use crate::query::{QueryContext, QueryStackFrame};
2012
......@@ -32,38 +24,10 @@ impl<'tcx> QueryInfo<QueryStackDeferred<'tcx>> {
3224 }
3325}
3426
35/// Map from query job IDs to job information collected by
36/// [`QueryContext::collect_active_jobs_from_all_queries`].
37pub type QueryMap<'tcx> = FxHashMap<QueryJobId, QueryJobInfo<'tcx>>;
38
3927/// A value uniquely identifying an active query job.
4028#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
4129pub struct QueryJobId(pub NonZero<u64>);
4230
43impl QueryJobId {
44 fn frame<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> QueryStackFrame<QueryStackDeferred<'tcx>> {
45 map.get(&self).unwrap().frame.clone()
46 }
47
48 fn span<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Span {
49 map.get(&self).unwrap().job.span
50 }
51
52 fn parent<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Option<QueryJobId> {
53 map.get(&self).unwrap().job.parent
54 }
55
56 fn latch<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Option<&'a QueryLatch<'tcx>> {
57 map.get(&self).unwrap().job.latch.as_ref()
58 }
59}
60
61#[derive(Clone, Debug)]
62pub struct QueryJobInfo<'tcx> {
63 pub frame: QueryStackFrame<QueryStackDeferred<'tcx>>,
64 pub job: QueryJob<'tcx>,
65}
66
6731/// Represents an active query job.
6832#[derive(Clone, Debug)]
6933pub struct QueryJob<'tcx> {
......@@ -76,7 +40,7 @@ pub struct QueryJob<'tcx> {
7640 pub parent: Option<QueryJobId>,
7741
7842 /// The latch that is used to wait on this job.
79 latch: Option<QueryLatch<'tcx>>,
43 pub latch: Option<QueryLatch<'tcx>>,
8044}
8145
8246impl<'tcx> QueryJob<'tcx> {
......@@ -105,91 +69,23 @@ impl<'tcx> QueryJob<'tcx> {
10569 }
10670}
10771
108impl QueryJobId {
109 pub fn find_cycle_in_stack<'tcx>(
110 &self,
111 query_map: QueryMap<'tcx>,
112 current_job: &Option<QueryJobId>,
113 span: Span,
114 ) -> CycleError<QueryStackDeferred<'tcx>> {
115 // Find the waitee amongst `current_job` parents
116 let mut cycle = Vec::new();
117 let mut current_job = Option::clone(current_job);
118
119 while let Some(job) = current_job {
120 let info = query_map.get(&job).unwrap();
121 cycle.push(QueryInfo { span: info.job.span, frame: info.frame.clone() });
122
123 if job == *self {
124 cycle.reverse();
125
126 // This is the end of the cycle
127 // The span entry we included was for the usage
128 // of the cycle itself, and not part of the cycle
129 // Replace it with the span which caused the cycle to form
130 cycle[0].span = span;
131 // Find out why the cycle itself was used
132 let usage = info
133 .job
134 .parent
135 .as_ref()
136 .map(|parent| (info.job.span, parent.frame(&query_map)));
137 return CycleError { usage, cycle };
138 }
139
140 current_job = info.job.parent;
141 }
142
143 panic!("did not find a cycle")
144 }
145
146 #[cold]
147 #[inline(never)]
148 pub fn find_dep_kind_root<'tcx>(
149 &self,
150 query_map: QueryMap<'tcx>,
151 ) -> (QueryJobInfo<'tcx>, usize) {
152 let mut depth = 1;
153 let info = query_map.get(&self).unwrap();
154 let dep_kind = info.frame.dep_kind;
155 let mut current_id = info.job.parent;
156 let mut last_layout = (info.clone(), depth);
157
158 while let Some(id) = current_id {
159 let info = query_map.get(&id).unwrap();
160 if info.frame.dep_kind == dep_kind {
161 depth += 1;
162 last_layout = (info.clone(), depth);
163 }
164 current_id = info.job.parent;
165 }
166 last_layout
167 }
168}
169
17072#[derive(Debug)]
171struct QueryWaiter<'tcx> {
172 query: Option<QueryJobId>,
173 condvar: Condvar,
174 span: Span,
175 cycle: Mutex<Option<CycleError<QueryStackDeferred<'tcx>>>>,
73pub struct QueryWaiter<'tcx> {
74 pub query: Option<QueryJobId>,
75 pub condvar: Condvar,
76 pub span: Span,
77 pub cycle: Mutex<Option<CycleError<QueryStackDeferred<'tcx>>>>,
17678}
17779
17880#[derive(Debug)]
179struct QueryLatchInfo<'tcx> {
180 complete: bool,
181 waiters: Vec<Arc<QueryWaiter<'tcx>>>,
81pub struct QueryLatchInfo<'tcx> {
82 pub complete: bool,
83 pub waiters: Vec<Arc<QueryWaiter<'tcx>>>,
18284}
18385
184#[derive(Debug)]
86#[derive(Clone, Debug)]
18587pub struct QueryLatch<'tcx> {
186 info: Arc<Mutex<QueryLatchInfo<'tcx>>>,
187}
188
189impl<'tcx> Clone for QueryLatch<'tcx> {
190 fn clone(&self) -> Self {
191 Self { info: Arc::clone(&self.info) }
192 }
88 pub info: Arc<Mutex<QueryLatchInfo<'tcx>>>,
19389}
19490
19591impl<'tcx> QueryLatch<'tcx> {
......@@ -256,399 +152,10 @@ impl<'tcx> QueryLatch<'tcx> {
256152
257153 /// Removes a single waiter from the list of waiters.
258154 /// This is used to break query cycles.
259 fn extract_waiter(&self, waiter: usize) -> Arc<QueryWaiter<'tcx>> {
155 pub fn extract_waiter(&self, waiter: usize) -> Arc<QueryWaiter<'tcx>> {
260156 let mut info = self.info.lock();
261157 debug_assert!(!info.complete);
262158 // Remove the waiter from the list of waiters
263159 info.waiters.remove(waiter)
264160 }
265161}
266
267/// A resumable waiter of a query. The usize is the index into waiters in the query's latch
268type Waiter = (QueryJobId, usize);
269
270/// Visits all the non-resumable and resumable waiters of a query.
271/// Only waiters in a query are visited.
272/// `visit` is called for every waiter and is passed a query waiting on `query_ref`
273/// and a span indicating the reason the query waited on `query_ref`.
274/// If `visit` returns Some, this function returns.
275/// For visits of non-resumable waiters it returns the return value of `visit`.
276/// For visits of resumable waiters it returns Some(Some(Waiter)) which has the
277/// required information to resume the waiter.
278/// If all `visit` calls returns None, this function also returns None.
279fn visit_waiters<'tcx, F>(
280 query_map: &QueryMap<'tcx>,
281 query: QueryJobId,
282 mut visit: F,
283) -> Option<Option<Waiter>>
284where
285 F: FnMut(Span, QueryJobId) -> Option<Option<Waiter>>,
286{
287 // Visit the parent query which is a non-resumable waiter since it's on the same stack
288 if let Some(parent) = query.parent(query_map)
289 && let Some(cycle) = visit(query.span(query_map), parent)
290 {
291 return Some(cycle);
292 }
293
294 // Visit the explicit waiters which use condvars and are resumable
295 if let Some(latch) = query.latch(query_map) {
296 for (i, waiter) in latch.info.lock().waiters.iter().enumerate() {
297 if let Some(waiter_query) = waiter.query {
298 if visit(waiter.span, waiter_query).is_some() {
299 // Return a value which indicates that this waiter can be resumed
300 return Some(Some((query, i)));
301 }
302 }
303 }
304 }
305
306 None
307}
308
309/// Look for query cycles by doing a depth first search starting at `query`.
310/// `span` is the reason for the `query` to execute. This is initially DUMMY_SP.
311/// If a cycle is detected, this initial value is replaced with the span causing
312/// the cycle.
313fn cycle_check<'tcx>(
314 query_map: &QueryMap<'tcx>,
315 query: QueryJobId,
316 span: Span,
317 stack: &mut Vec<(Span, QueryJobId)>,
318 visited: &mut FxHashSet<QueryJobId>,
319) -> Option<Option<Waiter>> {
320 if !visited.insert(query) {
321 return if let Some(p) = stack.iter().position(|q| q.1 == query) {
322 // We detected a query cycle, fix up the initial span and return Some
323
324 // Remove previous stack entries
325 stack.drain(0..p);
326 // Replace the span for the first query with the cycle cause
327 stack[0].0 = span;
328 Some(None)
329 } else {
330 None
331 };
332 }
333
334 // Query marked as visited is added it to the stack
335 stack.push((span, query));
336
337 // Visit all the waiters
338 let r = visit_waiters(query_map, query, |span, successor| {
339 cycle_check(query_map, successor, span, stack, visited)
340 });
341
342 // Remove the entry in our stack if we didn't find a cycle
343 if r.is_none() {
344 stack.pop();
345 }
346
347 r
348}
349
350/// Finds out if there's a path to the compiler root (aka. code which isn't in a query)
351/// from `query` without going through any of the queries in `visited`.
352/// This is achieved with a depth first search.
353fn connected_to_root<'tcx>(
354 query_map: &QueryMap<'tcx>,
355 query: QueryJobId,
356 visited: &mut FxHashSet<QueryJobId>,
357) -> bool {
358 // We already visited this or we're deliberately ignoring it
359 if !visited.insert(query) {
360 return false;
361 }
362
363 // This query is connected to the root (it has no query parent), return true
364 if query.parent(query_map).is_none() {
365 return true;
366 }
367
368 visit_waiters(query_map, query, |_, successor| {
369 connected_to_root(query_map, successor, visited).then_some(None)
370 })
371 .is_some()
372}
373
374// Deterministically pick an query from a list
375fn pick_query<'a, 'tcx, T, F>(query_map: &QueryMap<'tcx>, queries: &'a [T], f: F) -> &'a T
376where
377 F: Fn(&T) -> (Span, QueryJobId),
378{
379 // Deterministically pick an entry point
380 // FIXME: Sort this instead
381 queries
382 .iter()
383 .min_by_key(|v| {
384 let (span, query) = f(v);
385 let hash = query.frame(query_map).hash;
386 // Prefer entry points which have valid spans for nicer error messages
387 // We add an integer to the tuple ensuring that entry points
388 // with valid spans are picked first
389 let span_cmp = if span == DUMMY_SP { 1 } else { 0 };
390 (span_cmp, hash)
391 })
392 .unwrap()
393}
394
395/// Looks for query cycles starting from the last query in `jobs`.
396/// If a cycle is found, all queries in the cycle is removed from `jobs` and
397/// the function return true.
398/// If a cycle was not found, the starting query is removed from `jobs` and
399/// the function returns false.
400fn remove_cycle<'tcx>(
401 query_map: &QueryMap<'tcx>,
402 jobs: &mut Vec<QueryJobId>,
403 wakelist: &mut Vec<Arc<QueryWaiter<'tcx>>>,
404) -> bool {
405 let mut visited = FxHashSet::default();
406 let mut stack = Vec::new();
407 // Look for a cycle starting with the last query in `jobs`
408 if let Some(waiter) =
409 cycle_check(query_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited)
410 {
411 // The stack is a vector of pairs of spans and queries; reverse it so that
412 // the earlier entries require later entries
413 let (mut spans, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip();
414
415 // Shift the spans so that queries are matched with the span for their waitee
416 spans.rotate_right(1);
417
418 // Zip them back together
419 let mut stack: Vec<_> = iter::zip(spans, queries).collect();
420
421 // Remove the queries in our cycle from the list of jobs to look at
422 for r in &stack {
423 if let Some(pos) = jobs.iter().position(|j| j == &r.1) {
424 jobs.remove(pos);
425 }
426 }
427
428 // Find the queries in the cycle which are
429 // connected to queries outside the cycle
430 let entry_points = stack
431 .iter()
432 .filter_map(|&(span, query)| {
433 if query.parent(query_map).is_none() {
434 // This query is connected to the root (it has no query parent)
435 Some((span, query, None))
436 } else {
437 let mut waiters = Vec::new();
438 // Find all the direct waiters who lead to the root
439 visit_waiters(query_map, query, |span, waiter| {
440 // Mark all the other queries in the cycle as already visited
441 let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1));
442
443 if connected_to_root(query_map, waiter, &mut visited) {
444 waiters.push((span, waiter));
445 }
446
447 None
448 });
449 if waiters.is_empty() {
450 None
451 } else {
452 // Deterministically pick one of the waiters to show to the user
453 let waiter = *pick_query(query_map, &waiters, |s| *s);
454 Some((span, query, Some(waiter)))
455 }
456 }
457 })
458 .collect::<Vec<(Span, QueryJobId, Option<(Span, QueryJobId)>)>>();
459
460 // Deterministically pick an entry point
461 let (_, entry_point, usage) = pick_query(query_map, &entry_points, |e| (e.0, e.1));
462
463 // Shift the stack so that our entry point is first
464 let entry_point_pos = stack.iter().position(|(_, query)| query == entry_point);
465 if let Some(pos) = entry_point_pos {
466 stack.rotate_left(pos);
467 }
468
469 let usage = usage.as_ref().map(|(span, query)| (*span, query.frame(query_map)));
470
471 // Create the cycle error
472 let error = CycleError {
473 usage,
474 cycle: stack
475 .iter()
476 .map(|&(s, ref q)| QueryInfo { span: s, frame: q.frame(query_map) })
477 .collect(),
478 };
479
480 // We unwrap `waiter` here since there must always be one
481 // edge which is resumable / waited using a query latch
482 let (waitee_query, waiter_idx) = waiter.unwrap();
483
484 // Extract the waiter we want to resume
485 let waiter = waitee_query.latch(query_map).unwrap().extract_waiter(waiter_idx);
486
487 // Set the cycle error so it will be picked up when resumed
488 *waiter.cycle.lock() = Some(error);
489
490 // Put the waiter on the list of things to resume
491 wakelist.push(waiter);
492
493 true
494 } else {
495 false
496 }
497}
498
499/// Detects query cycles by using depth first search over all active query jobs.
500/// If a query cycle is found it will break the cycle by finding an edge which
501/// uses a query latch and then resuming that waiter.
502/// There may be multiple cycles involved in a deadlock, so this searches
503/// all active queries for cycles before finally resuming all the waiters at once.
504pub fn break_query_cycles<'tcx>(query_map: QueryMap<'tcx>, registry: &rustc_thread_pool::Registry) {
505 let mut wakelist = Vec::new();
506 // It is OK per the comments:
507 // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798854932
508 // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798866392
509 #[allow(rustc::potential_query_instability)]
510 let mut jobs: Vec<QueryJobId> = query_map.keys().cloned().collect();
511
512 let mut found_cycle = false;
513
514 while jobs.len() > 0 {
515 if remove_cycle(&query_map, &mut jobs, &mut wakelist) {
516 found_cycle = true;
517 }
518 }
519
520 // Check that a cycle was found. It is possible for a deadlock to occur without
521 // a query cycle if a query which can be waited on uses Rayon to do multithreading
522 // internally. Such a query (X) may be executing on 2 threads (A and B) and A may
523 // wait using Rayon on B. Rayon may then switch to executing another query (Y)
524 // which in turn will wait on X causing a deadlock. We have a false dependency from
525 // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here
526 // only considers the true dependency and won't detect a cycle.
527 if !found_cycle {
528 panic!(
529 "deadlock detected as we're unable to find a query cycle to break\n\
530 current query map:\n{:#?}",
531 query_map
532 );
533 }
534
535 // Mark all the thread we're about to wake up as unblocked. This needs to be done before
536 // we wake the threads up as otherwise Rayon could detect a deadlock if a thread we
537 // resumed fell asleep and this thread had yet to mark the remaining threads as unblocked.
538 for _ in 0..wakelist.len() {
539 rustc_thread_pool::mark_unblocked(registry);
540 }
541
542 for waiter in wakelist.into_iter() {
543 waiter.condvar.notify_one();
544 }
545}
546
547#[inline(never)]
548#[cold]
549pub fn report_cycle<'a>(
550 sess: &'a Session,
551 CycleError { usage, cycle: stack }: &CycleError,
552) -> Diag<'a> {
553 assert!(!stack.is_empty());
554
555 let span = stack[0].frame.info.default_span(stack[1 % stack.len()].span);
556
557 let mut cycle_stack = Vec::new();
558
559 use crate::error::StackCount;
560 let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple };
561
562 for i in 1..stack.len() {
563 let frame = &stack[i].frame;
564 let span = frame.info.default_span(stack[(i + 1) % stack.len()].span);
565 cycle_stack.push(CycleStack { span, desc: frame.info.description.to_owned() });
566 }
567
568 let mut cycle_usage = None;
569 if let Some((span, ref query)) = *usage {
570 cycle_usage = Some(crate::error::CycleUsage {
571 span: query.info.default_span(span),
572 usage: query.info.description.to_string(),
573 });
574 }
575
576 let alias =
577 if stack.iter().all(|entry| matches!(entry.frame.info.def_kind, Some(DefKind::TyAlias))) {
578 Some(crate::error::Alias::Ty)
579 } else if stack.iter().all(|entry| entry.frame.info.def_kind == Some(DefKind::TraitAlias)) {
580 Some(crate::error::Alias::Trait)
581 } else {
582 None
583 };
584
585 let cycle_diag = crate::error::Cycle {
586 span,
587 cycle_stack,
588 stack_bottom: stack[0].frame.info.description.to_owned(),
589 alias,
590 cycle_usage,
591 stack_count,
592 note_span: (),
593 };
594
595 sess.dcx().create_err(cycle_diag)
596}
597
598pub fn print_query_stack<'tcx, Qcx: QueryContext<'tcx>>(
599 qcx: Qcx,
600 mut current_query: Option<QueryJobId>,
601 dcx: DiagCtxtHandle<'_>,
602 limit_frames: Option<usize>,
603 mut file: Option<std::fs::File>,
604) -> usize {
605 // Be careful relying on global state here: this code is called from
606 // a panic hook, which means that the global `DiagCtxt` may be in a weird
607 // state if it was responsible for triggering the panic.
608 let mut count_printed = 0;
609 let mut count_total = 0;
610
611 // Make use of a partial query map if we fail to take locks collecting active queries.
612 let query_map = match qcx.collect_active_jobs_from_all_queries(false) {
613 Ok(query_map) => query_map,
614 Err(query_map) => query_map,
615 };
616
617 if let Some(ref mut file) = file {
618 let _ = writeln!(file, "\n\nquery stack during panic:");
619 }
620 while let Some(query) = current_query {
621 let Some(query_info) = query_map.get(&query) else {
622 break;
623 };
624 let query_extra = query_info.frame.info.extract();
625 if Some(count_printed) < limit_frames || limit_frames.is_none() {
626 // Only print to stderr as many stack frames as `num_frames` when present.
627 dcx.struct_failure_note(format!(
628 "#{} [{:?}] {}",
629 count_printed, query_info.frame.dep_kind, query_extra.description
630 ))
631 .with_span(query_info.job.span)
632 .emit();
633 count_printed += 1;
634 }
635
636 if let Some(ref mut file) = file {
637 let _ = writeln!(
638 file,
639 "#{} [{}] {}",
640 count_total,
641 qcx.dep_context().dep_kind_vtable(query_info.frame.dep_kind).name,
642 query_extra.description
643 );
644 }
645
646 current_query = query_info.job.parent;
647 count_total += 1;
648 }
649
650 if let Some(ref mut file) = file {
651 let _ = writeln!(file, "end of query stack");
652 }
653 count_total
654}
compiler/rustc_query_system/src/query/mod.rs+2-10
......@@ -15,10 +15,7 @@ use rustc_span::def_id::DefId;
1515pub use self::caches::{
1616 DefIdCache, DefaultCache, QueryCache, QueryCacheKey, SingleCache, VecCache,
1717};
18pub use self::job::{
19 QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryLatch, QueryMap, break_query_cycles,
20 print_query_stack, report_cycle,
21};
18pub use self::job::{QueryInfo, QueryJob, QueryJobId, QueryLatch, QueryWaiter};
2219pub use self::plumbing::*;
2320use crate::dep_graph::{DepKind, DepNodeIndex, HasDepContext, SerializedDepNodeIndex};
2421
......@@ -52,7 +49,7 @@ pub struct QueryStackFrame<I> {
5249 pub dep_kind: DepKind,
5350 /// This hash is used to deterministically pick
5451 /// a query to remove cycles in the parallel compiler.
55 hash: Hash64,
52 pub hash: Hash64,
5653 pub def_id: Option<DefId>,
5754 /// A def-id that is extracted from a `Ty` in a query key
5855 pub def_id_for_ty_in_cycle: Option<DefId>,
......@@ -161,11 +158,6 @@ pub trait QueryContext<'tcx>: HasDepContext {
161158 /// a token while waiting on a query.
162159 fn jobserver_proxy(&self) -> &Proxy;
163160
164 fn collect_active_jobs_from_all_queries(
165 self,
166 require_complete: bool,
167 ) -> Result<QueryMap<'tcx>, QueryMap<'tcx>>;
168
169161 /// Load a side effect associated to the node in the previous session.
170162 fn load_side_effect(
171163 self,
compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs+1-1
......@@ -7,7 +7,7 @@ pub(crate) fn target() -> Target {
77 llvm_target,
88 metadata: TargetMetadata {
99 description: Some("ARM64 Apple tvOS".into()),
10 tier: Some(3),
10 tier: Some(2),
1111 host_tools: Some(false),
1212 std: Some(true),
1313 },
compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs+1-1
......@@ -7,7 +7,7 @@ pub(crate) fn target() -> Target {
77 llvm_target,
88 metadata: TargetMetadata {
99 description: Some("ARM64 Apple tvOS Simulator".into()),
10 tier: Some(3),
10 tier: Some(2),
1111 host_tools: Some(false),
1212 std: Some(true),
1313 },
compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs+1-1
......@@ -7,7 +7,7 @@ pub(crate) fn target() -> Target {
77 llvm_target,
88 metadata: TargetMetadata {
99 description: Some("ARM64 Apple visionOS".into()),
10 tier: Some(3),
10 tier: Some(2),
1111 host_tools: Some(false),
1212 std: Some(true),
1313 },
compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs+1-1
......@@ -7,7 +7,7 @@ pub(crate) fn target() -> Target {
77 llvm_target,
88 metadata: TargetMetadata {
99 description: Some("ARM64 Apple visionOS simulator".into()),
10 tier: Some(3),
10 tier: Some(2),
1111 host_tools: Some(false),
1212 std: Some(true),
1313 },
compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs+1-1
......@@ -7,7 +7,7 @@ pub(crate) fn target() -> Target {
77 llvm_target,
88 metadata: TargetMetadata {
99 description: Some("ARM64 Apple watchOS".into()),
10 tier: Some(3),
10 tier: Some(2),
1111 host_tools: Some(false),
1212 std: Some(true),
1313 },
compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs+1-1
......@@ -7,7 +7,7 @@ pub(crate) fn target() -> Target {
77 llvm_target,
88 metadata: TargetMetadata {
99 description: Some("ARM64 Apple watchOS Simulator".into()),
10 tier: Some(3),
10 tier: Some(2),
1111 host_tools: Some(false),
1212 std: Some(true),
1313 },
compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs+2-2
......@@ -190,8 +190,8 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
190190 }
191191
192192 Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)) => {
193 debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index,);
194 debug!("id={:?}", id);
193 debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {debruijn_index:?}");
194 debug!("id={id:?}");
195195 if debruijn_index == self.current_index && id.to_def_id() == self.region_def_id {
196196 return ControlFlow::Break(());
197197 }
compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs+3-3
......@@ -239,14 +239,14 @@ pub fn suggest_new_region_bound(
239239 };
240240 spans_suggs.push((fn_return.span.shrink_to_hi(), format!(" + {name} ")));
241241 err.multipart_suggestion_verbose(
242 format!("{declare} `{ty}` {captures}, {use_lt}",),
242 format!("{declare} `{ty}` {captures}, {use_lt}"),
243243 spans_suggs,
244244 Applicability::MaybeIncorrect,
245245 );
246246 } else {
247247 err.span_suggestion_verbose(
248248 fn_return.span.shrink_to_hi(),
249 format!("{declare} `{ty}` {captures}, {explicit}",),
249 format!("{declare} `{ty}` {captures}, {explicit}"),
250250 &plus_lt,
251251 Applicability::MaybeIncorrect,
252252 );
......@@ -257,7 +257,7 @@ pub fn suggest_new_region_bound(
257257 if let LifetimeKind::ImplicitObjectLifetimeDefault = lt.kind {
258258 err.span_suggestion_verbose(
259259 fn_return.span.shrink_to_hi(),
260 format!("{declare} the trait object {captures}, {explicit}",),
260 format!("{declare} the trait object {captures}, {explicit}"),
261261 &plus_lt,
262262 Applicability::MaybeIncorrect,
263263 );
compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs+3-3
......@@ -710,7 +710,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
710710 predicate
711711 );
712712 let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) {
713 format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"),)
713 format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"))
714714 } else if post.len() == 1 {
715715 format!(": `{}`", post[0])
716716 } else {
......@@ -722,7 +722,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
722722 err.note(format!("cannot satisfy `{predicate}`"));
723723 }
724724 (0, _, 1) => {
725 err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,));
725 err.note(format!("{msg} in the `{}` crate{post}", crates[0]));
726726 }
727727 (0, _, _) => {
728728 err.note(format!(
......@@ -739,7 +739,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
739739 (_, 1, 1) => {
740740 let span: MultiSpan = spans.into();
741741 err.span_note(span, msg);
742 err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,));
742 err.note(format!("and another `impl` found in the `{}` crate{post}", crates[0]));
743743 }
744744 _ => {
745745 let span: MultiSpan = spans.into();
compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs+1-1
......@@ -2263,7 +2263,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
22632263 err.highlighted_span_help(
22642264 self.tcx.def_span(def_id),
22652265 vec![
2266 StringPart::normal(format!("the trait `{trait_}` ",)),
2266 StringPart::normal(format!("the trait `{trait_}` ")),
22672267 StringPart::highlighted("is"),
22682268 StringPart::normal(desc),
22692269 StringPart::highlighted(self_ty),
compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs+1-1
......@@ -888,7 +888,7 @@ impl<'tcx> OnUnimplementedFormatString {
888888 }
889889 } else {
890890 let reported =
891 struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,)
891 struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description)
892892 .emit();
893893 result = Err(reported);
894894 }
compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs+4-4
......@@ -312,7 +312,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
312312 // `async`/`gen` constructs get lowered to a special kind of coroutine that
313313 // should *not* `impl Coroutine`.
314314 ty::Coroutine(did, ..) if self.tcx().is_general_coroutine(*did) => {
315 debug!(?self_ty, ?obligation, "assemble_coroutine_candidates",);
315 debug!(?self_ty, ?obligation, "assemble_coroutine_candidates");
316316
317317 candidates.vec.push(CoroutineCandidate);
318318 }
......@@ -334,7 +334,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
334334 // async constructs get lowered to a special kind of coroutine that
335335 // should directly `impl Future`.
336336 if self.tcx().coroutine_is_async(*did) {
337 debug!(?self_ty, ?obligation, "assemble_future_candidates",);
337 debug!(?self_ty, ?obligation, "assemble_future_candidates");
338338
339339 candidates.vec.push(FutureCandidate);
340340 }
......@@ -352,7 +352,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
352352 if let ty::Coroutine(did, ..) = self_ty.kind()
353353 && self.tcx().coroutine_is_gen(*did)
354354 {
355 debug!(?self_ty, ?obligation, "assemble_iterator_candidates",);
355 debug!(?self_ty, ?obligation, "assemble_iterator_candidates");
356356
357357 candidates.vec.push(IteratorCandidate);
358358 }
......@@ -378,7 +378,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
378378 // gen constructs get lowered to a special kind of coroutine that
379379 // should directly `impl AsyncIterator`.
380380 if self.tcx().coroutine_is_async_gen(did) {
381 debug!(?self_ty, ?obligation, "assemble_iterator_candidates",);
381 debug!(?self_ty, ?obligation, "assemble_iterator_candidates");
382382
383383 // Can only confirm this candidate if we have constrained
384384 // the `Yield` type to at least `Poll<Option<?0>>`..
compiler/rustc_trait_selection/src/traits/select/confirmation.rs+2
......@@ -1246,6 +1246,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12461246 })
12471247 }
12481248
1249 /// This trait is indirectly exposed on stable, so do *not* extend the set of types that
1250 /// implement the trait without FCP!
12491251 fn confirm_bikeshed_guaranteed_no_drop_candidate(
12501252 &mut self,
12511253 obligation: &PolyTraitObligation<'tcx>,
compiler/rustc_trait_selection/src/traits/select/mod.rs+1-1
......@@ -1223,7 +1223,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12231223 && self.match_fresh_trait_preds(stack.fresh_trait_pred, prev.fresh_trait_pred)
12241224 })
12251225 {
1226 debug!("evaluate_stack --> unbound argument, recursive --> giving up",);
1226 debug!("evaluate_stack --> unbound argument, recursive --> giving up");
12271227 return Ok(EvaluatedToAmbigStackDependent);
12281228 }
12291229
compiler/rustc_trait_selection/src/traits/wf.rs+28
......@@ -951,6 +951,34 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
951951 ty::Binder::dummy(ty::PredicateKind::DynCompatible(principal)),
952952 ));
953953 }
954
955 if !t.has_escaping_bound_vars() {
956 for projection in data.projection_bounds() {
957 let pred_binder = projection
958 .with_self_ty(tcx, t)
959 .map_bound(|p| {
960 p.term.as_const().map(|ct| {
961 let assoc_const_ty = tcx
962 .type_of(p.projection_term.def_id)
963 .instantiate(tcx, p.projection_term.args);
964 ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(
965 ct,
966 assoc_const_ty,
967 ))
968 })
969 })
970 .transpose();
971 if let Some(pred_binder) = pred_binder {
972 self.out.push(traits::Obligation::with_depth(
973 tcx,
974 self.cause(ObligationCauseCode::WellFormed(None)),
975 self.recursion_depth,
976 self.param_env,
977 pred_binder,
978 ));
979 }
980 }
981 }
954982 }
955983
956984 // Inference variables are the complicated case, since we don't
library/core/src/array/drain.rs-1
......@@ -31,7 +31,6 @@ impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> {
3131}
3232
3333/// See [`Drain::new`]; this is our fake iterator.
34#[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
3534#[unstable(feature = "array_try_map", issue = "79711")]
3635pub(super) struct Drain<'l, 'f, T, const N: usize, F> {
3736 // FIXME(const-hack): This is essentially a slice::IterMut<'static>, replace when possible.
src/ci/github-actions/jobs.yml+26-2
......@@ -466,10 +466,34 @@ auto:
466466
467467 - name: dist-apple-various
468468 env:
469 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
469 # Build and distribute the standard library for these targets.
470 TARGETS: "aarch64-apple-ios,\
471 aarch64-apple-ios-sim,\
472 x86_64-apple-ios,\
473 aarch64-apple-ios-macabi,\
474 x86_64-apple-ios-macabi,\
475 aarch64-apple-tvos,\
476 aarch64-apple-tvos-sim,\
477 aarch64-apple-visionos,\
478 aarch64-apple-visionos-sim,\
479 aarch64-apple-watchos,\
480 aarch64-apple-watchos-sim"
481 SCRIPT: ./x.py dist bootstrap --include-default-paths --host='' --target=$TARGETS
470482 # Mac Catalyst cannot currently compile the sanitizer:
471483 # https://github.com/rust-lang/rust/issues/129069
472 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
484 #
485 # And tvOS and watchOS don't currently support the profiler runtime:
486 # https://github.com/rust-lang/rust/issues/152426
487 RUST_CONFIGURE_ARGS: >-
488 --enable-sanitizers
489 --enable-profiler
490 --set rust.jemalloc
491 --set target.aarch64-apple-ios-macabi.sanitizers=false
492 --set target.x86_64-apple-ios-macabi.sanitizers=false
493 --set target.aarch64-apple-tvos.profiler=false
494 --set target.aarch64-apple-tvos-sim.profiler=false
495 --set target.aarch64-apple-watchos.profiler=false
496 --set target.aarch64-apple-watchos-sim.profiler=false
473497 # Ensure that host tooling is built to support our minimum support macOS version.
474498 # FIXME(madsmtm): This might be redundant, as we're not building host tooling here (?)
475499 MACOSX_DEPLOYMENT_TARGET: 10.12
src/doc/book+1-1
......@@ -1 +1 @@
1Subproject commit 39aeceaa3aeab845bc4517e7a44e48727d3b9dbe
1Subproject commit 05d114287b7d6f6c9253d5242540f00fbd6172ab
src/doc/index.md+2-3
......@@ -194,9 +194,8 @@ resources maintained by the [Embedded Working Group] useful.
194194
195195#### The Embedded Rust Book
196196
197[The Embedded Rust Book] is targeted at developers familiar with embedded
198development and familiar with Rust, but have not used Rust for embedded
199development.
197[The Embedded Rust Book] is targeted at developers who are familiar with embedded
198development and Rust, but who have not used Rust for embedded development.
200199
201200[The Embedded Rust Book]: embedded-book/index.html
202201[Rust project]: https://www.rust-lang.org
src/doc/nomicon+1-1
......@@ -1 +1 @@
1Subproject commit 050c002a360fa45b701ea34feed7a860dc8a41bf
1Subproject commit b8f254a991b8b7e8f704527f0d4f343a4697dfa9
src/doc/not_found.md+1-1
......@@ -41,7 +41,7 @@ Some things that might be helpful to you though:
4141 <input type="submit" value="Search" id="search-but">
4242 <!--
4343 Don't show the options by default,
44 since "From the Standary Library" doesn't work without JavaScript
44 since "From the Standard Library" doesn't work without JavaScript
4545 -->
4646 <fieldset id="search-from" style="display:none">
4747 <label><input name="from" value="library" type="radio"> From the Standard Library</label>
src/doc/reference+1-1
......@@ -1 +1 @@
1Subproject commit 990819b86c22bbf538c0526f0287670f3dc1a67a
1Subproject commit addd0602c819b6526b9cc97653b0fadca395528c
src/doc/rustc/src/platform-support.md+6-6
......@@ -148,6 +148,12 @@ target | std | notes
148148[`aarch64-apple-ios`](platform-support/apple-ios.md) | ✓ | ARM64 iOS
149149[`aarch64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | Mac Catalyst on ARM64
150150[`aarch64-apple-ios-sim`](platform-support/apple-ios.md) | ✓ | Apple iOS Simulator on ARM64
151[`aarch64-apple-tvos`](platform-support/apple-tvos.md) | ✓ | ARM64 tvOS
152[`aarch64-apple-tvos-sim`](platform-support/apple-tvos.md) | ✓ | ARM64 tvOS Simulator
153[`aarch64-apple-visionos`](platform-support/apple-visionos.md) | ✓ | ARM64 Apple visionOS
154[`aarch64-apple-visionos-sim`](platform-support/apple-visionos.md) | ✓ | ARM64 Apple visionOS Simulator
155[`aarch64-apple-watchos`](platform-support/apple-watchos.md) | ✓ | ARM64 Apple WatchOS
156[`aarch64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | ARM64 Apple WatchOS Simulator
151157[`aarch64-linux-android`](platform-support/android.md) | ✓ | ARM64 Android
152158[`aarch64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | ARM64 Fuchsia
153159[`aarch64-unknown-none`](platform-support/aarch64-unknown-none.md) | * | Bare ARM64, hardfloat
......@@ -250,12 +256,6 @@ host tools.
250256
251257target | std | host | notes
252258-------|:---:|:----:|-------
253[`aarch64-apple-tvos`](platform-support/apple-tvos.md) | ✓ | | ARM64 tvOS
254[`aarch64-apple-tvos-sim`](platform-support/apple-tvos.md) | ✓ | | ARM64 tvOS Simulator
255[`aarch64-apple-visionos`](platform-support/apple-visionos.md) | ✓ | | ARM64 Apple visionOS
256[`aarch64-apple-visionos-sim`](platform-support/apple-visionos.md) | ✓ | | ARM64 Apple visionOS Simulator
257[`aarch64-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | ARM64 Apple WatchOS
258[`aarch64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | ARM64 Apple WatchOS Simulator
259259[`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ | | ARM64 SOLID with TOPPERS/ASP3
260260[`aarch64-nintendo-switch-freestanding`](platform-support/aarch64-nintendo-switch-freestanding.md) | * | | ARM64 Nintendo Switch, Horizon
261261[`aarch64-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | ARM64 FreeBSD
src/doc/rustc/src/platform-support/apple-tvos.md+9-9
......@@ -2,10 +2,13 @@
22
33Apple tvOS targets.
44
5**Tier: 3**
5**Tier: 2 (without Host Tools)**
66
77- `aarch64-apple-tvos`: Apple tvOS on ARM64.
88- `aarch64-apple-tvos-sim`: Apple tvOS Simulator on ARM64.
9
10**Tier: 3**
11
912- `x86_64-apple-tvos`: Apple tvOS Simulator on x86_64.
1013
1114## Target maintainers
......@@ -52,16 +55,13 @@ The following APIs are currently known to have missing or incomplete support:
5255
5356## Building the target
5457
55The targets can be built by enabling them for a `rustc` build in
56`bootstrap.toml`, by adding, for example:
57
58```toml
59[build]
60build-stage = 1
61target = ["aarch64-apple-tvos", "aarch64-apple-tvos-sim"]
58The tier 2 targets are distributed through `rustup`, and can be installed using one of:
59```console
60$ rustup target add aarch64-apple-tvos
61$ rustup target add aarch64-apple-tvos-sim
6262```
6363
64Using the unstable `-Zbuild-std` with a nightly Cargo may also work.
64See [the instructions for iOS](./apple-ios.md#building-the-target) for how to build the tier 3 target.
6565
6666## Building Rust programs
6767
src/doc/rustc/src/platform-support/apple-visionos.md+5-12
......@@ -2,7 +2,7 @@
22
33Apple visionOS / xrOS targets.
44
5**Tier: 3**
5**Tier: 2 (without Host Tools)**
66
77- `aarch64-apple-visionos`: Apple visionOS on arm64.
88- `aarch64-apple-visionos-sim`: Apple visionOS Simulator on arm64.
......@@ -31,19 +31,12 @@ case `XROS_DEPLOYMENT_TARGET`.
3131
3232## Building the target
3333
34The targets can be built by enabling them for a `rustc` build in
35`bootstrap.toml`, by adding, for example:
36
37```toml
38[build]
39target = ["aarch64-apple-visionos", "aarch64-apple-visionos-sim"]
34The targets are distributed through `rustup`, and can be installed using one of:
35```console
36$ rustup target add aarch64-apple-visionos
37$ rustup target add aarch64-apple-visionos-sim
4038```
4139
42Using the unstable `-Zbuild-std` with a nightly Cargo may also work.
43
44Note: Currently, a newer version of `libc` and `cc` may be required, this will
45be fixed in [#124560](https://github.com/rust-lang/rust/pull/124560).
46
4740## Building Rust programs
4841
4942See [the instructions for iOS](./apple-ios.md#building-rust-programs).
src/doc/rustc/src/platform-support/apple-watchos.md+9-9
......@@ -2,10 +2,13 @@
22
33Apple watchOS targets.
44
5**Tier: 3**
5**Tier: 2 (without Host Tools)**
66
77- `aarch64-apple-watchos`: Apple WatchOS on ARM64.
88- `aarch64-apple-watchos-sim`: Apple WatchOS Simulator on ARM64.
9
10**Tier: 3**
11
912- `x86_64-apple-watchos-sim`: Apple WatchOS Simulator on 64-bit x86.
1013- `arm64_32-apple-watchos`: Apple WatchOS on Arm 64_32.
1114- `armv7k-apple-watchos`: Apple WatchOS on Armv7k.
......@@ -37,16 +40,13 @@ case `WATCHOS_DEPLOYMENT_TARGET`.
3740
3841## Building the target
3942
40The targets can be built by enabling them for a `rustc` build in
41`bootstrap.toml`, by adding, for example:
42
43```toml
44[build]
45build-stage = 1
46target = ["aarch64-apple-watchos", "aarch64-apple-watchos-sim"]
43The tier 2 targets are distributed through `rustup`, and can be installed using one of:
44```console
45$ rustup target add aarch64-apple-watchos
46$ rustup target add aarch64-apple-watchos-sim
4747```
4848
49Using the unstable `-Zbuild-std` with a nightly Cargo may also work.
49See [the instructions for iOS](./apple-ios.md#building-the-target) for how to build the tier 3 targets.
5050
5151## Building Rust programs
5252
tests/ui/attributes/const-stability-on-macro.rs+3-3
......@@ -2,13 +2,13 @@
22#![stable(feature = "rust1", since = "1.0.0")]
33
44#[rustc_const_stable(feature = "foo", since = "3.3.3")]
5//~^ ERROR macros cannot have const stability attributes
5//~^ ERROR attribute cannot be used on macro defs
66macro_rules! foo {
77 () => {};
88}
99
10#[rustc_const_unstable(feature = "bar", issue="none")]
11//~^ ERROR macros cannot have const stability attributes
10#[rustc_const_unstable(feature = "bar", issue = "none")]
11//~^ ERROR attribute cannot be used on macro defs
1212macro_rules! bar {
1313 () => {};
1414}
tests/ui/attributes/const-stability-on-macro.stderr+9-11
......@@ -1,20 +1,18 @@
1error: macros cannot have const stability attributes
1error: `#[rustc_const_stable]` attribute cannot be used on macro defs
22 --> $DIR/const-stability-on-macro.rs:4:1
33 |
44LL | #[rustc_const_stable(feature = "foo", since = "3.3.3")]
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid const stability attribute
6LL |
7LL | macro_rules! foo {
8 | ---------------- const stability attribute affects this macro
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6 |
7 = help: `#[rustc_const_stable]` can be applied to associated consts, constants, crates, functions, impl blocks, statics, traits, and use statements
98
10error: macros cannot have const stability attributes
9error: `#[rustc_const_unstable]` attribute cannot be used on macro defs
1110 --> $DIR/const-stability-on-macro.rs:10:1
1211 |
13LL | #[rustc_const_unstable(feature = "bar", issue="none")]
14 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid const stability attribute
15LL |
16LL | macro_rules! bar {
17 | ---------------- const stability attribute affects this macro
12LL | #[rustc_const_unstable(feature = "bar", issue = "none")]
13 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14 |
15 = help: `#[rustc_const_unstable]` can be applied to associated consts, constants, crates, functions, impl blocks, statics, traits, and use statements
1816
1917error: aborting due to 2 previous errors
2018
tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-behind-trait-alias-mentions-self.rs+1
......@@ -15,6 +15,7 @@ trait Trait {
1515struct Hold<T: ?Sized>(T);
1616
1717trait Bound = Trait<Y = { Hold::<Self> }>;
18//~^ ERROR the constant `Hold::<Self>` is not of type `i32`
1819
1920fn main() {
2021 let _: dyn Bound; //~ ERROR associated constant binding in trait object type mentions `Self`
tests/ui/const-generics/associated-const-bindings/dyn-compat-const-projection-behind-trait-alias-mentions-self.stderr+14-2
......@@ -1,5 +1,17 @@
1error: the constant `Hold::<Self>` is not of type `i32`
2 --> $DIR/dyn-compat-const-projection-behind-trait-alias-mentions-self.rs:17:21
3 |
4LL | trait Bound = Trait<Y = { Hold::<Self> }>;
5 | ^^^^^^^^^^^^^^^^^^^^ expected `i32`, found struct constructor
6 |
7note: required by a const generic parameter in `Bound`
8 --> $DIR/dyn-compat-const-projection-behind-trait-alias-mentions-self.rs:17:21
9 |
10LL | trait Bound = Trait<Y = { Hold::<Self> }>;
11 | ^^^^^^^^^^^^^^^^^^^^ required by this const generic parameter in `Bound`
12
113error: associated constant binding in trait object type mentions `Self`
2 --> $DIR/dyn-compat-const-projection-behind-trait-alias-mentions-self.rs:20:12
14 --> $DIR/dyn-compat-const-projection-behind-trait-alias-mentions-self.rs:21:12
315 |
416LL | trait Bound = Trait<Y = { Hold::<Self> }>;
517 | -------------------- this binding mentions `Self`
......@@ -7,5 +19,5 @@ LL | trait Bound = Trait<Y = { Hold::<Self> }>;
719LL | let _: dyn Bound;
820 | ^^^^^^^^^ contains a mention of `Self`
921
10error: aborting due to 1 previous error
22error: aborting due to 2 previous errors
1123
tests/ui/const-generics/associated-const-bindings/dyn-const-projection-escaping-bound-vars.rs created+11
......@@ -0,0 +1,11 @@
1//! Check associated const binding with escaping bound vars doesn't cause ICE
2//! (#151642)
3//@ check-pass
4
5#![feature(min_generic_const_args)]
6#![expect(incomplete_features)]
7
8trait Trait2<'a> { type const ASSOC: i32; }
9fn g(_: for<'a> fn(Box<dyn Trait2<'a, ASSOC = 10>>)) {}
10
11fn main() {}
tests/ui/const-generics/associated-const-bindings/wf-mismatch-1.rs created+11
......@@ -0,0 +1,11 @@
1//! Check that we correctly handle associated const bindings
2//! in `impl Trait` where the RHS is a const param (#151642).
3
4#![feature(min_generic_const_args)]
5#![expect(incomplete_features)]
6
7trait Trait { type const CT: bool; }
8
9fn f<const N: i32>(_: impl Trait<CT = { N }>) {}
10//~^ ERROR the constant `N` is not of type `bool`
11fn main() {}
tests/ui/const-generics/associated-const-bindings/wf-mismatch-1.stderr created+14
......@@ -0,0 +1,14 @@
1error: the constant `N` is not of type `bool`
2 --> $DIR/wf-mismatch-1.rs:9:34
3 |
4LL | fn f<const N: i32>(_: impl Trait<CT = { N }>) {}
5 | ^^^^^^^^^^ expected `bool`, found `i32`
6 |
7note: required by a const generic parameter in `f`
8 --> $DIR/wf-mismatch-1.rs:9:34
9 |
10LL | fn f<const N: i32>(_: impl Trait<CT = { N }>) {}
11 | ^^^^^^^^^^ required by this const generic parameter in `f`
12
13error: aborting due to 1 previous error
14
tests/ui/const-generics/associated-const-bindings/wf-mismatch-2.rs created+13
......@@ -0,0 +1,13 @@
1//! Check that we correctly handle associated const bindings
2//! in `dyn Trait` where the RHS is a const param (#151642).
3
4#![feature(min_generic_const_args)]
5#![expect(incomplete_features)]
6
7trait Trait { type const CT: bool; }
8
9fn f<const N: i32>() {
10 let _: dyn Trait<CT = { N }>;
11 //~^ ERROR the constant `N` is not of type `bool`
12}
13fn main() {}
tests/ui/const-generics/associated-const-bindings/wf-mismatch-2.stderr created+8
......@@ -0,0 +1,8 @@
1error: the constant `N` is not of type `bool`
2 --> $DIR/wf-mismatch-2.rs:10:12
3 |
4LL | let _: dyn Trait<CT = { N }>;
5 | ^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `i32`
6
7error: aborting due to 1 previous error
8
tests/ui/const-generics/associated-const-bindings/wf-mismatch-3.rs created+17
......@@ -0,0 +1,17 @@
1//! Check that we correctly handle associated const bindings
2//! where the RHS is a normalizable const projection (#151642).
3
4#![feature(min_generic_const_args)]
5#![expect(incomplete_features)]
6
7trait Trait { type const CT: bool; }
8
9trait Bound { type const N: u32; }
10impl Bound for () { type const N: u32 = 0; }
11
12fn f() { let _: dyn Trait<CT = { <() as Bound>::N }>; }
13//~^ ERROR the constant `0` is not of type `bool`
14fn g(_: impl Trait<CT = { <() as Bound>::N }>) {}
15//~^ ERROR the constant `0` is not of type `bool`
16
17fn main() {}
tests/ui/const-generics/associated-const-bindings/wf-mismatch-3.stderr created+20
......@@ -0,0 +1,20 @@
1error: the constant `0` is not of type `bool`
2 --> $DIR/wf-mismatch-3.rs:14:20
3 |
4LL | fn g(_: impl Trait<CT = { <() as Bound>::N }>) {}
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `u32`
6 |
7note: required by a const generic parameter in `g`
8 --> $DIR/wf-mismatch-3.rs:14:20
9 |
10LL | fn g(_: impl Trait<CT = { <() as Bound>::N }>) {}
11 | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this const generic parameter in `g`
12
13error: the constant `0` is not of type `bool`
14 --> $DIR/wf-mismatch-3.rs:12:17
15 |
16LL | fn f() { let _: dyn Trait<CT = { <() as Bound>::N }>; }
17 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `u32`
18
19error: aborting due to 2 previous errors
20
tests/ui/generic-const-items/assoc-const-bindings.rs+8-10
......@@ -1,35 +1,33 @@
11//@ check-pass
22
33#![feature(generic_const_items, min_generic_const_args)]
4#![feature(adt_const_params)]
5#![allow(incomplete_features)]
4#![feature(adt_const_params, unsized_const_params, generic_const_parameter_types)]
5#![expect(incomplete_features)]
6
7use std::marker::{ConstParamTy, ConstParamTy_};
68
79trait Owner {
810 type const C<const N: u32>: u32;
911 type const K<const N: u32>: u32;
10 // #[type_const]
11 // const Q<T>: Maybe<T>;
12 type const Q<T: ConstParamTy_>: Maybe<T>;
1213}
1314
1415impl Owner for () {
1516 type const C<const N: u32>: u32 = N;
1617 type const K<const N: u32>: u32 = const { 99 + 1 };
17 // FIXME(mgca): re-enable once we properly support ctors and generics on paths
18 // #[type_const]
19 // const Q<T>: Maybe<T> = Maybe::Nothing;
18 type const Q<T: ConstParamTy_>: Maybe<T> = Maybe::Nothing::<T>;
2019}
2120
2221fn take0<const N: u32>(_: impl Owner<C<N> = { N }>) {}
2322fn take1(_: impl Owner<K<99> = 100>) {}
24// FIXME(mgca): re-enable once we properly support ctors and generics on paths
25// fn take2(_: impl Owner<Q<()> = { Maybe::Just(()) }>) {}
23fn take2(_: impl Owner<Q<()> = { Maybe::Just::<()>(()) }>) {}
2624
2725fn main() {
2826 take0::<128>(());
2927 take1(());
3028}
3129
32#[derive(PartialEq, Eq, std::marker::ConstParamTy)]
30#[derive(PartialEq, Eq, ConstParamTy)]
3331enum Maybe<T> {
3432 Nothing,
3533 Just(T),