Skip to content

Commit 0cd11b7

Browse files
authored
Unrolled build for #150788
Rollup merge of #150788 - thir-pat, r=Nadrieril THIR patterns: Replace `AscribeUserType` and `ExpandedConstant` wrappers with per-node data This PR removes the `AscribeUserType` and `ExpandedConstant` variants from `thir::PatKind`, and replaces them with an `Option<Box<PatExtra>>` field attached to every `thir::Pat`. ### Why remove these variants? Unlike other THIR pattern kinds, these variants are mere “wrappers” that exist to attach some additional information to an underlying pattern node. There are several places where code that consumes THIR patterns needs to carefully “unpeel” any wrapper nodes, in order to match on the underlying pattern. This is clunky, and easy to forget to do, especially since it's not always obvious where the wrapper nodes can and can't appear. Attaching the data to an optional per-node field makes it easier for consuming code to simply ignore the extra data when it is not relevant. (One downside is that it is now easier to accidentally ignore the extra data when it *is* relevant, but I think that's generally a favourable tradeoff.) ### Impact After this change, THIR pattern trees should be “logically identical” to the previous THIR pattern trees, in the sense that information that was carried by wrapper nodes should now be directly attached to the non-wrapper nodes that were being wrapped. Types and spans associated with THIR pattern nodes should (hopefully!) still be accurate. There should be no change to the output of THIR-based checks or MIR building.
2 parents ad04f76 + 8516c64 commit 0cd11b7

File tree

13 files changed

+222
-260
lines changed

13 files changed

+222
-260
lines changed

compiler/rustc_middle/src/thir.rs

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -643,10 +643,26 @@ pub struct FieldPat<'tcx> {
643643
pub pattern: Pat<'tcx>,
644644
}
645645

646+
/// Additional per-node data that is not present on most THIR pattern nodes.
647+
#[derive(Clone, Debug, Default, HashStable, TypeVisitable)]
648+
pub struct PatExtra<'tcx> {
649+
/// If present, this node represents a named constant that was lowered to
650+
/// a pattern using `const_to_pat`.
651+
///
652+
/// This is used by some diagnostics for non-exhaustive matches, to map
653+
/// the pattern node back to the `DefId` of its original constant.
654+
pub expanded_const: Option<DefId>,
655+
656+
/// User-written types that must be preserved into MIR so that they can be
657+
/// checked.
658+
pub ascriptions: Vec<Ascription<'tcx>>,
659+
}
660+
646661
#[derive(Clone, Debug, HashStable, TypeVisitable)]
647662
pub struct Pat<'tcx> {
648663
pub ty: Ty<'tcx>,
649664
pub span: Span,
665+
pub extra: Option<Box<PatExtra<'tcx>>>,
650666
pub kind: PatKind<'tcx>,
651667
}
652668

@@ -762,11 +778,6 @@ pub enum PatKind<'tcx> {
762778
/// A wildcard pattern: `_`.
763779
Wild,
764780

765-
AscribeUserType {
766-
ascription: Ascription<'tcx>,
767-
subpattern: Box<Pat<'tcx>>,
768-
},
769-
770781
/// `x`, `ref x`, `x @ P`, etc.
771782
Binding {
772783
name: Symbol,
@@ -831,21 +842,6 @@ pub enum PatKind<'tcx> {
831842
value: ty::Value<'tcx>,
832843
},
833844

