From ac609f98e80b25b27f779095e24a0ab726ec0361 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 2 Aug 2026 21:30:02 +1000 Subject: [PATCH 01/10] 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 02/10] 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 938b233e4c0e94db0974b958ecc5a5842fc1df21 Mon Sep 17 00:00:00 2001 From: Lieselotte <52315535+she3py@users.noreply.github.com> Date: Sun, 30 Aug 2026 15:05:23 +0200 Subject: [PATCH 03/10] doc: `PrintKind` --- compiler/rustc_session/src/config.rs | 9 ++-- .../rustc_session/src/config/print_request.rs | 53 +++++++++++++++++++ src/librustdoc/config.rs | 1 - 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 95f6348cfbdbb..bba0d8190dab7 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1414,12 +1414,11 @@ impl Sysroot { } } +/// Get the host triple out of the build environment. This ensures that our +/// idea of the host triple is the same as for the set of libraries we've +/// actually built. We can't just take LLVM's host triple because they +/// normalize all ix86 architectures to i386. pub fn host_tuple() -> &'static str { - // Get the host triple out of the build environment. This ensures that our - // idea of the host triple is the same as for the set of libraries we've - // actually built. We can't just take LLVM's host triple because they - // normalize all ix86 architectures to i386. - // // Instead of grabbing the host triple (for the current host), we grab (at // compile time) the target triple that this rustc is built with and // calling that (at runtime) the host triple. diff --git a/compiler/rustc_session/src/config/print_request.rs b/compiler/rustc_session/src/config/print_request.rs index 8201e1bfdd9a7..0cc805d4706cc 100644 --- a/compiler/rustc_session/src/config/print_request.rs +++ b/compiler/rustc_session/src/config/print_request.rs @@ -22,32 +22,85 @@ pub struct PrintRequest { #[derive(AllVariants)] pub enum PrintKind { // tidy-alphabetical-start + /// All target JSON specifications. AllTargetSpecsJson, + + /// Does the backend supports the [`PrintRequest::arg`] `asm!()` mnemonic? (perma-unstable) BackendHasMnemonic, + + /// Does the backend supports Zstd compression? (perma-unstable) BackendHasZstd, + + /// List of all calling conventions supported by rustc. CallingConventions, + + /// List of cfg values. Cfg, + + /// List of check-cfg values. CheckCfg, + + /// List of available code models for the current backend. CodeModels, + + /// Name of the crate being compiled. CrateName, + + /// Lint levels of the crate's root module. CrateRootLintLevels, + + /// The current selected deployment target. (Apple only) DeploymentTarget, + + /// The names of the files created by the `--emit=link` option. (e.g. `libfoo.a`) FileNames, + + /// Target-tuple of the host compiler. HostTuple, + + /// Linker invocations. LinkArgs, + + /// When compiling a `staticlib` crate, print the linker flags used. NativeStaticLibs, + + /// List of available relocation models for the current backend. RelocationModels, + + /// List of available split debuginfos for the current target. SplitDebuginfo, + + /// List of available stack protector strategies for the current backend. StackProtectorStrategies, + + /// List of available crate types for the current target. SupportedCrateTypes, + + /// Path to the sysroot. Sysroot, + + /// List of available CPU values for the current target. TargetCPUs, + + /// List of available target features for the current target. TargetFeatures, + + /// Path to the target libdir. TargetLibdir, + + /// List of supported targets. TargetList, + + /// Current target JSON specification. TargetSpecJson, + + /// Target JSON specification schema. TargetSpecJsonSchema, + + /// List of available TLS models for the current backend. TlsModels, + + /// Target-tuple for WebAssembly's proc-macro crates. WasmProcMacroTuple, // tidy-alphabetical-end } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 941632f0d283a..349fb9c0b2b08 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -336,7 +336,6 @@ impl FromStr for EmitType { fn from_str(s: &str) -> Result { match s { - // modern choices "html-static-files" => Ok(Self::HtmlStaticFiles), "html-non-static-files" => Ok(Self::HtmlNonStaticFiles), "dep-info" => Ok(Self::DepInfo(None)), From 2e7e5cb3a90baf22f14d7f1f6b55ce46342028a3 Mon Sep 17 00:00:00 2001 From: Lieselotte <52315535+she3py@users.noreply.github.com> Date: Sun, 30 Aug 2026 17:36:51 +0200 Subject: [PATCH 04/10] rustdoc: cleanup `main_args()` markdown handling --- src/librustdoc/lib.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index c4f7d2c361952..55eae627467a4 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -858,16 +858,17 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { ); } }; + let md_input = config::markdown_input(&input); - let output_format = options.output_format; + if options.should_test || options.output_format == config::OutputFormat::Doctest { + return match md_input { + Some(_) => wrap_return(dcx, doctest::test_markdown(&input, options, dcx)), + None => doctest::run(dcx, input, options), + }; + } - match ( - options.should_test || output_format == config::OutputFormat::Doctest, - config::markdown_input(&input), - ) { - (true, Some(_)) => return wrap_return(dcx, doctest::test_markdown(&input, options, dcx)), - (true, None) => return doctest::run(dcx, input, options), - (false, Some(md_input)) => { + if let Some(md_input) = md_input { + return { let md_input = md_input.to_owned(); let edition = options.edition; let config = core::create_config(input, options, &render_options); @@ -875,7 +876,7 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { // `markdown::render` can invoke `doctest::make_test`, which // requires session globals and a thread pool, so we use // `run_compiler`. - return wrap_return( + wrap_return( dcx, interface::run_compiler(config, |compiler| { // construct a phony "crate" without actually running the parser @@ -916,9 +917,8 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { }); res }), - ); - } - (false, None) => {} + ) + }; } // need to move these items separately because we lose them by the time the closure is called, 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 05/10] 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 06/10] 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 57cc38f1d6620a499e028908bd115ee94515eb70 Mon Sep 17 00:00:00 2001 From: Lieselotte <52315535+she3py@users.noreply.github.com> Date: Mon, 13 Jul 2026 15:33:54 +0200 Subject: [PATCH 07/10] rustdoc: add `--print` option --- compiler/rustc_driver_impl/src/lib.rs | 2 +- compiler/rustc_session/src/config.rs | 2 +- .../rustc_session/src/config/print_request.rs | 2 +- src/librustdoc/config.rs | 14 ++++-- src/librustdoc/core.rs | 2 + src/librustdoc/doctest.rs | 12 ++++- src/librustdoc/lib.rs | 49 +++++++++++++++++-- .../default-output/output-default.stdout | 2 + 8 files changed, 73 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 54a1babbaae72..ade737fe1a4a6 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -616,7 +616,7 @@ fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) { } } -fn print_crate_info( +pub fn print_crate_info( codegen_backend: &dyn CodegenBackend, sess: &Session, parse_attrs: bool, diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index bba0d8190dab7..ebbf973d334e6 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -35,7 +35,7 @@ use tracing::debug; pub use crate::config::cfg::{Cfg, CheckCfg, ExpectedValues}; use crate::config::native_libs::parse_native_libs; -pub use crate::config::print_request::{PrintKind, PrintRequest}; +pub use crate::config::print_request::{PrintKind, PrintRequest, collect_print_requests}; use crate::diagnostics::FileWriteFail; pub use crate::options::*; use crate::search_paths::SearchPath; diff --git a/compiler/rustc_session/src/config/print_request.rs b/compiler/rustc_session/src/config/print_request.rs index 0cc805d4706cc..dee63856c2ec7 100644 --- a/compiler/rustc_session/src/config/print_request.rs +++ b/compiler/rustc_session/src/config/print_request.rs @@ -197,7 +197,7 @@ pub(crate) static PRINT_HELP: LazyLock = LazyLock::new(|| { ) }); -pub(crate) fn collect_print_requests( +pub fn collect_print_requests( early_dcx: &EarlyDiagCtxt, cg: &mut CodegenOptions, unstable_opts: &UnstableOptions, diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 349fb9c0b2b08..886275ee29469 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -10,8 +10,9 @@ use rustc_errors::DiagCtxtHandle; use rustc_lint::Level; use rustc_session::config::{ self, CodegenOptions, ErrorOutputType, Externs, Input, JsonUnusedExterns, - OptionsTargetModifiers, OutFileName, Sysroot, UnstableOptions, get_cmd_lint_options, - nightly_options, parse_crate_types_from_list, parse_externs, parse_target_triple, + OptionsTargetModifiers, OutFileName, PrintRequest, Sysroot, UnstableOptions, + collect_print_requests, get_cmd_lint_options, nightly_options, parse_crate_types_from_list, + parse_externs, parse_target_triple, }; use rustc_session::search_paths::SearchPath; use rustc_session::{EarlyDiagCtxt, getopts}; @@ -105,6 +106,8 @@ pub(crate) struct Options { pub(crate) describe_lints: bool, /// What level to cap lints at. pub(crate) lint_cap: Option, + /// Print requests to hand to the compiler. + pub(crate) prints: Vec, // Options specific to running doctests /// Whether we should run doctests instead of generating docs. @@ -198,6 +201,7 @@ impl fmt::Debug for Options { .field("lint_opts", &self.lint_opts) .field("describe_lints", &self.describe_lints) .field("lint_cap", &self.lint_cap) + .field("prints", &self.prints) .field("should_test", &self.should_test) .field("test_args", &self.test_args) .field("test_run_directory", &self.test_run_directory) @@ -408,7 +412,7 @@ impl Options { let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_default(); let mut collected_options = Default::default(); - let codegen_options = CodegenOptions::build(early_dcx, matches, &mut collected_options); + let mut codegen_options = CodegenOptions::build(early_dcx, matches, &mut collected_options); let unstable_opts = UnstableOptions::build(early_dcx, matches, &mut collected_options); let remap_path_prefix = match parse_remap_path_prefix(matches) { @@ -570,6 +574,9 @@ impl Options { Err(err) => dcx.fatal(err), }; + let prints = + collect_print_requests(early_dcx, &mut codegen_options, &unstable_opts, matches); + let mut parts_out_dir = match matches.opt_str("write-doc-meta-dir").map(PathToParts::from_flag).transpose() { Ok(parts_out_dir) => parts_out_dir, @@ -905,6 +912,7 @@ impl Options { lint_opts, describe_lints, lint_cap, + prints, should_test, test_args, show_coverage, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index ad6718e75466e..db5e281f376ad 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -224,6 +224,7 @@ pub(crate) fn create_config( lint_opts, describe_lints, lint_cap, + prints, scrape_examples_options, remap_path_prefix, remap_path_scope, @@ -284,6 +285,7 @@ pub(crate) fn create_config( diagnostic_width, edition, describe_lints, + prints, crate_name, test, remap_path_prefix, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index d8064cec13b96..80affbd132bfe 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -176,6 +176,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions unstable_opts: options.unstable_opts.clone(), error_format: options.error_format.clone(), target_modifiers: options.target_modifiers.clone(), + describe_lints: options.describe_lints, ..config::Options::default() }; @@ -215,8 +216,17 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions let extract_doctests = options.output_format == OutputFormat::Doctest; let save_temps = options.codegen_options.save_temps; + let registered_lints = config.register_lints.is_some(); let result = interface::run_compiler(config, |compiler| { - let krate = rustc_interface::passes::parse(&compiler.sess); + let sess = &compiler.sess; + + // -W help + if sess.opts.describe_lints { + rustc_driver::describe_lints(sess, registered_lints); + return Ok(None); + } + + let krate = rustc_interface::passes::parse(sess); let (collector, _incr_comp_session) = rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 55eae627467a4..fd50a7b306783 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -542,6 +542,14 @@ fn opts() -> Vec { "Comma separated list of types of output for rustdoc to emit", "[html-static-files,html-non-static-files,dep-info]", ), + opt( + Unstable, + Multi, + "", + "print", + "Rustdoc information to print on stdout (or to a file)", + "[=]", + ), opt(Unstable, FlagMulti, "", "no-run", "Compile doctests without running them", ""), opt( Unstable, @@ -841,6 +849,10 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { let input = match input { config::InputMode::HasFile(input) => input, config::InputMode::NoInputMergeFinalize => { + if !options.prints.is_empty() { + dcx.fatal("`--print` is not supported for the `--write-doc-meta-dir` option"); + } + let config = core::create_config( Input::Str { name: rustc_span::FileName::Custom(String::new()), @@ -861,6 +873,13 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { let md_input = config::markdown_input(&input); if options.should_test || options.output_format == config::OutputFormat::Doctest { + if !options.prints.is_empty() { + dcx.fatal(format!( + "`--print` is not yet supported for the `{}` option", + if options.should_test { "--test" } else { "--output-format=doctest" } + )); + } + return match md_input { Some(_) => wrap_return(dcx, doctest::test_markdown(&input, options, dcx)), None => doctest::run(dcx, input, options), @@ -868,10 +887,15 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { } if let Some(md_input) = md_input { + if !options.prints.is_empty() { + dcx.fatal("`--print` is not yet supported for standalone Markdown files"); + } + return { let md_input = md_input.to_owned(); let edition = options.edition; let config = core::create_config(input, options, &render_options); + let registered_lints = config.register_lints.is_some(); // `markdown::render` can invoke `doctest::make_test`, which // requires session globals and a thread pool, so we use @@ -879,12 +903,20 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { wrap_return( dcx, interface::run_compiler(config, |compiler| { + let sess = &compiler.sess; + + // -W help + if sess.opts.describe_lints { + rustc_driver::describe_lints(sess, registered_lints); + return Ok(()); + } + // construct a phony "crate" without actually running the parser // allows us to use other compiler infrastructure like dep-info - let file = - compiler.sess.source_map().load_file(&md_input).map_err(|e| { - format!("{md_input}: {e}", md_input = md_input.display()) - })?; + let file = sess + .source_map() + .load_file(&md_input) + .map_err(|e| format!("{md_input}: {e}", md_input = md_input.display()))?; let inner_span = Span::new( file.start_pos, BytePos(file.start_pos.0 + file.normalized_source_len.0), @@ -940,7 +972,6 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { let output_format = options.output_format; let config = core::create_config(input, options, &render_options); - let registered_lints = config.register_lints.is_some(); interface::run_compiler(config, |compiler| { @@ -952,11 +983,19 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { let _ = sess.source_map().load_binary_file(external_path); } + // -W help if sess.opts.describe_lints { rustc_driver::describe_lints(sess, registered_lints); return; } + // --print + if rustc_driver::print_crate_info(&*compiler.codegen_backend, sess, true) + == rustc_driver::Compilation::Stop + { + return; + } + let krate = rustc_interface::passes::parse(sess); rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { if sess.dcx().has_errors().is_some() { diff --git a/tests/run-make/rustdoc/default-output/output-default.stdout b/tests/run-make/rustdoc/default-output/output-default.stdout index 78dfbf03c1b10..3093a01ec79f7 100644 --- a/tests/run-make/rustdoc/default-output/output-default.stdout +++ b/tests/run-make/rustdoc/default-output/output-default.stdout @@ -155,6 +155,8 @@ Options: --emit [html-static-files,html-non-static-files,dep-info] Comma separated list of types of output for rustdoc to emit + --print [=] + Rustdoc information to print on stdout (or to a file) --no-run Compile doctests without running them --merge-doctests yes|no|auto Force all doctests to be compiled as a single binary, From 736a14b73f6de4dc7b08bf734c37330d3dd280ff Mon Sep 17 00:00:00 2001 From: Lieselotte <52315535+she3py@users.noreply.github.com> Date: Sun, 30 Aug 2026 16:41:57 +0200 Subject: [PATCH 08/10] rustdoc: test `--print` option --- .../print-crate-root-lint-levels/lib.rs | 6 + .../print-crate-root-lint-levels/rmake.rs | 124 ++++++++++++++++++ .../invalid-print-request-help.err | 5 + .../rustdoc/print-request-help/rmake.rs | 10 ++ 4 files changed, 145 insertions(+) create mode 100644 tests/run-make/rustdoc/print-crate-root-lint-levels/lib.rs create mode 100644 tests/run-make/rustdoc/print-crate-root-lint-levels/rmake.rs create mode 100644 tests/run-make/rustdoc/print-request-help/invalid-print-request-help.err create mode 100644 tests/run-make/rustdoc/print-request-help/rmake.rs diff --git a/tests/run-make/rustdoc/print-crate-root-lint-levels/lib.rs b/tests/run-make/rustdoc/print-crate-root-lint-levels/lib.rs new file mode 100644 index 0000000000000..a4ec391dda950 --- /dev/null +++ b/tests/run-make/rustdoc/print-crate-root-lint-levels/lib.rs @@ -0,0 +1,6 @@ +#![allow(rustdoc::private_doc_tests)] +#![forbid(rustdoc::private_intra_doc_links)] +#![expect(unused_mut)] + +#[deny(unknown_lints)] +mod my_mod {} diff --git a/tests/run-make/rustdoc/print-crate-root-lint-levels/rmake.rs b/tests/run-make/rustdoc/print-crate-root-lint-levels/rmake.rs new file mode 100644 index 0000000000000..4f7d3e129b41c --- /dev/null +++ b/tests/run-make/rustdoc/print-crate-root-lint-levels/rmake.rs @@ -0,0 +1,124 @@ +//! This checks the output of `--print=crate-root-lint-levels` + +use std::collections::HashSet; +use std::iter::FromIterator; + +use run_make_support::rustdoc; + +struct CrateRootLintLevels { + args: &'static [&'static str], + contains: Contains, +} + +struct Contains { + contains: &'static [&'static str], + doesnt_contain: &'static [&'static str], +} + +fn main() { + // rustdoc don't run rustc lints, and ignores rustc lint check attributes + check(CrateRootLintLevels { + args: &[], + contains: Contains { + contains: &[ + "rustdoc::private_doc_tests=allow", + "unused_mut=allow", + "warnings=warn", + "stable_features=warn", + "unknown_lints=warn", + "rustdoc::broken_intra_doc_links=warn", + "rustdoc::private_intra_doc_links=forbid", + "rustdoc::missing_crate_level_docs=allow", + ], + doesnt_contain: &["rustdoc::private_doc_tests=warn", "unused_mut=expect"], + }, + }); + check(CrateRootLintLevels { + args: &["-Wrustdoc::private_doc_tests"], + contains: Contains { + contains: &["rustdoc::private_doc_tests=allow", "warnings=warn"], + doesnt_contain: &["rustdoc::private_doc_tests=warn"], + }, + }); + check(CrateRootLintLevels { + args: &["-Dwarnings"], + contains: Contains { + contains: &[ + "rustdoc::private_doc_tests=allow", + "warnings=deny", + "stable_features=deny", + "unknown_lints=deny", + ], + doesnt_contain: &["warnings=warn"], + }, + }); + check(CrateRootLintLevels { + args: &["-Dstable_features"], + contains: Contains { + contains: &[ + "warnings=warn", + "stable_features=deny", + "rustdoc::private_doc_tests=allow", + ], + doesnt_contain: &["warnings=deny"], + }, + }); + check(CrateRootLintLevels { + args: &["-Dwarnings", "--force-warn=stable_features"], + contains: Contains { + contains: &["warnings=deny", "stable_features=force-warn", "unknown_lints=deny"], + doesnt_contain: &["warnings=warn"], + }, + }); + check(CrateRootLintLevels { + args: &["-Dwarnings", "--cap-lints=warn"], + contains: Contains { + contains: &[ + "rustdoc::private_doc_tests=allow", + "warnings=warn", + "stable_features=warn", + "unknown_lints=warn", + ], + doesnt_contain: &["warnings=deny"], + }, + }); +} + +#[track_caller] +fn check(CrateRootLintLevels { args, contains }: CrateRootLintLevels) { + let output = rustdoc() + .input("lib.rs") + .arg("-Zunstable-options") + .arg("--print=crate-root-lint-levels") + .args(args) + .run(); + + let stdout = output.stdout_utf8(); + + let mut found = HashSet::::new(); + + for l in stdout.lines() { + assert!(l == l.trim()); + if let Some((left, right)) = l.split_once('=') { + assert!(!left.contains("\"")); + assert!(!right.contains("\"")); + } else { + assert!(l.contains('=')); + } + assert!(found.insert(l.to_string()), "{}", &l); + } + + let Contains { contains, doesnt_contain } = contains; + + { + let should_found = HashSet::::from_iter(contains.iter().map(|s| s.to_string())); + let diff: Vec<_> = should_found.difference(&found).collect(); + assert!(diff.is_empty(), "should found: {:?}, didn't found {:?}", &should_found, &diff); + } + { + let should_not_find = + HashSet::::from_iter(doesnt_contain.iter().map(|s| s.to_string())); + let diff: Vec<_> = should_not_find.intersection(&found).collect(); + assert!(diff.is_empty(), "should not find {:?}, did found {:?}", &should_not_find, &diff); + } +} diff --git a/tests/run-make/rustdoc/print-request-help/invalid-print-request-help.err b/tests/run-make/rustdoc/print-request-help/invalid-print-request-help.err new file mode 100644 index 0000000000000..06842577618c8 --- /dev/null +++ b/tests/run-make/rustdoc/print-request-help/invalid-print-request-help.err @@ -0,0 +1,5 @@ +error: unknown print request: `xxx` + | + = help: valid print requests are: `all-target-specs-json`, `backend-has-mnemonic`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models`, `wasm-proc-macro-tuple` + = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information + diff --git a/tests/run-make/rustdoc/print-request-help/rmake.rs b/tests/run-make/rustdoc/print-request-help/rmake.rs new file mode 100644 index 0000000000000..bfa494ca2ccb6 --- /dev/null +++ b/tests/run-make/rustdoc/print-request-help/rmake.rs @@ -0,0 +1,10 @@ +use run_make_support::{diff, rustdoc}; + +fn main() { + let invalid_print_request_help = + rustdoc().arg("-Zunstable-options").arg("--print=xxx").run_fail().stderr_utf8(); + diff() + .expected_file("invalid-print-request-help.err") + .actual_text("invalid_print_request_help", &invalid_print_request_help) + .run(); +} From 0e6616bfc06bf505ff53bb4db51b2e9204809bd4 Mon Sep 17 00:00:00 2001 From: Lieselotte <52315535+she3py@users.noreply.github.com> Date: Sun, 30 Aug 2026 17:02:03 +0200 Subject: [PATCH 09/10] rustdoc: filter print kinds --- compiler/rustc_session/src/config.rs | 13 ++++- .../rustc_session/src/config/print_request.rs | 53 +++++++++++++++++-- src/librustdoc/config.rs | 11 ++-- .../invalid-print-request-help.err | 3 +- 4 files changed, 68 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index ebbf973d334e6..9a0620caf1058 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -35,8 +35,11 @@ use tracing::debug; pub use crate::config::cfg::{Cfg, CheckCfg, ExpectedValues}; use crate::config::native_libs::parse_native_libs; -pub use crate::config::print_request::{PrintKind, PrintRequest, collect_print_requests}; +pub use crate::config::print_request::{ + PrintCategory, PrintKind, PrintRequest, collect_print_requests, +}; use crate::diagnostics::FileWriteFail; +use crate::macros::AllVariants; pub use crate::options::*; use crate::search_paths::SearchPath; use crate::utils::CanonicalizedPath; @@ -2897,7 +2900,13 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M )); } - let prints = print_request::collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches); + let prints = print_request::collect_print_requests( + early_dcx, + &mut cg, + &unstable_opts, + matches, + PrintCategory::ALL_VARIANTS, + ); // -Zretpoline-external-thunk also requires -Zretpoline if unstable_opts.retpoline_external_thunk { diff --git a/compiler/rustc_session/src/config/print_request.rs b/compiler/rustc_session/src/config/print_request.rs index dee63856c2ec7..b41ceb1699eaa 100644 --- a/compiler/rustc_session/src/config/print_request.rs +++ b/compiler/rustc_session/src/config/print_request.rs @@ -105,6 +105,15 @@ pub enum PrintKind { // tidy-alphabetical-end } +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(AllVariants)] +pub enum PrintCategory { + Target, + Codegen, + Linker, + Crate, +} + impl PrintKind { fn name(self) -> &'static str { use PrintKind::*; @@ -141,6 +150,28 @@ impl PrintKind { } } + fn category(self) -> PrintCategory { + use PrintKind::*; + match self { + TargetList | TargetSpecJsonSchema | AllTargetSpecsJson | TargetSpecJson + | TargetCPUs | TargetFeatures | DeploymentTarget | HostTuple | SupportedCrateTypes + | Sysroot | TargetLibdir | Cfg | CheckCfg | WasmProcMacroTuple => PrintCategory::Target, + + BackendHasMnemonic + | BackendHasZstd + | CallingConventions + | CodeModels + | SplitDebuginfo + | StackProtectorStrategies + | TlsModels + | RelocationModels => PrintCategory::Codegen, + + LinkArgs | NativeStaticLibs => PrintCategory::Linker, + + CrateName | CrateRootLintLevels | FileNames => PrintCategory::Crate, + } + } + fn is_stable(self) -> bool { use PrintKind::*; match self { @@ -202,6 +233,7 @@ pub fn collect_print_requests( cg: &mut CodegenOptions, unstable_opts: &UnstableOptions, matches: &getopts::Matches, + allowed: &[PrintCategory], ) -> Vec { let mut prints = Vec::::new(); if cg.target_cpu.as_deref() == Some("help") { @@ -243,12 +275,14 @@ pub fn collect_print_requests( for example: `--print=backend-has-mnemonic:RET`", ); } - } else if let Some(print_kind) = PrintKind::from_str(req) { + } else if let Some(print_kind) = PrintKind::from_str(req) + && allowed.contains(&print_kind.category()) + { check_print_request_stability(early_dcx, unstable_opts, print_kind); (print_kind, None) } else { let is_nightly = nightly_options::match_is_nightly_build(matches); - emit_unknown_print_request_help(early_dcx, req, is_nightly) + emit_unknown_print_request_help(early_dcx, req, is_nightly, allowed) }; let out = out.unwrap_or(OutFileName::Stdout); @@ -279,11 +313,17 @@ fn check_print_request_stability( } } -fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nightly: bool) -> ! { +fn emit_unknown_print_request_help( + early_dcx: &EarlyDiagCtxt, + req: &str, + is_nightly: bool, + allowed: &[PrintCategory], +) -> ! { let prints = PrintKind::ALL_VARIANTS .iter() // If we're not on nightly, we don't want to print unstable options .filter(|kind| is_nightly || kind.is_stable()) + .filter(|kind| allowed.contains(&kind.category())) .map(|kind| format!("`{kind}`")) .collect::>() .join(", "); @@ -292,9 +332,12 @@ fn emit_unknown_print_request_help(early_dcx: &EarlyDiagCtxt, req: &str, is_nigh diag.help(format!("valid print requests are: {prints}")); if req == "lints" { - diag.help(format!("use `-Whelp` to print a list of lints")); + diag.help("use `-Whelp` to print a list of lints"); + } + + if allowed == PrintCategory::ALL_VARIANTS { + diag.help("for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information"); } - diag.help(format!("for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information")); diag.emit() } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 886275ee29469..c2c58a345fa22 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -10,7 +10,7 @@ use rustc_errors::DiagCtxtHandle; use rustc_lint::Level; use rustc_session::config::{ self, CodegenOptions, ErrorOutputType, Externs, Input, JsonUnusedExterns, - OptionsTargetModifiers, OutFileName, PrintRequest, Sysroot, UnstableOptions, + OptionsTargetModifiers, OutFileName, PrintCategory, PrintRequest, Sysroot, UnstableOptions, collect_print_requests, get_cmd_lint_options, nightly_options, parse_crate_types_from_list, parse_externs, parse_target_triple, }; @@ -574,8 +574,13 @@ impl Options { Err(err) => dcx.fatal(err), }; - let prints = - collect_print_requests(early_dcx, &mut codegen_options, &unstable_opts, matches); + let prints = collect_print_requests( + early_dcx, + &mut codegen_options, + &unstable_opts, + matches, + &[PrintCategory::Target, PrintCategory::Crate], + ); let mut parts_out_dir = match matches.opt_str("write-doc-meta-dir").map(PathToParts::from_flag).transpose() { diff --git a/tests/run-make/rustdoc/print-request-help/invalid-print-request-help.err b/tests/run-make/rustdoc/print-request-help/invalid-print-request-help.err index 06842577618c8..02d14190030bb 100644 --- a/tests/run-make/rustdoc/print-request-help/invalid-print-request-help.err +++ b/tests/run-make/rustdoc/print-request-help/invalid-print-request-help.err @@ -1,5 +1,4 @@ error: unknown print request: `xxx` | - = help: valid print requests are: `all-target-specs-json`, `backend-has-mnemonic`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models`, `wasm-proc-macro-tuple` - = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information + = help: valid print requests are: `all-target-specs-json`, `cfg`, `check-cfg`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `wasm-proc-macro-tuple` From d2c6c811a1036efdf7e6435815bded1dde7e9c9c Mon Sep 17 00:00:00 2001 From: Lieselotte <52315535+she3py@users.noreply.github.com> Date: Mon, 31 Aug 2026 12:55:26 +0200 Subject: [PATCH 10/10] rustdoc: add tests for Markdown files --- .../run-make/rustdoc/doctest/markdown/aux.rs | 3 ++ .../run-make/rustdoc/doctest/markdown/bad.md | 9 ++++++ .../rustdoc/doctest/markdown/extern.md | 7 +++++ .../run-make/rustdoc/doctest/markdown/good.md | 23 +++++++++++++++ .../rustdoc/doctest/markdown/rmake.rs | 29 +++++++++++++++++++ 5 files changed, 71 insertions(+) create mode 100644 tests/run-make/rustdoc/doctest/markdown/aux.rs create mode 100644 tests/run-make/rustdoc/doctest/markdown/bad.md create mode 100644 tests/run-make/rustdoc/doctest/markdown/extern.md create mode 100644 tests/run-make/rustdoc/doctest/markdown/good.md create mode 100644 tests/run-make/rustdoc/doctest/markdown/rmake.rs diff --git a/tests/run-make/rustdoc/doctest/markdown/aux.rs b/tests/run-make/rustdoc/doctest/markdown/aux.rs new file mode 100644 index 0000000000000..77a01ad3ca498 --- /dev/null +++ b/tests/run-make/rustdoc/doctest/markdown/aux.rs @@ -0,0 +1,3 @@ +pub fn add(x: i32, y: i32) -> i32 { + x + y +} diff --git a/tests/run-make/rustdoc/doctest/markdown/bad.md b/tests/run-make/rustdoc/doctest/markdown/bad.md new file mode 100644 index 0000000000000..3d43232cd61f1 --- /dev/null +++ b/tests/run-make/rustdoc/doctest/markdown/bad.md @@ -0,0 +1,9 @@ +# Cool Title + +``` +assert!(true); +``` + +``` +assert_eq!("foo", "bar"); +``` diff --git a/tests/run-make/rustdoc/doctest/markdown/extern.md b/tests/run-make/rustdoc/doctest/markdown/extern.md new file mode 100644 index 0000000000000..27a2a0e28588a --- /dev/null +++ b/tests/run-make/rustdoc/doctest/markdown/extern.md @@ -0,0 +1,7 @@ +# With extern crate + +``` +# extern crate aux; + +assert_eq!(aux::add(3, 4), 7); +``` diff --git a/tests/run-make/rustdoc/doctest/markdown/good.md b/tests/run-make/rustdoc/doctest/markdown/good.md new file mode 100644 index 0000000000000..db7e85d3fc51f --- /dev/null +++ b/tests/run-make/rustdoc/doctest/markdown/good.md @@ -0,0 +1,23 @@ +# Title + +Some text + +``` +assert_eq!(0, 0); +``` + +```text +Some more text +``` + +```ignore (example) +assert_eq!(0, 1; +``` + +```no_run +assert_eq!(0, 1); +``` + +```rust,compile_fail +Something +``` diff --git a/tests/run-make/rustdoc/doctest/markdown/rmake.rs b/tests/run-make/rustdoc/doctest/markdown/rmake.rs new file mode 100644 index 0000000000000..207a8a18cb766 --- /dev/null +++ b/tests/run-make/rustdoc/doctest/markdown/rmake.rs @@ -0,0 +1,29 @@ +//@ needs-target-std (for doctests) + +use run_make_support::{rust_lib_name, rustc, rustdoc}; + +fn main() { + rustdoc().arg("--test").input("good.md").run().assert_exit_code(0).assert_stdout_contains( + "test result: ok. 3 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out;", + ); + + rustdoc() + .arg("--test") + .input("bad.md") + .run_fail() + .assert_exit_code(101) + .assert_stdout_contains( + "test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out;", + ); + + rustc().input("aux.rs").crate_type("rlib").run(); + rustdoc() + .arg("--test") + .extern_("aux", rust_lib_name("aux")) + .input("extern.md") + .run() + .assert_exit_code(0) + .assert_stdout_contains( + "test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out;", + ); +}