From ac609f98e80b25b27f779095e24a0ab726ec0361 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 2 Aug 2026 21:30:02 +1000 Subject: [PATCH 1/8] Distinguish or/refutable/irrefutable patterns in `InterPat` --- .../src/builder/matches/match_pair.rs | 322 +++++++++--------- 1 file changed, 165 insertions(+), 157 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index b4ce8149f5e4d..2dcfbf3ca3098 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -107,113 +107,81 @@ fn squash_inter_pat<'tcx>( extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here ) { // Destructure exhaustively to make sure we don't miss any fields. - let InterPat { - place, - testable_case, - subpats, - or_subpats, - ascriptions, - binding, - pattern_span, - is_never: _, // Not needed by `MatchPairTree` forests. - } = inter_pat; + // The `is_never` field is not needed by `MatchPairTree` forests. + let InterPat { kind, ascriptions, pattern_span, is_never: _ } = inter_pat; // Type ascriptions can appear regardless of whether the node is an or-pattern. extra_data.ascriptions.extend(ascriptions); - // Or and non-or patterns have very different handling. - if let Some(or_subpats) = or_subpats { - // We're dealing with an or-pattern node. - assert!(testable_case.is_none()); - assert!(subpats.is_empty()); - assert!(binding.is_none()); - - let or_subpats = or_subpats - .into_iter() - .map(|subpat| FlatPat::from_inter_pat(subpat)) - .collect::>(); - - if !or_subpats[0].extra_data.bindings.is_empty() { - // Hold a place for any bindings established in (possibly-nested) or-patterns. - // By only holding a place when bindings are present, we skip over any - // or-patterns that will be simplified by `merge_trivial_subcandidates`. In - // other words, we can assume this expands into subcandidates. - // FIXME(@dianne): this needs updating/removing if we always merge or-patterns - extra_data.bindings.push(super::SubpatternBindings::FromOrPattern); - } + // Or patterns, refutable patterns, and irrefutable patterns all have different handling. + match kind { + InterPatKind::Or { or_subpats } => { + let or_subpats = or_subpats + .into_iter() + .map(|subpat| FlatPat::from_inter_pat(subpat)) + .collect::>(); + + if !or_subpats[0].extra_data.bindings.is_empty() { + // Hold a place for any bindings established in (possibly-nested) or-patterns. + // By only holding a place when bindings are present, we skip over any + // or-patterns that will be simplified by `merge_trivial_subcandidates`. In + // other words, we can assume this expands into subcandidates. + // FIXME(@dianne): this needs updating/removing if we always merge or-patterns + extra_data.bindings.push(super::SubpatternBindings::FromOrPattern); + } - match_pairs.push(MatchPairTree { - // Or-patterns never need a place during MIR building. - place: None, - testable_case: TestableCase::Or { pats: or_subpats }, - subpairs: vec![], - pattern_span, - }); - } else { - // We're dealing with a node that isn't an or-pattern. - - // Recursively squash any subpatterns into refutable `MatchPairTree` forests. - // This must happen _before_ pushing the binding, as described by the binding step. - let mut subpairs = vec![]; - for subpat in subpats { - squash_inter_pat(subpat, &mut subpairs, extra_data); + match_pairs.push(MatchPairTree { + // Or-patterns never need a place during MIR building. + place: None, + testable_case: TestableCase::Or { pats: or_subpats }, + subpairs: vec![], + pattern_span, + }); } - if let Some(testable_case) = testable_case { + InterPatKind::Refutable { place, testable_case, subpats } => { + // Recursively squash any subpatterns into refutable `MatchPairTree` forests, + // which will become the children of a new node. + let mut subpairs = vec![]; + for subpat in subpats { + squash_inter_pat(subpat, &mut subpairs, extra_data); + } + // This pattern is refutable, so push a new match-pair node. - // - // If this match is inside a closure, it's essential that the place - // we're testing was actually captured! Be sure to keep `ExprUseVisitor` - // in sync with the refutability checks in this module. - assert!(place.is_some()); assert!(!matches!(testable_case, TestableCase::Or { .. })); - match_pairs.push(MatchPairTree { place, testable_case, subpairs, pattern_span }); - } else { - // This pattern is irrefutable, so it doesn't need its own match-pair node. - // Just push its refutable subpatterns instead, if any. - match_pairs.extend(subpairs); + match_pairs.push(MatchPairTree { + place: Some(place), + testable_case, + subpairs, + pattern_span, + }); } - // If present, the binding must be pushed _after_ traversing subpatterns. - // This is so that when lowering something like `x @ NonCopy { copy_field }`, - // the binding to `copy_field` will occur before the binding for `x`. - // See for more background. - if let Some(binding) = binding { - extra_data.bindings.push(super::SubpatternBindings::One(binding)); + InterPatKind::Irrefutable { subpats, binding } => { + // Recursively squash any subpatterns into refutable `MatchPairTree` forests. + // This must happen _before_ pushing the binding, as described by the binding step. + for subpat in subpats { + // For irrefutable nodes, squash directly into the caller's match pairs. + squash_inter_pat(subpat, match_pairs, extra_data); + } + + // If present, the binding must be pushed _after_ traversing subpatterns. + // This is so that when lowering something like `x @ NonCopy { copy_field }`, + // the binding to `copy_field` will occur before the binding for `x`. + // See for more background. + if let Some(binding) = binding { + extra_data.bindings.push(super::SubpatternBindings::One(binding)); + } } } } /// "Intermediate pattern", a partly-lowered THIR [`Pat`] that has not yet been /// squashed into a forest of refutable [`MatchPairTree`] nodes. -/// -/// FIXME(Zalathar): This could potentially be split into different enum variants -/// for or-patterns and non-or patterns, but for now the flat structure makes -/// construction a bit easier, at the cost of more complicated invariants. struct InterPat<'tcx> { - /// Place that this pattern node will test. - /// - /// If `None`, we're in a closure that didn't capture the relevant place, - /// because it won't actually be tested. - place: Option>, - /// Testable condition to compare the place to (e.g. "is 3" or "is Some"). - /// - /// If `None`, this pattern node is irrefutable or an or-pattern, - /// though it might have refutable descendants. - testable_case: Option>, - - /// Immediate subpatterns of a node that is *not* an or-pattern. - subpats: Vec>, - /// Immediate subpatterns of an or-pattern node. - /// - /// Invariant: If this is Some, then fields `subpats`, `testable_case`, - /// and `binding` must all be empty. - or_subpats: Option]>>, + kind: InterPatKind<'tcx>, ascriptions: Vec>, - /// Binding to establish for a [`PatKind::Binding`] node. - binding: Option>, - /// Span field of the THIR pattern this node was created from. pattern_span: Span, /// True if this pattern can never match, because all of its alternatives @@ -221,6 +189,33 @@ struct InterPat<'tcx> { is_never: bool, } +enum InterPatKind<'tcx> { + Or { + /// The alternatives of an or-pattern, e.g. `P` and `Q` in `P | Q`. + or_subpats: Box<[InterPat<'tcx>]>, + }, + + /// Pattern node that performs some kind of test on a place. + Refutable { + /// Place that this pattern node will test. + place: Place<'tcx>, + /// Testable condition to compare the place to (e.g. "is 3" or "is Some"). + /// + /// Invariant: Must not be [`TestableCase::Or`]. + testable_case: TestableCase<'tcx>, + /// Immediate subpatterns. + subpats: Vec>, + }, + + /// Pattern node that doesn't test anything, though it might have refutable descendants. + Irrefutable { + /// Immediate subpatterns. + subpats: Vec>, + /// Binding to establish for a [`PatKind::Binding`] node. + binding: Option>, + }, +} + impl<'tcx> InterPat<'tcx> { fn lower_thir_pat( cx: &mut Builder<'_, 'tcx>, @@ -250,44 +245,49 @@ impl<'tcx> InterPat<'tcx> { } } - // Variables that will become `InterPat` fields: let place = place_builder.try_to_place(cx); - let mut subpats = vec![]; - let mut or_subpats = None; - let mut ascriptions = vec![]; - let mut binding = None; // Apply any type ascriptions to the value at `match_pair.place`. + let mut ascriptions = vec![]; if let Some(place) = place && let Some(extra) = &pattern.extra { - for &Ascription { ref annotation, variance } in &extra.ascriptions { - ascriptions.push(super::Ascription { + ascriptions.extend(extra.ascriptions.iter().map( + |&Ascription { ref annotation, variance }| super::Ascription { source: place, annotation: annotation.clone(), variance, - }); - } + }, + )); } - let testable_case = match pattern.kind { - PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None, + // For refutable nodes a place must be available, either because it is not a + // closure upvar or because it was captured. + let unwrap_place = || place.expect("refutable patterns must have captured a place"); + + let kind: InterPatKind<'_> = match pattern.kind { + PatKind::Missing | PatKind::Wild | PatKind::Error(_) => { + InterPatKind::Irrefutable { subpats: vec![], binding: None } + } PatKind::Or { ref pats } => { - or_subpats = Some( - pats.iter() - .map(|subpat| InterPat::lower_thir_pat(cx, place_builder.clone(), subpat)) - .collect::>(), - ); - None + let or_subpats = pats + .iter() + .map(|subpat| InterPat::lower_thir_pat(cx, place_builder.clone(), subpat)) + .collect::>(); + InterPatKind::Or { or_subpats } } PatKind::Range(ref range) => { assert_eq!(pattern.ty, range.ty); if range.is_full_range(cx.tcx) == Some(true) { - None + InterPatKind::Irrefutable { subpats: vec![], binding: None } } else { - Some(TestableCase::Range(Arc::clone(range))) + InterPatKind::Refutable { + place: unwrap_place(), + testable_case: TestableCase::Range(Arc::clone(range)), + subpats: vec![], + } } } @@ -311,27 +311,30 @@ impl<'tcx> InterPat<'tcx> { // which could be split out into their own kinds. PatConstKind::Other }; - Some(TestableCase::Constant { value, kind: const_kind }) + + InterPatKind::Refutable { + place: unwrap_place(), + testable_case: TestableCase::Constant { value, kind: const_kind }, + subpats: vec![], + } } PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => { // First, recurse into the subpattern, if any. - if let Some(subpattern) = subpattern.as_ref() { - // this is the `x @ P` case; have to keep matching against `P` now - subpats.push(InterPat::lower_thir_pat(cx, place_builder, subpattern)); - } + // This is the `x @ P` case; have to keep matching against `P` now. + let subpat: Option> = subpattern + .as_deref() + .map(|subpattern| InterPat::lower_thir_pat(cx, place_builder, subpattern)); // Then push this binding, after any bindings in the subpattern. - if let Some(place) = place { - binding = Some(super::Binding { - span: pattern.span, - source: place, - var_id: var, - binding_mode: mode, - is_shorthand, - }); - } - None + let binding = place.map(|place| super::Binding { + span: pattern.span, + source: place, + var_id: var, + binding_mode: mode, + is_shorthand, + }); + InterPatKind::Irrefutable { subpats: Vec::from_iter(subpat), binding } } PatKind::Array { ref prefix, ref slice, ref suffix } => { @@ -343,6 +346,8 @@ impl<'tcx> InterPat<'tcx> { ty::Array(_, len) => len.try_to_target_usize(cx.tcx), _ => None, }; + + let mut subpats = vec![]; if let Some(array_len) = array_len { for (subplace, subpat) in prefix_slice_suffix(&place_builder, Some(array_len), prefix, slice, suffix) @@ -361,9 +366,10 @@ impl<'tcx> InterPat<'tcx> { ); } - None + InterPatKind::Irrefutable { subpats, binding: None } } PatKind::Slice { ref prefix, ref slice, ref suffix } => { + let mut subpats = vec![]; for (subplace, subpat) in prefix_slice_suffix(&place_builder, None, prefix, slice, suffix) { @@ -373,24 +379,26 @@ impl<'tcx> InterPat<'tcx> { if prefix.is_empty() && slice.is_some() && suffix.is_empty() { // A slice pattern shaped like `[..]` is irrefutable. // It can match a slice of any length, so no length test is needed. - None + InterPatKind::Irrefutable { subpats, binding: None } } else { // Any other shape of slice pattern requires a length test. // Slice patterns with a `..` subpattern require a minimum // length; those without `..` require an exact length. - Some(TestableCase::Slice { + let testable_case = TestableCase::Slice { len: u64::try_from(prefix.len() + suffix.len()).unwrap(), op: if slice.is_some() { SliceLenOp::GreaterOrEqual } else { SliceLenOp::Equal }, - }) + }; + InterPatKind::Refutable { place: unwrap_place(), testable_case, subpats } } } PatKind::Variant { adt_def, variant_index, args: _, ref subpatterns } => { let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)` + let mut subpats = vec![]; for &FieldPat { field, pattern: ref subpat } in subpatterns { let subplace = downcast_place.clone_project(PlaceElem::Field(field, subpat.ty)); subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat)); @@ -401,18 +409,20 @@ impl<'tcx> InterPat<'tcx> { let refutable = adt_def.variants().len() > 1 || adt_def.is_variant_list_non_exhaustive(); if refutable { - Some(TestableCase::Variant { adt_def, variant_index }) + let testable_case = TestableCase::Variant { adt_def, variant_index }; + InterPatKind::Refutable { place: unwrap_place(), testable_case, subpats } } else { - None + InterPatKind::Irrefutable { subpats, binding: None } } } PatKind::Leaf { ref subpatterns } => { + let mut subpats = vec![]; for &FieldPat { field, pattern: ref subpat } in subpatterns { let subplace = place_builder.clone_project(PlaceElem::Field(field, subpat.ty)); subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat)); } - None + InterPatKind::Irrefutable { subpats, binding: None } } PatKind::Deref { pin: Pinnedness::Pinned, ref subpattern } => { @@ -420,20 +430,20 @@ impl<'tcx> InterPat<'tcx> { Some(p_ty) if p_ty.is_ref() => p_ty, _ => span_bug!(pattern.span, "bad type for pinned deref: {:?}", pattern.ty), }; - subpats.push(InterPat::lower_thir_pat( + let subpat = InterPat::lower_thir_pat( cx, // Project into the `Pin(_)` struct, then deref the inner `&` or `&mut`. place_builder.field(FieldIdx::ZERO, pinned_ref_ty).deref(), subpattern, - )); + ); - None + InterPatKind::Irrefutable { subpats: vec![subpat], binding: None } } PatKind::Deref { pin: Pinnedness::Not, ref subpattern } | PatKind::DerefPattern { ref subpattern, borrow: DerefPatBorrowMode::Box } => { - subpats.push(InterPat::lower_thir_pat(cx, place_builder.deref(), subpattern)); - None + let subpat = InterPat::lower_thir_pat(cx, place_builder.deref(), subpattern); + InterPatKind::Irrefutable { subpats: vec![subpat], binding: None } } PatKind::DerefPattern { @@ -446,41 +456,39 @@ impl<'tcx> InterPat<'tcx> { Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability), pattern.span, ); - subpats.push(InterPat::lower_thir_pat( - cx, - PlaceBuilder::from(temp).deref(), - subpattern, - )); - Some(TestableCase::Deref { temp, mutability }) + let subpat = + InterPat::lower_thir_pat(cx, PlaceBuilder::from(temp).deref(), subpattern); + InterPatKind::Refutable { + place: unwrap_place(), + testable_case: TestableCase::Deref { temp, mutability }, + subpats: vec![subpat], + } } PatKind::Guard { .. } => { // FIXME(guard_patterns) - None + InterPatKind::Irrefutable { subpats: vec![], binding: None } } - PatKind::Never => Some(TestableCase::Never), + PatKind::Never => InterPatKind::Refutable { + place: unwrap_place(), + testable_case: TestableCase::Never, + subpats: vec![], + }, }; // A pattern node is guaranteed to never match if one of these is true: // - The node itself is a never pattern (`!`). // - It is not an or-pattern, and one of its subpatterns will never match. // - It is an or-pattern, and _all_ of its or-subpatterns will never match. - let is_never = matches!(pattern.kind, PatKind::Never) - || subpats.iter().any(|subpat| subpat.is_never) - || or_subpats - .as_ref() - .is_some_and(|or_subpats| or_subpats.iter().all(|subpat| subpat.is_never)); - - InterPat { - place, - testable_case, - subpats, - or_subpats, - ascriptions, - binding, - pattern_span: pattern.span, - is_never, - } + let is_never = match &kind { + InterPatKind::Refutable { testable_case: TestableCase::Never, .. } => true, + InterPatKind::Refutable { subpats, .. } | InterPatKind::Irrefutable { subpats, .. } => { + subpats.iter().any(|subpat| subpat.is_never) + } + InterPatKind::Or { or_subpats } => or_subpats.iter().all(|subpat| subpat.is_never), + }; + + InterPat { kind, ascriptions, pattern_span: pattern.span, is_never } } } From b12184e40190d7aa87279946ae6dae1290c31cfd Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 3 Aug 2026 00:03:21 +1000 Subject: [PATCH 2/8] Distinguish or/testable patterns in `MatchPairTree` --- .../src/builder/matches/buckets.rs | 30 ++++++--- .../src/builder/matches/match_pair.rs | 18 ++--- .../src/builder/matches/mod.rs | 65 ++++++++++--------- .../src/builder/matches/test.rs | 14 ++-- .../src/builder/matches/util.rs | 59 +++++++++-------- 5 files changed, 96 insertions(+), 90 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/buckets.rs b/compiler/rustc_mir_build/src/builder/matches/buckets.rs index 0d2e9bf87585d..36d3d78c21ec0 100644 --- a/compiler/rustc_mir_build/src/builder/matches/buckets.rs +++ b/compiler/rustc_mir_build/src/builder/matches/buckets.rs @@ -2,12 +2,12 @@ use std::cmp::Ordering; use rustc_data_structures::fx::FxIndexMap; use rustc_middle::mir::Place; -use rustc_middle::span_bug; +use rustc_middle::{bug, span_bug}; use tracing::debug; use crate::builder::Builder; use crate::builder::matches::{ - Candidate, PatConstKind, SliceLenOp, Test, TestBranch, TestKind, TestableCase, + Candidate, MatchPairKind, PatConstKind, SliceLenOp, Test, TestBranch, TestKind, TestableCase, }; /// Output of [`Builder::partition_candidates_into_buckets`]. @@ -131,17 +131,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // than one, but it'd be very unusual to have two sides that // both require tests; you'd expect one side to be simplified // away.) - let (match_pair_index, match_pair) = candidate - .match_pairs - .iter() - .enumerate() - .find(|&(_, mp)| mp.place == Some(test_place))?; + let (match_pair_index, match_pair_testable_case) = + candidate.match_pairs.iter().enumerate().find_map(|(i, mp)| { + if let MatchPairKind::Testable { place, ref testable_case, .. } = mp.kind + && place == test_place + { + Some((i, testable_case)) + } else { + None + } + })?; // If true, the match pair is completely entailed by its corresponding test // branch, so it can be removed. If false, the match pair is _compatible_ // with its test branch, but still needs a more specific test. let fully_matched; - let ret = match (&test.kind, &match_pair.testable_case) { + let ret = match (&test.kind, match_pair_testable_case) { // If we are performing a variant switch, then this // informs variant patterns, but nothing else. ( @@ -174,7 +179,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; let is_conflicting_candidate = |candidate: &&mut Candidate<'tcx>| { candidate.match_pairs.iter().any(|mp| { - mp.place == Some(test_place) && is_covering_range(&mp.testable_case) + matches!(mp.kind, MatchPairKind::Testable { place, ref testable_case, .. } + if place == test_place && is_covering_range(testable_case) + ) }) }; if prior_candidates @@ -364,7 +371,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if fully_matched { // Replace the match pair by its sub-pairs. let match_pair = candidate.match_pairs.remove(match_pair_index); - candidate.match_pairs.extend(match_pair.subpairs); + let MatchPairKind::Testable { subpairs, .. } = match_pair.kind else { + bug!("match pair must have been refutable"); + }; + candidate.match_pairs.extend(subpairs); // Move or-patterns to the end. candidate.sort_match_pairs(); } diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 2dcfbf3ca3098..7ad21b3272783 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -10,7 +10,7 @@ use rustc_span::Span; use crate::builder::Builder; use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder}; use crate::builder::matches::{ - FlatPat, MatchPairTree, PatConstKind, PatternExtraData, SliceLenOp, TestableCase, + FlatPat, MatchPairKind, MatchPairTree, PatConstKind, PatternExtraData, SliceLenOp, TestableCase, }; /// For an array or slice pattern's subpatterns (prefix/slice/suffix), returns a list @@ -130,13 +130,8 @@ fn squash_inter_pat<'tcx>( extra_data.bindings.push(super::SubpatternBindings::FromOrPattern); } - match_pairs.push(MatchPairTree { - // Or-patterns never need a place during MIR building. - place: None, - testable_case: TestableCase::Or { pats: or_subpats }, - subpairs: vec![], - pattern_span, - }); + match_pairs + .push(MatchPairTree { kind: MatchPairKind::Or { or_subpats }, pattern_span }); } InterPatKind::Refutable { place, testable_case, subpats } => { @@ -148,11 +143,8 @@ fn squash_inter_pat<'tcx>( } // This pattern is refutable, so push a new match-pair node. - assert!(!matches!(testable_case, TestableCase::Or { .. })); match_pairs.push(MatchPairTree { - place: Some(place), - testable_case, - subpairs, + kind: MatchPairKind::Testable { place, testable_case, subpairs }, pattern_span, }); } @@ -200,8 +192,6 @@ enum InterPatKind<'tcx> { /// Place that this pattern node will test. place: Place<'tcx>, /// Testable condition to compare the place to (e.g. "is 3" or "is Some"). - /// - /// Invariant: Must not be [`TestableCase::Or`]. testable_case: TestableCase<'tcx>, /// Immediate subpatterns. subpats: Vec>, diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 109f4de2698a4..ca1eebb3c69cf 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -1030,7 +1030,7 @@ struct Candidate<'tcx> { /// (see [`Builder::test_remaining_match_pairs_after_or`]). /// /// Invariants: - /// - All or-patterns ([`TestableCase::Or`]) have been sorted to the end. + /// - All or-patterns ([`MatchPairKind::Or`]) have been sorted to the end. match_pairs: Vec>, /// ...and if this is non-empty, one of these subcandidates also has to match... @@ -1116,14 +1116,14 @@ impl<'tcx> Candidate<'tcx> { /// Restores the invariant that or-patterns must be sorted to the end. fn sort_match_pairs(&mut self) { - self.match_pairs.sort_by_key(|pair| matches!(pair.testable_case, TestableCase::Or { .. })); + self.match_pairs.sort_by_key(|pair| matches!(pair.kind, MatchPairKind::Or { .. })); } /// Returns whether the first match pair of this candidate is an or-pattern. fn starts_with_or_pattern(&self) -> bool { matches!( - &*self.match_pairs, - [MatchPairTree { testable_case: TestableCase::Or { .. }, .. }, ..] + self.match_pairs.first(), + Some(MatchPairTree { kind: MatchPairKind::Or { .. }, .. }) ) } @@ -1223,7 +1223,6 @@ enum TestableCase<'tcx> { Slice { len: u64, op: SliceLenOp }, Deref { temp: Place<'tcx>, mutability: Mutability }, Never, - Or { pats: Box<[FlatPat<'tcx>]> }, } impl<'tcx> TestableCase<'tcx> { @@ -1261,32 +1260,32 @@ enum PatConstKind { /// Each node also has a list of subpairs (possibly empty) that must also match, /// and some additional information from the THIR pattern it represents. #[derive(Debug, Clone)] -pub(crate) struct MatchPairTree<'tcx> { - /// This place... - /// - /// --- - /// This can be `None` if it referred to a non-captured place in a closure. - /// - /// Invariant: Can only be `None` when `testable_case` is `Or`. - /// Therefore this must be `Some(_)` after or-pattern expansion. - place: Option>, - - /// ... must pass this test... - testable_case: TestableCase<'tcx>, - - /// ... and these subpairs must match. - /// - /// --- - /// Subpairs typically represent tests that can only be performed after their - /// parent has succeeded. For example, the pattern `Some(3)` might have an - /// outer match pair that tests for the variant `Some`, and then a subpair - /// that tests its field for the value `3`. - subpairs: Vec, +struct MatchPairTree<'tcx> { + kind: MatchPairKind<'tcx>, /// Span field of the THIR pattern this node was created from. pattern_span: Span, } +#[derive(Debug, Clone)] +enum MatchPairKind<'tcx> { + Or { + or_subpats: Box<[FlatPat<'tcx>]>, + }, + Testable { + /// Place that will be tested. + place: Place<'tcx>, + /// Test to perform against the place, and the desired outcome. + testable_case: TestableCase<'tcx>, + + /// Further tests that can only be performed after this test has succeeded. + /// For example, in the pattern `Some(3)` this node might represent a test + /// for the variant `Some`, while a subpair would test its field for the + /// value `3`. + subpairs: Vec>, + }, +} + /// A runtime test to perform to determine which candidates match a scrutinee place. /// /// The kind of test to perform is indicated by [`TestKind`]. @@ -1950,10 +1949,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate: &mut Candidate<'tcx>, match_pair: MatchPairTree<'tcx>, ) { - let TestableCase::Or { pats } = match_pair.testable_case else { bug!() }; - debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats); + let MatchPairKind::Or { or_subpats } = match_pair.kind else { bug!() }; + debug!("expanding or-pattern: candidate={:#?}\nor_subpats={:#?}", candidate, or_subpats); candidate.or_span = Some(match_pair.pattern_span); - candidate.subcandidates = pats + candidate.subcandidates = or_subpats .into_iter() .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard)) .collect(); @@ -2118,7 +2117,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug_assert!( remaining_match_pairs .iter() - .all(|match_pair| matches!(match_pair.testable_case, TestableCase::Or { .. })) + .all(|match_pair| matches!(match_pair.kind, MatchPairKind::Or { .. })) ); // Visit each leaf candidate within this subtree, add a copy of the remaining @@ -2169,8 +2168,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Extract the match-pair from the highest priority candidate let match_pair = &candidates[0].match_pairs[0]; let test = self.pick_test_for_match_pair(match_pair); - // Unwrap is ok after simplification. - let match_place = match_pair.place.unwrap(); + + let MatchPairKind::Testable { place: match_place, .. } = match_pair.kind else { + bug!("match pair must be testable") + }; debug!(?test, ?match_pair); (match_place, test) diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 1c234bb8d70dc..8e8c73bcb87a2 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -19,7 +19,8 @@ use tracing::{debug, instrument}; use crate::builder::Builder; use crate::builder::matches::{ - MatchPairTree, PatConstKind, SliceLenOp, Test, TestBranch, TestKind, TestableCase, + MatchPairKind, MatchPairTree, PatConstKind, SliceLenOp, Test, TestBranch, TestKind, + TestableCase, }; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -30,7 +31,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, match_pair: &MatchPairTree<'tcx>, ) -> Test<'tcx> { - let kind = match match_pair.testable_case { + // Or-patterns are not tested directly; instead they are expanded into subcandidates, + // which are then distinguished by testing whatever non-or patterns they contain. + let MatchPairKind::Testable { ref testable_case, .. } = match_pair.kind else { + bug!("or-patterns should have already been handled") + }; + let kind = match *testable_case { TestableCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def }, TestableCase::Constant { value: _, kind: PatConstKind::Bool } => TestKind::If, @@ -51,10 +57,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TestableCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability }, TestableCase::Never => TestKind::Never, - - // Or-patterns are not tested directly; instead they are expanded into subcandidates, - // which are then distinguished by testing whatever non-or patterns they contain. - TestableCase::Or { .. } => bug!("or-patterns should have already been handled"), }; Test { span: match_pair.pattern_span, kind } diff --git a/compiler/rustc_mir_build/src/builder/matches/util.rs b/compiler/rustc_mir_build/src/builder/matches/util.rs index 3246dab73dcbf..fa94a41bad339 100644 --- a/compiler/rustc_mir_build/src/builder/matches/util.rs +++ b/compiler/rustc_mir_build/src/builder/matches/util.rs @@ -6,7 +6,9 @@ use tracing::debug; use crate::builder::Builder; use crate::builder::expr::as_place::PlaceBase; -use crate::builder::matches::{Binding, Candidate, FlatPat, MatchPairTree, TestableCase}; +use crate::builder::matches::{ + Binding, Candidate, FlatPat, MatchPairKind, MatchPairTree, TestableCase, +}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Creates a false edge to `imaginary_target` and a real edge to @@ -159,35 +161,36 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } fn visit_match_pair(&mut self, match_pair: &MatchPairTree<'tcx>) { - if let TestableCase::Or { pats, .. } = &match_pair.testable_case { - for flat_pat in pats.iter() { - self.visit_flat_pat(flat_pat) - } - } else if matches!(match_pair.testable_case, TestableCase::Deref { .. }) { - // The subpairs of a deref pattern are all places relative to the deref temporary, so we - // don't fake borrow them. Problem is, if we only shallowly fake-borrowed - // `match_pair.place`, this would allow: - // ``` - // let mut b = Box::new(false); - // match b { - // deref!(true) => {} // not reached because `*b == false` - // _ if { *b = true; false } => {} // not reached because the guard is `false` - // deref!(false) => {} // not reached because the guard changed it - // // UB because we reached the unreachable. - // } - // ``` - // Hence we fake borrow using a deep borrow. - if let Some(place) = match_pair.place { - self.fake_borrow(place, FakeBorrowKind::Deep); - } - } else { - // Insert a Shallow borrow of any place that is switched on. - if let Some(place) = match_pair.place { - self.fake_borrow(place, FakeBorrowKind::Shallow); + match match_pair.kind { + MatchPairKind::Or { ref or_subpats } => { + for flat_pat in or_subpats { + self.visit_flat_pat(flat_pat); + } } + MatchPairKind::Testable { place, ref testable_case, ref subpairs } => { + if matches!(testable_case, TestableCase::Deref { .. }) { + // The subpairs of a deref pattern are all places relative to the deref temporary, so we + // don't fake borrow them. Problem is, if we only shallowly fake-borrowed + // `match_pair.place`, this would allow: + // ``` + // let mut b = Box::new(false); + // match b { + // deref!(true) => {} // not reached because `*b == false` + // _ if { *b = true; false } => {} // not reached because the guard is `false` + // deref!(false) => {} // not reached because the guard changed it + // // UB because we reached the unreachable. + // } + // ``` + // Hence we fake borrow using a deep borrow. + self.fake_borrow(place, FakeBorrowKind::Deep); + } else { + // Insert a Shallow borrow of any place that is switched on. + self.fake_borrow(place, FakeBorrowKind::Shallow); - for subpair in &match_pair.subpairs { - self.visit_match_pair(subpair); + for subpair in subpairs { + self.visit_match_pair(subpair); + } + } } } } From 178eb4b50fd1bbeef1e2c62c314c8eafe05a235d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 6 Aug 2026 19:15:04 +0200 Subject: [PATCH 3/8] attach naked function target features to module assembly --- .../rustc_codegen_cranelift/src/global_asm.rs | 1 + compiler/rustc_codegen_gcc/src/asm.rs | 1 + compiler/rustc_codegen_llvm/src/asm.rs | 19 +- compiler/rustc_codegen_ssa/src/base.rs | 2 +- .../rustc_codegen_ssa/src/mir/naked_asm.rs | 4 +- compiler/rustc_codegen_ssa/src/traits/asm.rs | 5 + .../naked-functions/target-feature.rs | 165 ++++++++++++++++++ .../naked-functions/target-feature-aarch64.rs | 47 +++++ .../target-feature-aarch64.sha3.stderr | 10 ++ .../target-feature-aarch64.vanilla.stderr | 18 ++ .../naked-functions/target-feature-s390x.rs | 30 ++++ .../target-feature-s390x.stderr | 10 ++ 12 files changed, 307 insertions(+), 5 deletions(-) create mode 100644 tests/assembly-llvm/naked-functions/target-feature.rs create mode 100644 tests/ui/asm/naked-functions/target-feature-aarch64.rs create mode 100644 tests/ui/asm/naked-functions/target-feature-aarch64.sha3.stderr create mode 100644 tests/ui/asm/naked-functions/target-feature-aarch64.vanilla.stderr create mode 100644 tests/ui/asm/naked-functions/target-feature-s390x.rs create mode 100644 tests/ui/asm/naked-functions/target-feature-s390x.stderr diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 9763b0c0fa867..c5b164b8e9cce 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -30,6 +30,7 @@ impl<'tcx> AsmCodegenMethods<'tcx> for GlobalAsmContext<'_, 'tcx> { operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, _line_spans: &[Span], + _extra_rust_target_features: &[String], ) { codegen_global_asm_inner(self.tcx, self.global_asm, template, operands, options); } diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index ac86fbe7428b0..733dc52465dea 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -928,6 +928,7 @@ impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, line_spans: &[Span], + _extra_rust_target_features: &[String], ) { let asm_arch = self.tcx.sess.asm_arch.unwrap(); diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 549769547da78..6f9ddc1fe2c88 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -414,6 +414,7 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, _line_spans: &[Span], + extra_rust_target_features: &[String], ) { let asm_arch = self.tcx.sess.asm_arch.unwrap(); @@ -499,14 +500,26 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { template_str.push_str("\n.att_syntax\n"); } - let target_features = self.tcx.global_backend_features(()).join(","); - let target_cpu = llvm_util::target_cpu(self.tcx.sess); + // Globally-enabled features that are already in the backend format. + let global_features = self.tcx.global_backend_features(()).iter().map(String::as_str); + + // Features enabled on a particular instance, in the rust format. + // These need to be translated to the LLVM format. + let function_features: Vec<_> = extra_rust_target_features + .iter() + .flat_map(|feat| llvm_util::to_llvm_features(self.tcx.sess, feat)) + .flat_map(|feat| feat.into_iter().map(|f| format!("+{f}"))) + .collect(); + + let function_features = function_features.iter().map(String::as_str); + let target_features = + global_features.chain(function_features).intersperse(",").collect::(); llvm::append_module_inline_asm( self.llmod, template_str.as_bytes(), &target_features, - target_cpu, + llvm_util::target_cpu(self.tcx.sess), ); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 9eb4fd510fd7f..c870d1694d068 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -490,7 +490,7 @@ where }) .collect(); - cx.codegen_global_asm(asm.template, &operands, asm.options, asm.line_spans); + cx.codegen_global_asm(asm.template, &operands, asm.options, asm.line_spans, &[]); } else { span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type") } diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 05b87bb6d7159..939e5395e4741 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -54,7 +54,9 @@ pub fn codegen_naked_asm< template_vec.extend(template.iter().cloned()); template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(end.into())); - cx.codegen_global_asm(&template_vec, &operands, options, line_spans); + let target_features: Vec<_> = + cx.tcx().asm_target_features(instance.def_id()).iter().map(|s| s.to_string()).collect(); + cx.codegen_global_asm(&template_vec, &operands, options, line_spans, &target_features); } fn inline_to_global_operand<'a, 'tcx, Cx: LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>>>( diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index 85a2fe09ba414..1deed8c4dc016 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -66,12 +66,17 @@ pub trait AsmBuilderMethods<'tcx>: BackendTypes { } pub trait AsmCodegenMethods<'tcx> { + /// Codegen a module-level assembly block. + /// + /// NOTE: the target features must be the rust target feature names, not backend target + /// feature names. This argument is used to forward target features on naked functions. fn codegen_global_asm( &mut self, template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef<'tcx>], options: InlineAsmOptions, line_spans: &[Span], + extra_rust_target_features: &[String], ); /// The mangled name of this instance diff --git a/tests/assembly-llvm/naked-functions/target-feature.rs b/tests/assembly-llvm/naked-functions/target-feature.rs new file mode 100644 index 0000000000000..500e2a778e475 --- /dev/null +++ b/tests/assembly-llvm/naked-functions/target-feature.rs @@ -0,0 +1,165 @@ +//@ revisions: aarch64-elf aarch64-macho aarch64-coff x86_64 s390x riscv64 powerpc64 loongarch64 +//@ add-minicore +//@ assembly-output: emit-asm +//@ min-llvm-version: 23 +// +//@ [x86_64] compile-flags: --target x86_64-unknown-linux-gnu +//@ [x86_64] needs-llvm-components: x86 +// +//@ [aarch64-elf] compile-flags: --target aarch64-unknown-linux-gnu +//@ [aarch64-elf] needs-llvm-components: aarch64 +//@ [aarch64-macho] compile-flags: --target aarch64-apple-darwin +//@ [aarch64-macho] needs-llvm-components: aarch64 +//@ [aarch64-coff] compile-flags: --target aarch64-pc-windows-gnullvm +//@ [aarch64-coff] needs-llvm-components: aarch64 +// +//@ [s390x] compile-flags: --target s390x-unknown-linux-gnu +//@ [s390x] needs-llvm-components: systemz +// +//@ [powerpc64] compile-flags: --target powerpc64-unknown-linux-gnu +//@ [powerpc64] needs-llvm-components: powerpc +// +//@ [riscv64] compile-flags: --target riscv64gc-unknown-linux-gnu +//@ [riscv64] needs-llvm-components: riscv +// +// NOTE: loongarch64 does not error when using an instruction without enabling the corresponding +// target feature. +//@ [loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu +//@ [loongarch64] needs-llvm-components: loongarch + +// Test that the #[target_feature(enable = ...)]` works on naked functions. + +#![crate_type = "lib"] +#![feature(no_core, naked_functions_target_feature)] +#![feature(s390x_target_feature, powerpc_target_feature, loongarch_target_feature)] +#![no_core] + +extern crate minicore; +use minicore::*; + +// x86_64-LABEL: vpclmulqdq: +// x86_64: vpclmulqdq +#[no_mangle] +#[unsafe(naked)] +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "vpclmulqdq")] +unsafe extern "C" fn vpclmulqdq() { + naked_asm!("vpclmulqdq zmm1, zmm2, zmm3, 4") +} + +// i8mm is not enabled by default +// +// note that aarch64-apple-darwin enables more features than aarch64-unknown-linux-gnu +// +// aarch64-elf-LABEL: i8mm: +// aarch64-elf: usdot +// aarch64-macho-LABEL: i8mm: +// aarch64-macho: usdot +// aarch64-coff-LABEL: i8mm: +// aarch64-coff: usdot +#[no_mangle] +#[unsafe(naked)] +#[cfg(target_arch = "aarch64")] +#[target_feature(enable = "i8mm")] +unsafe extern "C" fn i8mm() { + naked_asm!("usdot v0.4s, v1.16b, v2.4b[3]") +} + +// riscv64: sh1add: +// riscv64: sh1add +#[no_mangle] +#[unsafe(naked)] +#[cfg(target_arch = "riscv64")] +#[target_feature(enable = "zba")] +unsafe extern "C" fn sh1add() { + naked_asm!("sh1add a0, a1, a2", "ret"); +} + +#[cfg(target_arch = "s390x")] +mod s390x { + use super::*; + + // s390x: vector: + // s390x: vavglg + #[no_mangle] + #[unsafe(naked)] + #[target_feature(enable = "vector")] + unsafe extern "C" fn vector() { + naked_asm!("vavglg %v0, %v0, %v0") + } + + // s390x: vector_enhancements_1: + // s390x: vfcesbs + #[no_mangle] + #[unsafe(naked)] + #[target_feature(enable = "vector-enhancements-1")] + unsafe extern "C" fn vector_enhancements_1() { + naked_asm!("vfcesbs %v0, %v0, %v0") + } + + // s390x: vector_enhancements_2: + // s390x: vclfp + #[no_mangle] + #[unsafe(naked)] + #[target_feature(enable = "vector-enhancements-2")] + unsafe extern "C" fn vector_enhancements_2() { + naked_asm!("vclfp %v0, %v0, 0, 0, 0") + } + + // s390x: vector_packed_decimal: + // s390x: vlrlr + #[no_mangle] + #[unsafe(naked)] + #[target_feature(enable = "vector-packed-decimal")] + unsafe extern "C" fn vector_packed_decimal() { + naked_asm!("vlrlr %v24, %r3, 0(%r2)", "br %r14") + } + + // s390x: vector_packed_decimal_enhancement: + // s390x: vcvbg + #[no_mangle] + #[unsafe(naked)] + #[target_feature(enable = "vector-packed-decimal-enhancement")] + unsafe extern "C" fn vector_packed_decimal_enhancement() { + naked_asm!("vcvbg %r0, %v0, 0, 1") + } + + // s390x: vector_packed_decimal_enhancement_2: + // s390x: vupkzl + #[no_mangle] + #[unsafe(naked)] + #[target_feature(enable = "vector-packed-decimal-enhancement-2")] + unsafe extern "C" fn vector_packed_decimal_enhancement_2() { + naked_asm!("vupkzl %v0, %v0, 0") + } +} + +// powerpc64: power10_vector: +// powerpc64: xxpermx +#[no_mangle] +#[unsafe(naked)] +#[cfg(target_arch = "powerpc64")] +#[target_feature(enable = "power10-vector")] +unsafe extern "C" fn power10_vector() { + naked_asm!("xxpermx 34, 0, 1, 2, 0", "blr") +} + +// loongarch64: lasx: +// loongarch64: xvadd.b +#[no_mangle] +#[unsafe(naked)] +#[cfg(target_arch = "loongarch64")] +#[target_feature(enable = "lasx")] +unsafe extern "C" fn lasx() { + naked_asm!("xvadd.b $xr0, $xr0, $xr1", "ret") +} + +// wasm32: simd128: +// wasm32: i8x16.shuffle +#[no_mangle] +#[unsafe(naked)] +#[cfg(target_arch = "wasm32")] +#[target_feature(enable = "simd128")] +unsafe extern "C" fn simd128() { + naked_asm!("i8x16.shuffle 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15", "return"); +} diff --git a/tests/ui/asm/naked-functions/target-feature-aarch64.rs b/tests/ui/asm/naked-functions/target-feature-aarch64.rs new file mode 100644 index 0000000000000..f82122f773ca0 --- /dev/null +++ b/tests/ui/asm/naked-functions/target-feature-aarch64.rs @@ -0,0 +1,47 @@ +//@ add-minicore +//@ build-fail +//@ revisions: vanilla sha3 +//@ compile-flags: --target aarch64-unknown-linux-gnu -Z deduplicate-diagnostics=yes +//@[sha3] compile-flags: -Ctarget-feature=+sha3 +//@ needs-llvm-components: aarch64 +//@ min-llvm-version: 23 + +#![crate_type = "lib"] +#![feature(no_core, naked_functions_target_feature)] +#![no_core] + +extern crate minicore; +use minicore::*; + +// check that a naked function using target features does not keep these features enabled +// for subsequent asm blocks. + +#[no_mangle] +#[unsafe(naked)] +#[target_feature(enable = "i8mm")] +unsafe extern "C" fn a() { + naked_asm!("usdot v0.4s, v1.16b, v2.4b[3]") +} + +//~? ERROR instruction requires: i8mm + +#[no_mangle] +#[unsafe(naked)] +unsafe extern "C" fn c() { + naked_asm!("usdot v0.4s, v2.16b, v2.4b[3]") +} + +#[no_mangle] +#[unsafe(naked)] +#[target_feature(enable = "sha3")] +unsafe extern "C" fn d() { + naked_asm!("eor3 v0.16b, v1.16b, v2.16b, v3.16b") +} + +//[vanilla]~? ERROR instruction requires: sha3 + +#[no_mangle] +#[unsafe(naked)] +unsafe extern "C" fn b() { + naked_asm!("eor3 v0.16b, v1.16b, v2.16b, v3.16b") +} diff --git a/tests/ui/asm/naked-functions/target-feature-aarch64.sha3.stderr b/tests/ui/asm/naked-functions/target-feature-aarch64.sha3.stderr new file mode 100644 index 0000000000000..49a65eaadb904 --- /dev/null +++ b/tests/ui/asm/naked-functions/target-feature-aarch64.sha3.stderr @@ -0,0 +1,10 @@ +error: instruction requires: i8mm + | +note: instantiated into assembly here + --> :15:1 + | +LL | usdot v0.4s, v2.16b, v2.4b[3] + | ^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/asm/naked-functions/target-feature-aarch64.vanilla.stderr b/tests/ui/asm/naked-functions/target-feature-aarch64.vanilla.stderr new file mode 100644 index 0000000000000..8ac31d19f5e3e --- /dev/null +++ b/tests/ui/asm/naked-functions/target-feature-aarch64.vanilla.stderr @@ -0,0 +1,18 @@ +error: instruction requires: sha3 + | +note: instantiated into assembly here + --> :6:1 + | +LL | eor3 v0.16b, v1.16b, v2.16b, v3.16b + | ^ + +error: instruction requires: i8mm + | +note: instantiated into assembly here + --> :15:1 + | +LL | usdot v0.4s, v2.16b, v2.4b[3] + | ^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/asm/naked-functions/target-feature-s390x.rs b/tests/ui/asm/naked-functions/target-feature-s390x.rs new file mode 100644 index 0000000000000..b0f806c4c0a16 --- /dev/null +++ b/tests/ui/asm/naked-functions/target-feature-s390x.rs @@ -0,0 +1,30 @@ +//@ add-minicore +//@ build-fail +//@ compile-flags: --target s390x-unknown-linux-gnu -Z deduplicate-diagnostics=yes +//@ needs-llvm-components: systemz +//@ min-llvm-version: 23 + +#![crate_type = "lib"] +#![feature(no_core, naked_functions_target_feature)] +#![no_core] + +extern crate minicore; +use minicore::*; + +// check that a naked function using target features does not keep these features enabled +// for subsequent asm blocks. + +#[no_mangle] +#[unsafe(naked)] +#[target_feature(enable = "vector-packed-decimal")] +unsafe extern "C" fn a() { + naked_asm!("vlrlr %v24, %r3, 0(%r2)") +} + +//~? ERROR instruction requires: vector-packed-decimal + +#[no_mangle] +#[unsafe(naked)] +unsafe extern "C" fn b() { + naked_asm!("vlrlr %v24, %r3, 0(%r3)") +} diff --git a/tests/ui/asm/naked-functions/target-feature-s390x.stderr b/tests/ui/asm/naked-functions/target-feature-s390x.stderr new file mode 100644 index 0000000000000..84d60c43bc765 --- /dev/null +++ b/tests/ui/asm/naked-functions/target-feature-s390x.stderr @@ -0,0 +1,10 @@ +error: instruction requires: vector-packed-decimal + | +note: instantiated into assembly here + --> :6:1 + | +LL | vlrlr %v24, %r3, 0(%r3) + | ^ + +error: aborting due to 1 previous error + From 892c6bb8e88536aafabe1d4734073b82c15da566 Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Mon, 31 Aug 2026 14:20:09 +0200 Subject: [PATCH 4/8] explicitly track inherent const generic args kind --- compiler/rustc_borrowck/src/type_check/mod.rs | 3 +- .../src/check/compare_impl_item.rs | 4 +- .../src/hir_ty_lowering/bounds.rs | 7 +- .../src/hir_ty_lowering/errors.rs | 1 + .../src/hir_ty_lowering/mod.rs | 39 ++-- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 48 +---- compiler/rustc_hir_typeck/src/lib.rs | 3 +- compiler/rustc_infer/src/infer/mod.rs | 3 +- .../src/infer/relate/generalize.rs | 3 +- compiler/rustc_middle/src/mir/consts.rs | 10 +- .../rustc_middle/src/mir/interpret/queries.rs | 5 +- compiler/rustc_middle/src/mir/pretty.rs | 3 +- compiler/rustc_middle/src/ty/context.rs | 177 ++++++++++++++---- .../src/ty/context/impl_interner.rs | 46 ++++- compiler/rustc_middle/src/ty/error.rs | 3 +- compiler/rustc_middle/src/ty/print/pretty.rs | 8 +- compiler/rustc_middle/src/ty/sty.rs | 16 -- compiler/rustc_middle/src/ty/util.rs | 3 +- .../src/builder/expr/as_constant.rs | 6 +- .../src/thir/pattern/const_to_pat.rs | 9 +- .../rustc_mir_build/src/thir/pattern/mod.rs | 6 +- .../src/solve/eval_ctxt/mod.rs | 16 +- .../src/solve/normalizes_to.rs | 37 ++-- .../src/solve/project_goals/inherent.rs | 81 +++++--- .../src/solve/project_goals/mod.rs | 4 +- .../src/unstable/convert/stable/ty.rs | 3 +- .../cfi/typeid/itanium_cxx_abi/transform.rs | 1 + compiler/rustc_symbol_mangling/src/v0.rs | 3 +- .../src/error_reporting/infer/mod.rs | 3 +- .../src/traits/fulfill.rs | 3 +- .../src/traits/normalize.rs | 2 +- .../src/traits/project.rs | 35 ++-- .../src/traits/query/normalize.rs | 4 +- .../traits/query/type_op/ascribe_user_type.rs | 22 --- .../src/traits/select/mod.rs | 3 +- .../rustc_trait_selection/src/traits/wf.rs | 6 +- .../src/normalize_projection_ty.rs | 6 - compiler/rustc_ty_utils/src/consts.rs | 11 +- compiler/rustc_type_ir/src/const_kind.rs | 73 ++++++-- compiler/rustc_type_ir/src/interner.rs | 27 ++- compiler/rustc_type_ir/src/predicate.rs | 5 +- compiler/rustc_type_ir/src/relate.rs | 13 +- compiler/rustc_type_ir/src/term_kind.rs | 68 ++++--- compiler/rustc_type_ir/src/ty_kind.rs | 28 +-- src/librustdoc/clean/utils.rs | 3 +- .../gca/path-to-non-type-const.rs | 17 +- ...h-to-non-type-inherent-associated-const.rs | 31 --- ...-non-type-inherent-associated-const.stderr | 24 --- 48 files changed, 563 insertions(+), 369 deletions(-) delete mode 100644 tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.rs delete mode 100644 tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.stderr diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 6f89a64f95360..d9534527dc48f 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1769,7 +1769,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { Const::Ty(_, ct) => match ct.kind() { ty::ConstKind::Alias(_, alias_const) => match alias_const.kind { ty::AliasConstKind::Projection { def_id } - | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::InherentSelf { def_id } + | ty::AliasConstKind::InherentImpl { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => Some(UnevaluatedConst { def: def_id, diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 7ad57107eebbd..e5d26cf72f9a5 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -2727,9 +2727,9 @@ fn param_env_with_gat_bounds<'tcx>( _ => clauses.push( ty::Binder::bind_with_vars( ty::ProjectionClause { - projection_term: ty::AliasTerm::new_from_def_id( + projection_term: ty::AliasTerm::new( tcx, - trait_ty.def_id, + ty::AliasTermKind::ProjectionTy { def_id: trait_ty.def_id }, rebased_args, ), term: normalize_impl_ty.into(), diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index c12bafd9d5d57..9fde34f473205 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -477,7 +477,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); debug!(?alias_args); - ty::AliasTerm::new_from_def_id(tcx, assoc_item.def_id, alias_args) + ty::AliasTerm::new_from_def_id( + tcx, + assoc_item.def_id, + alias_args, + ty::AliasConstInherentArgsKind::WithSelf, + ) }) }; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index dc108c41cf787..8c22506a8b918 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -485,6 +485,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx, assoc_item.def_id, alias_args, + ty::AliasConstInherentArgsKind::WithSelf, ) }); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 16a622da61c2b..cfff8d1768f0e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1609,7 +1609,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); } - Ok(TypeRelativePath::AssocItem(ty::AliasTerm::new_from_def_id(tcx, item_def_id, args))) + Ok(TypeRelativePath::AssocItem(ty::AliasTerm::new_from_def_id( + tcx, + item_def_id, + args, + ty::AliasConstInherentArgsKind::WithSelf, + ))) } /// Resolve a [type-relative](hir::QPath::TypeRelative) (and type-level) path. @@ -1773,12 +1778,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let kind = match assoc_tag { ty::AssocTag::Type => ty::AliasTermKind::InherentTy { def_id: assoc_item }, - ty::AssocTag::Const => { - // FIXME(mgca): drop once `InherentConst` accepts IAC-shaped args (issue #156181) - // without this, `new_from_args` errors (#155341). - self.require_type_const_attribute(assoc_item, span)?; - ty::AliasTermKind::InherentConst { def_id: assoc_item } - } + ty::AssocTag::Const => ty::AliasTermKind::InherentConstSelf { def_id: assoc_item }, ty::AssocTag::Fn => unreachable!(), }; @@ -1948,7 +1948,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.require_type_const_attribute(item_def_id, span)?; let alias_const = ty::AliasConst::new( tcx, - ty::AliasConstKind::new_from_def_id(tcx, item_def_id), + ty::AliasConstKind::new_from_def_id( + tcx, + item_def_id, + ty::AliasConstInherentArgsKind::WithSelf, + ), item_args, ); Ok(Const::new_alias(tcx, ty::IsRigid::No, alias_const)) @@ -2903,7 +2907,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Const::new_alias( tcx, ty::IsRigid::No, - ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, did), args), + ty::AliasConst::new( + tcx, + ty::AliasConstKind::new_from_def_id( + tcx, + did, + ty::AliasConstInherentArgsKind::WithSelf, + ), + args, + ), ) } Res::Def(kind @ DefKind::Ctor(ctor_of, CtorKind::Const), did) => { @@ -3141,14 +3153,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, ) -> Result<(), ErrorGuaranteed> { let tcx = self.tcx(); - // FIXME(gca): Intentionally disallowing paths to inherent associated non-type constants - // until a refactoring for how generic args for IACs are represented has been landed. - let is_inherent_assoc_const = tcx.def_kind(def_id) - == DefKind::AssocConst { is_type_const: false } - && tcx.def_kind(tcx.parent(def_id)) == DefKind::Impl { of_trait: false }; - if tcx.is_type_const(def_id) - || tcx.features().generic_const_args() && !is_inherent_assoc_const - { + if tcx.is_type_const(def_id) || tcx.features().generic_const_args() { Ok(()) } else { let mut err = self.dcx().struct_span_err( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 651b4ca33be99..b59dc21981ce6 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -48,38 +48,6 @@ use crate::method::{self, MethodCallee}; use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Transform generic args for inherent associated type constants (IACs). - /// - /// IACs have a different generic parameter structure than regular associated constants: - /// - Regular assoc const: parent (impl) generic params + own generic params - /// - IAC (type_const): Self type + own generic params - pub(crate) fn transform_args_for_inherent_type_const( - &self, - def_id: DefId, - args: GenericArgsRef<'tcx>, - ) -> GenericArgsRef<'tcx> { - let tcx = self.tcx; - if !tcx.is_type_const(def_id) { - return args; - } - let Some(assoc_item) = tcx.opt_associated_item(def_id) else { - return args; - }; - if !matches!(assoc_item.container, ty::AssocContainer::InherentImpl) { - return args; - } - - let impl_def_id = assoc_item.container_id(tcx); - let generics = tcx.generics_of(def_id); - let impl_args = &args[..generics.parent_count]; - let self_ty = tcx.type_of(impl_def_id).instantiate(tcx, impl_args).skip_norm_wip(); - // Build new args: [Self, own_args...] - let own_args = &args[generics.parent_count..]; - tcx.mk_args_from_iter( - std::iter::once(ty::GenericArg::from(self_ty)).chain(own_args.iter().copied()), - ) - } - /// Produces warning on the given node, if the current point in the /// function is unreachable, and there hasn't been another warning. pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) { @@ -1399,7 +1367,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let args_raw = implicit_args.unwrap_or_else(|| { + let args_for_user_type = implicit_args.unwrap_or_else(|| { lower_generic_args( self, def_id, @@ -1417,17 +1385,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }); - let args_for_user_type = if let Res::Def(DefKind::AssocConst { .. }, def_id) = res { - self.transform_args_for_inherent_type_const(def_id, args_raw) - } else { - args_raw - }; - // First, store the "user args" for later. self.write_user_type_annotation_from_args(hir_id, def_id, args_for_user_type, user_self_ty); // Normalize only after registering type annotations. - let args = self.normalize(span, Unnormalized::new_wip(args_raw)); + let args = self.normalize(span, Unnormalized::new_wip(args_for_user_type)); self.add_required_obligations_for_hir(span, def_id, args, hir_id); @@ -1465,12 +1427,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_instantiated); - let args = if let Res::Def(DefKind::AssocConst { .. }, def_id) = res { - self.transform_args_for_inherent_type_const(def_id, args) - } else { - args - }; - self.write_args(hir_id, args); (ty_instantiated, res) diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 57fd6a8658ae3..0f977710fbe09 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -392,7 +392,8 @@ fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Opti impl_def_id, impl_trait_ref.args, ); - tcx.check_args_compatible(trait_item_def_id, args) + let alias_kind = ty::AliasTermKind::ProjectionConst { def_id: trait_item_def_id }; + tcx.check_alias_term_args_compatible(alias_kind, args) .then(|| tcx.type_of(trait_item_def_id).instantiate(tcx, args).skip_norm_wip()) } else { Some(fcx.next_ty_var(span)) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index a49a4355b66b1..773cd9b75aaea 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -994,7 +994,8 @@ impl<'tcx> InferCtxt<'tcx> { | ty::AliasTermKind::OpaqueTy { .. } | ty::AliasTermKind::FreeTy { .. } => self.next_ty_var(span).into(), ty::AliasTermKind::FreeConst { .. } - | ty::AliasTermKind::InherentConst { .. } + | ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } | ty::AliasTermKind::AnonConst { .. } | ty::AliasTermKind::ProjectionConst { .. } => self.next_const_var(span).into(), } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index afdabb38c3b20..35d04597451c0 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -182,7 +182,8 @@ impl<'tcx> InferCtxt<'tcx> { | ty::AliasTermKind::OpaqueTy { .. } => { return Err(TypeError::CyclicTy(source_term.expect_type())); } - ty::AliasTermKind::InherentConst { .. } + ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } | ty::AliasTermKind::FreeConst { .. } | ty::AliasTermKind::AnonConst { .. } => { return Err(TypeError::CyclicConst(source_term.expect_const())); diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 54e64b37245c3..3b85651ee5f76 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -474,7 +474,15 @@ impl<'tcx> UnevaluatedConst<'tcx> { #[inline] pub fn shrink(self, tcx: TyCtxt<'tcx>) -> ty::AliasConst<'tcx> { assert_eq!(self.promoted, None); - ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, self.def), self.args) + ty::AliasConst::new( + tcx, + ty::AliasConstKind::new_from_def_id( + tcx, + self.def, + ty::AliasConstInherentArgsKind::Impl, + ), + self.args, + ) } } diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 9b98f4787371b..406a96ff7ca57 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -104,8 +104,11 @@ impl<'tcx> TyCtxt<'tcx> { } let def_id = match ct.kind { + ty::AliasConstKind::InherentSelf { .. } => { + bug!("got AliasConstKind::InherentSelf in const_eval_resolve_for_typeck") + } ty::AliasConstKind::Projection { def_id } - | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::InherentImpl { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => def_id, }; diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 021c1c176d788..c8d0820a78903 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1494,7 +1494,8 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { ty::ConstKind::Alias(_, alias_const) => { let kind = match alias_const.kind { ty::AliasConstKind::Projection { def_id } - | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::InherentSelf { def_id } + | ty::AliasConstKind::InherentImpl { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => self.tcx.def_path_str(def_id), }; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 7a3b4c7fbbeb8..924dc7552e59b 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -13,7 +13,7 @@ use std::hash::{Hash, Hasher}; use std::marker::PointeeSized; use std::ops::Deref; use std::sync::{Arc, OnceLock}; -use std::{fmt, iter, mem}; +use std::{debug_assert_matches, fmt, iter, mem}; use rustc_abi::{ExternAbi, FieldIdx, Layout, LayoutData, TargetDataLayout, VariantIdx}; use rustc_ast as ast; @@ -2117,27 +2117,44 @@ impl<'tcx> TyCtxt<'tcx> { if pred.kind() != binder { self.mk_predicate(binder) } else { pred } } + /// If you have a [`ty::Alias`], you should almost certainly be calling + /// [`Self::check_alias_term_args_compatible`] instead. This method assumes that inherent alias + /// consts always have `impl`-form args, and will return an invalid result if the `def_id` comes + /// from a [`ty::AliasConstKind::InherentSelf`] (see the doc on that for what "impl form args" + /// means). pub fn check_args_compatible(self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>]) -> bool { - self.check_args_compatible_inner(def_id, args, false) + let is_inherent_assoc_ty = matches!(self.def_kind(def_id), DefKind::AssocTy) + && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }); + self.check_args_compatible_inner(def_id, args, is_inherent_assoc_ty) + } + + pub fn check_alias_term_args_compatible( + self, + kind: ty::AliasTermKind<'tcx>, + args: &'tcx [ty::GenericArg<'tcx>], + ) -> bool { + let (def_id, is_self_args) = match kind { + ty::AliasTermKind::ProjectionTy { def_id } + | ty::AliasTermKind::OpaqueTy { def_id } + | ty::AliasTermKind::FreeTy { def_id } + | ty::AliasTermKind::AnonConst { def_id } + | ty::AliasTermKind::ProjectionConst { def_id } + | ty::AliasTermKind::FreeConst { def_id } + | ty::AliasTermKind::InherentConstImpl { def_id } => (def_id, false), + ty::AliasTermKind::InherentTy { def_id } + | ty::AliasTermKind::InherentConstSelf { def_id } => (def_id, true), + }; + self.check_args_compatible_inner(def_id, args, is_self_args) } fn check_args_compatible_inner( self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>], - nested: bool, + is_self_args: bool, ) -> bool { let generics = self.generics_of(def_id); - - // IATs and IACs (inherent associated types/consts with `type const`) themselves have a - // weird arg setup (self + own args), but nested items *in* IATs (namely: opaques, i.e. - // ATPITs) do not. - let is_inherent_assoc_ty = matches!(self.def_kind(def_id), DefKind::AssocTy) - && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }); - let is_inherent_assoc_type_const = - matches!(self.def_kind(def_id), DefKind::AssocConst { is_type_const: true }) - && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }); - let own_args = if !nested && (is_inherent_assoc_ty || is_inherent_assoc_type_const) { + let own_args = if is_self_args { if generics.own_params.len() + 1 != args.len() { return false; } @@ -2154,8 +2171,11 @@ impl<'tcx> TyCtxt<'tcx> { let (parent_args, own_args) = args.split_at(generics.parent_count); + // In the type system, IATs and IACs (inherent associated types/consts) themselves have a + // weird arg setup (self + own args), but nested items *in* IATs (namely: opaques, i.e. + // ATPITs) do not. So, set `is_self_args` to false for the parent generic check. if let Some(parent) = generics.parent - && !self.check_args_compatible_inner(parent, parent_args, true) + && !self.check_args_compatible_inner(parent, parent_args, false) { return false; } @@ -2177,39 +2197,116 @@ impl<'tcx> TyCtxt<'tcx> { /// With `cfg(debug_assertions)`, assert that args are compatible with their generics, /// and print out the args if not. + /// + /// If you have a [`ty::Alias`], you should use + /// [`Self::debug_assert_alias_term_args_compatible`] instead. See note on + /// [`Self::check_args_compatible`]. pub fn debug_assert_args_compatible(self, def_id: DefId, args: &'tcx [ty::GenericArg<'tcx>]) { if cfg!(debug_assertions) && !self.check_args_compatible(def_id, args) { let is_inherent_assoc_ty = matches!(self.def_kind(def_id), DefKind::AssocTy) && matches!(self.def_kind(self.parent(def_id)), DefKind::Impl { of_trait: false }); - let is_inherent_assoc_type_const = - matches!(self.def_kind(def_id), DefKind::AssocConst { is_type_const: true }) - && matches!( - self.def_kind(self.parent(def_id)), - DefKind::Impl { of_trait: false } - ); - if is_inherent_assoc_ty || is_inherent_assoc_type_const { - bug!( - "args not compatible with generics for {}: args={:#?}, generics={:#?}", - self.def_path_str(def_id), - args, - // Make `[Self, GAT_ARGS...]` (this could be simplified) - self.mk_args_from_iter( - [self.types.self_param.into()].into_iter().chain( - self.generics_of(def_id) - .own_args(ty::GenericArgs::identity_for_item(self, def_id)) - .iter() - .copied() - ) - ) + self.emit_bug_args_compatible(def_id, args, is_inherent_assoc_ty); + } + } + + pub fn debug_assert_alias_term_args_compatible( + self, + kind: ty::AliasTermKind<'tcx>, + args: ty::GenericArgsRef<'tcx>, + ) { + if cfg!(debug_assertions) { + self.debug_assert_alias_term_kind_matches_def_kind(kind); + if !self.check_alias_term_args_compatible(kind, args) { + let (def_id, is_self_args) = match kind { + ty::AliasTermKind::ProjectionTy { def_id } + | ty::AliasTermKind::OpaqueTy { def_id } + | ty::AliasTermKind::FreeTy { def_id } + | ty::AliasTermKind::AnonConst { def_id } + | ty::AliasTermKind::ProjectionConst { def_id } + | ty::AliasTermKind::FreeConst { def_id } + | ty::AliasTermKind::InherentConstImpl { def_id } => (def_id, false), + ty::AliasTermKind::InherentTy { def_id } + | ty::AliasTermKind::InherentConstSelf { def_id } => (def_id, true), + }; + self.emit_bug_args_compatible(def_id, args, is_self_args); + } + } + } + + fn debug_assert_alias_term_kind_matches_def_kind(self, kind: ty::AliasTermKind<'tcx>) { + match kind { + ty::AliasTermKind::ProjectionTy { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy); + debug_assert_matches!( + self.def_kind(self.parent(def_id)), + DefKind::Trait | DefKind::Impl { of_trait: true } + ); + } + ty::AliasTermKind::InherentTy { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy); + debug_assert_matches!( + self.def_kind(self.parent(def_id)), + DefKind::Impl { of_trait: false } + ); + } + ty::AliasTermKind::OpaqueTy { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::OpaqueTy); + } + ty::AliasTermKind::FreeTy { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::TyAlias); + } + ty::AliasTermKind::AnonConst { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::AnonConst); + } + ty::AliasTermKind::ProjectionConst { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::AssocConst { .. }); + debug_assert_matches!( + self.def_kind(self.parent(def_id)), + DefKind::Trait | DefKind::Impl { of_trait: true } ); - } else { - bug!( - "args not compatible with generics for {}: args={:#?}, generics={:#?}", - self.def_path_str(def_id), - args, - ty::GenericArgs::identity_for_item(self, def_id) + } + ty::AliasTermKind::InherentConstSelf { def_id } + | ty::AliasTermKind::InherentConstImpl { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::AssocConst { .. }); + debug_assert_matches!( + self.def_kind(self.parent(def_id)), + DefKind::Impl { of_trait: false } ); } + ty::AliasTermKind::FreeConst { def_id } => { + debug_assert_matches!(self.def_kind(def_id), DefKind::Const { .. }); + } + } + } + + fn emit_bug_args_compatible( + self, + def_id: DefId, + args: &'tcx [ty::GenericArg<'tcx>], + is_self_args: bool, + ) -> ! { + if is_self_args { + bug!( + "args not compatible with generics for {}: args={:#?}, generics={:#?}", + self.def_path_str(def_id), + args, + // Make `[Self, GAT_ARGS...]` (this could be simplified) + self.mk_args_from_iter( + [self.types.self_param.into()].into_iter().chain( + self.generics_of(def_id) + .own_args(ty::GenericArgs::identity_for_item(self, def_id)) + .iter() + .copied() + ) + ) + ); + } else { + bug!( + "args not compatible with generics for {}: args={:#?}, generics={:#?}", + self.def_path_str(def_id), + args, + ty::GenericArgs::identity_for_item(self, def_id) + ); } } diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 048e509ec88e0..576fdd8cb6053 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -204,11 +204,22 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.adt_def(adt_def_id) } - fn alias_const_kind_from_def_id(self, def_id: Self::DefId) -> ty::AliasConstKind<'tcx> { + fn alias_const_kind_from_def_id( + self, + def_id: Self::DefId, + inherent_args: ty::AliasConstInherentArgsKind, + ) -> ty::AliasConstKind<'tcx> { match self.def_kind(def_id) { DefKind::AssocConst { .. } => { if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) { - ty::AliasConstKind::Inherent { def_id } + match inherent_args { + ty::AliasConstInherentArgsKind::WithSelf => { + ty::AliasConstKind::InherentSelf { def_id } + } + ty::AliasConstInherentArgsKind::Impl => { + ty::AliasConstKind::InherentImpl { def_id } + } + } } else { ty::AliasConstKind::Projection { def_id } } @@ -221,7 +232,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> { } } - fn alias_term_kind_from_def_id(self, def_id: DefId) -> ty::AliasTermKind<'tcx> { + fn alias_term_kind_from_def_id( + self, + def_id: DefId, + inherent_args: ty::AliasConstInherentArgsKind, + ) -> ty::AliasTermKind<'tcx> { match self.def_kind(def_id) { DefKind::AssocTy => { if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) { @@ -232,7 +247,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> { } DefKind::AssocConst { .. } => { if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) { - ty::AliasTermKind::InherentConst { def_id } + match inherent_args { + ty::AliasConstInherentArgsKind::WithSelf => { + ty::AliasTermKind::InherentConstSelf { def_id } + } + ty::AliasConstInherentArgsKind::Impl => { + ty::AliasTermKind::InherentConstImpl { def_id } + } + } } else { ty::AliasTermKind::ProjectionConst { def_id } } @@ -271,14 +293,26 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.mk_args_from_iter(args) } - fn check_args_compatible(self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> bool { - self.check_args_compatible(def_id, args) + fn check_alias_term_args_compatible( + self, + kind: ty::AliasTermKind<'tcx>, + args: ty::GenericArgsRef<'tcx>, + ) -> bool { + self.check_alias_term_args_compatible(kind, args) } fn debug_assert_args_compatible(self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) { self.debug_assert_args_compatible(def_id, args); } + fn debug_assert_alias_term_args_compatible( + self, + kind: ty::AliasTermKind<'tcx>, + args: ty::GenericArgsRef<'tcx>, + ) { + self.debug_assert_alias_term_args_compatible(kind, args); + } + /// Assert that the args from an `ExistentialTraitRef` or `ExistentialProjection` /// are compatible with the `DefId`. Since we're missing a `Self` type, stick on /// a dummy self type and forward to `debug_assert_args_compatible`. diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 33541dee52fe6..fb4e30b161d44 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -334,7 +334,8 @@ impl<'tcx> TyCtxt<'tcx> { | ty::AliasTermKind::AnonConst { def_id } | ty::AliasTermKind::ProjectionConst { def_id } | ty::AliasTermKind::FreeConst { def_id } - | ty::AliasTermKind::InherentConst { def_id } => self.def_path_str(def_id), + | ty::AliasTermKind::InherentConstSelf { def_id } + | ty::AliasTermKind::InherentConstImpl { def_id } => self.def_path_str(def_id), } } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index f055051580e81..f5960e65c4493 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1539,7 +1539,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::ConstKind::Alias(_, ty::AliasConst { kind, args, .. }) => { match kind { ty::AliasConstKind::Projection { def_id } - | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::InherentSelf { def_id } + | ty::AliasConstKind::InherentImpl { def_id } | ty::AliasConstKind::Free { def_id } => { self.pretty_print_value_path(def_id, args)?; } @@ -3172,7 +3173,7 @@ define_print! { ty::AliasTerm<'tcx> { match self.kind { - ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::InherentConst { .. } => { + ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::InherentConstSelf { .. } => { p.pretty_print_inherent_projection(*self)?; } ty::AliasTermKind::ProjectionTy { def_id } => { @@ -3188,7 +3189,8 @@ define_print! { | ty::AliasTermKind::FreeConst { def_id } | ty::AliasTermKind::OpaqueTy { def_id } | ty::AliasTermKind::AnonConst { def_id } - | ty::AliasTermKind::ProjectionConst { def_id } => { + | ty::AliasTermKind::ProjectionConst { def_id } + | ty::AliasTermKind::InherentConstImpl { def_id } => { p.print_def_path(def_id, self.args)?; } } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index bef267b7eaf27..013064b5cec4b 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -478,22 +478,6 @@ impl<'tcx> Ty<'tcx> { is_rigid: ty::IsRigid, alias_ty: ty::AliasTy<'tcx>, ) -> Ty<'tcx> { - if cfg!(debug_assertions) { - match alias_ty.kind { - ty::AliasTyKind::Projection { def_id } => { - debug_assert_matches!(tcx.def_kind(def_id), DefKind::AssocTy) - } - ty::AliasTyKind::Inherent { def_id } => { - debug_assert_matches!(tcx.def_kind(def_id), DefKind::AssocTy) - } - ty::AliasTyKind::Opaque { def_id } => { - debug_assert_matches!(tcx.def_kind(def_id), DefKind::OpaqueTy) - } - ty::AliasTyKind::Free { def_id } => { - debug_assert_matches!(tcx.def_kind(def_id), DefKind::TyAlias) - } - } - } Ty::new(tcx, Alias(is_rigid, alias_ty)) } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 09963dba563ec..622086b56c638 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -962,7 +962,8 @@ impl<'tcx> TyCtxt<'tcx> { } ty::AliasTermKind::OpaqueTy { def_id } => Some(self.variances_of(def_id)), ty::AliasTermKind::InherentTy { .. } - | ty::AliasTermKind::InherentConst { .. } + | ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } | ty::AliasTermKind::FreeTy { .. } | ty::AliasTermKind::FreeConst { .. } | ty::AliasTermKind::AnonConst { .. } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index 6e09c365dbf7c..5996073241e2c 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -74,7 +74,11 @@ pub(crate) fn as_constant_inner<'tcx>( if tcx.is_type_const(def_id) { let uneval = ty::AliasConst::new( tcx, - ty::AliasConstKind::new_from_def_id(tcx, def_id), + ty::AliasConstKind::new_from_def_id( + tcx, + def_id, + ty::AliasConstInherentArgsKind::Impl, + ), args, ); let ct = ty::Const::new_alias(tcx, ty::IsRigid::No, uneval); diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 0230840ef2fb8..86387f5caf325 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -80,14 +80,16 @@ impl<'tcx> ConstToPat<'tcx> { fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box> { if let ty::ConstKind::Alias(_, alias_const) = self.c.kind() { if let ty::AliasConstKind::Projection { def_id } - | ty::AliasConstKind::Inherent { def_id } = alias_const.kind + | ty::AliasConstKind::InherentSelf { def_id } + | ty::AliasConstKind::InherentImpl { def_id } = alias_const.kind && let Some(def_id) = def_id.as_local() { // Include the container item in the output. err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), ""); } if let ty::AliasConstKind::Projection { def_id } - | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::InherentSelf { def_id } + | ty::AliasConstKind::InherentImpl { def_id } | ty::AliasConstKind::Free { def_id } = alias_const.kind { err.span_label(self.tcx.def_span(def_id), msg!("constant defined here")); @@ -166,7 +168,8 @@ impl<'tcx> ConstToPat<'tcx> { // on its use as well. if let ty::ConstKind::Alias(_, alias_const) = self.c.kind() && let ty::AliasConstKind::Projection { .. } - | ty::AliasConstKind::Inherent { .. } + | ty::AliasConstKind::InherentSelf { .. } + | ty::AliasConstKind::InherentImpl { .. } | ty::AliasConstKind::Free { .. } = alias_const.kind { err.downgrade_to_delayed_bug(); diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index b69519f3c714f..d64f98542b3a3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -658,7 +658,11 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { ty::IsRigid::No, ty::AliasConst::new( self.tcx, - ty::AliasConstKind::new_from_def_id(self.tcx, def_id), + ty::AliasConstKind::new_from_def_id( + self.tcx, + def_id, + ty::AliasConstInherentArgsKind::Impl, + ), args, ), ); diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index a0abc918107df..034ad3463ba13 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1074,7 +1074,8 @@ where | ty::AliasTermKind::OpaqueTy { .. } | ty::AliasTermKind::FreeTy { .. } => self.next_ty_infer().into(), ty::AliasTermKind::FreeConst { .. } - | ty::AliasTermKind::InherentConst { .. } + | ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } | ty::AliasTermKind::AnonConst { .. } | ty::AliasTermKind::ProjectionConst { .. } => self.next_const_infer().into(), } @@ -1440,12 +1441,15 @@ where if self.resolve_vars_if_possible(alias_const).has_non_region_infer() { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } else { + // Evaluation failed because the const was too generic or was an invalid type + // for const generics. The result of normalization is the alias itself, + // unchanged, but marked as rigid. + // // We do not instantiate to the `alias_const` passed in, but rather - // `goal.predicate.alias`. The `alias_const` passed in might correspond to the `impl` - // form of a constant (with generic arguments corresponding to the impl block), - // however, we want to structurally instantiate to the original, non-rebased, - // trait `Self` form of the constant (with generic arguments being the trait - // `Self` type). + // `projection_term`, which is the unprocessed, original alias contained within + // the goal. The `alias_const` passed in might be a Projection whose DefId is an + // impl of the trait, however, we want to structurally instantiate to the + // original DefId on the trait itself. self.eq( param_env, projection_term.to_term(self.cx(), ty::IsRigid::Yes), diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs index f5b1df1be3eff..75f15623a9ba7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs @@ -417,7 +417,17 @@ where target_container_def_id, )?; - if !cx.check_args_compatible(target_item_def_id.into(), target_args) { + let target_item_def_id: I::DefId = target_item_def_id.into(); + + let target_item_kind = if goal.predicate.alias.kind.is_type() { + ty::AliasTermKind::ProjectionTy { def_id: target_item_def_id.try_into().unwrap() } + } else { + ty::AliasTermKind::ProjectionConst { + def_id: target_item_def_id.try_into().unwrap(), + } + }; + + if !cx.check_alias_term_args_compatible(target_item_kind, target_args) { return error_response( ecx, cx.delay_bug("associated item has mismatched arguments"), @@ -427,15 +437,14 @@ where // Finally we construct the actual value of the associated type. let term = match goal.predicate.alias.kind { ty::AliasTermKind::ProjectionTy { .. } => { - let t = cx.type_of(target_item_def_id.into()).instantiate(cx, target_args); + let t = cx.type_of(target_item_def_id).instantiate(cx, target_args); let t = ecx.normalize(GoalSource::Misc, goal.param_env, t)?; t.into() } ty::AliasTermKind::ProjectionConst { .. } - if cx.is_type_const(target_item_def_id.into()) => + if cx.is_type_const(target_item_def_id) => { - let c = - cx.const_of_item(target_item_def_id.into()).instantiate(cx, target_args); + let c = cx.const_of_item(target_item_def_id).instantiate(cx, target_args); let c = ecx.normalize(GoalSource::Misc, goal.param_env, c)?; c.into() } @@ -443,7 +452,7 @@ where let alias_const = ty::AliasConst::new( cx, ty::AliasConstKind::Projection { - def_id: target_item_def_id.into().try_into().unwrap(), + def_id: target_item_def_id.try_into().unwrap(), }, target_args, ); @@ -827,13 +836,7 @@ where CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, ty::ProjectionClause { - projection_term: ty::AliasTerm::new( - ecx.cx(), - cx.alias_term_kind_from_def_id( - goal.predicate.alias.expect_projection_def_id().into(), - ), - [self_ty], - ), + projection_term: ty::AliasTerm::new(ecx.cx(), goal.predicate.alias.kind, [self_ty]), term, } .upcast(cx), @@ -865,13 +868,7 @@ where CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, ty::ProjectionClause { - projection_term: ty::AliasTerm::new( - ecx.cx(), - cx.alias_term_kind_from_def_id( - goal.predicate.alias.expect_projection_def_id().into(), - ), - [self_ty], - ), + projection_term: ty::AliasTerm::new(ecx.cx(), goal.predicate.alias.kind, [self_ty]), term, } .upcast(cx), diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs index a7480cded0514..51c2475a33461 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/inherent.rs @@ -5,7 +5,7 @@ //! 2. equate the self type, and //! 3. instantiate and register where clauses. -use rustc_type_ir::solve::QueryResultOrRerunNonErased; +use rustc_type_ir::solve::{NoSolutionOrRerunNonErased, QueryResultOrRerunNonErased}; use rustc_type_ir::{self as ty, Interner, Unnormalized}; use crate::delegate::SolverDelegate; @@ -21,20 +21,9 @@ where goal: Goal>, ) -> QueryResultOrRerunNonErased { let cx = self.cx(); - let inherent = goal.predicate.projection_term; - let def_id = inherent.expect_inherent_def_id(); - let impl_def_id = cx.inherent_alias_term_parent(def_id); - let impl_args = self.fresh_args_for_item(impl_def_id.into()); - - // Equate impl header and add impl where clauses - self.eq( - goal.param_env, - inherent.self_ty(), - cx.type_of(impl_def_id.into()).instantiate(cx, impl_args).skip_norm_wip(), - )?; - - // Equate IAT with the RHS of the project goal - let inherent_args = inherent.rebase_inherent_args_onto_impl(impl_args, cx); + let def_id = goal.predicate.projection_term.expect_inherent_def_id(); + let (inherent_kind, inherent_args) = + self.convert_inherent_self_to_impl(goal.param_env, goal.predicate.projection_term)?; // Check both where clauses on the impl and IAT // @@ -53,25 +42,28 @@ where .map(|clause| goal.with(cx, clause)), )?; - let normalized: I::Term = match inherent.kind { + let normalized: I::Term = match inherent_kind { ty::AliasTermKind::InherentTy { def_id } => { let inherent = cx.type_of(def_id.into()).instantiate(cx, inherent_args); let inherent = self.normalize(GoalSource::Misc, goal.param_env, inherent)?; inherent.into() } - ty::AliasTermKind::InherentConst { def_id } if cx.is_type_const(def_id.into()) => { + ty::AliasTermKind::InherentConstImpl { def_id } if cx.is_type_const(def_id.into()) => { let inherent = cx.const_of_item(def_id.into()).instantiate(cx, inherent_args); let inherent = self.normalize(GoalSource::Misc, goal.param_env, inherent)?; inherent.into() } - ty::AliasTermKind::InherentConst { .. } => { - // FIXME(gca): This is dead code at the moment. It should eventually call - // self.evaluate_const like projected consts do in consider_impl_candidate in - // normalizes_to/mod.rs. However, how generic args are represented for IACs is up in - // the air right now. - // Will self.evaluate_const eventually take the inherent_args or the impl_args form - // of args? It might be either. - panic!("References to inherent associated consts should have been blocked"); + ty::AliasTermKind::InherentConstImpl { .. } => { + let term = ty::AliasTerm::new_from_args(cx, inherent_kind, inherent_args); + // NOTE: we intentionally pass in the `InherentConstImpl` form as the term to + // instantiate to upon too-generic CTFE failure, as we ought to consistently compare + // identities via `InherentConstImpl` rather than `InherentConstSelf`. + return self.evaluate_const_and_instantiate_projection_term( + goal.param_env, + term, + goal.predicate.term, + term.expect_ct(), + ); } kind => panic!("expected inherent alias, found {kind:?}"), }; @@ -84,4 +76,43 @@ where self.eq(goal.param_env, goal.predicate.term, normalized)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + + fn convert_inherent_self_to_impl( + &mut self, + param_env: I::ParamEnv, + term: ty::AliasTerm, + ) -> Result<(ty::AliasTermKind, I::GenericArgs), NoSolutionOrRerunNonErased> { + match term.kind { + ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::InherentConstSelf { .. } => { + let cx = self.cx(); + let def_id = term.expect_inherent_def_id(); + let impl_def_id = cx.inherent_alias_term_parent(def_id); + let impl_args = self.fresh_args_for_item(impl_def_id.into()); + + // Equate impl header and add impl where clauses + self.eq( + param_env, + term.self_ty(), + cx.type_of(impl_def_id.into()).instantiate(cx, impl_args).skip_norm_wip(), + )?; + + // Equate IAT with the RHS of the project goal + let inherent_args = term.rebase_inherent_args_onto_impl(impl_args, cx); + + let kind = match term.kind { + ty::AliasTermKind::InherentTy { def_id } => { + ty::AliasTermKind::InherentTy { def_id } + } + ty::AliasTermKind::InherentConstSelf { def_id } => { + ty::AliasTermKind::InherentConstImpl { def_id } + } + _ => unreachable!(), + }; + + Ok((kind, inherent_args)) + } + ty::AliasTermKind::InherentConstImpl { .. } => Ok((term.kind, term.args)), + kind => panic!("expected inherent alias, found {kind:?}"), + } + } } diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs index 6ec82aefb523f..db326e6d736a4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs @@ -27,7 +27,9 @@ where ty::AliasTermKind::ProjectionTy { .. } | ty::AliasTermKind::ProjectionConst { .. } => { self.normalize_associated_term(goal) } - ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::InherentConst { .. } => { + ty::AliasTermKind::InherentTy { .. } + | ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } => { self.normalize_inherent_associated_term(goal) } ty::AliasTermKind::OpaqueTy { .. } => self.normalize_opaque_type(goal), diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index e142cb26447a8..17ce015d4ce10 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -60,7 +60,8 @@ impl<'tcx> Stable<'tcx> for ty::AliasTerm<'tcx> { | ty::AliasTermKind::AnonConst { def_id } | ty::AliasTermKind::ProjectionConst { def_id } | ty::AliasTermKind::FreeConst { def_id } - | ty::AliasTermKind::InherentConst { def_id } => def_id, + | ty::AliasTermKind::InherentConstSelf { def_id } + | ty::AliasTermKind::InherentConstImpl { def_id } => def_id, }; crate::ty::AliasTerm { def_id: tables.alias_def(def_id), args: args.stable(tables, cx) } } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 8a44589f5052c..2019f7e15dc70 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -250,6 +250,7 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc tcx, assoc_item.def_id, super_trait_ref.args, + ty::AliasConstInherentArgsKind::WithSelf, ); let term = tcx.normalize_erasing_regions( ty::TypingEnv::fully_monomorphized(), diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index cf08d3e858ec5..5ed41ac456031 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -749,7 +749,8 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { // logic sometimes passing identity-substituted impl headers. ty::ConstKind::Alias(_, ty::AliasConst { kind, args, .. }) => match kind { ty::AliasConstKind::Projection { def_id } - | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::InherentSelf { def_id } + | ty::AliasConstKind::InherentImpl { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => { return self.print_def_path(def_id, args); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 1210a3ef57e32..34df03e2584e6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -1613,7 +1613,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty::AliasTermKind::AnonConst { def_id } => def_id.into(), ty::AliasTermKind::ProjectionConst { def_id } => def_id.into(), ty::AliasTermKind::FreeConst { def_id } => def_id.into(), - ty::AliasTermKind::InherentConst { def_id } => def_id.into(), + ty::AliasTermKind::InherentConstSelf { def_id } => def_id.into(), + ty::AliasTermKind::InherentConstImpl { def_id } => def_id.into(), }; (false, Mismatch::Fixed(self.tcx.def_descr(def_id))) } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index bca336c2a0449..ddbb56affcff1 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -720,7 +720,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { && matches!( a.kind, ty::AliasConstKind::Projection { .. } - | ty::AliasConstKind::Inherent { .. } + | ty::AliasConstKind::InherentSelf { .. } + | ty::AliasConstKind::InherentImpl { .. } ) => { if let Ok(new_obligations) = infcx diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index f00b300c7e971..0d22ca4973511 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -491,7 +491,7 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx ty::AliasConstKind::Projection { .. } => { self.normalize_trait_projection(alias_const.into()).expect_const() } - ty::AliasConstKind::Inherent { .. } => { + ty::AliasConstKind::InherentSelf { .. } | ty::AliasConstKind::InherentImpl { .. } => { self.normalize_inherent_projection(alias_const.into()).expect_const() } ty::AliasConstKind::Free { .. } => { diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index eaaf082b105c2..9d0daa3a8672b 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -470,18 +470,7 @@ fn normalize_to_error<'a, 'tcx>( depth: usize, ) -> NormalizedTerm<'tcx> { let trait_ref = ty::Binder::dummy(projection_term.trait_ref(selcx.tcx())); - let new_value = match projection_term.kind { - ty::AliasTermKind::ProjectionTy { .. } - | ty::AliasTermKind::InherentTy { .. } - | ty::AliasTermKind::OpaqueTy { .. } - | ty::AliasTermKind::FreeTy { .. } => selcx.infcx.next_ty_var(cause.span).into(), - ty::AliasTermKind::FreeConst { .. } - | ty::AliasTermKind::InherentConst { .. } - | ty::AliasTermKind::AnonConst { .. } - | ty::AliasTermKind::ProjectionConst { .. } => { - selcx.infcx.next_const_var(cause.span).into() - } - }; + let new_value = selcx.infcx.next_term_var_of_alias_kind(projection_term, cause.span); let mut obligations = PredicateObligations::new(); obligations.push(Obligation { cause, @@ -608,7 +597,13 @@ pub fn compute_inherent_assoc_term_args<'a, 'b, 'tcx>( ) -> ty::GenericArgsRef<'tcx> { let tcx = selcx.tcx(); - let alias_def_id = alias_term.expect_inherent_def_id(); + let alias_def_id = match alias_term.kind { + ty::AliasTermKind::InherentTy { def_id } => def_id, + ty::AliasTermKind::InherentConstSelf { def_id } => def_id, + ty::AliasTermKind::InherentConstImpl { .. } => return alias_term.args, + kind => panic!("expected inherent alias, found {kind:?}"), + }; + let impl_def_id = tcx.parent(alias_def_id); let impl_args = selcx.infcx.fresh_args_for_item(cause.span, impl_def_id); @@ -2101,13 +2096,13 @@ fn confirm_impl_candidate<'cx, 'tcx>( let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args); let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_term.defining_node); - let term = if obligation.predicate.kind.is_type() { - tcx.type_of(assoc_term.item.def_id).map_bound(|ty| ty.into()) + let term_kind = if obligation.predicate.kind.is_type() { + ty::AliasTermKind::ProjectionTy { def_id: assoc_term.item.def_id } } else { - tcx.const_of_item(assoc_term.item.def_id).map_bound(|ct| ct.into()) + ty::AliasTermKind::ProjectionConst { def_id: assoc_term.item.def_id } }; - let progress = if !tcx.check_args_compatible(assoc_term.item.def_id, args) { + let progress = if !tcx.check_alias_term_args_compatible(term_kind, args) { let msg = "impl item and trait item have different parameters"; let span = obligation.cause.span; let err = if obligation.predicate.kind.is_type() { @@ -2117,6 +2112,12 @@ fn confirm_impl_candidate<'cx, 'tcx>( }; Progress { term: ty::Unnormalized::dummy(err), obligations: nested } } else { + let term = if obligation.predicate.kind.is_type() { + tcx.type_of(assoc_term.item.def_id).map_bound(|ty| ty.into()) + } else { + tcx.const_of_item(assoc_term.item.def_id).map_bound(|ct| ct.into()) + }; + assoc_term_own_obligations(selcx, obligation, &mut nested); let instantiated_term = term.instantiate(tcx, args); let term_for_obligation = instantiated_term.skip_norm_wip(); diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 489e4f7a93d53..96e41f89be573 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -331,7 +331,9 @@ impl<'a, 'tcx> QueryNormalizer<'a, 'tcx> { ty::AliasTermKind::FreeTy { .. } | ty::AliasTermKind::FreeConst { .. } => { tcx.normalize_canonicalized_free_alias(c_term) } - ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::InherentConst { .. } => { + ty::AliasTermKind::InherentTy { .. } + | ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } => { tcx.normalize_canonicalized_inherent_projection(c_term) } kind @ (ty::AliasTermKind::OpaqueTy { .. } | ty::AliasTermKind::AnonConst { .. }) => { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index e8814c56c5016..4dda9ca5646ea 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -1,4 +1,3 @@ -use rustc_hir::def::DefKind; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_infer::traits::Obligation; use rustc_middle::traits::query::NoSolution; @@ -99,27 +98,6 @@ fn relate_mir_and_user_args<'tcx>( let tcx = ocx.infcx.tcx; let cause = ObligationCause::dummy_with_span(span); - // For IACs, the user args are in the format [SelfTy, GAT_args...] but type_of expects [impl_args..., GAT_args...]. - // We need to infer the impl args by equating the impl's self type with the user-provided self type. - let is_inherent_assoc_const = matches!(tcx.def_kind(def_id), DefKind::AssocConst { .. }) - && tcx.def_kind(tcx.parent(def_id)) == DefKind::Impl { of_trait: false } - && tcx.is_type_const(def_id); - - let args = if is_inherent_assoc_const { - let impl_def_id = tcx.parent(def_id); - let impl_args = ocx.infcx.fresh_args_for_item(span, impl_def_id); - let impl_self_ty = - ocx.normalize(&cause, param_env, tcx.type_of(impl_def_id).instantiate(tcx, impl_args)); - let user_self_ty = - ocx.normalize(&cause, param_env, Unnormalized::new_wip(args[0].expect_ty())); - ocx.eq(&cause, param_env, impl_self_ty, user_self_ty)?; - - let gat_args = &args[1..]; - tcx.mk_args_from_iter(impl_args.iter().chain(gat_args.iter().copied())) - } else { - args - }; - let ty = tcx.type_of(def_id).instantiate(tcx, args); let ty = ocx.normalize(&cause, param_env, ty); debug!("relate_type_and_user_type: ty of def-id is {:?}", ty); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index f1eaa50797c49..a2785a7ca75dc 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -876,7 +876,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { && matches!( a.kind, ty::AliasConstKind::Projection { .. } - | ty::AliasConstKind::Inherent { .. } + | ty::AliasConstKind::InherentSelf { .. } + | ty::AliasConstKind::InherentImpl { .. } ) => { if let Ok(InferOk { obligations, value: () }) = self diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 5427d14c55af9..dc29b6311cc7e 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1095,10 +1095,14 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { } match alias_const.kind { - ty::AliasConstKind::Inherent { .. } => { + ty::AliasConstKind::InherentSelf { .. } => { self.add_wf_preds_for_inherent_projection(alias_const.into()); return; // Subtree is handled by above function } + // please ping khyperia and/or BoxyUwU if this `bug!` fires + ty::AliasConstKind::InherentImpl { .. } => bug!( + "This ought to be unreachable, the entrypoints of WF should still have InherentSelf-form alias consts." + ), ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => { diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index 3710d41dba0d9..f826b2641bb15 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -147,12 +147,6 @@ fn normalize_canonicalized_inherent_projection<'tcx>( 0, &mut obligations, ); - obligations.extend(const_arg_has_type_obligation( - tcx, - param_env, - normalized_term, - goal, - )); ocx.register_obligations(obligations); Ok(NormalizationResult { normalized_term }) diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index cd35423c5ef14..d48438819040a 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -70,8 +70,15 @@ fn recurse_build<'tcx>( } &ExprKind::ZstLiteral { user_ty: _ } => ty::Const::zero_sized(tcx, node.ty), &ExprKind::NamedConst { def_id, args, user_ty: _ } => { - let uneval = - ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, def_id), args); + let uneval = ty::AliasConst::new( + tcx, + ty::AliasConstKind::new_from_def_id( + tcx, + def_id, + ty::AliasConstInherentArgsKind::Impl, + ), + args, + ); ty::Const::new_alias(tcx, ty::IsRigid::No, uneval) } ExprKind::ConstParam { param, .. } => ty::Const::new_param(tcx, *param), diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 29c65974d8b28..26a4edccd0134 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -73,13 +73,7 @@ impl AliasConst { #[inline] pub fn new(interner: I, kind: AliasConstKind, args: I::GenericArgs) -> AliasConst { if cfg!(debug_assertions) { - let def_id = match kind { - ty::AliasConstKind::Projection { def_id } => def_id.into(), - ty::AliasConstKind::Inherent { def_id } => def_id.into(), - ty::AliasConstKind::Free { def_id } => def_id.into(), - ty::AliasConstKind::Anon { def_id } => def_id.into(), - }; - interner.debug_assert_args_compatible(def_id, args); + interner.debug_assert_alias_term_args_compatible(kind.into(), args); } AliasConst { kind, args, _use_alias_new_instead: () } } @@ -87,7 +81,12 @@ impl AliasConst { pub fn type_of(self, interner: I) -> ty::Unnormalized { let def_id = match self.kind { ty::AliasConstKind::Projection { def_id } => def_id.into(), - ty::AliasConstKind::Inherent { def_id } => def_id.into(), + ty::AliasConstKind::InherentSelf { .. } => { + panic!( + "AliasConst::type_of got InherentSelf - args should always be InherentImpl at this point" + ) + } + ty::AliasConstKind::InherentImpl { def_id } => def_id.into(), ty::AliasConstKind::Free { def_id } => def_id.into(), ty::AliasConstKind::Anon { def_id } => def_id.into(), }; @@ -107,23 +106,65 @@ impl AliasConst { pub enum AliasConstKind { /// A projection `::AssocConst` Projection { def_id: I::TraitAssocConstId }, - /// An associated constant in an inherent `impl` - Inherent { def_id: I::InherentAssocConstId }, + /// An associated const in an inherent `impl`. + /// + /// The generic args are in "Self form", i.e. + /// there is a single `Self` type parameter, followed by any GAT args on the inherent const + /// itself. + /// + /// The "impl form" args can be obtained by generating fresh vars for each of the impl params, + /// instantiating the impl block's Self type with the fresh vars, equating the resulting type + /// with the `Self` generic argument, and using the result of what the fresh vars resolved to as + /// the "impl form" args. Doing so without considering the extra predicates generated by the + /// equate is a lossy operation, consider the following impl block: + /// + /// ```rust,ignore (illustrative) + /// impl Struct<'static, T> { + /// const ASSOC: () = (); + /// } + /// ``` + /// + /// If we have `Struct::<'a, u32>::Assoc`, the Self args form would be `[Struct<'a, u32>, + /// usize]`. The "impl form" args would be `[u32, usize]`, with an extra constraint generated + /// that `'a == 'static`. Disregarding this extra constraint would be wrong. + /// + /// Hence, when HIR lowering wants to construct an inherent alias, it must use the "Self form" + /// to let the trait solver do the equate and consider additional constraints. + /// + /// FIXME(inherent_associated_types): This ideally ought be a list of candidate DefIds that a + /// path could resolve to, then the trait solver does the above-written routine to figure out + /// which exact impl to use. `InherentSelf` could be conceptually be thought of as corresponding + /// to `Projection` where the def_id is a trait, and `InherentImpl` is `Projection` where the + /// def_id is an impl. + InherentSelf { def_id: I::InherentAssocConstId }, + /// An associated const in an inherent `impl`. See [`Self::InherentSelf`] for a description on + /// the difference between `InherentSelf` and `InherentImpl`. + InherentImpl { def_id: I::InherentAssocConstId }, /// A free constant, outside an impl block. Free { def_id: I::FreeConstAliasId }, /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]`. Anon { def_id: I::AnonConstId }, } +pub enum AliasConstInherentArgsKind { + WithSelf, + Impl, +} + impl AliasConstKind { - pub fn new_from_def_id(interner: I, def_id: I::DefId) -> Self { - interner.alias_const_kind_from_def_id(def_id) + pub fn new_from_def_id( + interner: I, + def_id: I::DefId, + inherent_args: AliasConstInherentArgsKind, + ) -> Self { + interner.alias_const_kind_from_def_id(def_id, inherent_args) } pub fn is_type_const(self, interner: I) -> bool { match self { AliasConstKind::Projection { def_id } => interner.is_type_const(def_id.into()), - AliasConstKind::Inherent { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::InherentSelf { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::InherentImpl { def_id } => interner.is_type_const(def_id.into()), AliasConstKind::Free { def_id } => interner.is_type_const(def_id.into()), AliasConstKind::Anon { def_id } => interner.is_type_const(def_id.into()), } @@ -132,7 +173,8 @@ impl AliasConstKind { pub fn def_span(self, interner: I) -> I::Span { match self { AliasConstKind::Projection { def_id } => interner.def_span(def_id.into()), - AliasConstKind::Inherent { def_id } => interner.def_span(def_id.into()), + AliasConstKind::InherentSelf { def_id } => interner.def_span(def_id.into()), + AliasConstKind::InherentImpl { def_id } => interner.def_span(def_id.into()), AliasConstKind::Free { def_id } => interner.def_span(def_id.into()), AliasConstKind::Anon { def_id } => interner.def_span(def_id.into()), } @@ -141,7 +183,8 @@ impl AliasConstKind { pub fn opt_def_id(self) -> Option { match self { AliasConstKind::Projection { def_id } => Some(def_id.into()), - AliasConstKind::Inherent { def_id } => Some(def_id.into()), + AliasConstKind::InherentSelf { def_id } => Some(def_id.into()), + AliasConstKind::InherentImpl { def_id } => Some(def_id.into()), AliasConstKind::Free { def_id } => Some(def_id.into()), AliasConstKind::Anon { def_id } => Some(def_id.into()), } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index d230791304527..7060bae7d12ec 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -21,8 +21,8 @@ use crate::solve::{ }; use crate::visit::{Flags, TypeVisitable}; use crate::{ - self as ty, BoundRegion, BoundVar, CanonicalParamEnvCache, DebruijnIndex, Region, RegionKind, - TraitRef, search_graph, + self as ty, AliasTermKind, BoundRegion, BoundVar, CanonicalParamEnvCache, DebruijnIndex, + Region, RegionKind, TraitRef, search_graph, }; /// The central trait in the shared abstraction layer, specifying all implementation-specific @@ -275,10 +275,18 @@ pub trait Interner: type AdtDef: AdtDef; fn adt_def(self, adt_def_id: Self::AdtId) -> Self::AdtDef; - fn alias_const_kind_from_def_id(self, def_id: Self::DefId) -> ty::AliasConstKind; + fn alias_const_kind_from_def_id( + self, + def_id: Self::DefId, + inherent_args: ty::AliasConstInherentArgsKind, + ) -> ty::AliasConstKind; // FIXME: remove in favor of explicit construction - fn alias_term_kind_from_def_id(self, def_id: Self::DefId) -> ty::AliasTermKind; + fn alias_term_kind_from_def_id( + self, + def_id: Self::DefId, + inherent_args: ty::AliasConstInherentArgsKind, + ) -> ty::AliasTermKind; fn trait_ref_and_own_args_for_alias( self, @@ -293,9 +301,18 @@ pub trait Interner: I: Iterator, T: CollectAndApply; - fn check_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) -> bool; + fn check_alias_term_args_compatible( + self, + term_kind: AliasTermKind, + args: Self::GenericArgs, + ) -> bool; fn debug_assert_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs); + fn debug_assert_alias_term_args_compatible( + self, + term_kind: AliasTermKind, + args: Self::GenericArgs, + ); /// Assert that the args from an `ExistentialTraitRef` or `ExistentialProjection` /// are compatible with the `DefId`. diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 7d281663d5033..7ad23d3e5432a 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -502,7 +502,10 @@ impl ExistentialProjection { ProjectionClause { projection_term: ty::AliasTerm::new( interner, - interner.alias_term_kind_from_def_id(self.def_id.into()), + interner.alias_term_kind_from_def_id( + self.def_id.into(), + ty::AliasConstInherentArgsKind::WithSelf, + ), [self_ty.into()].iter().chain(self.args.iter()), ), term: self.term, diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 98d251c6f1d64..f6491bac642e3 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -262,7 +262,8 @@ impl Relate for ty::AliasTerm { | ty::AliasTermKind::FreeConst { .. } | ty::AliasTermKind::FreeTy { .. } | ty::AliasTermKind::InherentTy { .. } - | ty::AliasTermKind::InherentConst { .. } + | ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } | ty::AliasTermKind::AnonConst { .. } | ty::AliasTermKind::ProjectionConst { .. } => { relate_args_invariantly(relation, a.args, b.args)? @@ -281,8 +282,14 @@ impl Relate for ty::ExistentialProjection { ) -> RelateResult> { if a.def_id != b.def_id { Err(TypeError::ProjectionMismatched(ExpectedFound::new( - relation.cx().alias_term_kind_from_def_id(a.def_id.into()), - relation.cx().alias_term_kind_from_def_id(b.def_id.into()), + relation.cx().alias_term_kind_from_def_id( + a.def_id.into(), + ty::AliasConstInherentArgsKind::WithSelf, + ), + relation.cx().alias_term_kind_from_def_id( + b.def_id.into(), + ty::AliasConstInherentArgsKind::WithSelf, + ), ))) } else { let term = relation.relate_with_variance( diff --git a/compiler/rustc_type_ir/src/term_kind.rs b/compiler/rustc_type_ir/src/term_kind.rs index aed634d4f3a21..bb4ebf054d263 100644 --- a/compiler/rustc_type_ir/src/term_kind.rs +++ b/compiler/rustc_type_ir/src/term_kind.rs @@ -66,8 +66,12 @@ pub enum AliasTermKind { ProjectionConst { def_id: I::TraitAssocConstId }, /// A top level const item not part of a trait or impl. FreeConst { def_id: I::FreeConstAliasId }, - /// An associated const in an inherent `impl` - InherentConst { def_id: I::InherentAssocConstId }, + /// An associated const in an inherent `impl`. See [`ty::AliasConstKind::InherentSelf`] for a + /// description on the difference between `InherentConstSelf` and `InherentConstImpl`. + InherentConstSelf { def_id: I::InherentAssocConstId }, + /// An associated const in an inherent `impl`. See [`ty::AliasConstKind::InherentSelf`] for a + /// description on the difference between `InherentConstSelf` and `InherentConstImpl`. + InherentConstImpl { def_id: I::InherentAssocConstId }, } impl AliasTermKind { @@ -76,7 +80,9 @@ impl AliasTermKind { AliasTermKind::ProjectionTy { .. } => "associated type", AliasTermKind::ProjectionConst { .. } => "associated const", AliasTermKind::InherentTy { .. } => "inherent associated type", - AliasTermKind::InherentConst { .. } => "inherent associated const", + AliasTermKind::InherentConstSelf { .. } | AliasTermKind::InherentConstImpl { .. } => { + "inherent associated const" + } AliasTermKind::OpaqueTy { .. } => "opaque type", AliasTermKind::FreeTy { .. } => "type alias", AliasTermKind::FreeConst { .. } => "const alias", @@ -93,7 +99,8 @@ impl AliasTermKind { AliasTermKind::AnonConst { .. } | AliasTermKind::ProjectionConst { .. } - | AliasTermKind::InherentConst { .. } + | AliasTermKind::InherentConstSelf { .. } + | AliasTermKind::InherentConstImpl { .. } | AliasTermKind::FreeConst { .. } => false, } } @@ -106,7 +113,8 @@ impl AliasTermKind { | AliasTermKind::FreeTy { .. } | AliasTermKind::AnonConst { .. } | AliasTermKind::FreeConst { .. } - | AliasTermKind::InherentConst { .. } => false, + | AliasTermKind::InherentConstSelf { .. } + | AliasTermKind::InherentConstImpl { .. } => false, } } } @@ -126,7 +134,12 @@ impl From> for AliasTermKind { fn from(value: ty::AliasConstKind) -> Self { match value { ty::AliasConstKind::Projection { def_id } => AliasTermKind::ProjectionConst { def_id }, - ty::AliasConstKind::Inherent { def_id } => AliasTermKind::InherentConst { def_id }, + ty::AliasConstKind::InherentSelf { def_id } => { + AliasTermKind::InherentConstSelf { def_id } + } + ty::AliasConstKind::InherentImpl { def_id } => { + AliasTermKind::InherentConstImpl { def_id } + } ty::AliasConstKind::Free { def_id } => AliasTermKind::FreeConst { def_id }, ty::AliasConstKind::Anon { def_id } => AliasTermKind::AnonConst { def_id }, } @@ -140,17 +153,7 @@ impl AliasTerm { args: I::GenericArgs, ) -> AliasTerm { if cfg!(debug_assertions) { - let def_id = match kind { - AliasTermKind::ProjectionTy { def_id } => def_id.into(), - AliasTermKind::InherentTy { def_id } => def_id.into(), - AliasTermKind::OpaqueTy { def_id } => def_id.into(), - AliasTermKind::FreeTy { def_id } => def_id.into(), - AliasTermKind::AnonConst { def_id } => def_id.into(), - AliasTermKind::ProjectionConst { def_id } => def_id.into(), - AliasTermKind::FreeConst { def_id } => def_id.into(), - AliasTermKind::InherentConst { def_id } => def_id.into(), - }; - interner.debug_assert_args_compatible(def_id, args); + interner.debug_assert_alias_term_args_compatible(kind, args); } AliasTerm { kind, args, _use_alias_new_instead: () } } @@ -164,8 +167,13 @@ impl AliasTerm { Self::new_from_args(interner, kind, args) } - pub fn new_from_def_id(interner: I, def_id: I::DefId, args: I::GenericArgs) -> AliasTerm { - let kind = interner.alias_term_kind_from_def_id(def_id); + pub fn new_from_def_id( + interner: I, + def_id: I::DefId, + args: I::GenericArgs, + inherent_args: ty::AliasConstInherentArgsKind, + ) -> AliasTerm { + let kind = interner.alias_term_kind_from_def_id(def_id, inherent_args); Self::new_from_args(interner, kind, args) } @@ -175,7 +183,8 @@ impl AliasTerm { AliasTermKind::InherentTy { def_id } => ty::AliasTyKind::Inherent { def_id }, AliasTermKind::OpaqueTy { def_id } => ty::AliasTyKind::Opaque { def_id }, AliasTermKind::FreeTy { def_id } => ty::AliasTyKind::Free { def_id }, - kind @ (AliasTermKind::InherentConst { .. } + kind @ (AliasTermKind::InherentConstSelf { .. } + | AliasTermKind::InherentConstImpl { .. } | AliasTermKind::FreeConst { .. } | AliasTermKind::AnonConst { .. } | AliasTermKind::ProjectionConst { .. }) => { @@ -187,7 +196,12 @@ impl AliasTerm { pub fn expect_ct(self) -> ty::AliasConst { let kind = match self.kind { - AliasTermKind::InherentConst { def_id } => ty::AliasConstKind::Inherent { def_id }, + AliasTermKind::InherentConstSelf { def_id } => { + ty::AliasConstKind::InherentSelf { def_id } + } + AliasTermKind::InherentConstImpl { def_id } => { + ty::AliasConstKind::InherentImpl { def_id } + } AliasTermKind::FreeConst { def_id } => ty::AliasConstKind::Free { def_id }, AliasTermKind::AnonConst { def_id } => ty::AliasConstKind::Anon { def_id }, AliasTermKind::ProjectionConst { def_id } => ty::AliasConstKind::Projection { def_id }, @@ -212,8 +226,11 @@ impl AliasTerm { }; match self.kind { AliasTermKind::FreeConst { def_id } => alias_const(ty::AliasConstKind::Free { def_id }), - AliasTermKind::InherentConst { def_id } => { - alias_const(ty::AliasConstKind::Inherent { def_id }) + AliasTermKind::InherentConstSelf { def_id } => { + alias_const(ty::AliasConstKind::InherentSelf { def_id }) + } + AliasTermKind::InherentConstImpl { def_id } => { + alias_const(ty::AliasConstKind::InherentImpl { def_id }) } AliasTermKind::AnonConst { def_id } => alias_const(ty::AliasConstKind::Anon { def_id }), AliasTermKind::ProjectionConst { def_id } => { @@ -305,7 +322,8 @@ impl AliasTerm { pub fn expect_inherent_def_id(self) -> I::InherentAssocTermId { match self.kind { AliasTermKind::InherentTy { def_id } => def_id.into(), - AliasTermKind::InherentConst { def_id } => def_id.into(), + AliasTermKind::InherentConstSelf { def_id } => def_id.into(), + AliasTermKind::InherentConstImpl { def_id } => def_id.into(), kind => panic!("expected inherent alias, found {kind:?}"), } } @@ -327,7 +345,7 @@ impl AliasTerm { ) -> I::GenericArgs { debug_assert!(matches!( self.kind, - AliasTermKind::InherentTy { .. } | AliasTermKind::InherentConst { .. } + AliasTermKind::InherentTy { .. } | AliasTermKind::InherentConstSelf { .. } )); interner.mk_args_from_iter(impl_args.iter().chain(self.args.iter().skip(1))) } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 94e6be03c766f..3b84c8e9a1c35 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -483,13 +483,7 @@ impl fmt::Debug for TyKind { impl AliasTy { pub fn new_from_args(interner: I, kind: AliasTyKind, args: I::GenericArgs) -> AliasTy { if cfg!(debug_assertions) { - let def_id = match kind { - AliasTyKind::Projection { def_id } => def_id.into(), - AliasTyKind::Inherent { def_id } => def_id.into(), - AliasTyKind::Opaque { def_id } => def_id.into(), - AliasTyKind::Free { def_id } => def_id.into(), - }; - interner.debug_assert_args_compatible(def_id, args); + interner.debug_assert_alias_term_args_compatible(kind.into(), args); } AliasTy { kind, args, _use_alias_new_instead: () } } @@ -551,7 +545,10 @@ impl ProjectionAliasTy { kind: I::TraitAssocTyId, args: I::GenericArgs, ) -> Self { - interner.debug_assert_args_compatible(kind.into(), args); + interner.debug_assert_alias_term_args_compatible( + ty::AliasTermKind::ProjectionTy { def_id: kind }, + args, + ); Self { kind, args, _use_alias_new_instead: () } } @@ -622,7 +619,10 @@ impl InherentAliasTy { kind: I::InherentAssocTyId, args: I::GenericArgs, ) -> Self { - interner.debug_assert_args_compatible(kind.into(), args); + interner.debug_assert_alias_term_args_compatible( + ty::AliasTermKind::InherentTy { def_id: kind }, + args, + ); Self { kind, args, _use_alias_new_instead: () } } @@ -637,7 +637,10 @@ impl InherentAliasTy { impl OpaqueAliasTy { pub fn new_opaque_from_args(interner: I, kind: I::OpaqueTyId, args: I::GenericArgs) -> Self { - interner.debug_assert_args_compatible(kind.into(), args); + interner.debug_assert_alias_term_args_compatible( + ty::AliasTermKind::OpaqueTy { def_id: kind }, + args, + ); Self { kind, args, _use_alias_new_instead: () } } @@ -652,7 +655,10 @@ impl OpaqueAliasTy { impl FreeAliasTy { pub fn new_free_from_args(interner: I, kind: I::FreeTyAliasId, args: I::GenericArgs) -> Self { - interner.debug_assert_args_compatible(kind.into(), args); + interner.debug_assert_alias_term_args_compatible( + ty::AliasTermKind::FreeTy { def_id: kind }, + args, + ); Self { kind, args, _use_alias_new_instead: () } } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index d13a3fdb864bf..012c4997db9c1 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -358,7 +358,8 @@ pub(crate) fn print_const(tcx: TyCtxt<'_>, n: ty::Const<'_>) -> String { ty::ConstKind::Alias(_, ty::AliasConst { kind, .. }) => { let def_id: DefId = match kind { ty::AliasConstKind::Projection { def_id } => def_id.into(), - ty::AliasConstKind::Inherent { def_id } => def_id.into(), + ty::AliasConstKind::InherentSelf { def_id } => def_id.into(), + ty::AliasConstKind::InherentImpl { def_id } => def_id.into(), ty::AliasConstKind::Free { def_id } => def_id.into(), ty::AliasConstKind::Anon { def_id } => def_id.into(), }; diff --git a/tests/ui/const-generics/gca/path-to-non-type-const.rs b/tests/ui/const-generics/gca/path-to-non-type-const.rs index 9deb517095cbd..53382fe4aa247 100644 --- a/tests/ui/const-generics/gca/path-to-non-type-const.rs +++ b/tests/ui/const-generics/gca/path-to-non-type-const.rs @@ -1,7 +1,12 @@ //@ check-pass //@ compile-flags: -Znext-solver -#![feature(min_generic_const_args, macroless_generic_const_args, generic_const_args)] +#![feature( + min_generic_const_args, + macroless_generic_const_args, + generic_const_args, + inherent_associated_types +)] #![expect(incomplete_features)] trait Trait { @@ -21,6 +26,14 @@ impl Trait for GenericStructImpl { const PROJECTED: usize = A; } +impl StructImpl { + const INHERENT: usize = 1; +} + +impl GenericStructImpl { + const INHERENT: usize = A; +} + struct Struct; fn f() { @@ -31,4 +44,6 @@ fn main() { let _ = Struct::; let _ = Struct::<{ ::PROJECTED }>; let _ = Struct::<{ as Trait>::PROJECTED }>; + let _ = Struct::<{ StructImpl::INHERENT }>; + let _ = Struct::<{ GenericStructImpl::<2>::INHERENT }>; } diff --git a/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.rs b/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.rs deleted file mode 100644 index d15341836e493..0000000000000 --- a/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! This test should be part of path-to-non-type-const.rs, and should pass. However, we are holding -//! off on implementing paths to IACs until a refactoring of how IAC generics are represented. -//@ compile-flags: -Znext-solver - -#![feature( - inherent_associated_types, - min_generic_const_args, - generic_const_args, - macroless_generic_const_args -)] -#![expect(incomplete_features)] - -struct StructImpl; -struct GenericStructImpl; - -impl StructImpl { - const INHERENT: usize = 1; -} - -impl GenericStructImpl { - const INHERENT: usize = A; -} - -struct Struct; - -fn main() { - let _ = Struct::<{ StructImpl::INHERENT }>; - //~^ ERROR use of `const` in the type system not defined as `type const` - let _ = Struct::<{ GenericStructImpl::<2>::INHERENT }>; - //~^ ERROR use of `const` in the type system not defined as `type const` -} diff --git a/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.stderr b/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.stderr deleted file mode 100644 index af671fb614e31..0000000000000 --- a/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error: use of `const` in the type system not defined as `type const` - --> $DIR/path-to-non-type-inherent-associated-const.rs:27:24 - | -LL | let _ = Struct::<{ StructImpl::INHERENT }>; - | ^^^^^^^^^^^^^^^^^^^^ - | -help: add `type` before `const` for `StructImpl::INHERENT` - | -LL | type const INHERENT: usize = 1; - | ++++ - -error: use of `const` in the type system not defined as `type const` - --> $DIR/path-to-non-type-inherent-associated-const.rs:29:24 - | -LL | let _ = Struct::<{ GenericStructImpl::<2>::INHERENT }>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: add `type` before `const` for `GenericStructImpl::::INHERENT` - | -LL | type const INHERENT: usize = A; - | ++++ - -error: aborting due to 2 previous errors - From af2d4bc7bd39e7b3d2abae51881625b74b9f4486 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 30 Aug 2026 10:52:01 -0400 Subject: [PATCH 5/8] Switch dist-aarch64-linux to EC2 and update dist-x86_64-linux For dist-aarch64-linux (full): * GHA 8c takes 2h25m ($2.03/build) * c8g.8xl takes 1h20m ($1.69/build) * c9g.8xl takes 1h ($1.38/build) * c9g.4xl takes 1h10m ($0.81/build) * m9g.2xl takes 1h30m ($0.59/build) - selected And adds a dist-aarch64-linux-quick: * c8g.8xl takes 50m ($1.059/build) * c9g.8xl takes 40m ($0.924/build) * c9g.4xl takes 47m ($0.543/build) - selected * m9g.2xl takes 64m ($0.417/build) For now I've chosen a balance between cost and speed (c9g.4xl). Once we decide where to enable this (e.g., in try builds by default) we can consider aligning with other tasks and saving $/build if we're not able to benefit from increased speed (e.g., because perf won't run until the try build as a whole finishes). For dist-x86_64-linux-full we have this breakdown: * c8a.8xl takes 1h34m ($2.64/build) - current * c8a.4xl takes 1h45m ($1.51/build) - selected * m8a.2xl takes 2h10m ($1.05/build) I'll re-benchmark dist-x86_64-linux-quick in a future PR, for now it will stay on c8a.8xl. This drops codebuild configuration (but not yet cleaning up various related pieces that are more tied into our CI) since it doesn't seem relevant anymore. --- rust-bors.toml | 35 +++++++------------- src/ci/github-actions/jobs.yml | 60 +++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 46 deletions(-) diff --git a/rust-bors.toml b/rust-bors.toml index 02effccdeeeb3..527d44126bf2d 100644 --- a/rust-bors.toml +++ b/rust-bors.toml @@ -87,31 +87,20 @@ images = { "arm64ami" = "latest-gha-runner-ami-arm64", } jit_runner = "organization" +# Prices per hour of on-demand compute in us-east-2 (as of Aug 2026) +# See build speed estimates in https://github.com/rust-lang/simpleinfra/issues/1132 allowed_instances = [ - # AMD Zen 5 (x86_64) instances, a subset of these is used in production. - # Prices per hour of on-demand compute in us-east-2 (as of Aug 2026) - # See rough assessment of build speed for dist-x86_64-quick in https://github.com/rust-lang/simpleinfra/issues/1132 - # m8a.2x 8 vCPU, 32 GB $0.48688/hr - # c8a.4x 16 vCPU, 32 GB $0.86216/hr - # c8a.8x 32 vCPU, 64 GB $1.72432/hr - # c8a.12x 48 vCPU, 96 GB $2.58648/hr - # CodeBuild 36 vCPU $4.78799/hr - "m8a.2xlarge", - "c8a.4xlarge", - "c8a.8xlarge", - "c8a.12xlarge", + # AMD Zen 5 (x86_64) + "m8a.2xlarge", # $0.48688/hr + "c8a.4xlarge", # $0.86216/hr + "c8a.8xlarge", # $1.72432/hr + "c8a.12xlarge", # $2.58648/hr - # Graviton 4 (aarch64) instances, currently just for experimentation - "m8g.2xlarge", - "c8g.4xlarge", - "c8g.8xlarge", - "c8g.12xlarge", - - # Graviton 5 (aarch64) instances, currently just for experimentation - "m9g.2xlarge", - "c9g.4xlarge", - "c9g.8xlarge", - "c9g.12xlarge", + # Graviton (aarch64) + "m9g.2xlarge", # $0.39136/hr + "c9g.4xlarge", # $0.69312/hr + "c9g.8xlarge", # $1.38624/hr + "c9g.12xlarge", # $2.07936/hr ] # Enable unrolling of rollup member PRs after rollup merge diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 387d0b77f1af5..688d75589dd9a 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -41,24 +41,24 @@ runners: os: ubuntu-24.04-arm <<: *base-job - - &job-aarch64-linux-8c - os: ubuntu-24.04-arm64-8core-32gb + - &job-linux-x86-8c-ec2 + os: ec2-x86_64ami-m8a.2xlarge-x64-linux-$github.run_id-$github.run_attempt <<: *base-job - # Codebuild runners are provisioned in - # https://github.com/rust-lang/simpleinfra/blob/b7ddd5e6bec8a93ec30510cdddec02c5666fefe9/terragrunt/accounts/ci-prod/ci-runners/terragrunt.hcl#L2 - - &job-linux-36c-codebuild - free_disk: true - codebuild: true - os: codebuild-ubuntu-22-36c-$github.run_id-$github.run_attempt + - &job-linux-x86-16c-ec2 + os: ec2-x86_64ami-c8a.4xlarge-x64-linux-$github.run_id-$github.run_attempt <<: *base-job - &job-linux-x86-32c-ec2 os: ec2-x86_64ami-c8a.8xlarge-x64-linux-$github.run_id-$github.run_attempt <<: *base-job - - &job-linux-x86-8c-ec2 - os: ec2-x86_64ami-m8a.2xlarge-x64-linux-$github.run_id-$github.run_attempt + - &job-linux-aarch64-8c-ec2 + os: ec2-arm64ami-m9g.2xlarge-aarch64-linux-$github.run_id-$github.run_attempt + <<: *base-job + + - &job-linux-aarch64-16c-ec2 + os: ec2-arm64ami-c9g.4xlarge-aarch64-linux-$github.run_id-$github.run_attempt <<: *base-job envs: @@ -96,6 +96,11 @@ jobs: IMAGE: dist-x86_64-linux CODEGEN_BACKENDS: llvm,cranelift DOCKER_SCRIPT: dist.sh + dist-aarch64-linux: &job-dist-aarch64-linux + name: dist-aarch64-linux + env: + IMAGE: dist-aarch64-linux + CODEGEN_BACKENDS: llvm,cranelift # Jobs that run on each push to a pull request (PR). @@ -167,6 +172,17 @@ pr: try: - <<: [*job-dist-x86_64-linux, *job-linux-x86-32c-ec2] name: dist-x86_64-linux-quick + env: + IMAGE: dist-x86_64-linux + CODEGEN_BACKENDS: llvm,cranelift + DOCKER_SCRIPT: dist.sh + DIST_TRY_BUILD: 1 + - <<: [*job-dist-aarch64-linux, *job-linux-aarch64-16c-ec2] + name: dist-aarch64-linux-quick + env: + IMAGE: dist-aarch64-linux + CODEGEN_BACKENDS: llvm,cranelift + DIST_TRY_BUILD: 1 # Jobs that only run when explicitly invoked in one of the following ways: # - comment `@bors try jobs=` @@ -178,19 +194,20 @@ optional: env: IMAGE: pr-check-1 <<: *job-linux-4c - - <<: [*job-dist-x86_64-linux, *job-linux-36c-codebuild] - name: dist-x86_64-linux-codebuild - - <<: [*job-dist-x86_64-linux, *job-linux-36c-codebuild] - name: dist-x86_64-linux-quick-codebuild + # Duplicate the try jobs here so that we can run them via jobs=... + - <<: [*job-dist-x86_64-linux, *job-linux-x86-32c-ec2] + name: dist-x86_64-linux-quick env: IMAGE: dist-x86_64-linux CODEGEN_BACKENDS: llvm,cranelift DOCKER_SCRIPT: dist.sh DIST_TRY_BUILD: 1 - # We repeat the try job here so that it can be explicitly executed using `@bors try jobs`, to test - # full x64 Linux dist try builds on EC2. - - <<: [*job-dist-x86_64-linux, *job-linux-x86-32c-ec2] - name: dist-x86_64-linux-quick + - <<: [*job-dist-aarch64-linux, *job-linux-aarch64-16c-ec2] + name: dist-aarch64-linux-quick + env: + IMAGE: dist-aarch64-linux + CODEGEN_BACKENDS: llvm,cranelift + DIST_TRY_BUILD: 1 # Main CI jobs that have to be green to merge a commit into the default branch. # @@ -218,10 +235,7 @@ auto: - name: armhf-gnu <<: *job-linux-4c - - name: dist-aarch64-linux - env: - CODEGEN_BACKENDS: llvm,cranelift - <<: *job-aarch64-linux-8c + - <<: [*job-dist-aarch64-linux, *job-linux-aarch64-16c-ec2] - name: dist-android <<: *job-linux-4c @@ -298,7 +312,7 @@ auto: - name: dist-x86_64-illumos <<: *job-linux-4c - - <<: [*job-dist-x86_64-linux, *job-linux-x86-32c-ec2] + - <<: [*job-dist-x86_64-linux, *job-linux-x86-16c-ec2] - name: dist-x86_64-linux-alt env: From 9f89751ce757eb17da891b958924a43174d6bd32 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 28 Aug 2026 13:22:55 +0000 Subject: [PATCH 6/8] Move polonius loan liveness computation prior to RegionInferenceContext::new --- compiler/rustc_borrowck/src/nll.rs | 23 ++++++++++++------- compiler/rustc_borrowck/src/polonius/mod.rs | 16 +++++++------ .../rustc_borrowck/src/region_infer/mod.rs | 7 ------ 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 672b58fcbfbea..f4a1edb0b675d 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -126,7 +126,7 @@ pub(crate) fn compute_regions<'tcx>( let polonius_output = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_output()) || infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled(); - let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints( + let mut lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints( constraints, &universal_region_relations, infcx, @@ -144,6 +144,20 @@ pub(crate) fn compute_regions<'tcx>( &lowered_constraints, ); + // If requested for `-Zpolonius=next`, compute loan liveness information. + // This is done prior to `RegionInferenceContext::new`, because we may add + // additional liveness constraints. + if let Some(polonius_context) = polonius_context.as_mut() { + let _timer = infcx.tcx.prof.generic_activity("borrowck_polonius_loan_liveness"); + polonius_context.compute_loan_liveness( + &mut lowered_constraints.liveness_constraints, + lowered_constraints.outlives_constraints.outlives().iter().copied(), + &universal_region_relations.universal_regions, + body, + borrow_set, + ); + } + let mut regioncx = RegionInferenceContext::new( infcx, lowered_constraints, @@ -151,13 +165,6 @@ pub(crate) fn compute_regions<'tcx>( location_map, ); - // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints - // and use them to compute loan liveness. - if let Some(polonius_context) = polonius_context.as_mut() { - let _timer = infcx.tcx.prof.generic_activity("borrowck_polonius_loan_liveness"); - polonius_context.compute_loan_liveness(&mut regioncx, body, borrow_set) - } - // If requested: dump NLL facts, and run legacy polonius analysis. let polonius_output = polonius_facts.as_ref().and_then(|polonius_facts| { if infcx.tcx.sess.opts.unstable_opts.nll_facts { diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 45108bfcb79ba..1c9242a3127a9 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -48,9 +48,11 @@ use rustc_mir_dataflow::points::PointIndex; pub(self) use self::constraints::*; pub(crate) use self::dump::dump_polonius_mir; +use crate::BorrowSet; +use crate::constraints::OutlivesConstraint; use crate::dataflow::BorrowIndex; use crate::region_infer::values::LivenessValues; -use crate::{BorrowSet, RegionInferenceContext}; +use crate::universal_regions::UniversalRegions; pub(crate) type LiveLoans = SparseBitMatrix; @@ -101,19 +103,19 @@ impl PoloniusContext { /// The constraint data will be used to compute errors and diagnostics. pub(crate) fn compute_loan_liveness<'tcx>( &mut self, - regioncx: &mut RegionInferenceContext<'tcx>, + liveness: &mut LivenessValues, + outlives_constraints: impl Iterator>, + universal_regions: &UniversalRegions<'tcx>, body: &Body<'tcx>, borrow_set: &BorrowSet<'tcx>, ) { - let liveness = regioncx.liveness_constraints(); - // We don't need to prepare the graph (index NLL constraints, etc.) if we have no loans to // trace throughout localized constraints. if borrow_set.len() > 0 { // From the outlives constraints, liveness, and variances, we can compute reachability // on the lazy localized constraint graph to trace the liveness of loans, for the next // step in the chain (the NLL loan scope and active loans computations). - let graph = LocalizedConstraintGraph::new(liveness, regioncx.outlives_constraints()); + let graph = LocalizedConstraintGraph::new(liveness, outlives_constraints); let mut live_loans = LiveLoans::new(borrow_set.len()); let mut visitor = LoanLivenessVisitor { liveness, live_loans: &mut live_loans }; @@ -121,11 +123,11 @@ impl PoloniusContext { body, liveness, &self.live_region_variances, - regioncx.universal_regions(), + universal_regions, borrow_set, &mut visitor, ); - regioncx.record_live_loans(live_loans); + liveness.record_live_loans(live_loans); // The graph can be traversed again during MIR dumping, so we store it here. self.graph = Some(graph); diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 19aa4081bfc88..d3fc7152acc44 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -30,7 +30,6 @@ use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstra use crate::dataflow::BorrowIndex; use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; use crate::handle_placeholders::{LoweredConstraints, RegionTracker}; -use crate::polonius::LiveLoans; use crate::polonius::legacy::PoloniusOutput; use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues}; use crate::type_check::Locations; @@ -1874,12 +1873,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self.liveness_constraints } - /// When using `-Zpolonius=next`, records the given live loans for the loan scopes and active - /// loans dataflow computations. - pub(crate) fn record_live_loans(&mut self, live_loans: LiveLoans) { - self.liveness_constraints.record_live_loans(live_loans); - } - /// Returns whether the `loan_idx` is live at the given `location`: whether its issuing /// region is contained within the type of a variable that is live at this point. /// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`. From 6ad5c1731c751fb25781cc3f7e74730b019544e9 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 28 Aug 2026 13:35:17 +0000 Subject: [PATCH 7/8] Move record_live_region_variance to be a freestanding function --- .../src/polonius/liveness_constraints.rs | 32 +++++++++---------- compiler/rustc_borrowck/src/polonius/mod.rs | 5 +-- .../src/type_check/liveness/mod.rs | 9 ++++-- .../src/type_check/liveness/trace.rs | 5 +-- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs index b6f8b4a79f39b..4009a85180571 100644 --- a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs @@ -6,25 +6,23 @@ use rustc_middle::ty::relate::{ }; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable}; -use super::{ConstraintDirection, PoloniusContext}; +use super::ConstraintDirection; use crate::universal_regions::UniversalRegions; -impl PoloniusContext { - /// Record the variance of each region contained within the given value. - pub(crate) fn record_live_region_variance<'tcx>( - &mut self, - tcx: TyCtxt<'tcx>, - universal_regions: &UniversalRegions<'tcx>, - value: impl TypeVisitable> + Relate>, - ) { - let mut extractor = VarianceExtractor { - tcx, - ambient_variance: ty::Variance::Covariant, - directions: &mut self.live_region_variances, - universal_regions, - }; - extractor.relate(value, value).expect("Can't have a type error relating to itself"); - } +/// Record the variance of each region contained within the given value. +pub(crate) fn record_live_region_variance<'tcx>( + tcx: TyCtxt<'tcx>, + live_region_variances: &mut BTreeMap, + universal_regions: &UniversalRegions<'tcx>, + value: impl TypeVisitable> + Relate>, +) { + let mut extractor = VarianceExtractor { + tcx, + ambient_variance: ty::Variance::Covariant, + directions: live_region_variances, + universal_regions, + }; + extractor.relate(value, value).expect("Can't have a type error relating to itself"); } /// Extracts variances for regions contained within types. Follows the same structure as diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index 1c9242a3127a9..cbac05d2eff67 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -48,6 +48,7 @@ use rustc_mir_dataflow::points::PointIndex; pub(self) use self::constraints::*; pub(crate) use self::dump::dump_polonius_mir; +pub(crate) use self::liveness_constraints::record_live_region_variance; use crate::BorrowSet; use crate::constraints::OutlivesConstraint; use crate::dataflow::BorrowIndex; @@ -67,7 +68,7 @@ pub(crate) struct PoloniusContext { /// The expected edge direction per live region: the kind of directed edge we'll create as /// liveness constraints depends on the variance of types with respect to each contained region. - live_region_variances: BTreeMap, + pub(crate) live_region_variances: BTreeMap, /// The regions that outlive free regions are used to distinguish relevant live locals from /// boring locals. A boring local is one whose type contains only such regions. Polonius @@ -79,7 +80,7 @@ pub(crate) struct PoloniusContext { /// The direction a constraint can flow into. Used to create liveness constraints according to /// variance. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum ConstraintDirection { +pub(crate) enum ConstraintDirection { /// For covariant cases, we add a forward edge `O at P1 -> O at P2`. Forward, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index fd8502773c51e..189a1634e56f8 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -11,7 +11,7 @@ use tracing::debug; use super::TypeChecker; use crate::constraints::OutlivesConstraintSet; -use crate::polonius::PoloniusContext; +use crate::polonius::{PoloniusContext, record_live_region_variance}; use crate::region_infer::values::LivenessValues; use crate::universal_regions::UniversalRegions; @@ -220,7 +220,12 @@ impl<'a, 'tcx> LiveVariablesVisitor<'a, 'tcx> { // When using `-Zpolonius=next`, we record the variance of each live region. if let Some(polonius_context) = self.polonius_context { - polonius_context.record_live_region_variance(self.tcx, self.universal_regions, value); + record_live_region_variance( + self.tcx, + &mut polonius_context.live_region_variances, + self.universal_regions, + value, + ); } } } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index fe20bb6c28c0c..33e2da693ed1e 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -19,7 +19,7 @@ use rustc_trait_selection::traits::query::dropck_outlives; use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOpOutput}; use tracing::debug; -use crate::polonius; +use crate::polonius::{self, record_live_region_variance}; use crate::region_infer::values; use crate::type_check::liveness::local_use_map::LocalUseMap; use crate::type_check::{NormalizeLocation, TypeChecker}; @@ -627,8 +627,9 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { // When using `-Zpolonius=next`, we record the variance of each live region. if let Some(polonius_context) = typeck.polonius_context.as_mut() { - polonius_context.record_live_region_variance( + record_live_region_variance( typeck.infcx.tcx, + &mut polonius_context.live_region_variances, typeck.universal_regions, value, ); From b58ee5bb9a6171b5b4519a237f0bdcef2a5e7eb2 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 28 Aug 2026 14:50:00 +0000 Subject: [PATCH 8/8] Minor trace updates --- .../src/type_check/liveness/mod.rs | 2 +- .../src/type_check/liveness/trace.rs | 80 ++++++++----------- 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 189a1634e56f8..dfab2fd071773 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -67,7 +67,7 @@ pub(super) fn generate<'tcx>( let (relevant_live_locals, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body); - trace::trace(typeck, location_map, move_data, relevant_live_locals, boring_locals); + trace::trace(typeck, location_map, move_data, &relevant_live_locals, &boring_locals); // Mark regions that should be live where they appear within rvalues or within a call: like // args, regions, and types. diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 33e2da693ed1e..89a8899a991c9 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -3,7 +3,7 @@ use rustc_index::bit_set::DenseBitSet; use rustc_index::interval::IntervalSet; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::traits::TraitErrors; -use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, HasLocalDecls, Local, Location}; +use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location}; use rustc_middle::traits::query::DropckOutlivesResult; use rustc_middle::ty::relate::Relate; use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; @@ -19,6 +19,7 @@ use rustc_trait_selection::traits::query::dropck_outlives; use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOpOutput}; use tracing::debug; +use crate::BorrowckInferCtxt; use crate::polonius::{self, record_live_region_variance}; use crate::region_infer::values; use crate::type_check::liveness::local_use_map::LocalUseMap; @@ -42,8 +43,8 @@ pub(super) fn trace<'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, location_map: &DenseLocationMap, move_data: &MoveData<'tcx>, - relevant_live_locals: Vec, - boring_locals: Vec, + relevant_live_locals: &[Local], + boring_locals: &[Local], ) { let _timer = typeck.tcx().prof.generic_activity("borrowck_liveness_trace"); @@ -59,7 +60,7 @@ pub(super) fn trace<'tcx>( let mut results = LivenessResults::new(cx); - results.add_extra_drop_facts(&relevant_live_locals); + results.add_extra_drop_facts(relevant_live_locals); results.compute_for_all_locals(relevant_live_locals); @@ -131,8 +132,8 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { } } - fn compute_for_all_locals(&mut self, relevant_live_locals: Vec) { - for local in relevant_live_locals { + fn compute_for_all_locals(&mut self, relevant_live_locals: &[Local]) { + for &local in relevant_live_locals { self.reset_local_state(); self.add_defs_for(local); self.compute_use_live_points_for(local); @@ -161,20 +162,11 @@ impl<'a, 'typeck, 'tcx> LivenessResults<'a, 'typeck, 'tcx> { /// These are all the locals which do not potentially reference a region local /// to this body. Locals which only reference free regions are always drop-live /// and can therefore safely be dropped. - fn dropck_boring_locals(&mut self, boring_locals: Vec) { - for local in boring_locals { + fn dropck_boring_locals(&mut self, boring_locals: &[Local]) { + for &local in boring_locals { let local_ty = self.cx.body().local_decls[local].ty; let local_span = self.cx.body().local_decls[local].source_info.span; - let drop_data = self.cx.drop_data.entry(local_ty).or_insert_with({ - let typeck = &self.cx.typeck; - move || LivenessContext::compute_drop_data(typeck, local_ty, local_span) - }); - - drop_data.dropck_result.report_overflows( - self.cx.typeck.infcx.tcx, - self.cx.typeck.body.local_decls[local].source_info.span, - local_ty, - ); + dropck_local(&self.cx.typeck.infcx, &mut self.cx.drop_data, local_ty, local_span); } } @@ -567,11 +559,9 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { values::pretty_print_points(self.location_map, live_at.iter()), ); - let local_span = self.body().local_decls()[dropped_local].source_info.span; - let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({ - let typeck = &self.typeck; - move || Self::compute_drop_data(typeck, dropped_ty, local_span) - }); + let dropped_span = self.body().local_decls[dropped_local].source_info.span; + let drop_data = + dropck_local(&self.typeck.infcx, &mut self.drop_data, dropped_ty, dropped_span); if let Some(data) = &drop_data.region_constraint_data { for &drop_location in drop_locations { @@ -583,12 +573,6 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { } } - drop_data.dropck_result.report_overflows( - self.typeck.infcx.tcx, - self.typeck.body.source_info(*drop_locations.first().unwrap()).span, - dropped_ty, - ); - // All things in the `outlives` array may be touched by // the destructor and must be live at this point. for &kind in &drop_data.dropck_result.kinds { @@ -635,17 +619,19 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { ); } } +} - fn compute_drop_data( - typeck: &TypeChecker<'_, 'tcx>, - dropped_ty: Ty<'tcx>, - span: Span, - ) -> DropData<'tcx> { - debug!("compute_drop_data(dropped_ty={:?})", dropped_ty); - - let goal = DropckOutlives { dropped_ty }; - - match typeck.infcx.fully_perform(goal, DUMMY_SP) { +/// Computes the `DropData` for a given type, caching the result. +/// This also reports the overflow errors from the computation, if any. +fn dropck_local<'tcx, 'd>( + infcx: &BorrowckInferCtxt<'tcx>, + drop_data: &'d mut FxIndexMap, DropData<'tcx>>, + local_ty: Ty<'tcx>, + local_span: Span, +) -> &'d DropData<'tcx> { + let compute_drop_data = || { + let goal = DropckOutlives { dropped_ty: local_ty }; + match infcx.fully_perform(goal, DUMMY_SP) { Ok(TypeOpOutput { output, constraints, .. }) => { DropData { dropck_result: output, region_constraint_data: constraints } } @@ -657,12 +643,12 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { // // Do this inside of a probe because we don't particularly care (or want) // any region side-effects of this operation in our infcx. - typeck.infcx.probe(|_| { - let ocx = ObligationCtxt::new_with_diagnostics(&typeck.infcx); + infcx.probe(|_| { + let ocx = ObligationCtxt::new_with_diagnostics(infcx); let errors = match dropck_outlives::compute_dropck_outlives_with_errors( &ocx, - typeck.infcx.param_env.and(goal), - span, + infcx.param_env.and(goal), + local_span, ) { Ok(_) => ocx.evaluate_obligations_error_on_ambiguity(), Err(e) => TraitErrors::HasErrors(e), @@ -671,11 +657,15 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { // Could have no errors if a type lowering error, say, caused the query // to fail. if let TraitErrors::HasErrors(errors) = errors { - typeck.infcx.err_ctxt().report_fulfillment_errors(errors); + infcx.err_ctxt().report_fulfillment_errors(errors); } }); DropData { dropck_result: Default::default(), region_constraint_data: None } } } - } + }; + + let drop_data = drop_data.entry(local_ty).or_insert_with(compute_drop_data); + drop_data.dropck_result.report_overflows(infcx.tcx, local_span, local_ty); + drop_data }