834-
/// Wrapper node representing a named constant that was lowered to a pattern
835-
/// using `const_to_pat`.
836-
///
837-
/// This is used by some diagnostics for non-exhaustive matches, to map
838-
/// the pattern node back to the `DefId` of its original constant.
839-
///
840-
/// FIXME(#150498): Can we make this an `Option<DefId>` field on `Pat`
841-
/// instead, so that non-diagnostic code can ignore it more easily?
842-
ExpandedConstant {
843-
/// [DefId] of the constant item.
844-
def_id: DefId,
845-
/// The pattern that the constant lowered to.
846-
subpattern: Box<Pat<'tcx>>,
847-
},
848-
849845
Range(Arc<PatRange<'tcx>>),
850846

851847
/// Matches against a slice, checking the length and extracting elements.
@@ -1119,7 +1115,7 @@ mod size_asserts {
11191115
static_assert_size!(Block, 48);
11201116
static_assert_size!(Expr<'_>, 64);
11211117
static_assert_size!(ExprKind<'_>, 40);
1122-
static_assert_size!(Pat<'_>, 64);
1118+
static_assert_size!(Pat<'_>, 72);
11231119
static_assert_size!(PatKind<'_>, 48);
11241120
static_assert_size!(Stmt<'_>, 48);
11251121
static_assert_size!(StmtKind<'_>, 48);

compiler/rustc_middle/src/thir/visit.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ pub(crate) fn for_each_immediate_subpat<'a, 'tcx>(
259259
pat: &'a Pat<'tcx>,
260260
mut callback: impl FnMut(&'a Pat<'tcx>),
261261
) {
262-
let Pat { kind, ty: _, span: _ } = pat;
262+
let Pat { kind, ty: _, span: _, extra: _ } = pat;
263263
match kind {
264264
PatKind::Missing
265265
| PatKind::Wild
@@ -269,11 +269,9 @@ pub(crate) fn for_each_immediate_subpat<'a, 'tcx>(
269269
| PatKind::Never
270270
| PatKind::Error(_) => {}
271271

272-
PatKind::AscribeUserType { subpattern, .. }
273-
| PatKind::Binding { subpattern: Some(subpattern), .. }
272+
PatKind::Binding { subpattern: Some(subpattern), .. }
274273
| PatKind::Deref { subpattern }
275-
| PatKind::DerefPattern { subpattern, .. }
276-
| PatKind::ExpandedConstant { subpattern, .. } => callback(subpattern),
274+
| PatKind::DerefPattern { subpattern, .. } => callback(subpattern),
277275

278276
PatKind::Variant { subpatterns, .. } | PatKind::Leaf { subpatterns } => {
279277
for field_pat in subpatterns {

compiler/rustc_mir_build/src/builder/custom/parse.rs

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -287,22 +287,14 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
287287
self.parse_var(pattern)
288288
}
289289

290-
fn parse_var(&mut self, mut pat: &Pat<'tcx>) -> PResult<(LocalVarId, Ty<'tcx>, Span)> {
291-
// Make sure we throw out any `AscribeUserType` we find
292-
loop {
293-
match &pat.kind {
294-
PatKind::Binding { var, ty, .. } => break Ok((*var, *ty, pat.span)),
295-
PatKind::AscribeUserType { subpattern, .. } => {
296-
pat = subpattern;
297-
}
298-
_ => {
299-
break Err(ParseError {
300-
span: pat.span,
301-
item_description: format!("{:?}", pat.kind),
302-
expected: "local".to_string(),
303-
});
304-
}
305-
}
290+
fn parse_var(&mut self, pat: &Pat<'tcx>) -> PResult<(LocalVarId, Ty<'tcx>, Span)> {
291+
match &pat.kind {
292+
PatKind::Binding { var, ty, .. } => Ok((*var, *ty, pat.span)),
293+
_ => Err(ParseError {
294+
span: pat.span,
295+
item_description: format!("{:?}", pat.kind),
296+
expected: "local".to_string(),
297+
}),
306298
}
307299
}
308300

compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,6 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
144144
let arm = &self.thir[*arm];
145145
let value = match arm.pattern.kind {
146146
PatKind::Constant { value } => value,
147-
PatKind::ExpandedConstant { ref subpattern, def_id: _ }
148-
if let PatKind::Constant { value } = subpattern.kind =>
149-
{
150-
value
151-
}
152147
_ => {
153148
return Err(ParseError {
154149
span: arm.pattern.span,

compiler/rustc_mir_build/src/builder/matches/match_pair.rs

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,20 @@ impl<'tcx> MatchPairTree<'tcx> {
133133
}
134134

135135
let place = place_builder.try_to_place(cx);
136+
137+
// Apply any type ascriptions to the value at `match_pair.place`.
138+
if let Some(place) = place
139+
&& let Some(extra) = &pattern.extra
140+
{
141+
for &Ascription { ref annotation, variance } in &extra.ascriptions {
142+
extra_data.ascriptions.push(super::Ascription {
143+
source: place,
144+
annotation: annotation.clone(),
145+
variance,
146+
});
147+
}
148+
}
149+
136150
let mut subpairs = Vec::new();
137151
let testable_case = match pattern.kind {
138152
PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None,
@@ -195,28 +209,6 @@ impl<'tcx> MatchPairTree<'tcx> {
195209
Some(TestableCase::Constant { value, kind: const_kind })
196210
}
197211

198-
PatKind::AscribeUserType {
199-
ascription: Ascription { ref annotation, variance },
200-
ref subpattern,
201-
..
202-
} => {
203-
MatchPairTree::for_pattern(
204-
place_builder,
205-
subpattern,
206-
cx,
207-
&mut subpairs,
208-
extra_data,
209-
);
210-
211-
// Apply the type ascription to the value at `match_pair.place`
212-
if let Some(source) = place {
213-
let annotation = annotation.clone();
214-
extra_data.ascriptions.push(super::Ascription { source, annotation, variance });
215-
}
216-
217-
None
218-
}
219-
220212
PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => {
221213
// In order to please the borrow checker, when lowering a pattern
222214
// like `x @ subpat` we must establish any bindings in `subpat`
@@ -263,11 +255,6 @@ impl<'tcx> MatchPairTree<'tcx> {
263255
None
264256
}
265257

266-
PatKind::ExpandedConstant { subpattern: ref pattern, .. } => {
267-
MatchPairTree::for_pattern(place_builder, pattern, cx, &mut subpairs, extra_data);
268-
None
269-
}
270-
271258
PatKind::Array { ref prefix, ref slice, ref suffix } => {
272259
cx.prefix_slice_suffix(
273260
&mut subpairs,

compiler/rustc_mir_build/src/builder/matches/mod.rs

Lines changed: 32 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
576576
initializer_id: ExprId,
577577
) -> BlockAnd<()> {
578578
match irrefutable_pat.kind {
579-
// Optimize the case of `let x = ...` to write directly into `x`
579+
// Optimize `let x = ...` and `let x: T = ...` to write directly into `x`,
580+
// and then require that `T == typeof(x)` if present.
580581
PatKind::Binding { mode: BindingMode(ByRef::No, _), var, subpattern: None, .. } => {
581582
let place = self.storage_live_binding(
582583
block,
@@ -592,43 +593,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
592593
let source_info = self.source_info(irrefutable_pat.span);
593594
self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet(None), place);
594595

595-
self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
596-
block.unit()
597-
}
596+
let ascriptions: &[_] =
597+
try { irrefutable_pat.extra.as_deref()?.ascriptions.as_slice() }
598+
.unwrap_or_default();
599+
for thir::Ascription { annotation, variance: _ } in ascriptions {
600+
let ty_source_info = self.source_info(annotation.span);
598601

599-
// Optimize the case of `let x: T = ...` to write directly
600-
// into `x` and then require that `T == typeof(x)`.
601-
PatKind::AscribeUserType {
602-
ref subpattern,
603-
ascription: thir::Ascription { ref annotation, variance: _ },
604-
} if let PatKind::Binding {
605-
mode: BindingMode(ByRef::No, _),
606-
var,
607-
subpattern: None,
608-
..
609-
} = subpattern.kind =>
610-
{
611-
let place = self.storage_live_binding(
612-
block,
613-
var,
614-
irrefutable_pat.span,
615-
false,
616-
OutsideGuard,
617-
ScheduleDrops::Yes,
618-
);
619-
block = self.expr_into_dest(place, block, initializer_id).into_block();
620-
621-
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
622-
let pattern_source_info = self.source_info(irrefutable_pat.span);
623-
let cause_let = FakeReadCause::ForLet(None);
624-
self.cfg.push_fake_read(block, pattern_source_info, cause_let, place);
625-
626-
let ty_source_info = self.source_info(annotation.span);
627-
628-
let base = self.canonical_user_type_annotations.push(annotation.clone());
629-
self.cfg.push(
630-
block,
631-
Statement::new(
602+
let base = self.canonical_user_type_annotations.push(annotation.clone());
603+
let stmt = Statement::new(
632604
ty_source_info,
633605
StatementKind::AscribeUserType(
634606
Box::new((place, UserTypeProjection { base, projs: Vec::new() })),
@@ -648,8 +620,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
648620
// `<expr>`.
649621
ty::Invariant,
650622
),
651-
),
652-
);
623+
);
624+
self.cfg.push(block, stmt);
625+
}
653626

654627
self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
655628
block.unit()
@@ -879,6 +852,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
879852
&ProjectedUserTypesNode<'_>,
880853
),
881854
) {
855+
// Ascriptions correspond to user-written types like `let A::<'a>(_): A<'static> = ...;`.
856+
//
857+
// Caution: Pushing user types here is load-bearing even for
858+
// patterns containing no bindings, to ensure that the type ends
859+
// up represented in MIR _somewhere_.
860+
let user_tys = match pattern.extra.as_deref() {
861+
Some(PatExtra { ascriptions, .. }) if !ascriptions.is_empty() => {
862+
let base_user_tys = ascriptions
863+
.iter()
864+
.map(|thir::Ascription { annotation, variance: _ }| {
865+
// Note that the variance doesn't apply here, as we are tracking the effect
866+
// of user types on any bindings contained with subpattern.
867+
self.canonical_user_type_annotations.push(annotation.clone())
868+
})
869+
.collect();
870+
&user_tys.push_user_types(base_user_tys)
871+
}
872+
_ => user_tys,
873+
};
874+
882875
// Avoid having to write the full method name at each recursive call.
883876
let visit_subpat = |this: &mut Self, subpat, user_tys: &_, f: &mut _| {
884877
this.visit_primary_bindings_special(subpat, user_tys, f)
@@ -924,31 +917,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
924917
visit_subpat(self, subpattern, &ProjectedUserTypesNode::None, f);
925918
}
926919

927-
PatKind::AscribeUserType {
928-
ref subpattern,
929-
ascription: thir::Ascription { ref annotation, variance: _ },
930-
} => {
931-
// This corresponds to something like
932-
//
933-
// ```
934-
// let A::<'a>(_): A<'static> = ...;
935-
// ```
936-
//
937-
// Note that the variance doesn't apply here, as we are tracking the effect
938-
// of `user_ty` on any bindings contained with subpattern.
939-
940-
// Caution: Pushing this user type here is load-bearing even for
941-
// patterns containing no bindings, to ensure that the type ends
942-
// up represented in MIR _somewhere_.
943-
let base_user_ty = self.canonical_user_type_annotations.push(annotation.clone());
944-
let subpattern_user_tys = user_tys.push_user_type(base_user_ty);
945-
visit_subpat(self, subpattern, &subpattern_user_tys, f)
946-
}
947-
948-
PatKind::ExpandedConstant { ref subpattern, .. } => {
949-
visit_subpat(self, subpattern, user_tys, f)
950-
}
951-
952920
PatKind::Leaf { ref subpatterns } => {
953921
for subpattern in subpatterns {
954922
let subpattern_user_tys = user_tys.leaf(subpattern.field);

compiler/rustc_mir_build/src/builder/matches/user_ty.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,20 @@ use std::assert_matches::assert_matches;
88
use std::iter;
99

1010
use rustc_abi::{FieldIdx, VariantIdx};
11+
use rustc_data_structures::smallvec::SmallVec;
1112
use rustc_middle::mir::{ProjectionElem, UserTypeProjection, UserTypeProjections};
1213
use rustc_middle::ty::{AdtDef, UserTypeAnnotationIndex};
1314
use rustc_span::Symbol;
1415

16+
/// A single `thir::Pat` node should almost never have more than 0-2 user types.
17+
/// We can store up to 4 inline in the same size as an ordinary `Vec`.
18+
pub(crate) type UserTypeIndices = SmallVec<[UserTypeAnnotationIndex; 4]>;
19+
1520
/// One of a list of "operations" that can be used to lazily build projections
1621
/// of user-specified types.
17-
#[derive(Clone, Debug)]
22+
#[derive(Debug)]
1823
pub(crate) enum ProjectedUserTypesOp {
19-
PushUserType { base: UserTypeAnnotationIndex },
24+
PushUserTypes { base_types: UserTypeIndices },
2025

2126
Index,
2227
Subslice { from: u64, to: u64 },
@@ -32,9 +37,10 @@ pub(crate) enum ProjectedUserTypesNode<'a> {
3237
}
3338

3439
impl<'a> ProjectedUserTypesNode<'a> {
35-
pub(crate) fn push_user_type(&'a self, base: UserTypeAnnotationIndex) -> Self {
36-
// Pushing a base user type always causes the chain to become non-empty.
37-
Self::Chain { parent: self, op: ProjectedUserTypesOp::PushUserType { base } }
40+
pub(crate) fn push_user_types(&'a self, base_types: UserTypeIndices) -> Self {
41+
assert!(!base_types.is_empty());
42+
// Pushing one or more base user types always causes the chain to become non-empty.
43+
Self::Chain { parent: self, op: ProjectedUserTypesOp::PushUserTypes { base_types } }
3844
}
3945

4046
/// Push another projection op onto the chain, but only if it is already non-empty.
@@ -94,16 +100,19 @@ impl<'a> ProjectedUserTypesNode<'a> {
94100
return None;
95101
}
96102

97-
let ops_reversed = self.iter_ops_reversed().cloned().collect::<Vec<_>>();
103+
let ops_reversed = self.iter_ops_reversed().collect::<Vec<_>>();
98104
// The "first" op should always be `PushUserType`.
99105
// Other projections are only added if there is at least one user type.
100-
assert_matches!(ops_reversed.last(), Some(ProjectedUserTypesOp::PushUserType { .. }));
106+
assert_matches!(ops_reversed.last(), Some(ProjectedUserTypesOp::PushUserTypes { .. }));
101107

102108
let mut projections = vec![];
103109
for op in ops_reversed.into_iter().rev() {
104-
match op {
105-
ProjectedUserTypesOp::PushUserType { base } => {
106-
projections.push(UserTypeProjection { base, projs: vec![] })
110+
match *op {
111+
ProjectedUserTypesOp::PushUserTypes { ref base_types } => {
112+
assert!(!base_types.is_empty());
113+
for &base in base_types {
114+
projections.push(UserTypeProjection { base, projs: vec![] })
115+
}
107116
}
108117

109118
ProjectedUserTypesOp::Index => {

0 commit comments

Comments
 (0)