Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle bindings in substructure of patterns with type ascriptions #55274

Merged
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
3 changes: 3 additions & 0 deletions src/librustc/ich/impls_mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,3 +606,6 @@ impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::UserTypeAnnotation<
}
}
}

impl_stable_hash_for!(struct mir::UserTypeProjection<'tcx> { base, projs });
impl_stable_hash_for!(struct mir::UserTypeProjections<'tcx> { contents });
123 changes: 119 additions & 4 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,7 @@ pub struct LocalDecl<'tcx> {
/// e.g. via `let x: T`, then we carry that type here. The MIR
/// borrow checker needs this information since it can affect
/// region inference.
pub user_ty: Option<(UserTypeAnnotation<'tcx>, Span)>,
pub user_ty: UserTypeProjections<'tcx>,

/// Name of the local, used in debuginfo and pretty-printing.
///
Expand Down Expand Up @@ -882,7 +882,7 @@ impl<'tcx> LocalDecl<'tcx> {
LocalDecl {
mutability,
ty,
user_ty: None,
user_ty: UserTypeProjections::none(),
name: None,
source_info: SourceInfo {
span,
Expand All @@ -903,7 +903,7 @@ impl<'tcx> LocalDecl<'tcx> {
LocalDecl {
mutability: Mutability::Mut,
ty: return_ty,
user_ty: None,
user_ty: UserTypeProjections::none(),
source_info: SourceInfo {
span,
scope: OUTERMOST_SOURCE_SCOPE,
Expand Down Expand Up @@ -1741,7 +1741,7 @@ pub enum StatementKind<'tcx> {
/// - `Contravariant` -- requires that `T_y :> T`
/// - `Invariant` -- requires that `T_y == T`
/// - `Bivariant` -- no effect
AscribeUserType(Place<'tcx>, ty::Variance, Box<UserTypeAnnotation<'tcx>>),
AscribeUserType(Place<'tcx>, ty::Variance, Box<UserTypeProjection<'tcx>>),

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

/// Alias for projections as they appear in `UserTypeProjection`, where we
/// need neither the `V` parameter for `Index` nor the `T` for `Field`.
pub type ProjectionKind<'tcx> = ProjectionElem<'tcx, (), ()>;

newtype_index! {
pub struct Field {
DEBUG_FORMAT = "field[{}]"
Expand Down Expand Up @@ -2449,6 +2453,117 @@ EnumLiftImpl! {
}
}

/// A collection of projections into user types.
///
/// They are projections because a binding can occur a part of a
/// parent pattern that has been ascribed a type.
pnkfelix marked this conversation as resolved.
Show resolved Hide resolved
///
/// Its a collection because there can be multiple type ascriptions on
/// the path from the root of the pattern down to the binding itself.
///
/// An example:
///
/// ```rust
/// struct S<'a>((i32, &'a str), String);
/// let S((_, w): (i32, &'static str), _): S = ...;
/// // ------ ^^^^^^^^^^^^^^^^^^^ (1)
/// // --------------------------------- ^ (2)
/// ```
///
/// The highlights labelled `(1)` show the subpattern `(_, w)` being
/// ascribed the type `(i32, &'static str)`.
///
/// The highlights labelled `(2)` show the whole pattern being
/// ascribed the type `S`.
///
/// In this example, when we descend to `w`, we will have built up the
/// following two projected types:
///
/// * base: `S`, projection: `(base.0).1`
/// * base: `(i32, &'static str)`, projection: `base.1`
///
/// The first will lead to the constraint `w: &'1 str` (for some
/// inferred region `'1`). The second will lead to the constraint `w:
/// &'static str`.
#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
pub struct UserTypeProjections<'tcx> {
pub(crate) contents: Vec<(UserTypeProjection<'tcx>, Span)>,
}

BraceStructTypeFoldableImpl! {
impl<'tcx> TypeFoldable<'tcx> for UserTypeProjections<'tcx> {
contents
}
}

impl<'tcx> UserTypeProjections<'tcx> {
pub fn none() -> Self {
UserTypeProjections { contents: vec![] }
}

pub fn from_projections(projs: impl Iterator<Item=(UserTypeProjection<'tcx>, Span)>) -> Self {
UserTypeProjections { contents: projs.collect() }
}

pub fn projections_and_spans(&self) -> impl Iterator<Item=&(UserTypeProjection<'tcx>, Span)> {
self.contents.iter()
}

pub fn projections(&self) -> impl Iterator<Item=&UserTypeProjection<'tcx>> {
self.contents.iter().map(|&(ref user_type, _span)| user_type)
}
}

/// Encodes the effect of a user-supplied type annotation on the
/// subcomponents of a pattern. The effect is determined by applying the
/// given list of proejctions to some underlying base type. Often,
/// the projection element list `projs` is empty, in which case this
/// directly encodes a type in `base`. But in the case of complex patterns with
/// subpatterns and bindings, we want to apply only a *part* of the type to a variable,
/// in which case the `projs` vector is used.
///
/// Examples:
///
/// * `let x: T = ...` -- here, the `projs` vector is empty.
///
/// * `let (x, _): T = ...` -- here, the `projs` vector would contain
/// `field[0]` (aka `.0`), indicating that the type of `s` is
/// determined by finding the type of the `.0` field from `T`.
#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
pub struct UserTypeProjection<'tcx> {
pnkfelix marked this conversation as resolved.
Show resolved Hide resolved
pub base: UserTypeAnnotation<'tcx>,
pub projs: Vec<ProjectionElem<'tcx, (), ()>>,
}

impl<'tcx> Copy for ProjectionKind<'tcx> { }

CloneTypeFoldableAndLiftImpls! { ProjectionKind<'tcx>, }

impl<'tcx> TypeFoldable<'tcx> for UserTypeProjection<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
use mir::ProjectionElem::*;

let base = self.base.fold_with(folder);
let projs: Vec<_> = self.projs
.iter()
.map(|elem| {
match elem {
Deref => Deref,
Field(f, ()) => Field(f.clone(), ()),
Index(()) => Index(()),
elem => elem.clone(),
}})
.collect();

UserTypeProjection { base, projs }
}

fn super_visit_with<Vs: TypeVisitor<'tcx>>(&self, visitor: &mut Vs) -> bool {
self.base.visit_with(visitor)
// Note: there's nothing in `self.proj` to visit.
}
}

newtype_index! {
pub struct Promoted {
DEBUG_FORMAT = "promoted[{}]"
Expand Down
56 changes: 53 additions & 3 deletions src/librustc/mir/tcx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,59 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> {
}
}

/// `place_ty.field_ty(tcx, f)` computes the type at a given field
/// of a record or enum-variant. (Most clients of `PlaceTy` can
/// instead just extract the relevant type directly from their
/// `PlaceElem`, but some instances of `ProjectionElem<V, T>` do
/// not carry a `Ty` for `T`.)
///
/// Note that the resulting type has not been normalized.
pub fn field_ty(self, tcx: TyCtxt<'a, 'gcx, 'tcx>, f: &Field) -> Ty<'tcx>
{
// Pass `0` here so it can be used as a "default" variant_index in first arm below
let answer = match (self, 0) {
(PlaceTy::Ty {
ty: &ty::TyS { sty: ty::TyKind::Adt(adt_def, substs), .. } }, variant_index) |
(PlaceTy::Downcast { adt_def, substs, variant_index }, _) => {
let variant_def = &adt_def.variants[variant_index];
let field_def = &variant_def.fields[f.index()];
field_def.ty(tcx, substs)
pnkfelix marked this conversation as resolved.
Show resolved Hide resolved
}
(PlaceTy::Ty { ty }, _) => {
match ty.sty {
ty::Tuple(ref tys) => tys[f.index()],
_ => bug!("extracting field of non-tuple non-adt: {:?}", self),
}
}
};
debug!("field_ty self: {:?} f: {:?} yields: {:?}", self, f, answer);
answer
}

/// Convenience wrapper around `projection_ty_core` for
/// `PlaceElem`, where we can just use the `Ty` that is already
/// stored inline on field projection elems.
pub fn projection_ty(self, tcx: TyCtxt<'a, 'gcx, 'tcx>,
elem: &PlaceElem<'tcx>)
-> PlaceTy<'tcx>
{
match *elem {
self.projection_ty_core(tcx, elem, |_, _, ty| ty)
}

/// `place_ty.projection_ty_core(tcx, elem, |...| { ... })`
/// projects `place_ty` onto `elem`, returning the appropriate
/// `Ty` or downcast variant corresponding to that projection.
/// The `handle_field` callback must map a `Field` to its `Ty`,
/// (which should be trivial when `T` = `Ty`).
pub fn projection_ty_core<V, T>(self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
elem: &ProjectionElem<'tcx, V, T>,
mut handle_field: impl FnMut(&Self, &Field, &T) -> Ty<'tcx>)
-> PlaceTy<'tcx>
where
V: ::std::fmt::Debug, T: ::std::fmt::Debug
{
let answer = match *elem {
ProjectionElem::Deref => {
let ty = self.to_ty(tcx)
.builtin_deref(true)
Expand Down Expand Up @@ -94,8 +142,10 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> {
bug!("cannot downcast non-ADT type: `{:?}`", self)
}
},
ProjectionElem::Field(_, fty) => PlaceTy::Ty { ty: fty }
}
ProjectionElem::Field(ref f, ref fty) => PlaceTy::Ty { ty: handle_field(&self, f, fty) }
};
debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
answer
}
}

Expand Down
34 changes: 25 additions & 9 deletions src/librustc/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ macro_rules! make_mir_visitor {
fn visit_ascribe_user_ty(&mut self,
place: & $($mutability)* Place<'tcx>,
variance: & $($mutability)* ty::Variance,
user_ty: & $($mutability)* UserTypeAnnotation<'tcx>,
user_ty: & $($mutability)* UserTypeProjection<'tcx>,
location: Location) {
self.super_ascribe_user_ty(place, variance, user_ty, location);
}
Expand Down Expand Up @@ -175,9 +175,8 @@ macro_rules! make_mir_visitor {

fn visit_projection_elem(&mut self,
place: & $($mutability)* PlaceElem<'tcx>,
context: PlaceContext<'tcx>,
location: Location) {
self.super_projection_elem(place, context, location);
self.super_projection_elem(place, location);
}

fn visit_branch(&mut self,
Expand Down Expand Up @@ -214,6 +213,13 @@ macro_rules! make_mir_visitor {
self.super_ty(ty);
}

fn visit_user_type_projection(
&mut self,
ty: & $($mutability)* UserTypeProjection<'tcx>,
) {
self.super_user_type_projection(ty);
}

fn visit_user_type_annotation(
&mut self,
ty: & $($mutability)* UserTypeAnnotation<'tcx>,
Expand Down Expand Up @@ -640,10 +646,10 @@ macro_rules! make_mir_visitor {
fn super_ascribe_user_ty(&mut self,
place: & $($mutability)* Place<'tcx>,
_variance: & $($mutability)* ty::Variance,
user_ty: & $($mutability)* UserTypeAnnotation<'tcx>,
user_ty: & $($mutability)* UserTypeProjection<'tcx>,
location: Location) {
self.visit_place(place, PlaceContext::Validate, location);
self.visit_user_type_annotation(user_ty);
self.visit_user_type_projection(user_ty);
}

fn super_place(&mut self,
Expand Down Expand Up @@ -692,12 +698,11 @@ macro_rules! make_mir_visitor {
PlaceContext::Projection(Mutability::Not)
};
self.visit_place(base, context, location);
self.visit_projection_elem(elem, context, location);
self.visit_projection_elem(elem, location);
}

fn super_projection_elem(&mut self,
proj: & $($mutability)* PlaceElem<'tcx>,
_context: PlaceContext<'tcx>,
location: Location) {
match *proj {
ProjectionElem::Deref => {
Expand Down Expand Up @@ -738,8 +743,8 @@ macro_rules! make_mir_visitor {
local,
source_info: *source_info,
});
if let Some((user_ty, _)) = user_ty {
self.visit_user_type_annotation(user_ty);
for (user_ty, _) in & $($mutability)* user_ty.contents {
self.visit_user_type_projection(user_ty);
}
self.visit_source_info(source_info);
self.visit_source_scope(visibility_scope);
Expand Down Expand Up @@ -786,6 +791,17 @@ macro_rules! make_mir_visitor {
self.visit_source_scope(scope);
}

fn super_user_type_projection(
&mut self,
ty: & $($mutability)* UserTypeProjection<'tcx>,
) {
let UserTypeProjection {
ref $($mutability)* base,
projs: _, // Note: Does not visit projection elems!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any particular reason for this? I guess just because nobody has any reason to.

Copy link
Member Author

@pnkfelix pnkfelix Oct 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the existing MIR visit methods take a PlaceElem, not a ProjectionElem<(), ()>. So it would have been a bit of extra complexity in the visit API to try to support it ... and to what end? There isn't any recursive structure to visit, and you would probably want to know all about the surrounding context when inspecting any particular ProjectionElem<(), ()> within the UserTypeProjection ... so better to just do it explicitly in one's visit_user_type_projection method?

} = *ty;
self.visit_user_type_annotation(base);
}

fn super_user_type_annotation(
&mut self,
_ty: & $($mutability)* UserTypeAnnotation<'tcx>,
Expand Down
11 changes: 7 additions & 4 deletions src/librustc/traits/query/type_op/ascribe_user_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use infer::canonical::{Canonical, Canonicalized, CanonicalizedQueryResponse, QueryResponse};
use traits::query::Fallible;
use hir::def_id::DefId;
use mir::ProjectionKind;
use ty::{self, ParamEnvAnd, Ty, TyCtxt};
use ty::subst::UserSubsts;

Expand All @@ -20,6 +21,7 @@ pub struct AscribeUserType<'tcx> {
pub variance: ty::Variance,
pub def_id: DefId,
pub user_substs: UserSubsts<'tcx>,
pub projs: &'tcx ty::List<ProjectionKind<'tcx>>,
}

impl<'tcx> AscribeUserType<'tcx> {
Expand All @@ -28,8 +30,9 @@ impl<'tcx> AscribeUserType<'tcx> {
variance: ty::Variance,
def_id: DefId,
user_substs: UserSubsts<'tcx>,
projs: &'tcx ty::List<ProjectionKind<'tcx>>,
) -> Self {
AscribeUserType { mir_ty, variance, def_id, user_substs }
AscribeUserType { mir_ty, variance, def_id, user_substs, projs }
}
}

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

BraceStructTypeFoldableImpl! {
impl<'tcx> TypeFoldable<'tcx> for AscribeUserType<'tcx> {
mir_ty, variance, def_id, user_substs
mir_ty, variance, def_id, user_substs, projs
}
}

BraceStructLiftImpl! {
impl<'a, 'tcx> Lift<'tcx> for AscribeUserType<'a> {
type Lifted = AscribeUserType<'tcx>;
mir_ty, variance, def_id, user_substs
mir_ty, variance, def_id, user_substs, projs
}
}

impl_stable_hash_for! {
struct AscribeUserType<'tcx> {
mir_ty, variance, def_id, user_substs
mir_ty, variance, def_id, user_substs, projs
}
}
Loading