Skip to content

Path types to associated types with form T::A #19789

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

Merged
merged 2 commits into from
Dec 17, 2014
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
4 changes: 4 additions & 0 deletions src/librustc/middle/astencode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down
25 changes: 24 additions & 1 deletion src/librustc/middle/def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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<F>(self, f: F) -> MethodProvenance where
F: FnOnce(ast::DefId) -> ast::DefId,
Expand All @@ -73,14 +83,27 @@ 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 {
DefFn(id, _) | DefStaticMethod(id, _) | DefMod(id) |
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) |
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
44 changes: 33 additions & 11 deletions src/librustc/middle/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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_));

Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/librustc_trans/save/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(_) |
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_trans/trans/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {} \
Expand Down
76 changes: 67 additions & 9 deletions src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ pub trait AstConv<'tcx> {
fn get_item_ty(&self, id: ast::DefId) -> ty::Polytype<'tcx>;
fn get_trait_def(&self, id: ast::DefId) -> Rc<ty::TraitDef<'tcx>>;

/// 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
}

/// What type should we use when a type is omitted?
fn ty_infer(&self, span: Span) -> Ty<'tcx>;

Expand Down Expand Up @@ -517,9 +525,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,
Expand Down Expand Up @@ -846,18 +854,29 @@ fn qpath_to_ty<'tcx,AC,RS>(this: &AC,

debug!("qpath_to_ty: trait_ref={}", trait_ref.repr(this.tcx()));

if let Some(ty) = find_assoc_ty(this, &*trait_ref, qpath.item_name) {
return ty;
}

this.tcx().sess.span_bug(ast_ty.span,
"this associated type didn't get added \
as a parameter for some reason")
}

fn find_assoc_ty<'tcx, AC>(this: &AC,
trait_ref: &ty::TraitRef<'tcx>,
type_name: ast::Ident)
-> Option<Ty<'tcx>>
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 == 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 ty_param_def.name == type_name.name {
return Some(trait_ref.substs.type_for_def(ty_param_def));
}
}

this.tcx().sess.span_bug(ast_ty.span,
"this associated type didn't get added \
as a parameter for some reason")
None
}

// Parses the programmer's textual representation of a type into our
Expand Down Expand Up @@ -1011,6 +1030,45 @@ 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) |
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()
.filter_map(|b| find_assoc_ty(this, &**b, assoc_ident))
.collect();
(assoc_tys, token::get_name(tp_def.name).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 \
Expand Down
6 changes: 6 additions & 0 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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(..) => {
Expand Down
2 changes: 0 additions & 2 deletions src/test/compile-fail/assoc-eq-1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ pub trait Foo {

fn foo2<I: Foo>(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() {}
26 changes: 26 additions & 0 deletions src/test/compile-fail/assoc-path-1.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T>(a: T, x: T::A) {} //~ERROR associated type `A` not found
pub fn f2<T: Foo + Bar>(a: T, x: T::A) {} //~ERROR ambiguous associated type `A`

pub fn main() {}

34 changes: 34 additions & 0 deletions src/test/compile-fail/assoc-path-2.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T: Foo>(a: T, x: T::A) {}
pub fn f2<T: Foo>(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`
}
Loading