Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 17 additions & 21 deletions compiler/rustc_middle/src/thir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -643,10 +643,26 @@ pub struct FieldPat<'tcx> {
pub pattern: Pat<'tcx>,
}

/// Additional per-node data that is not present on most THIR pattern nodes.
#[derive(Clone, Debug, Default, HashStable, TypeVisitable)]
pub struct PatExtra<'tcx> {
/// If present, this node represents a named constant that was lowered to
/// a pattern using `const_to_pat`.
///
/// This is used by some diagnostics for non-exhaustive matches, to map
/// the pattern node back to the `DefId` of its original constant.
pub expanded_const: Option<DefId>,

/// User-written types that must be preserved into MIR so that they can be
/// checked.
pub ascriptions: Vec<Ascription<'tcx>>,
}

#[derive(Clone, Debug, HashStable, TypeVisitable)]
pub struct Pat<'tcx> {
pub ty: Ty<'tcx>,
pub span: Span,
pub extra: Option<Box<PatExtra<'tcx>>>,
pub kind: PatKind<'tcx>,
}

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

AscribeUserType {
ascription: Ascription<'tcx>,
subpattern: Box<Pat<'tcx>>,
},

/// `x`, `ref x`, `x @ P`, etc.
Binding {
name: Symbol,
Expand Down Expand Up @@ -831,21 +842,6 @@ pub enum PatKind<'tcx> {
value: ty::Value<'tcx>,
},

/// Wrapper node representing a named constant that was lowered to a pattern
/// using `const_to_pat`.
///
/// This is used by some diagnostics for non-exhaustive matches, to map
/// the pattern node back to the `DefId` of its original constant.
///
/// FIXME(#150498): Can we make this an `Option<DefId>` field on `Pat`
/// instead, so that non-diagnostic code can ignore it more easily?
ExpandedConstant {
/// [DefId] of the constant item.
def_id: DefId,
/// The pattern that the constant lowered to.
subpattern: Box<Pat<'tcx>>,
},

