From 3c6f41026bea2a99ab09d15f3dbed838af1ebcac Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 11 Feb 2016 18:31:42 +0200 Subject: [PATCH] store the normalized types of field accesses Fixes #31504 --- src/librustc/middle/ty/context.rs | 7 ++++ src/librustc/mir/repr.rs | 10 ++--- src/librustc/mir/tcx.rs | 18 +------- src/librustc_mir/build/expr/as_lvalue.rs | 2 +- src/librustc_mir/build/expr/as_rvalue.rs | 31 +++++++------- src/librustc_mir/build/matches/test.rs | 3 +- src/librustc_mir/build/matches/util.rs | 3 +- src/librustc_mir/hair/cx/expr.rs | 23 +++++++++-- src/librustc_mir/hair/cx/pattern.rs | 17 ++++++++ src/librustc_mir/hair/mod.rs | 8 +++- src/librustc_mir/transform/type_check.rs | 1 + src/librustc_trans/trans/mir/lvalue.rs | 2 +- src/librustc_typeck/check/mod.rs | 23 ++++++++--- src/librustc_typeck/check/writeback.rs | 17 +++++++- src/test/run-pass/mir_struct_with_assoc_ty.rs | 41 +++++++++++++++++++ 15 files changed, 150 insertions(+), 56 deletions(-) create mode 100644 src/test/run-pass/mir_struct_with_assoc_ty.rs diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs index 1d071cd604db4..ea6b4df8104d5 100644 --- a/src/librustc/middle/ty/context.rs +++ b/src/librustc/middle/ty/context.rs @@ -131,6 +131,12 @@ pub struct Tables<'tcx> { /// equivalents. This table is not used in trans (since regions /// are erased there) and hence is not serialized to metadata. pub liberated_fn_sigs: NodeMap>, + + /// For each FRU expression, record the normalized types of the fields + /// of the struct - this is needed because it is non-trivial to + /// normalize while preserving regions. This table is used only in + /// MIR construction and hence is not serialized to metadata. + pub fru_field_types: NodeMap>> } impl<'tcx> Tables<'tcx> { @@ -144,6 +150,7 @@ impl<'tcx> Tables<'tcx> { closure_tys: DefIdMap(), closure_kinds: DefIdMap(), liberated_fn_sigs: NodeMap(), + fru_field_types: NodeMap() } } diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 2f24a1a8962de..ce7b1ceb35540 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -502,7 +502,7 @@ pub struct Projection<'tcx, B, V> { #[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] pub enum ProjectionElem<'tcx, V> { Deref, - Field(Field), + Field(Field, Ty<'tcx>), Index(V), /// These indices are generated by slice patterns. Easiest to explain @@ -553,8 +553,8 @@ impl Field { } impl<'tcx> Lvalue<'tcx> { - pub fn field(self, f: Field) -> Lvalue<'tcx> { - self.elem(ProjectionElem::Field(f)) + pub fn field(self, f: Field, ty: Ty<'tcx>) -> Lvalue<'tcx> { + self.elem(ProjectionElem::Field(f, ty)) } pub fn deref(self) -> Lvalue<'tcx> { @@ -594,8 +594,8 @@ impl<'tcx> Debug for Lvalue<'tcx> { write!(fmt, "({:?} as {})", data.base, adt_def.variants[index].name), ProjectionElem::Deref => write!(fmt, "(*{:?})", data.base), - ProjectionElem::Field(field) => - write!(fmt, "{:?}.{:?}", data.base, field.index()), + ProjectionElem::Field(field, ty) => + write!(fmt, "({:?}.{:?}: {:?})", data.base, field.index(), ty), ProjectionElem::Index(ref index) => write!(fmt, "{:?}[{:?}]", data.base, index), ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index e8b83962b521b..20e083f840f8c 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -73,23 +73,7 @@ impl<'tcx> LvalueTy<'tcx> { tcx.sess.bug(&format!("cannot downcast non-enum type: `{:?}`", self)) } }, - ProjectionElem::Field(field) => { - let field_ty = match self { - LvalueTy::Ty { ty } => match ty.sty { - ty::TyStruct(adt_def, substs) => - adt_def.struct_variant().fields[field.index()].ty(tcx, substs), - ty::TyTuple(ref tys) => - tys[field.index()], - ty::TyClosure(_, ref closure_substs) => - closure_substs.upvar_tys[field.index()], - _ => - tcx.sess.bug(&format!("cannot get field of type: `{:?}`", ty)), - }, - LvalueTy::Downcast { adt_def, substs, variant_index } => - adt_def.variants[variant_index].fields[field.index()].ty(tcx, substs), - }; - LvalueTy::Ty { ty: field_ty } - } + ProjectionElem::Field(_, fty) => LvalueTy::Ty { ty: fty } } } } diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs index 4e03ed489eb9f..b2c7507ed7b28 100644 --- a/src/librustc_mir/build/expr/as_lvalue.rs +++ b/src/librustc_mir/build/expr/as_lvalue.rs @@ -41,7 +41,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { } ExprKind::Field { lhs, name } => { let lvalue = unpack!(block = this.as_lvalue(block, lhs)); - let lvalue = lvalue.field(name); + let lvalue = lvalue.field(name, expr.ty); block.and(lvalue) } ExprKind::Deref { arg } => { diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index dea2d750b981b..53b106d6d8679 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -139,7 +139,9 @@ impl<'a,'tcx> Builder<'a,'tcx> { .collect(); block.and(Rvalue::Aggregate(AggregateKind::Closure(closure_id, substs), upvars)) } - ExprKind::Adt { adt_def, variant_index, substs, fields, base } => { // see (*) above + ExprKind::Adt { + adt_def, variant_index, substs, fields, base + } => { // see (*) above // first process the set of fields that were provided // (evaluating them in order given by user) let fields_map: FnvHashMap<_, _> = @@ -147,25 +149,20 @@ impl<'a,'tcx> Builder<'a,'tcx> { .map(|f| (f.name, unpack!(block = this.as_operand(block, f.expr)))) .collect(); - // if base expression is given, evaluate it now - let base = base.map(|base| unpack!(block = this.as_lvalue(block, base))); - - // get list of all fields that we will need let field_names = this.hir.all_fields(adt_def, variant_index); - // for the actual values we use, take either the - // expr the user specified or, if they didn't - // specify something for this field name, create a - // path relative to the base (which must have been - // supplied, or the IR is internally - // inconsistent). - let fields: Vec<_> = + let fields = if let Some(FruInfo { base, field_types }) = base { + let base = unpack!(block = this.as_lvalue(block, base)); field_names.into_iter() - .map(|n| match fields_map.get(&n) { - Some(v) => v.clone(), - None => Operand::Consume(base.clone().unwrap().field(n)), - }) - .collect(); + .zip(field_types.into_iter()) + .map(|(n, ty)| match fields_map.get(&n) { + Some(v) => v.clone(), + None => Operand::Consume(base.clone().field(n, ty)) + }) + .collect() + } else { + field_names.iter().map(|n| fields_map[n].clone()).collect() + }; block.and(Rvalue::Aggregate(AggregateKind::Adt(adt_def, variant_index, substs), fields)) diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index ec67429379f95..02f32da2b83fc 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -404,7 +404,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { subpatterns.iter() .map(|subpattern| { // e.g., `(x as Variant).0` - let lvalue = downcast_lvalue.clone().field(subpattern.field); + let lvalue = downcast_lvalue.clone().field(subpattern.field, + subpattern.field_ty()); // e.g., `(x as Variant).0 @ P1` MatchPair::new(lvalue, &subpattern.pattern) }); diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs index dbb00a13cd3b4..c295ed168badb 100644 --- a/src/librustc_mir/build/matches/util.rs +++ b/src/librustc_mir/build/matches/util.rs @@ -21,7 +21,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { -> Vec> { subpatterns.iter() .map(|fieldpat| { - let lvalue = lvalue.clone().field(fieldpat.field); + let lvalue = lvalue.clone().field(fieldpat.field, + fieldpat.field_ty()); MatchPair::new(lvalue, &fieldpat.pattern) }) .collect() diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 4a91f2b66a1eb..facc2541652e8 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -248,13 +248,23 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { variant_index: 0, substs: substs, fields: field_refs, - base: base.to_ref(), + base: base.as_ref().map(|base| { + FruInfo { + base: base.to_ref(), + field_types: cx.tcx.tables + .borrow() + .fru_field_types[&self.id] + .clone() + } + }) } } ty::TyEnum(adt, substs) => { match cx.tcx.def_map.borrow()[&self.id].full_def() { Def::Variant(enum_id, variant_id) => { debug_assert!(adt.did == enum_id); + assert!(base.is_none()); + let index = adt.variant_index_with_id(variant_id); let field_refs = field_refs(&adt.variants[index], fields); ExprKind::Adt { @@ -262,7 +272,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { variant_index: index, substs: substs, fields: field_refs, - base: base.to_ref(), + base: None } } ref def => { @@ -810,11 +820,16 @@ fn convert_var<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, }; match upvar_capture { ty::UpvarCapture::ByValue => field_kind, - ty::UpvarCapture::ByRef(_) => { + ty::UpvarCapture::ByRef(borrow) => { ExprKind::Deref { arg: Expr { temp_lifetime: temp_lifetime, - ty: var_ty, + ty: cx.tcx.mk_ref( + cx.tcx.mk_region(borrow.region), + ty::TypeAndMut { + ty: var_ty, + mutbl: borrow.kind.to_mutbl_lossy() + }), span: expr.span, kind: field_kind, }.to_ref() diff --git a/src/librustc_mir/hair/cx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs index c5b34d9246694..6f4375d53ec4b 100644 --- a/src/librustc_mir/hair/cx/pattern.rs +++ b/src/librustc_mir/hair/cx/pattern.rs @@ -314,3 +314,20 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { } } } + +impl<'tcx> FieldPattern<'tcx> { + pub fn field_ty(&self) -> Ty<'tcx> { + debug!("field_ty({:?},ty={:?})", self, self.pattern.ty); + let r = match *self.pattern.kind { + PatternKind::Binding { mode: BindingMode::ByRef(..), ..} => { + match self.pattern.ty.sty { + ty::TyRef(_, mt) => mt.ty, + _ => unreachable!() + } + } + _ => self.pattern.ty + }; + debug!("field_ty -> {:?}", r); + r + } +} diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 0891c2845b0ba..e8edd4067e2f8 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -229,7 +229,7 @@ pub enum ExprKind<'tcx> { variant_index: usize, substs: &'tcx Substs<'tcx>, fields: Vec>, - base: Option>, + base: Option> }, Closure { closure_id: DefId, @@ -256,6 +256,12 @@ pub struct FieldExprRef<'tcx> { pub expr: ExprRef<'tcx>, } +#[derive(Clone, Debug)] +pub struct FruInfo<'tcx> { + pub base: ExprRef<'tcx>, + pub field_types: Vec> +} + #[derive(Clone, Debug)] pub struct Arm<'tcx> { pub patterns: Vec>, diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index f4d83cd05e08f..8f4d452df51fa 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -47,6 +47,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> { fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: visit::LvalueContext) { self.super_lvalue(lvalue, context); + debug!("visiting lvalue {:?}", lvalue); let lv_ty = self.mir.lvalue_ty(self.tcx(), lvalue).to_ty(self.tcx()); self.sanitize_type(lvalue, lv_ty); } diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs index 002584f51c6d7..826fb025bc1f7 100644 --- a/src/librustc_trans/trans/mir/lvalue.rs +++ b/src/librustc_trans/trans/mir/lvalue.rs @@ -126,7 +126,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } }) } - mir::ProjectionElem::Field(ref field) => { + mir::ProjectionElem::Field(ref field, _) => { let base_ty = tr_base.ty.to_ty(tcx); let base_repr = adt::represent_type(ccx, base_ty); let discr = match tr_base.ty { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 3768a2e30337e..7ab4975c8b8ae 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3179,8 +3179,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, check_struct_fields_on_error(fcx, expr.id, fields, base_expr); return; } - let (adt, variant) = match fcx.def_struct_variant(def, path.span) { - Some((adt, variant)) => (adt, variant), + let variant = match fcx.def_struct_variant(def, path.span) { + Some((_, variant)) => variant, None => { span_err!(fcx.tcx().sess, path.span, E0071, "`{}` does not name a structure", @@ -3195,12 +3195,23 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, check_expr_struct_fields(fcx, expr_ty, expr.span, variant, fields, base_expr.is_none()); - if let &Some(ref base_expr) = base_expr { check_expr_has_type(fcx, base_expr, expr_ty); - if adt.adt_kind() == ty::AdtKind::Enum { - span_err!(tcx.sess, base_expr.span, E0436, - "functional record update syntax requires a struct"); + match expr_ty.sty { + ty::TyStruct(adt, substs) => { + fcx.inh.tables.borrow_mut().fru_field_types.insert( + expr.id, + adt.struct_variant().fields.iter().map(|f| { + fcx.normalize_associated_types_in( + expr.span, &f.ty(tcx, substs) + ) + }).collect() + ); + } + _ => { + span_err!(tcx.sess, base_expr.span, E0436, + "functional record update syntax requires a struct"); + } } } } diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 5135323979034..9b8b6dedb6345 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -43,6 +43,7 @@ pub fn resolve_type_vars_in_expr(fcx: &FnCtxt, e: &hir::Expr) { wbcx.visit_upvar_borrow_map(); wbcx.visit_closures(); wbcx.visit_liberated_fn_sigs(); + wbcx.visit_fru_field_types(); } pub fn resolve_type_vars_in_fn(fcx: &FnCtxt, @@ -64,6 +65,7 @@ pub fn resolve_type_vars_in_fn(fcx: &FnCtxt, wbcx.visit_upvar_borrow_map(); wbcx.visit_closures(); wbcx.visit_liberated_fn_sigs(); + wbcx.visit_fru_field_types(); } /////////////////////////////////////////////////////////////////////////// @@ -371,6 +373,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + fn visit_fru_field_types(&self) { + for (&node_id, ftys) in self.fcx.inh.tables.borrow().fru_field_types.iter() { + let ftys = self.resolve(ftys, ResolvingFieldTypes(node_id)); + self.tcx().tables.borrow_mut().fru_field_types.insert(node_id, ftys); + } + } + fn resolve>(&self, t: &T, reason: ResolveReason) -> T { t.fold_with(&mut Resolver::new(self.fcx, reason)) } @@ -387,6 +396,7 @@ enum ResolveReason { ResolvingUpvar(ty::UpvarId), ResolvingClosure(DefId), ResolvingFnSig(ast::NodeId), + ResolvingFieldTypes(ast::NodeId) } impl ResolveReason { @@ -401,6 +411,9 @@ impl ResolveReason { ResolvingFnSig(id) => { tcx.map.span(id) } + ResolvingFieldTypes(id) => { + tcx.map.span(id) + } ResolvingClosure(did) => { if let Some(node_id) = tcx.map.as_local_node_id(did) { tcx.expr_span(node_id) @@ -478,14 +491,14 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { "cannot determine a type for this closure") } - ResolvingFnSig(id) => { + ResolvingFnSig(id) | ResolvingFieldTypes(id) => { // any failures here should also fail when // resolving the patterns, closure types, or // something else. let span = self.reason.span(self.tcx); self.tcx.sess.delay_span_bug( span, - &format!("cannot resolve some aspect of fn sig for {:?}", id)); + &format!("cannot resolve some aspect of data for {:?}", id)); } } } diff --git a/src/test/run-pass/mir_struct_with_assoc_ty.rs b/src/test/run-pass/mir_struct_with_assoc_ty.rs new file mode 100644 index 0000000000000..1f75369b94a86 --- /dev/null +++ b/src/test/run-pass/mir_struct_with_assoc_ty.rs @@ -0,0 +1,41 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +use std::marker::PhantomData; + +pub trait DataBind { + type Data; +} + +impl DataBind for Global { + type Data = T; +} + +pub struct Global(PhantomData); + +pub struct Data { + pub offsets: as DataBind>::Data, +} + +#[rustc_mir] +fn create_data() -> Data { + let mut d = Data { offsets: [1, 2] }; + d.offsets[0] = 3; + d +} + + +fn main() { + let d = create_data(); + assert_eq!(d.offsets[0], 3); + assert_eq!(d.offsets[1], 2); +}