From 65616644af943c6de7879d16799e5fb488055fd5 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Thu, 4 Dec 2014 11:58:52 -0800 Subject: [PATCH 1/2] Path types to associated types with form `T::A` Closes #18433 --- src/librustc/middle/astencode.rs | 4 ++ src/librustc/middle/def.rs | 25 ++++++- src/librustc/middle/mem_categorization.rs | 2 +- src/librustc/middle/resolve.rs | 44 +++++++++---- src/librustc_trans/save/mod.rs | 1 + src/librustc_trans/trans/callee.rs | 2 +- src/librustc_typeck/astconv.rs | 79 ++++++++++++++++++++--- src/librustc_typeck/check/mod.rs | 6 ++ src/test/compile-fail/assoc-eq-1.rs | 2 - src/test/compile-fail/assoc-path-1.rs | 26 ++++++++ src/test/compile-fail/assoc-path-2.rs | 34 ++++++++++ src/test/run-pass/assoc-sugar-path.rs | 46 +++++++++++++ 12 files changed, 245 insertions(+), 26 deletions(-) create mode 100644 src/test/compile-fail/assoc-path-1.rs create mode 100644 src/test/compile-fail/assoc-path-2.rs create mode 100644 src/test/run-pass/assoc-sugar-path.rs diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 26d70502a5b..dafe372c9a9 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -443,6 +443,10 @@ impl tr for def::Def { def::DefTrait(did) => def::DefTrait(did.tr(dcx)), def::DefTy(did, is_enum) => def::DefTy(did.tr(dcx), is_enum), def::DefAssociatedTy(did) => def::DefAssociatedTy(did.tr(dcx)), + def::DefAssociatedPath(def::TyParamProvenance::FromSelf(did), ident) => + def::DefAssociatedPath(def::TyParamProvenance::FromSelf(did.tr(dcx)), ident), + def::DefAssociatedPath(def::TyParamProvenance::FromParam(did), ident) => + def::DefAssociatedPath(def::TyParamProvenance::FromParam(did.tr(dcx)), ident), def::DefPrimTy(p) => def::DefPrimTy(p), def::DefTyParam(s, did, v) => def::DefTyParam(s, did.tr(dcx), v), def::DefUse(did) => def::DefUse(did.tr(dcx)), diff --git a/src/librustc/middle/def.rs b/src/librustc/middle/def.rs index ca60ac45e26..8573dc747bb 100644 --- a/src/librustc/middle/def.rs +++ b/src/librustc/middle/def.rs @@ -28,6 +28,10 @@ pub enum Def { DefVariant(ast::DefId /* enum */, ast::DefId /* variant */, bool /* is_structure */), DefTy(ast::DefId, bool /* is_enum */), DefAssociatedTy(ast::DefId), + // A partially resolved path to an associated type `T::U` where `T` is a concrete + // type (indicated by the DefId) which implements a trait which has an associated + // type `U` (indicated by the Ident). + DefAssociatedPath(TyParamProvenance, ast::Ident), DefTrait(ast::DefId), DefPrimTy(ast::PrimTy), DefTyParam(ParamSpace, ast::DefId, uint), @@ -60,6 +64,12 @@ pub enum MethodProvenance { FromImpl(ast::DefId), } +#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] +pub enum TyParamProvenance { + FromSelf(ast::DefId), + FromParam(ast::DefId), +} + impl MethodProvenance { pub fn map(self, f: F) -> MethodProvenance where F: FnOnce(ast::DefId) -> ast::DefId, @@ -73,6 +83,17 @@ impl MethodProvenance { impl Copy for MethodProvenance {} +impl TyParamProvenance { + pub fn def_id(&self) -> ast::DefId { + match *self { + TyParamProvenance::FromSelf(ref did) => did.clone(), + TyParamProvenance::FromParam(ref did) => did.clone(), + } + } +} + +impl Copy for TyParamProvenance {} + impl Def { pub fn def_id(&self) -> ast::DefId { match *self { @@ -80,7 +101,9 @@ impl Def { DefForeignMod(id) | DefStatic(id, _) | DefVariant(_, id, _) | DefTy(id, _) | DefAssociatedTy(id) | DefTyParam(_, id, _) | DefUse(id) | DefStruct(id) | DefTrait(id) | - DefMethod(id, _, _) | DefConst(id) => { + DefMethod(id, _, _) | DefConst(id) | + DefAssociatedPath(TyParamProvenance::FromSelf(id), _) | + DefAssociatedPath(TyParamProvenance::FromParam(id), _) => { id } DefLocal(id) | diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 652847a6343..86b912a579f 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -595,7 +595,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { def::DefTrait(_) | def::DefTy(..) | def::DefPrimTy(_) | def::DefTyParam(..) | def::DefTyParamBinder(..) | def::DefRegion(_) | def::DefLabel(_) | def::DefSelfTy(..) | def::DefMethod(..) | - def::DefAssociatedTy(..) => { + def::DefAssociatedTy(..) | def::DefAssociatedPath(..)=> { Ok(Rc::new(cmt_ { id:id, span:span, diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 30bad840e17..734453db693 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -2027,7 +2027,7 @@ impl<'a> Resolver<'a> { is_public, DUMMY_SP) } - DefTy(..) | DefAssociatedTy(..) => { + DefTy(..) | DefAssociatedTy(..) | DefAssociatedPath(..) => { debug!("(building reduced graph for external \ crate) building type {}", final_ident); @@ -3361,8 +3361,7 @@ impl<'a> Resolver<'a> { let module_path_len = module_path.len(); assert!(module_path_len > 0); - debug!("(resolving module path for import) processing `{}` rooted at \ - `{}`", + debug!("(resolving module path for import) processing `{}` rooted at `{}`", self.names_to_string(module_path), self.module_to_string(&*module_)); @@ -4960,14 +4959,10 @@ impl<'a> Resolver<'a> { result_def = Some((DefPrimTy(primitive_type), LastMod(AllPublic))); - if path.segments - .iter() - .any(|s| s.parameters.has_lifetimes()) { + if path.segments[0].parameters.has_lifetimes() { span_err!(self.session, path.span, E0157, "lifetime parameters are not allowed on this type"); - } else if path.segments - .iter() - .any(|s| !s.parameters.is_empty()) { + } else if !path.segments[0].parameters.is_empty() { span_err!(self.session, path.span, E0153, "type parameters are not allowed on this type"); } @@ -5309,6 +5304,34 @@ impl<'a> Resolver<'a> { self.resolve_type(&*binding.ty); } + // A special case for sugared associated type paths `T::A` where `T` is + // a type parameter and `A` is an associated type on some bound of `T`. + if namespace == TypeNS && path.segments.len() == 2 { + match self.resolve_identifier(path.segments[0].identifier, + TypeNS, + true, + path.span) { + Some((def, last_private)) => { + match def { + DefTyParam(_, did, _) => { + let def = DefAssociatedPath(TyParamProvenance::FromParam(did), + path.segments.last() + .unwrap().identifier); + return Some((def, last_private)); + } + DefSelfTy(nid) => { + let def = DefAssociatedPath(TyParamProvenance::FromSelf(local_def(nid)), + path.segments.last() + .unwrap().identifier); + return Some((def, last_private)); + } + _ => {} + } + } + _ => {} + } + } + if path.global { return self.resolve_crate_relative_path(path, namespace); } @@ -5561,8 +5584,7 @@ impl<'a> Resolver<'a> { let search_result = match namespace { ValueNS => { let renamed = mtwt::resolve(ident); - self.search_ribs(self.value_ribs.as_slice(), - renamed, span) + self.search_ribs(self.value_ribs.as_slice(), renamed, span) } TypeNS => { let name = ident.name; diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 712d6217dde..1a4f06663ef 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -221,6 +221,7 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> { def::DefStruct(_) => Some(recorder::StructRef), def::DefTy(..) | def::DefAssociatedTy(..) | + def::DefAssociatedPath(..) | def::DefTrait(_) => Some(recorder::TypeRef), def::DefStatic(_, _) | def::DefConst(_) | diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index 68d39a7e2ac..ff2f686fff8 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -205,7 +205,7 @@ fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr) def::DefTy(..) | def::DefPrimTy(..) | def::DefAssociatedTy(..) | def::DefUse(..) | def::DefTyParamBinder(..) | def::DefRegion(..) | def::DefLabel(..) | def::DefTyParam(..) | - def::DefSelfTy(..) => { + def::DefSelfTy(..) | def::DefAssociatedPath(..) => { bcx.tcx().sess.span_bug( ref_expr.span, format!("cannot translate def {} \ diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index b3272a14753..02fdd59a1c9 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -73,6 +73,9 @@ pub trait AstConv<'tcx> { fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>; fn get_item_ty(&self, id: ast::DefId) -> ty::Polytype<'tcx>; fn get_trait_def(&self, id: ast::DefId) -> Rc>; + fn get_free_substs(&self) -> Option<&Substs<'tcx>> { + None + } /// What type should we use when a type is omitted? fn ty_infer(&self, span: Span) -> Ty<'tcx>; @@ -517,9 +520,9 @@ fn convert_parenthesized_parameters<'tcx,AC>(this: &AC, } -/// Instantiates the path for the given trait reference, assuming that it's bound to a valid trait -/// type. Returns the def_id for the defining trait. Fails if the type is a type other than a trait -/// type. +/// Instantiates the path for the given trait reference, assuming that it's +/// bound to a valid trait type. Returns the def_id for the defining trait. +/// Fails if the type is a type other than a trait type. pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC, rscope: &RS, ast_trait_ref: &ast::TraitRef, @@ -846,13 +849,8 @@ fn qpath_to_ty<'tcx,AC,RS>(this: &AC, debug!("qpath_to_ty: trait_ref={}", trait_ref.repr(this.tcx())); - let trait_def = this.get_trait_def(trait_ref.def_id); - - for ty_param_def in trait_def.generics.types.get_slice(AssocSpace).iter() { - if ty_param_def.name == qpath.item_name.name { - debug!("qpath_to_ty: corresponding ty_param_def={}", ty_param_def); - return trait_ref.substs.type_for_def(ty_param_def); - } + if let Some(ty) = find_assoc_ty(this, &*trait_ref, qpath.item_name) { + return ty; } this.tcx().sess.span_bug(ast_ty.span, @@ -860,6 +858,22 @@ fn qpath_to_ty<'tcx,AC,RS>(this: &AC, as a parameter for some reason") } +fn find_assoc_ty<'tcx, AC>(this: &AC, + trait_ref: &ty::TraitRef<'tcx>, + type_name: ast::Ident) + -> Option> +where AC: AstConv<'tcx> { + let trait_def = this.get_trait_def(trait_ref.def_id); + + for ty_param_def in trait_def.generics.types.get_slice(AssocSpace).iter() { + if ty_param_def.name == type_name.name { + return Some(trait_ref.substs.type_for_def(ty_param_def)); + } + } + + None +} + // Parses the programmer's textual representation of a type into our // internal notion of a type. pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( @@ -1011,6 +1025,51 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( .get()).as_slice()); ty::mk_err() } + def::DefAssociatedPath(typ, assoc_ident) => { + // FIXME(#19541): in both branches we should consider + // associated types in super-traits. + let (assoc_tys, tp_name): (Vec<_>, _) = match typ { + def::TyParamProvenance::FromParam(did) => { + let ty_param_defs = tcx.ty_param_defs.borrow(); + let tp_def = &(*ty_param_defs)[did.node]; + let assoc_tys = tp_def.bounds.trait_bounds.iter() + .filter_map(|b| find_assoc_ty(this, &**b, assoc_ident)) + .collect(); + (assoc_tys, token::get_name(tp_def.name).to_string()) + } + def::TyParamProvenance::FromSelf(did) => { + let assoc_tys = find_assoc_ty(this, + &*this.get_trait_def(did).trait_ref, + assoc_ident) + .into_iter().collect(); + (assoc_tys, "Self".to_string()) + } + }; + + if assoc_tys.len() == 0 { + tcx.sess.span_err(ast_ty.span, + format!("associated type `{}` not \ + found for type parameter `{}`", + token::get_ident(assoc_ident), + tp_name).as_slice()); + return ty::mk_err() + } + + if assoc_tys.len() > 1 { + tcx.sess.span_err(ast_ty.span, + format!("ambiguous associated type \ + `{}` in bounds of `{}`", + token::get_ident(assoc_ident), + tp_name).as_slice()); + } + + let mut result_ty = assoc_tys[0]; + if let Some(substs) = this.get_free_substs() { + result_ty = result_ty.subst(tcx, substs); + } + + result_ty + } _ => { tcx.sess.span_fatal(ast_ty.span, format!("found value name used \ diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index cf7868148e7..d4e29892c9f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1535,6 +1535,10 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { ty::lookup_trait_def(self.tcx(), id) } + fn get_free_substs(&self) -> Option<&Substs<'tcx>> { + Some(&self.inh.param_env.free_substs) + } + fn ty_infer(&self, _span: Span) -> Ty<'tcx> { self.infcx().next_ty_var() } @@ -4866,6 +4870,7 @@ pub fn polytype_for_def<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, def::DefTrait(_) | def::DefTy(..) | def::DefAssociatedTy(..) | + def::DefAssociatedPath(..) | def::DefPrimTy(_) | def::DefTyParam(..)=> { fcx.ccx.tcx.sess.span_bug(sp, "expected value, found type"); @@ -4974,6 +4979,7 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, def::DefTyParamBinder(..) | def::DefTy(..) | def::DefAssociatedTy(..) | + def::DefAssociatedPath(..) | def::DefTrait(..) | def::DefPrimTy(..) | def::DefTyParam(..) => { diff --git a/src/test/compile-fail/assoc-eq-1.rs b/src/test/compile-fail/assoc-eq-1.rs index 4fd53150618..e93d9db28cf 100644 --- a/src/test/compile-fail/assoc-eq-1.rs +++ b/src/test/compile-fail/assoc-eq-1.rs @@ -20,8 +20,6 @@ pub trait Foo { fn foo2(x: I) { let _: A = x.boo(); //~ERROR use of undeclared - let _: I::A = x.boo(); //~ERROR failed to resolve - //~^ERROR use of undeclared type name `I::A` } pub fn main() {} diff --git a/src/test/compile-fail/assoc-path-1.rs b/src/test/compile-fail/assoc-path-1.rs new file mode 100644 index 00000000000..41f5bc17b56 --- /dev/null +++ b/src/test/compile-fail/assoc-path-1.rs @@ -0,0 +1,26 @@ +// 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 that we have one and only one associated type per ref. + +#![feature(associated_types)] + +pub trait Foo { + type A; +} +pub trait Bar { + type A; +} + +pub fn f1(a: T, x: T::A) {} //~ERROR associated type `A` not found +pub fn f2(a: T, x: T::A) {} //~ERROR ambiguous associated type `A` + +pub fn main() {} + diff --git a/src/test/compile-fail/assoc-path-2.rs b/src/test/compile-fail/assoc-path-2.rs new file mode 100644 index 00000000000..caf8ab3695d --- /dev/null +++ b/src/test/compile-fail/assoc-path-2.rs @@ -0,0 +1,34 @@ +// 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 type checking of uses of associated types via sugary paths. + +#![feature(associated_types)] + +pub trait Foo { + type A; +} + +impl Foo for int { + type A = uint; +} + +pub fn f1(a: T, x: T::A) {} +pub fn f2(a: T) -> T::A { + panic!(); +} + +pub fn main() { + f1(2i, 4i); //~ERROR the trait `Foo` is not implemented + f1(2u, 4u); //~ERROR the trait `Foo` is not implemented + f1(2u, 4i); //~ERROR the trait `Foo` is not implemented + + let _: int = f2(2i); //~ERROR mismatched types: expected `int`, found `uint` +} diff --git a/src/test/run-pass/assoc-sugar-path.rs b/src/test/run-pass/assoc-sugar-path.rs new file mode 100644 index 00000000000..28c06f51ceb --- /dev/null +++ b/src/test/run-pass/assoc-sugar-path.rs @@ -0,0 +1,46 @@ +// 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 paths to associated types using the type-parameter-only sugar. + +#![feature(associated_types)] + +pub trait Foo { + type A; + fn boo(&self) -> Self::A; +} + +impl Foo for int { + type A = uint; + fn boo(&self) -> uint { + 5 + } +} + +// Using a type via a function. +pub fn bar(a: T, x: T::A) -> T::A { + let _: T::A = a.boo(); + x +} + +// Using a type via an impl. +trait C { + fn f(); +} +struct B; +impl C for B { + fn f() { + let x: T::A = panic!(); + } +} + +pub fn main() { + let z: uint = bar(2i, 4u); +} From 743d6a413248a50bff762d1bfa46d31424be1b39 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Tue, 16 Dec 2014 13:48:18 +1300 Subject: [PATCH 2/2] Review changes --- src/librustc_typeck/astconv.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 02fdd59a1c9..87eda76db29 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -73,6 +73,11 @@ pub trait AstConv<'tcx> { fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx>; fn get_item_ty(&self, id: ast::DefId) -> ty::Polytype<'tcx>; fn get_trait_def(&self, id: ast::DefId) -> Rc>; + + /// Return an (optional) substitution to convert bound type parameters that + /// are in scope into free ones. This function should only return Some + /// within a fn body. + /// See ParameterEnvironment::free_substs for more information. fn get_free_substs(&self) -> Option<&Substs<'tcx>> { None } @@ -1029,7 +1034,8 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( // FIXME(#19541): in both branches we should consider // associated types in super-traits. let (assoc_tys, tp_name): (Vec<_>, _) = match typ { - def::TyParamProvenance::FromParam(did) => { + def::TyParamProvenance::FromParam(did) | + def::TyParamProvenance::FromSelf(did) => { let ty_param_defs = tcx.ty_param_defs.borrow(); let tp_def = &(*ty_param_defs)[did.node]; let assoc_tys = tp_def.bounds.trait_bounds.iter() @@ -1037,13 +1043,6 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( .collect(); (assoc_tys, token::get_name(tp_def.name).to_string()) } - def::TyParamProvenance::FromSelf(did) => { - let assoc_tys = find_assoc_ty(this, - &*this.get_trait_def(did).trait_ref, - assoc_ident) - .into_iter().collect(); - (assoc_tys, "Self".to_string()) - } }; if assoc_tys.len() == 0 {