Range(Arc<PatRange<'tcx>>),

/// Matches against a slice, checking the length and extracting elements.
Expand Down Expand Up @@ -1119,7 +1115,7 @@ mod size_asserts {
static_assert_size!(Block, 48);
static_assert_size!(Expr<'_>, 64);
static_assert_size!(ExprKind<'_>, 40);
static_assert_size!(Pat<'_>, 64);
static_assert_size!(Pat<'_>, 72);
static_assert_size!(PatKind<'_>, 48);
static_assert_size!(Stmt<'_>, 48);
static_assert_size!(StmtKind<'_>, 48);
Expand Down
8 changes: 3 additions & 5 deletions compiler/rustc_middle/src/thir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ pub(crate) fn for_each_immediate_subpat<'a, 'tcx>(
pat: &'a Pat<'tcx>,
mut callback: impl FnMut(&'a Pat<'tcx>),
) {
let Pat { kind, ty: _, span: _ } = pat;
let Pat { kind, ty: _, span: _, extra: _ } = pat;
match kind {
PatKind::Missing
| PatKind::Wild
Expand All @@ -269,11 +269,9 @@ pub(crate) fn for_each_immediate_subpat<'a, 'tcx>(
| PatKind::Never
| PatKind::Error(_) => {}

PatKind::AscribeUserType { subpattern, .. }
| PatKind::Binding { subpattern: Some(subpattern), .. }
PatKind::Binding { subpattern: Some(subpattern), .. }
| PatKind::Deref { subpattern }
| PatKind::DerefPattern { subpattern, .. }
| PatKind::ExpandedConstant { subpattern, .. } => callback(subpattern),
| PatKind::DerefPattern { subpattern, .. } => callback(subpattern),

PatKind::Variant { subpatterns, .. } | PatKind::Leaf { subpatterns } => {
for field_pat in subpatterns {
Expand Down
24 changes: 8 additions & 16 deletions compiler/rustc_mir_build/src/builder/custom/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,22 +287,14 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
self.parse_var(pattern)
}

fn parse_var(&mut self, mut pat: &Pat<'tcx>) -> PResult<(LocalVarId, Ty<'tcx>, Span)> {
// Make sure we throw out any `AscribeUserType` we find
loop {
match &pat.kind {
PatKind::Binding { var, ty, .. } => break Ok((*var, *ty, pat.span)),
PatKind::AscribeUserType { subpattern, .. } => {
pat = subpattern;
}
_ => {
break Err(ParseError {
span: pat.span,
item_description: format!("{:?}", pat.kind),
expected: "local".to_string(),
});
}
}
fn parse_var(&mut self, pat: &Pat<'tcx>) -> PResult<(LocalVarId, Ty<'tcx>, Span)> {
match &pat.kind {
PatKind::Binding { var, ty, .. } => Ok((*var, *ty, pat.span)),
_ => Err(ParseError {
span: pat.span,
item_description: format!("{:?}", pat.kind),
expected: "local".to_string(),
}),
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,6 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
let arm = &self.thir[*arm];
let value = match arm.pattern.kind {
PatKind::Constant { value } => value,
PatKind::ExpandedConstant { ref subpattern, def_id: _ }
if let PatKind::Constant { value } = subpattern.kind =>
{
value
}
_ => {
return Err(ParseError {
span: arm.pattern.span,
Expand Down
41 changes: 14 additions & 27 deletions compiler/rustc_mir_build/src/builder/matches/match_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,20 @@ impl<'tcx> MatchPairTree<'tcx> {
}

let place = place_builder.try_to_place(cx);

// Apply any type ascriptions to the value at `match_pair.place`.
if let Some(place) = place
&& let Some(extra) = &pattern.extra
{
for &Ascription { ref annotation, variance } in &extra.ascriptions {
extra_data.ascriptions.push(super::Ascription {
source: place,
annotation: annotation.clone(),
variance,
});
}
}

let mut subpairs = Vec::new();
let testable_case = match pattern.kind {
PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None,
Expand Down Expand Up @@ -195,28 +209,6 @@ impl<'tcx> MatchPairTree<'tcx> {
Some(TestableCase::Constant { value, kind: const_kind })
}

PatKind::AscribeUserType {
ascription: Ascription { ref annotation, variance },
ref subpattern,
..
} => {
MatchPairTree::for_pattern(
place_builder,
subpattern,
cx,
&mut subpairs,
extra_data,
);

// Apply the type ascription to the value at `match_pair.place`
if let Some(source) = place {
let annotation = annotation.clone();
extra_data.ascriptions.push(super::Ascription { source, annotation, variance });
}

None
}

PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => {
// In order to please the borrow checker, when lowering a pattern
// like `x @ subpat` we must establish any bindings in `subpat`
Expand Down Expand Up @@ -263,11 +255,6 @@ impl<'tcx> MatchPairTree<'tcx> {
None
}

PatKind::ExpandedConstant { subpattern: ref pattern, .. } => {
MatchPairTree::for_pattern(place_builder, pattern, cx, &mut subpairs, extra_data);
None
}

PatKind::Array { ref prefix, ref slice, ref suffix } => {
cx.prefix_slice_suffix(
&mut subpairs,
Expand Down
96 changes: 32 additions & 64 deletions compiler/rustc_mir_build/src/builder/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
initializer_id: ExprId,
) -> BlockAnd<()> {
match irrefutable_pat.kind {
// Optimize the case of `let x = ...` to write directly into `x`
// Optimize `let x = ...` and `let x: T = ...` to write directly into `x`,
// and then require that `T == typeof(x)` if present.
PatKind::Binding { mode: BindingMode(ByRef::No, _), var, subpattern: None, .. } => {
let place = self.storage_live_binding(
block,
Expand All @@ -592,43 +593,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let source_info = self.source_info(irrefutable_pat.span);
self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet(None), place);

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

// Optimize the case of `let x: T = ...` to write directly
// into `x` and then require that `T == typeof(x)`.
PatKind::AscribeUserType {
ref subpattern,
ascription: thir::Ascription { ref annotation, variance: _ },
} if let PatKind::Binding {
mode: BindingMode(ByRef::No, _),
var,
subpattern: None,
..
} = subpattern.kind =>
{
let place = self.storage_live_binding(
block,
var,
irrefutable_pat.span,
false,
OutsideGuard,
ScheduleDrops::Yes,
);
block = self.expr_into_dest(place, block, initializer_id).into_block();

// Inject a fake read, see comments on `FakeReadCause::ForLet`.
let pattern_source_info = self.source_info(irrefutable_pat.span);
let cause_let = FakeReadCause::ForLet(None);
self.cfg.push_fake_read(block, pattern_source_info, cause_let, place);

let ty_source_info = self.source_info(annotation.span);

let base = self.canonical_user_type_annotations.push(annotation.clone());
self.cfg.push(
block,
Statement::new(
let base = self.canonical_user_type_annotations.push(annotation.clone());
let stmt = Statement::new(
ty_source_info,
StatementKind::AscribeUserType(
Box::new((place, UserTypeProjection { base, projs: Vec::new() })),
Expand All @@ -648,8 +620,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// `<expr>`.
ty::Invariant,
),
),
);
);
self.cfg.push(block, stmt);
}

self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard);
block.unit()
Expand Down Expand Up @@ -879,6 +852,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&ProjectedUserTypesNode<'_>,
),
) {
// Ascriptions correspond to user-written types like `let A::<'a>(_): A<'static> = ...;`.
//
// Caution: Pushing user types here is load-bearing even for
// patterns containing no bindings, to ensure that the type ends
// up represented in MIR _somewhere_.
let user_tys = match pattern.extra.as_deref() {
Some(PatExtra { ascriptions, .. }) if !ascriptions.is_empty() => {
let base_user_tys = ascriptions
.iter()
.map(|thir::Ascription { annotation, variance: _ }| {
// Note that the variance doesn't apply here, as we are tracking the effect
// of user types on any bindings contained with subpattern.
self.canonical_user_type_annotations.push(annotation.clone())
})
.collect();
&user_tys.push_user_types(base_user_tys)
}
_ => user_tys,
};

// Avoid having to write the full method name at each recursive call.
let visit_subpat = |this: &mut Self, subpat, user_tys: &_, f: &mut _| {
this.visit_primary_bindings_special(subpat, user_tys, f)
Expand Down Expand Up @@ -924,31 +917,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
visit_subpat(self, subpattern, &ProjectedUserTypesNode::None, f);
}

PatKind::AscribeUserType {
ref subpattern,
ascription: thir::Ascription { ref annotation, variance: _ },
} => {
// This corresponds to something like
//
// ```
// let A::<'a>(_): A<'static> = ...;
// ```
//
// Note that the variance doesn't apply here, as we are tracking the effect
// of `user_ty` on any bindings contained with subpattern.

// Caution: Pushing this user type here is load-bearing even for
// patterns containing no bindings, to ensure that the type ends
// up represented in MIR _somewhere_.
let base_user_ty = self.canonical_user_type_annotations.push(annotation.clone());
let subpattern_user_tys = user_tys.push_user_type(base_user_ty);
visit_subpat(self, subpattern, &subpattern_user_tys, f)
}

PatKind::ExpandedConstant { ref subpattern, .. } => {
visit_subpat(self, subpattern, user_tys, f)
}

PatKind::Leaf { ref subpatterns } => {
for subpattern in subpatterns {
let subpattern_user_tys = user_tys.leaf(subpattern.field);
Expand Down
29 changes: 19 additions & 10 deletions compiler/rustc_mir_build/src/builder/matches/user_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@ use std::assert_matches::assert_matches;
use std::iter;

use rustc_abi::{FieldIdx, VariantIdx};
use rustc_data_structures::smallvec::SmallVec;
use rustc_middle::mir::{ProjectionElem, UserTypeProjection, UserTypeProjections};
use rustc_middle::ty::{AdtDef, UserTypeAnnotationIndex};
use rustc_span::Symbol;

/// A single `thir::Pat` node should almost never have more than 0-2 user types.
/// We can store up to 4 inline in the same size as an ordinary `Vec`.
pub(crate) type UserTypeIndices = SmallVec<[UserTypeAnnotationIndex; 4]>;

/// One of a list of "operations" that can be used to lazily build projections
/// of user-specified types.
#[derive(Clone, Debug)]
#[derive(Debug)]
pub(crate) enum ProjectedUserTypesOp {
PushUserType { base: UserTypeAnnotationIndex },
PushUserTypes { base_types: UserTypeIndices },

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

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

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

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

let mut projections = vec![];
for op in ops_reversed.into_iter().rev() {
match op {
ProjectedUserTypesOp::PushUserType { base } => {
projections.push(UserTypeProjection { base, projs: vec![] })
match *op {
ProjectedUserTypesOp::PushUserTypes { ref base_types } => {
assert!(!base_types.is_empty());
for &base in base_types {
projections.push(UserTypeProjection { base, projs: vec![] })
}
}

ProjectedUserTypesOp::Index => {
Expand Down
Loading
Loading