Skip to content

Commit 10f42cb

Browse files
committed
Auto merge of #55274 - pnkfelix:issue-54570-proj-path-into-pats-with-type-take-2, r=nikomatsakis
Handle bindings in substructure of patterns with type ascriptions This attempts to follow the outline described by @nikomatsakis [here](#47184 (comment)). Its a bit more complicated than expected for two reasons: 1. In general it handles sets of type ascriptions, because such ascriptions can be nested within patterns 2. It has a separate types in the HAIR, `PatternTypeProjections` and `PatternTypeProjection`, which are analogues to the corresponding types in the MIR. The main reason I added the new HAIR types was because I am worried that the current implementation is inefficent, and asymptotically so: It makes copies of vectors as it descends the patterns, even when those accumulated vectors are never used. Longer term, I would like to used a linked tree structure for the `PatternTypeProjections` and `PatternTypeProjection`, and save the construction of standalone vectors for the MIR types. I didn't want to block landing this on that hypoethetical revision; but I figured I could at least make the future change easier by differentiating between the two types now. Oh, one more thing: This doesn't attempt to handle `ref x` (in terms of ensuring that any necessary types are ascribed to `x` in that scenario as well). We should open an issue to investigate supporting that as well. But I didn't want to block this PR on that future work. Fix #54570
2 parents fa45602 + 639a3ff commit 10f42cb

File tree

29 files changed

+622
-127
lines changed

29 files changed

+622
-127
lines changed

src/librustc/ich/impls_mir.rs

+3
Original file line numberDiff line numberDiff line change
@@ -606,3 +606,6 @@ impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::UserTypeAnnotation<
606606
}
607607
}
608608
}
609+
610+
impl_stable_hash_for!(struct mir::UserTypeProjection<'tcx> { base, projs });
611+
impl_stable_hash_for!(struct mir::UserTypeProjections<'tcx> { contents });

src/librustc/mir/mod.rs

+119-4
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,7 @@ pub struct LocalDecl<'tcx> {
710710
/// e.g. via `let x: T`, then we carry that type here. The MIR
711711
/// borrow checker needs this information since it can affect
712712
/// region inference.
713-
pub user_ty: Option<(UserTypeAnnotation<'tcx>, Span)>,
713+
pub user_ty: UserTypeProjections<'tcx>,
714714

715715
/// Name of the local, used in debuginfo and pretty-printing.
716716
///
@@ -882,7 +882,7 @@ impl<'tcx> LocalDecl<'tcx> {
882882
LocalDecl {
883883
mutability,
884884
ty,
885-
user_ty: None,
885+
user_ty: UserTypeProjections::none(),
886886
name: None,
887887
source_info: SourceInfo {
888888
span,
@@ -903,7 +903,7 @@ impl<'tcx> LocalDecl<'tcx> {
903903
LocalDecl {
904904
mutability: Mutability::Mut,
905905
ty: return_ty,
906-
user_ty: None,
906+
user_ty: UserTypeProjections::none(),
907907
source_info: SourceInfo {
908908
span,
909909
scope: OUTERMOST_SOURCE_SCOPE,
@@ -1741,7 +1741,7 @@ pub enum StatementKind<'tcx> {
17411741
/// - `Contravariant` -- requires that `T_y :> T`
17421742
/// - `Invariant` -- requires that `T_y == T`
17431743
/// - `Bivariant` -- no effect
1744-
AscribeUserType(Place<'tcx>, ty::Variance, Box<UserTypeAnnotation<'tcx>>),
1744+
AscribeUserType(Place<'tcx>, ty::Variance, Box<UserTypeProjection<'tcx>>),
17451745

17461746
/// No-op. Useful for deleting instructions without affecting statement indices.
17471747
Nop,
@@ -1944,6 +1944,10 @@ pub type PlaceProjection<'tcx> = Projection<'tcx, Place<'tcx>, Local, Ty<'tcx>>;
19441944
/// and the index is a local.
19451945
pub type PlaceElem<'tcx> = ProjectionElem<'tcx, Local, Ty<'tcx>>;
19461946

1947+
/// Alias for projections as they appear in `UserTypeProjection`, where we
1948+
/// need neither the `V` parameter for `Index` nor the `T` for `Field`.
1949+
pub type ProjectionKind<'tcx> = ProjectionElem<'tcx, (), ()>;
1950+
19471951
newtype_index! {
19481952
pub struct Field {
19491953
DEBUG_FORMAT = "field[{}]"
@@ -2449,6 +2453,117 @@ EnumLiftImpl! {
24492453
}
24502454
}
24512455

2456+
/// A collection of projections into user types.
2457+
///
2458+
/// They are projections because a binding can occur a part of a
2459+
/// parent pattern that has been ascribed a type.
2460+
///
2461+
/// Its a collection because there can be multiple type ascriptions on
2462+
/// the path from the root of the pattern down to the binding itself.
2463+
///
2464+
/// An example:
2465+
///
2466+
/// ```rust
2467+
/// struct S<'a>((i32, &'a str), String);
2468+
/// let S((_, w): (i32, &'static str), _): S = ...;
2469+
/// // ------ ^^^^^^^^^^^^^^^^^^^ (1)
2470+
/// // --------------------------------- ^ (2)
2471+
/// ```
2472+
///
2473+
/// The highlights labelled `(1)` show the subpattern `(_, w)` being
2474+
/// ascribed the type `(i32, &'static str)`.
2475+
///
2476+
/// The highlights labelled `(2)` show the whole pattern being
2477+
/// ascribed the type `S`.
2478+
///
2479+
/// In this example, when we descend to `w`, we will have built up the
2480+
/// following two projected types:
2481+
///
2482+
/// * base: `S`, projection: `(base.0).1`
2483+
/// * base: `(i32, &'static str)`, projection: `base.1`
2484+
///
2485+
/// The first will lead to the constraint `w: &'1 str` (for some
2486+
/// inferred region `'1`). The second will lead to the constraint `w:
2487+
/// &'static str`.
2488+
#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
2489+
pub struct UserTypeProjections<'tcx> {
2490+
pub(crate) contents: Vec<(UserTypeProjection<'tcx>, Span)>,
2491+
}
2492+
2493+
BraceStructTypeFoldableImpl! {
2494+
impl<'tcx> TypeFoldable<'tcx> for UserTypeProjections<'tcx> {
2495+
contents
2496+
}
2497+
}
2498+
2499+
impl<'tcx> UserTypeProjections<'tcx> {
2500+
pub fn none() -> Self {
2501+
UserTypeProjections { contents: vec![] }
2502+
}
2503+
2504+
pub fn from_projections(projs: impl Iterator<Item=(UserTypeProjection<'tcx>, Span)>) -> Self {
2505+
UserTypeProjections { contents: projs.collect() }
2506+
}
2507+
2508+
pub fn projections_and_spans(&self) -> impl Iterator<Item=&(UserTypeProjection<'tcx>, Span)> {
2509+
self.contents.iter()
2510+
}
2511+
2512+
pub fn projections(&self) -> impl Iterator<Item=&UserTypeProjection<'tcx>> {
2513+
self.contents.iter().map(|&(ref user_type, _span)| user_type)
2514+
}
2515+
}
2516+
2517+
/// Encodes the effect of a user-supplied type annotation on the
2518+
/// subcomponents of a pattern. The effect is determined by applying the
2519+
/// given list of proejctions to some underlying base type. Often,
2520+
/// the projection element list `projs` is empty, in which case this
2521+
/// directly encodes a type in `base`. But in the case of complex patterns with
2522+
/// subpatterns and bindings, we want to apply only a *part* of the type to a variable,
2523+
/// in which case the `projs` vector is used.
2524+
///
2525+
/// Examples:
2526+
///
2527+
/// * `let x: T = ...` -- here, the `projs` vector is empty.
2528+
///
2529+
/// * `let (x, _): T = ...` -- here, the `projs` vector would contain
2530+
/// `field[0]` (aka `.0`), indicating that the type of `s` is
2531+
/// determined by finding the type of the `.0` field from `T`.
2532+
#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
2533+
pub struct UserTypeProjection<'tcx> {
2534+
pub base: UserTypeAnnotation<'tcx>,
2535+
pub projs: Vec<ProjectionElem<'tcx, (), ()>>,
2536+
}
2537+
2538+
impl<'tcx> Copy for ProjectionKind<'tcx> { }
2539+
2540+
CloneTypeFoldableAndLiftImpls! { ProjectionKind<'tcx>, }
2541+
2542+
impl<'tcx> TypeFoldable<'tcx> for UserTypeProjection<'tcx> {
2543+
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
2544+
use mir::ProjectionElem::*;
2545+
2546+
let base = self.base.fold_with(folder);
2547+
let projs: Vec<_> = self.projs
2548+
.iter()
2549+
.map(|elem| {
2550+
match elem {
2551+
Deref => Deref,
2552+
Field(f, ()) => Field(f.clone(), ()),
2553+
Index(()) => Index(()),
2554+
elem => elem.clone(),
2555+
}})
2556+
.collect();
2557+
2558+
UserTypeProjection { base, projs }
2559+
}
2560+
2561+
fn super_visit_with<Vs: TypeVisitor<'tcx>>(&self, visitor: &mut Vs) -> bool {
2562+
self.base.visit_with(visitor)
2563+
// Note: there's nothing in `self.proj` to visit.
2564+
}
2565+
}
2566+
24522567
newtype_index! {
24532568
pub struct Promoted {
24542569
DEBUG_FORMAT = "promoted[{}]"

src/librustc/mir/tcx.rs

+53-3
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,59 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> {
4444
}
4545
}
4646

47+
/// `place_ty.field_ty(tcx, f)` computes the type at a given field
48+
/// of a record or enum-variant. (Most clients of `PlaceTy` can
49+
/// instead just extract the relevant type directly from their
50+
/// `PlaceElem`, but some instances of `ProjectionElem<V, T>` do
51+
/// not carry a `Ty` for `T`.)
52+
///
53+
/// Note that the resulting type has not been normalized.
54+
pub fn field_ty(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, f: &Field) -> Ty<'tcx>
55+
{
56+
// Pass `0` here so it can be used as a "default" variant_index in first arm below
57+
let answer = match (self, 0) {
58+
(PlaceTy::Ty {
59+
ty: &ty::TyS { sty: ty::TyKind::Adt(adt_def, substs), .. } }, variant_index) |
60+
(PlaceTy::Downcast { adt_def, substs, variant_index }, _) => {
61+
let variant_def = &adt_def.variants[variant_index];
62+
let field_def = &variant_def.fields[f.index()];
63+
field_def.ty(tcx, substs)
64+
}
65+
(PlaceTy::Ty { ty }, _) => {
66+
match ty.sty {
67+
ty::Tuple(ref tys) => tys[f.index()],
68+
_ => bug!("extracting field of non-tuple non-adt: {:?}", self),
69+
}
70+
}
71+
};
72+
debug!("field_ty self: {:?} f: {:?} yields: {:?}", self, f, answer);
73+
answer
74+
}
75+
76+
/// Convenience wrapper around `projection_ty_core` for
77+
/// `PlaceElem`, where we can just use the `Ty` that is already
78+
/// stored inline on field projection elems.
4779
pub fn projection_ty(self, tcx: TyCtxt<'a, 'gcx, 'tcx>,
4880
elem: &PlaceElem<'tcx>)
4981
-> PlaceTy<'tcx>
5082
{
51-
match *elem {
83+
self.projection_ty_core(tcx, elem, |_, _, ty| ty)
84+
}
85+
86+
/// `place_ty.projection_ty_core(tcx, elem, |...| { ... })`
87+
/// projects `place_ty` onto `elem`, returning the appropriate
88+
/// `Ty` or downcast variant corresponding to that projection.
89+
/// The `handle_field` callback must map a `Field` to its `Ty`,
90+
/// (which should be trivial when `T` = `Ty`).
91+
pub fn projection_ty_core<V, T>(self,
92+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
93+
elem: &ProjectionElem<'tcx, V, T>,
94+
mut handle_field: impl FnMut(&Self, &Field, &T) -> Ty<'tcx>)
95+
-> PlaceTy<'tcx>
96+
where
97+
V: ::std::fmt::Debug, T: ::std::fmt::Debug
98+
{
99+
let answer = match *elem {
52100
ProjectionElem::Deref => {
53101
let ty = self.to_ty(tcx)
54102
.builtin_deref(true)
@@ -94,8 +142,10 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> {
94142
bug!("cannot downcast non-ADT type: `{:?}`", self)
95143
}
96144
},
97-
ProjectionElem::Field(_, fty) => PlaceTy::Ty { ty: fty }
98-
}
145+
ProjectionElem::Field(ref f, ref fty) => PlaceTy::Ty { ty: handle_field(&self, f, fty) }
146+
};
147+
debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
148+
answer
99149
}
100150
}
101151

src/librustc/mir/visit.rs

+25-9
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ macro_rules! make_mir_visitor {
147147
fn visit_ascribe_user_ty(&mut self,
148148
place: & $($mutability)* Place<'tcx>,
149149
variance: & $($mutability)* ty::Variance,
150-
user_ty: & $($mutability)* UserTypeAnnotation<'tcx>,
150+
user_ty: & $($mutability)* UserTypeProjection<'tcx>,
151151
location: Location) {
152152
self.super_ascribe_user_ty(place, variance, user_ty, location);
153153
}
@@ -175,9 +175,8 @@ macro_rules! make_mir_visitor {
175175

176176
fn visit_projection_elem(&mut self,
177177
place: & $($mutability)* PlaceElem<'tcx>,
178-
context: PlaceContext<'tcx>,
179178
location: Location) {
180-
self.super_projection_elem(place, context, location);
179+
self.super_projection_elem(place, location);
181180
}
182181

183182
fn visit_branch(&mut self,
@@ -214,6 +213,13 @@ macro_rules! make_mir_visitor {
214213
self.super_ty(ty);
215214
}
216215

216+
fn visit_user_type_projection(
217+
&mut self,
218+
ty: & $($mutability)* UserTypeProjection<'tcx>,
219+
) {
220+
self.super_user_type_projection(ty);
221+
}
222+
217223
fn visit_user_type_annotation(
218224
&mut self,
219225
ty: & $($mutability)* UserTypeAnnotation<'tcx>,
@@ -640,10 +646,10 @@ macro_rules! make_mir_visitor {
640646
fn super_ascribe_user_ty(&mut self,
641647
place: & $($mutability)* Place<'tcx>,
642648
_variance: & $($mutability)* ty::Variance,
643-
user_ty: & $($mutability)* UserTypeAnnotation<'tcx>,
649+
user_ty: & $($mutability)* UserTypeProjection<'tcx>,
644650
location: Location) {
645651
self.visit_place(place, PlaceContext::Validate, location);
646-
self.visit_user_type_annotation(user_ty);
652+
self.visit_user_type_projection(user_ty);
647653
}
648654

649655
fn super_place(&mut self,
@@ -692,12 +698,11 @@ macro_rules! make_mir_visitor {
692698
PlaceContext::Projection(Mutability::Not)
693699
};
694700
self.visit_place(base, context, location);
695-
self.visit_projection_elem(elem, context, location);
701+
self.visit_projection_elem(elem, location);
696702
}
697703

698704
fn super_projection_elem(&mut self,
699705
proj: & $($mutability)* PlaceElem<'tcx>,
700-
_context: PlaceContext<'tcx>,
701706
location: Location) {
702707
match *proj {
703708
ProjectionElem::Deref => {
@@ -738,8 +743,8 @@ macro_rules! make_mir_visitor {
738743
local,
739744
source_info: *source_info,
740745
});
741-
if let Some((user_ty, _)) = user_ty {
742-
self.visit_user_type_annotation(user_ty);
746+
for (user_ty, _) in & $($mutability)* user_ty.contents {
747+
self.visit_user_type_projection(user_ty);
743748
}
744749
self.visit_source_info(source_info);
745750
self.visit_source_scope(visibility_scope);
@@ -786,6 +791,17 @@ macro_rules! make_mir_visitor {
786791
self.visit_source_scope(scope);
787792
}
788793

794+
fn super_user_type_projection(
795+
&mut self,
796+
ty: & $($mutability)* UserTypeProjection<'tcx>,
797+
) {
798+
let UserTypeProjection {
799+
ref $($mutability)* base,
800+
projs: _, // Note: Does not visit projection elems!
801+
} = *ty;
802+
self.visit_user_type_annotation(base);
803+
}
804+
789805
fn super_user_type_annotation(
790806
&mut self,
791807
_ty: & $($mutability)* UserTypeAnnotation<'tcx>,

src/librustc/traits/query/type_op/ascribe_user_type.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResponse, QueryResponse};
1212
use traits::query::Fallible;
1313
use hir::def_id::DefId;
14+
use mir::ProjectionKind;
1415
use ty::{self, ParamEnvAnd, Ty, TyCtxt};
1516
use ty::subst::UserSubsts;
1617

@@ -20,6 +21,7 @@ pub struct AscribeUserType<'tcx> {
2021
pub variance: ty::Variance,
2122
pub def_id: DefId,
2223
pub user_substs: UserSubsts<'tcx>,
24+
pub projs: &'tcx ty::List<ProjectionKind<'tcx>>,
2325
}
2426

2527
impl<'tcx> AscribeUserType<'tcx> {
@@ -28,8 +30,9 @@ impl<'tcx> AscribeUserType<'tcx> {
2830
variance: ty::Variance,
2931
def_id: DefId,
3032
user_substs: UserSubsts<'tcx>,
33+
projs: &'tcx ty::List<ProjectionKind<'tcx>>,
3134
) -> Self {
32-
AscribeUserType { mir_ty, variance, def_id, user_substs }
35+
AscribeUserType { mir_ty, variance, def_id, user_substs, projs }
3336
}
3437
}
3538

@@ -59,19 +62,19 @@ impl<'gcx: 'tcx, 'tcx> super::QueryTypeOp<'gcx, 'tcx> for AscribeUserType<'tcx>
5962

6063
BraceStructTypeFoldableImpl! {
6164
impl<'tcx> TypeFoldable<'tcx> for AscribeUserType<'tcx> {
62-
mir_ty, variance, def_id, user_substs
65+
mir_ty, variance, def_id, user_substs, projs
6366
}
6467
}
6568

6669
BraceStructLiftImpl! {
6770
impl<'a, 'tcx> Lift<'tcx> for AscribeUserType<'a> {
6871
type Lifted = AscribeUserType<'tcx>;
69-
mir_ty, variance, def_id, user_substs
72+
mir_ty, variance, def_id, user_substs, projs
7073
}
7174
}
7275

7376
impl_stable_hash_for! {
7477
struct AscribeUserType<'tcx> {
75-
mir_ty, variance, def_id, user_substs
78+
mir_ty, variance, def_id, user_substs, projs
7679
}
7780
}

0 commit comments

Comments
 (0)