From 064cf553c7a9ccbb854081ce8c7e006fa0d9f81a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 7 Jan 2015 13:58:27 -0500 Subject: [PATCH 1/7] Normalize associated types in various parts of `adt` --- src/librustc_trans/trans/adt.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs index 231de71848a..2942ca2e1b5 100644 --- a/src/librustc_trans/trans/adt.rs +++ b/src/librustc_trans/trans/adt.rs @@ -51,7 +51,11 @@ use std::rc::Rc; use llvm::{ValueRef, True, IntEQ, IntNE}; use back::abi::FAT_PTR_ADDR; use middle::subst; -use middle::subst::Subst; +use middle::ty::{mod, Ty, UnboxedClosureTyper}; +use middle::ty::Disr; +use syntax::ast; +use syntax::attr; +use syntax::attr::IntType; use trans::_match; use trans::build::*; use trans::cleanup; @@ -59,13 +63,9 @@ use trans::cleanup::CleanupMethods; use trans::common::*; use trans::datum; use trans::machine; +use trans::monomorphize; use trans::type_::Type; use trans::type_of; -use middle::ty::{self, Ty, UnboxedClosureTyper}; -use middle::ty::Disr; -use syntax::ast; -use syntax::attr; -use syntax::attr::IntType; use util::ppaux::ty_to_string; type Hint = attr::ReprAttr; @@ -159,7 +159,8 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty::ty_struct(def_id, substs) => { let fields = ty::lookup_struct_fields(cx.tcx(), def_id); let mut ftys = fields.iter().map(|field| { - ty::lookup_field_type(cx.tcx(), def_id, field.id, substs) + let fty = ty::lookup_field_type(cx.tcx(), def_id, field.id, substs); + monomorphize::normalize_associated_type(cx.tcx(), &fty) }).collect::>(); let packed = ty::lookup_packed(cx.tcx(), def_id); let dtor = ty::ty_dtor(cx.tcx(), def_id).has_drop_flag(); @@ -432,7 +433,7 @@ fn get_cases<'tcx>(tcx: &ty::ctxt<'tcx>, -> Vec> { ty::enum_variants(tcx, def_id).iter().map(|vi| { let arg_tys = vi.args.iter().map(|&raw_ty| { - raw_ty.subst(tcx, substs) + monomorphize::apply_param_substs(tcx, substs, &raw_ty) }).collect(); Case { discr: vi.disr_val, tys: arg_tys } }).collect() From 6300a97216998ad92ac0be33ccced089f9d8bf0a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 7 Jan 2015 13:59:02 -0500 Subject: [PATCH 2/7] Remove assertion that substitutions are erased. It'd be nice if they always were but it's dang annoying to weed out all the places that fail to meet the assertion, and it doesn't really hurt things if we don't always get it right. --- src/librustc_trans/trans/monomorphize.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs index e2594765f4f..63d4781ec37 100644 --- a/src/librustc_trans/trans/monomorphize.rs +++ b/src/librustc_trans/trans/monomorphize.rs @@ -300,8 +300,6 @@ pub fn apply_param_substs<'tcx,T>(tcx: &ty::ctxt<'tcx>, -> T where T : TypeFoldable<'tcx> + Repr<'tcx> + HasProjectionTypes + Clone { - assert!(param_substs.regions.is_erased()); - let substituted = value.subst(tcx, param_substs); normalize_associated_type(tcx, &substituted) } From 9e4e8823c7cb553f07536ff2cbdaabdcd0dc9cc0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 7 Jan 2015 12:49:52 -0500 Subject: [PATCH 3/7] Use ty::type_is_sized() so that we handle projection types properly. --- src/librustc/middle/infer/freshen.rs | 5 +---- src/librustc/middle/traits/select.rs | 17 ++++++++++++++++- src/librustc_trans/trans/common.rs | 7 ++++--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/librustc/middle/infer/freshen.rs b/src/librustc/middle/infer/freshen.rs index 02c52f82967..6bc424fdf95 100644 --- a/src/librustc/middle/infer/freshen.rs +++ b/src/librustc/middle/infer/freshen.rs @@ -135,10 +135,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { t } - ty::ty_open(..) => { - self.tcx().sess.bug("Cannot freshen an open existential type"); - } - + ty::ty_open(..) | ty::ty_bool | ty::ty_char | ty::ty_int(..) | diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 25a33de1cc7..756ddd91782 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -1457,11 +1457,26 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(AmbiguousBuiltin) } + ty::ty_open(ty) => { + // these only crop up in trans, and represent an + // "opened" unsized/existential type (one that has + // been dereferenced) + match bound { + ty::BoundCopy | + ty::BoundSync | + ty::BoundSend => { + Ok(If(vec!(ty))) + } + + ty::BoundSized => { + Err(Unimplemented) + } + } + } ty::ty_err => { Ok(If(Vec::new())) } - ty::ty_open(_) | ty::ty_infer(ty::FreshTy(_)) | ty::ty_infer(ty::FreshIntTy(_)) => { self.tcx().sess.bug( diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 237fc185636..057c6619d72 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -50,7 +50,7 @@ use std::vec::Vec; use syntax::ast::Ident; use syntax::ast; use syntax::ast_map::{PathElem, PathName}; -use syntax::codemap::Span; +use syntax::codemap::{DUMMY_SP, Span}; use syntax::parse::token::InternedString; use syntax::parse::token; use util::common::memoized; @@ -114,8 +114,9 @@ pub fn normalize_ty<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { } // Is the type's representation size known at compile time? -pub fn type_is_sized<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool { -ty::type_contents(cx, ty).is_sized(cx) +pub fn type_is_sized<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool { + let param_env = ty::empty_parameter_environment(tcx); + ty::type_is_sized(¶m_env, DUMMY_SP, ty) } pub fn lltype_is_sized<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool { From 4dd368b90a8d53af1ce582ec45d03a70d8fe2051 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 7 Jan 2015 13:53:54 -0500 Subject: [PATCH 4/7] Normalize associated types in `with_field_tys` --- src/librustc/middle/ty.rs | 16 +++++++++++++++- src/librustc/middle/ty_fold.rs | 9 +++++++++ src/librustc_trans/trans/expr.rs | 12 +++++++----- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 90716844fbe..ead670f7b7a 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -107,7 +107,7 @@ pub struct CrateAnalysis<'tcx> { pub glob_map: Option, } -#[derive(Copy, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct field<'tcx> { pub name: ast::Name, pub mt: mt<'tcx> @@ -7212,6 +7212,12 @@ impl<'tcx> HasProjectionTypes for FnSig<'tcx> { } } +impl<'tcx> HasProjectionTypes for field<'tcx> { + fn has_projection_types(&self) -> bool { + self.mt.ty.has_projection_types() + } +} + impl<'tcx> HasProjectionTypes for BareFnTy<'tcx> { fn has_projection_types(&self) -> bool { self.sig.has_projection_types() @@ -7311,3 +7317,11 @@ impl<'tcx> Repr<'tcx> for UnboxedClosureUpvar<'tcx> { self.ty.repr(tcx)) } } + +impl<'tcx> Repr<'tcx> for field<'tcx> { + fn repr(&self, tcx: &ctxt<'tcx>) -> String { + format!("field({},{})", + self.name.repr(tcx), + self.mt.repr(tcx)) + } +} diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index dadbae9349f..6c007902582 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -273,6 +273,15 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TraitRef<'tcx> { } } +impl<'tcx> TypeFoldable<'tcx> for ty::field<'tcx> { + fn fold_with>(&self, folder: &mut F) -> ty::field<'tcx> { + ty::field { + name: self.name, + mt: self.mt.fold_with(folder), + } + } +} + impl<'tcx> TypeFoldable<'tcx> for ty::Region { fn fold_with>(&self, folder: &mut F) -> ty::Region { folder.fold_region(*self) diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 120e2e955e4..21b400a710f 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -50,6 +50,7 @@ use trans::debuginfo; use trans::glue; use trans::machine; use trans::meth; +use trans::monomorphize; use trans::inline; use trans::tvec; use trans::type_of; @@ -1318,7 +1319,9 @@ pub fn with_field_tys<'tcx, R, F>(tcx: &ty::ctxt<'tcx>, { match ty.sty { ty::ty_struct(did, substs) => { - op(0, struct_fields(tcx, did, substs).index(&FullRange)) + let fields = struct_fields(tcx, did, substs); + let fields = monomorphize::normalize_associated_type(tcx, &fields); + op(0, fields.index(&FullRange)) } ty::ty_tup(ref v) => { @@ -1340,10 +1343,9 @@ pub fn with_field_tys<'tcx, R, F>(tcx: &ty::ctxt<'tcx>, def::DefVariant(enum_id, variant_id, _) => { let variant_info = ty::enum_variant_with_id( tcx, enum_id, variant_id); - op(variant_info.disr_val, - struct_fields(tcx, - variant_id, - substs).index(&FullRange)) + let fields = struct_fields(tcx, variant_id, substs); + let fields = monomorphize::normalize_associated_type(tcx, &fields); + op(variant_info.disr_val, fields.index(&FullRange)) } _ => { tcx.sess.bug("resolve didn't map this expr to a \ From 55c6a68f1184eefb9e475b5f58272931688d2bc9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 7 Jan 2015 13:57:42 -0500 Subject: [PATCH 5/7] Add rather involved run-pass test case. --- .../associated-types-ref-from-struct.rs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/test/run-pass/associated-types-ref-from-struct.rs diff --git a/src/test/run-pass/associated-types-ref-from-struct.rs b/src/test/run-pass/associated-types-ref-from-struct.rs new file mode 100644 index 00000000000..3c7cc7c4975 --- /dev/null +++ b/src/test/run-pass/associated-types-ref-from-struct.rs @@ -0,0 +1,62 @@ +// Copyright 2014 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. + +// Test associated type references in structure fields. + +trait Test { + type V; + + fn test(&self, value: &Self::V) -> bool; +} + +/////////////////////////////////////////////////////////////////////////// + +struct TesterPair { + tester: T, + value: T::V, +} + +impl TesterPair { + fn new(tester: T, value: T::V) -> TesterPair { + TesterPair { tester: tester, value: value } + } + + fn test(&self) -> bool { + self.tester.test(&self.value) + } +} + +/////////////////////////////////////////////////////////////////////////// + +struct EqU32(u32); +impl Test for EqU32 { + type V = u32; + + fn test(&self, value: &u32) -> bool { + self.0 == *value + } +} + +struct EqI32(i32); +impl Test for EqI32 { + type V = i32; + + fn test(&self, value: &i32) -> bool { + self.0 == *value + } +} + +fn main() { + let tester = TesterPair::new(EqU32(22), 23); + tester.test(); + + let tester = TesterPair::new(EqI32(22), 23); + tester.test(); +} From 18f426e647d05a422d0fcae65c8a87a8e4befb69 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 7 Jan 2015 15:32:36 -0500 Subject: [PATCH 6/7] Update compile-fail test with new message that is generated as a result of using `ty::type_is_sized` --- src/test/compile-fail/recursion.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/compile-fail/recursion.rs b/src/test/compile-fail/recursion.rs index 67177eff9f9..da05514f763 100644 --- a/src/test/compile-fail/recursion.rs +++ b/src/test/compile-fail/recursion.rs @@ -8,6 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//~^^^^^^^^^^ ERROR overflow +// +// We also get a second error message at the top of file (dummy +// span). This is not helpful, but also kind of annoying to prevent, +// so for now just live with it, since we also get a second message +// that is more helpful. + enum Nil {NilValue} struct Cons {head:int, tail:T} trait Dot {fn dot(&self, other:Self) -> int;} From cb98c3d93a4c0e79ae87a04a6d3a439ac8ed87b1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 7 Jan 2015 18:45:36 -0500 Subject: [PATCH 7/7] Normalize types of fields in struct literals during type-checking. Fixes #20535. --- src/librustc_typeck/astconv.rs | 47 +--------------- src/librustc_typeck/check/mod.rs | 53 +++++++++++++++---- .../associated-types-ref-in-struct-literal.rs | 29 ++++++++++ 3 files changed, 74 insertions(+), 55 deletions(-) create mode 100644 src/test/run-pass/associated-types-ref-in-struct-literal.rs diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 265ebe00d53..3534d3ff3af 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -52,7 +52,6 @@ use middle::const_eval; use middle::def; use middle::resolve_lifetime as rl; use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs}; -use middle::subst::{VecPerParamSpace}; use middle::traits; use middle::ty::{self, RegionEscape, ToPolyTraitRef, Ty}; use rscope::{self, UnelidableRscope, RegionScope, SpecificRscope, @@ -244,7 +243,7 @@ pub fn opt_ast_region_to_region<'tcx>( /// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`, /// returns an appropriate set of substitutions for this particular reference to `I`. -fn ast_path_substs_for_ty<'tcx>( +pub fn ast_path_substs_for_ty<'tcx>( this: &AstConv<'tcx>, rscope: &RegionScope, decl_generics: &ty::Generics<'tcx>, @@ -762,50 +761,6 @@ pub fn ast_path_to_ty<'tcx>( TypeAndSubsts { substs: substs, ty: ty } } -/// Returns the type that this AST path refers to. If the path has no type -/// parameters and the corresponding type has type parameters, fresh type -/// and/or region variables are substituted. -/// -/// This is used when checking the constructor in struct literals. -pub fn ast_path_to_ty_relaxed<'tcx>( - this: &AstConv<'tcx>, - rscope: &RegionScope, - did: ast::DefId, - path: &ast::Path) - -> TypeAndSubsts<'tcx> -{ - let tcx = this.tcx(); - let ty::TypeScheme { - generics, - ty: decl_ty - } = this.get_item_type_scheme(did); - - let wants_params = - generics.has_type_params(TypeSpace) || generics.has_region_params(TypeSpace); - - let needs_defaults = - wants_params && - path.segments.iter().all(|s| s.parameters.is_empty()); - - let substs = if needs_defaults { - let type_params: Vec<_> = range(0, generics.types.len(TypeSpace)) - .map(|_| this.ty_infer(path.span)).collect(); - let region_params = - rscope.anon_regions(path.span, generics.regions.len(TypeSpace)) - .unwrap(); - Substs::new(VecPerParamSpace::params_from_type(type_params), - VecPerParamSpace::params_from_type(region_params)) - } else { - ast_path_substs_for_ty(this, rscope, &generics, path) - }; - - let ty = decl_ty.subst(tcx, &substs); - TypeAndSubsts { - substs: substs, - ty: ty, - } -} - /// Converts the given AST type to a built-in type. A "built-in type" is, at /// present, either a core numeric type, a string, or `Box`. pub fn ast_ty_to_builtin_ty<'tcx>( diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9563dd45ca2..87175279bae 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -90,7 +90,7 @@ use middle::mem_categorization as mc; use middle::mem_categorization::McResult; use middle::pat_util::{self, pat_id_map}; use middle::region::CodeExtent; -use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace}; +use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace, TypeSpace}; use middle::traits; use middle::ty::{FnSig, VariantInfo, TypeScheme}; use middle::ty::{Disr, ParamTy, ParameterEnvironment}; @@ -1947,6 +1947,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Returns the type that this AST path refers to. If the path has no type + /// parameters and the corresponding type has type parameters, fresh type + /// and/or region variables are substituted. + /// + /// This is used when checking the constructor in struct literals. + fn instantiate_struct_literal_ty(&self, + did: ast::DefId, + path: &ast::Path) + -> TypeAndSubsts<'tcx> + { + let tcx = self.tcx(); + + let ty::TypeScheme { generics, ty: decl_ty } = ty::lookup_item_type(tcx, did); + + let wants_params = + generics.has_type_params(TypeSpace) || generics.has_region_params(TypeSpace); + + let needs_defaults = + wants_params && + path.segments.iter().all(|s| s.parameters.is_empty()); + + let substs = if needs_defaults { + let tps = + self.infcx().next_ty_vars(generics.types.len(TypeSpace)); + let rps = + self.infcx().region_vars_for_defs(path.span, + generics.regions.get_slice(TypeSpace)); + Substs::new_type(tps, rps) + } else { + astconv::ast_path_substs_for_ty(self, self, &generics, path) + }; + + let ty = self.instantiate_type_scheme(path.span, &substs, &decl_ty); + + TypeAndSubsts { substs: substs, ty: ty } + } + pub fn write_nil(&self, node_id: ast::NodeId) { self.write_ty(node_id, ty::mk_nil(self.tcx())); } @@ -3490,17 +3527,18 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, expected_field_type = ty::lookup_field_type( tcx, class_id, field_id, substitutions); + expected_field_type = + fcx.normalize_associated_types_in( + field.span, &expected_field_type); class_field_map.insert( field.ident.node.name, (field_id, true)); fields_found += 1; } } + // Make sure to give a type to the field even if there's // an error, so we can continue typechecking - check_expr_coercable_to_type( - fcx, - &*field.expr, - expected_field_type); + check_expr_coercable_to_type(fcx, &*field.expr, expected_field_type); } if error_happened { @@ -4149,10 +4187,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, // parameters correctly. let actual_structure_type = fcx.expr_ty(&*expr); if !ty::type_is_error(actual_structure_type) { - let type_and_substs = astconv::ast_path_to_ty_relaxed(fcx, - fcx, - struct_id, - path); + let type_and_substs = fcx.instantiate_struct_literal_ty(struct_id, path); match fcx.mk_subty(false, infer::Misc(path.span), actual_structure_type, diff --git a/src/test/run-pass/associated-types-ref-in-struct-literal.rs b/src/test/run-pass/associated-types-ref-in-struct-literal.rs new file mode 100644 index 00000000000..b51d44a0c24 --- /dev/null +++ b/src/test/run-pass/associated-types-ref-in-struct-literal.rs @@ -0,0 +1,29 @@ +// Copyright 2014 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. + +// Test associated type references in a struct literal. Issue #20535. + +pub trait Foo { + type Bar; +} + +impl Foo for int { + type Bar = int; +} + +struct Thing { + a: F, + b: F::Bar, +} + +fn main() { + let thing = Thing{a: 1i, b: 2i}; + assert_eq!(thing.a + 1, thing.b); +}