From 397dda8aa08ee540cffd36f542ebd1140227d0bd Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Sat, 29 Nov 2014 17:08:30 +1300 Subject: [PATCH] Add support for equality constraints on associated types --- src/librustc/diagnostics.rs | 9 +- src/librustc/middle/infer/error_reporting.rs | 16 +- src/librustc/middle/privacy.rs | 11 +- src/librustc/middle/resolve.rs | 64 ++++--- src/librustc/middle/resolve_lifetime.rs | 19 +- src/librustc/middle/subst.rs | 7 + src/librustc/middle/traits/util.rs | 15 +- src/librustc_typeck/astconv.rs | 139 +++++++++++--- src/librustc_typeck/check/method/probe.rs | 18 +- src/librustc_typeck/check/mod.rs | 64 ++++++- src/librustc_typeck/collect.rs | 74 +++++--- src/libsyntax/ast.rs | 43 ++++- src/libsyntax/ast_util.rs | 16 ++ src/libsyntax/ext/build.rs | 14 +- src/libsyntax/ext/deriving/generic/mod.rs | 2 +- src/libsyntax/ext/deriving/generic/ty.rs | 4 +- src/libsyntax/ext/deriving/rand.rs | 1 + src/libsyntax/ext/env.rs | 3 +- src/libsyntax/ext/format.rs | 1 + src/libsyntax/fold.rs | 48 ++++- src/libsyntax/parse/parser.rs | 177 +++++++++++++----- src/libsyntax/print/pprust.rs | 28 ++- src/libsyntax/visit.rs | 18 +- src/test/compile-fail/assoc-eq-1.rs | 27 +++ src/test/compile-fail/assoc-eq-2.rs | 30 +++ src/test/compile-fail/assoc-eq-3.rs | 48 +++++ src/test/compile-fail/issue-3973.rs | 2 +- .../compile-fail/macro-inner-attributes.rs | 2 +- src/test/run-pass/assoc-eq.rs | 55 ++++++ 29 files changed, 791 insertions(+), 164 deletions(-) create mode 100644 src/test/compile-fail/assoc-eq-1.rs create mode 100644 src/test/compile-fail/assoc-eq-2.rs create mode 100644 src/test/compile-fail/assoc-eq-3.rs create mode 100644 src/test/run-pass/assoc-eq.rs diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 81209763a0c..641d1e1f299 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -148,5 +148,12 @@ register_diagnostics!( E0169, E0170, E0171, - E0172 + E0172, + E0173, + E0174, + E0175, + E0176, + E0177, + E0178, + E0179 ) diff --git a/src/librustc/middle/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs index 657ee088758..d24eddf9ab0 100644 --- a/src/librustc/middle/infer/error_reporting.rs +++ b/src/librustc/middle/infer/error_reporting.rs @@ -1405,10 +1405,22 @@ impl<'a, 'tcx> Rebuilder<'a, 'tcx> { let new_types = data.types.map(|t| { self.rebuild_arg_ty_or_output(&**t, lifetime, anon_nums, region_names) }); + let new_bindings = data.bindings.map(|b| { + P(ast::TypeBinding { + id: b.id, + ident: b.ident, + ty: self.rebuild_arg_ty_or_output(&*b.ty, + lifetime, + anon_nums, + region_names), + span: b.span + }) + }); ast::AngleBracketedParameters(ast::AngleBracketedParameterData { lifetimes: new_lts, - types: new_types - }) + types: new_types, + bindings: new_bindings, + }) } }; let new_seg = ast::PathSegment { diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 5770b601a69..37b70535306 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -1453,8 +1453,15 @@ impl<'a, 'tcx, 'v> Visitor<'v> for VisiblePrivateTypesVisitor<'a, 'tcx> { } } for predicate in generics.where_clause.predicates.iter() { - for bound in predicate.bounds.iter() { - self.check_ty_param_bound(predicate.span, bound) + match predicate { + &ast::BoundPredicate(ref bound_pred) => { + for bound in bound_pred.bounds.iter() { + self.check_ty_param_bound(bound_pred.span, bound) + } + } + &ast::EqPredicate(ref eq_pred) => { + self.visit_ty(&*eq_pred.ty); + } } } } diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 36b87bbd423..e36fefe578d 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -3407,9 +3407,8 @@ impl<'a> Resolver<'a> { // This is not a crate-relative path. We resolve the // first component of the path in the current lexical // scope and then proceed to resolve below that. - match self.resolve_module_in_lexical_scope( - module_, - module_path[0]) { + match self.resolve_module_in_lexical_scope(module_, + module_path[0]) { Failed(err) => return Failed(err), Indeterminate => { debug!("(resolving module path for import) \ @@ -4590,25 +4589,42 @@ impl<'a> Resolver<'a> { fn resolve_where_clause(&mut self, where_clause: &ast::WhereClause) { for predicate in where_clause.predicates.iter() { - match self.resolve_identifier(predicate.ident, - TypeNS, - true, - predicate.span) { - Some((def @ DefTyParam(_, _, _), last_private)) => { - self.record_def(predicate.id, (def, last_private)); - } - _ => { - self.resolve_error( - predicate.span, - format!("undeclared type parameter `{}`", - token::get_ident( - predicate.ident)).as_slice()); - } - } + match predicate { + &ast::BoundPredicate(ref bound_pred) => { + match self.resolve_identifier(bound_pred.ident, + TypeNS, + true, + bound_pred.span) { + Some((def @ DefTyParam(..), last_private)) => { + self.record_def(bound_pred.id, (def, last_private)); + } + _ => { + self.resolve_error( + bound_pred.span, + format!("undeclared type parameter `{}`", + token::get_ident( + bound_pred.ident)).as_slice()); + } + } - for bound in predicate.bounds.iter() { - self.resolve_type_parameter_bound(predicate.id, bound, - TraitBoundingTypeParameter); + for bound in bound_pred.bounds.iter() { + self.resolve_type_parameter_bound(bound_pred.id, bound, + TraitBoundingTypeParameter); + } + } + &ast::EqPredicate(ref eq_pred) => { + match self.resolve_path(eq_pred.id, &eq_pred.path, TypeNS, true) { + Some((def @ DefTyParam(..), last_private)) => { + self.record_def(eq_pred.id, (def, last_private)); + } + _ => { + self.resolve_error(eq_pred.path.span, + "undeclared associated type"); + } + } + + self.resolve_type(&*eq_pred.ty); + } } } } @@ -5269,15 +5285,19 @@ impl<'a> Resolver<'a> { path: &Path, namespace: Namespace, check_ribs: bool) -> Option<(Def, LastPrivate)> { - // First, resolve the types. + // First, resolve the types and associated type bindings. for ty in path.segments.iter().flat_map(|s| s.parameters.types().into_iter()) { self.resolve_type(&**ty); } + for binding in path.segments.iter().flat_map(|s| s.parameters.bindings().into_iter()) { + self.resolve_type(&*binding.ty); + } if path.global { return self.resolve_crate_relative_path(path, namespace); } + // Try to find a path to an item in a module. let unqualified_def = self.resolve_identifier(path.segments .last().unwrap() diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 2ba9ba5631d..b822e658c0d 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -210,8 +210,16 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> { } } for predicate in generics.where_clause.predicates.iter() { - self.visit_ident(predicate.span, predicate.ident); - visit::walk_ty_param_bounds_helper(self, &predicate.bounds); + match predicate { + &ast::BoundPredicate(ast::WhereBoundPredicate{ident, ref bounds, span, ..}) => { + self.visit_ident(span, ident); + visit::walk_ty_param_bounds_helper(self, bounds); + } + &ast::EqPredicate(ast::WhereEqPredicate{id, ref path, ref ty, ..}) => { + self.visit_path(path, id); + self.visit_ty(&**ty); + } + } } } @@ -486,7 +494,12 @@ fn early_bound_lifetime_names(generics: &ast::Generics) -> Vec { visit::walk_ty_param_bounds_helper(&mut collector, &ty_param.bounds); } for predicate in generics.where_clause.predicates.iter() { - visit::walk_ty_param_bounds_helper(&mut collector, &predicate.bounds); + match predicate { + &ast::BoundPredicate(ast::WhereBoundPredicate{ref bounds, ..}) => { + visit::walk_ty_param_bounds_helper(&mut collector, bounds); + } + _ => {} + } } } diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index bcc762a9640..fccb45f8724 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -123,6 +123,13 @@ pub fn self_ty(&self) -> Option> { s } + pub fn with_assoc_tys(&self, assoc_tys: Vec>) -> Substs<'tcx> { + assert!(self.types.is_empty_in(AssocSpace)); + let mut s = (*self).clone(); + s.types.replace(AssocSpace, assoc_tys); + s + } + pub fn erase_regions(self) -> Substs<'tcx> { let Substs { types, regions: _ } = self; Substs { types: types, regions: ErasedRegions } diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index 1b7998a9263..66716267135 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -10,7 +10,7 @@ // except according to those terms. use middle::subst; -use middle::subst::{ParamSpace, Substs, VecPerParamSpace}; +use middle::subst::{ParamSpace, Substs, VecPerParamSpace, Subst}; use middle::infer::InferCtxt; use middle::ty::{mod, Ty}; use std::collections::HashSet; @@ -149,7 +149,18 @@ pub fn fresh_substs_for_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, { let tcx = infcx.tcx; let impl_generics = ty::lookup_item_type(tcx, impl_def_id).generics; - infcx.fresh_substs_for_generics(span, &impl_generics) + let input_substs = infcx.fresh_substs_for_generics(span, &impl_generics); + + // Add substs for the associated types bound in the impl. + let ref items = tcx.impl_items.borrow()[impl_def_id]; + let mut assoc_tys = Vec::new(); + for item in items.iter() { + if let &ty::ImplOrTraitItemId::TypeTraitItemId(id) = item { + assoc_tys.push(tcx.tcache.borrow()[id].ty.subst(tcx, &input_substs)); + } + } + + input_substs.with_assoc_tys(assoc_tys) } impl<'tcx, N> fmt::Show for VtableImplData<'tcx, N> { diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 7f1aad8ca77..89e10270f01 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -83,13 +83,18 @@ pub trait AstConv<'tcx> { trait_id: ast::DefId) -> bool; - /// Returns the binding of the given associated type for some type. + /// Returns the concrete type bound to the given associated type (indicated + /// by associated_type_id) in the current context. For example, + /// in `trait Foo { type A; }` looking up `A` will give a type variable; + /// in `impl Foo for ... { type A = int; ... }` looking up `A` will give `int`. fn associated_type_binding(&self, span: Span, - ty: Option>, + self_ty: Option>, + // DefId for the declaration of the trait + // in which the associated type is declared. trait_id: ast::DefId, associated_type_id: ast::DefId) - -> Ty<'tcx>; + -> Option>; } pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &ast::Lifetime) @@ -207,7 +212,6 @@ fn ast_path_substs_for_ty<'tcx,AC,RS>( rscope: &RS, decl_def_id: ast::DefId, decl_generics: &ty::Generics<'tcx>, - self_ty: Option>, path: &ast::Path) -> Substs<'tcx> where AC: AstConv<'tcx>, RS: RegionScope @@ -225,19 +229,26 @@ fn ast_path_substs_for_ty<'tcx,AC,RS>( assert!(decl_generics.regions.all(|d| d.space == TypeSpace)); assert!(decl_generics.types.all(|d| d.space != FnSpace)); - let (regions, types) = match path.segments.last().unwrap().parameters { + let (regions, types, assoc_bindings) = match path.segments.last().unwrap().parameters { ast::AngleBracketedParameters(ref data) => { convert_angle_bracketed_parameters(this, rscope, data) } ast::ParenthesizedParameters(ref data) => { span_err!(tcx.sess, path.span, E0169, "parenthesized parameters may only be used with a trait"); - (Vec::new(), convert_parenthesized_parameters(this, data)) + (Vec::new(), convert_parenthesized_parameters(this, data), Vec::new()) } }; - create_substs_for_ast_path(this, rscope, path.span, decl_def_id, - decl_generics, self_ty, types, regions) + create_substs_for_ast_path(this, + rscope, + path.span, + decl_def_id, + decl_generics, + None, + types, + regions, + assoc_bindings) } fn create_substs_for_ast_path<'tcx,AC,RS>( @@ -248,7 +259,8 @@ fn create_substs_for_ast_path<'tcx,AC,RS>( decl_generics: &ty::Generics<'tcx>, self_ty: Option>, types: Vec>, - regions: Vec) + regions: Vec, + assoc_bindings: Vec<(ast::Ident, Ty<'tcx>)>) -> Substs<'tcx> where AC: AstConv<'tcx>, RS: RegionScope { @@ -355,13 +367,49 @@ fn create_substs_for_ast_path<'tcx,AC,RS>( } } - for param in decl_generics.types.get_slice(AssocSpace).iter() { - substs.types.push( - AssocSpace, - this.associated_type_binding(span, - self_ty, - decl_def_id, - param.def_id)); + let mut matched_assoc = 0u; + for formal_assoc in decl_generics.types.get_slice(AssocSpace).iter() { + let mut found = false; + for &(ident, ty) in assoc_bindings.iter() { + if formal_assoc.name.ident() == ident { + substs.types.push(AssocSpace, ty); + matched_assoc += 1; + found = true; + break; + } + } + if !found { + match this.associated_type_binding(span, + self_ty, + decl_def_id, + formal_assoc.def_id) { + Some(ty) => { + substs.types.push(AssocSpace, ty); + matched_assoc += 1; + } + None => { + span_err!(this.tcx().sess, span, E0179, + "missing type for associated type `{}`", + token::get_ident(formal_assoc.name.ident())); + } + } + } + } + + if decl_generics.types.get_slice(AssocSpace).len() != matched_assoc { + span_err!(tcx.sess, span, E0171, + "wrong number of associated type parameters: expected {}, found {}", + decl_generics.types.get_slice(AssocSpace).len(), matched_assoc); + } + + for &(ident, _) in assoc_bindings.iter() { + let mut formal_idents = decl_generics.types.get_slice(AssocSpace) + .iter().map(|t| t.name.ident()); + if !formal_idents.any(|i| i == ident) { + span_err!(this.tcx().sess, span, E0177, + "associated type `{}` does not exist", + token::get_ident(ident)); + } } return substs; @@ -370,7 +418,9 @@ fn create_substs_for_ast_path<'tcx,AC,RS>( fn convert_angle_bracketed_parameters<'tcx, AC, RS>(this: &AC, rscope: &RS, data: &ast::AngleBracketedParameterData) - -> (Vec, Vec>) + -> (Vec, + Vec>, + Vec<(ast::Ident, Ty<'tcx>)>) where AC: AstConv<'tcx>, RS: RegionScope { let regions: Vec<_> = @@ -383,7 +433,12 @@ fn convert_angle_bracketed_parameters<'tcx, AC, RS>(this: &AC, .map(|t| ast_ty_to_ty(this, rscope, &**t)) .collect(); - (regions, types) + let assoc_bindings: Vec<_> = + data.bindings.iter() + .map(|b| (b.ident, ast_ty_to_ty(this, rscope, &*b.ty))) + .collect(); + + (regions, types, assoc_bindings) } /// Returns the appropriate lifetime to use for any output lifetimes @@ -484,7 +539,8 @@ pub fn instantiate_poly_trait_ref<'tcx,AC,RS>( pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC, rscope: &RS, ast_trait_ref: &ast::TraitRef, - self_ty: Option>) + self_ty: Option>, + allow_eq: AllowEqConstraints) -> Rc> where AC: AstConv<'tcx>, RS: RegionScope @@ -493,8 +549,12 @@ pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC, ast_trait_ref.path.span, ast_trait_ref.ref_id) { def::DefTrait(trait_def_id) => { - let trait_ref = Rc::new(ast_path_to_trait_ref(this, rscope, trait_def_id, - self_ty, &ast_trait_ref.path)); + let trait_ref = Rc::new(ast_path_to_trait_ref(this, + rscope, + trait_def_id, + self_ty, + &ast_trait_ref.path, + allow_eq)); this.tcx().trait_refs.borrow_mut().insert(ast_trait_ref.ref_id, trait_ref.clone()); trait_ref @@ -507,15 +567,23 @@ pub fn instantiate_trait_ref<'tcx,AC,RS>(this: &AC, } } +#[deriving(PartialEq,Show)] +pub enum AllowEqConstraints { + Allow, + DontAllow +} + fn ast_path_to_trait_ref<'tcx,AC,RS>( this: &AC, rscope: &RS, trait_def_id: ast::DefId, self_ty: Option>, - path: &ast::Path) + path: &ast::Path, + allow_eq: AllowEqConstraints) -> ty::TraitRef<'tcx> where AC: AstConv<'tcx>, RS: RegionScope { + debug!("ast_path_to_trait_ref {}", path); let trait_def = this.get_trait_def(trait_def_id); // the trait reference introduces a binding level here, so @@ -525,15 +593,20 @@ fn ast_path_to_trait_ref<'tcx,AC,RS>( // lifetimes. Oh well, not there yet. let shifted_rscope = ShiftedRscope::new(rscope); - let (regions, types) = match path.segments.last().unwrap().parameters { + let (regions, types, assoc_bindings) = match path.segments.last().unwrap().parameters { ast::AngleBracketedParameters(ref data) => { convert_angle_bracketed_parameters(this, &shifted_rscope, data) } ast::ParenthesizedParameters(ref data) => { - (Vec::new(), convert_parenthesized_parameters(this, data)) + (Vec::new(), convert_parenthesized_parameters(this, data), Vec::new()) } }; + if allow_eq == AllowEqConstraints::DontAllow && assoc_bindings.len() > 0 { + span_err!(this.tcx().sess, path.span, E0173, + "equality constraints are not allowed in this position"); + } + let substs = create_substs_for_ast_path(this, &shifted_rscope, path.span, @@ -541,7 +614,8 @@ fn ast_path_to_trait_ref<'tcx,AC,RS>( &trait_def.generics, self_ty, types, - regions); + regions, + assoc_bindings); ty::TraitRef::new(trait_def_id, substs) } @@ -693,7 +767,8 @@ fn ast_ty_to_trait_ref<'tcx,AC,RS>(this: &AC, rscope, trait_def_id, None, - path)); + path, + AllowEqConstraints::Allow)); } _ => { span_err!(this.tcx().sess, ty.span, E0172, "expected a reference to a trait"); @@ -772,7 +847,8 @@ fn qpath_to_ty<'tcx,AC,RS>(this: &AC, let trait_ref = instantiate_trait_ref(this, rscope, &*qpath.trait_ref, - Some(self_type)); + Some(self_type), + AllowEqConstraints::DontAllow); debug!("qpath_to_ty: trait_ref={}", trait_ref.repr(this.tcx())); @@ -916,7 +992,8 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( rscope, trait_def_id, None, - path); + path, + AllowEqConstraints::Allow); trait_ref_to_object_type(this, rscope, path.span, result, &[]) } def::DefTy(did, _) | def::DefStruct(did) => { @@ -1336,7 +1413,11 @@ fn conv_ty_poly_trait_ref<'tcx, AC, RS>( let main_trait_bound = match partitioned_bounds.trait_bounds.remove(0) { Some(trait_bound) => { - Some(instantiate_poly_trait_ref(this, rscope, trait_bound, None)) + Some(instantiate_trait_ref(this, + rscope, + &trait_bound.trait_ref, + None, + AllowEqConstraints::Allow)) } None => { this.tcx().sess.span_err( diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 6ff276edbce..3fa1234ee6e 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -318,7 +318,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { substs: rcvr_substs.clone() }); - self.elaborate_bounds(&[trait_ref.clone()], |this, new_trait_ref, m, method_num| { + self.elaborate_bounds(&[trait_ref.clone()], false, |this, new_trait_ref, m, method_num| { let vtable_index = get_method_index(tcx, &*new_trait_ref, trait_ref.clone(), method_num); @@ -365,7 +365,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { let bounds = self.fcx.inh.param_env.bounds.get(space, index).trait_bounds .as_slice(); - self.elaborate_bounds(bounds, |this, trait_ref, m, method_num| { + self.elaborate_bounds(bounds, true, |this, trait_ref, m, method_num| { let xform_self_ty = this.xform_self_ty(&m, &trait_ref.substs); @@ -402,6 +402,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { fn elaborate_bounds( &mut self, bounds: &[Rc>], + num_includes_types: bool, mk_cand: for<'a> |this: &mut ProbeContext<'a, 'tcx>, tr: Rc>, m: Rc>, @@ -415,7 +416,10 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { continue; } - let (pos, method) = match trait_method(tcx, bound_trait_ref.def_id, self.method_name) { + let (pos, method) = match trait_method(tcx, + bound_trait_ref.def_id, + self.method_name, + num_includes_types) { Some(v) => v, None => { continue; } }; @@ -988,12 +992,18 @@ fn impl_method<'tcx>(tcx: &ty::ctxt<'tcx>, /// index (or `None`, if no such method). fn trait_method<'tcx>(tcx: &ty::ctxt<'tcx>, trait_def_id: ast::DefId, - method_name: ast::Name) + method_name: ast::Name, + num_includes_types: bool) -> Option<(uint, Rc>)> { let trait_items = ty::trait_items(tcx, trait_def_id); trait_items .iter() + .filter(|item| + num_includes_types || match *item { + &ty::MethodTraitItem(_) => true, + &ty::TypeTraitItem(_) => false + }) .enumerate() .find(|&(_, ref item)| item.name() == method_name) .and_then(|(idx, item)| item.as_opt_method().map(|m| (idx, m))) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 1a8b06ec12d..6a7f6bd0ea1 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -112,7 +112,7 @@ use std::collections::hash_map::{Occupied, Vacant}; use std::mem::replace; use std::rc::Rc; use syntax::{mod, abi, attr}; -use syntax::ast::{mod, ProvidedMethod, RequiredMethod, TypeTraitItem}; +use syntax::ast::{mod, ProvidedMethod, RequiredMethod, TypeTraitItem, DefId}; use syntax::ast_util::{mod, local_def, PostExpansionMethod}; use syntax::codemap::{mod, Span}; use syntax::owned_slice::OwnedSlice; @@ -1585,9 +1585,9 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { _: Option>, _: ast::DefId, _: ast::DefId) - -> Ty<'tcx> { + -> Option> { self.tcx().sess.span_err(span, "unsupported associated type binding"); - ty::mk_err() + Some(ty::mk_err()) } } @@ -5152,12 +5152,18 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, } Some(space) => { + let trait_def_id = match def { + def::DefTrait(did) => Some(did), + _ => None + }; push_explicit_parameters_from_segment_to_substs(fcx, space, path.span, type_defs, region_defs, segment, + trait_def_id, + path.span, &mut substs); } } @@ -5244,12 +5250,14 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, type_defs: &VecPerParamSpace>, region_defs: &VecPerParamSpace, segment: &ast::PathSegment, + trait_def_id: Option, + path_span: Span, substs: &mut Substs<'tcx>) { match segment.parameters { ast::AngleBracketedParameters(ref data) => { push_explicit_angle_bracketed_parameters_from_segment_to_substs( - fcx, space, type_defs, region_defs, data, substs); + fcx, space, type_defs, region_defs, data, trait_def_id, path_span, substs); } ast::ParenthesizedParameters(ref data) => { @@ -5265,6 +5273,8 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, type_defs: &VecPerParamSpace>, region_defs: &VecPerParamSpace, data: &ast::AngleBracketedParameterData, + trait_def_id: Option, + path_span: Span, substs: &mut Substs<'tcx>) { { @@ -5281,10 +5291,56 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, found {} parameter(s)", type_count, data.types.len()); substs.types.truncate(space, 0); + break; } } } + if let Some(trait_def_id) = trait_def_id { + let ref items = fcx.tcx().trait_item_def_ids.borrow()[trait_def_id]; + let mut assoc_tys = Vec::new(); + for item in items.iter() { + if let &ty::ImplOrTraitItemId::TypeTraitItemId(id) = item { + if let ty::ImplOrTraitItem::TypeTraitItem(ref ty) = + fcx.tcx().impl_or_trait_items.borrow()[id] { + assoc_tys.push(ty.clone()); + } + } + } + + if data.bindings.len() > assoc_tys.len() { + span_err!(fcx.tcx().sess, data.bindings[assoc_tys.len()].span, E0174, + "too many type equality constraints provided: \ + expected at most {} constraint(s), \ + found {} constraint(s)", + assoc_tys.len(), data.types.len()); + substs.types.truncate(space, 0); + } else if data.bindings.len() > 0 { + for assoc_ty in assoc_tys.iter() { + let mut matched = false; + for binding in data.bindings.iter() { + if assoc_ty.name.ident() == binding.ident { + let t = fcx.to_ty(&*binding.ty); + substs.types.push(space, t); + matched = true; + break; + } + } + if !matched { + span_err!(fcx.tcx().sess, path_span, E0176, + "missing type equality constraint for associated type: {}", + assoc_ty.name); + substs.types.truncate(space, 0); + break; + } + } + } + } else if data.bindings.len() > 0 { + span_err!(fcx.tcx().sess, path_span, E0175, + "type equality constraints provided on a non-trait type"); + substs.types.truncate(space, 0); + } + { let region_count = region_defs.len(space); assert_eq!(substs.regions().len(space), 0); diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 74ac9c480de..32892aa94ee 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -32,7 +32,7 @@ as `ty_param()` instances. use self::ConvertMethodContext::*; use self::CreateTypeParametersForAssociatedTypesFlag::*; -use astconv::{AstConv, ty_of_arg}; +use astconv::{AstConv, ty_of_arg, AllowEqConstraints}; use astconv::{ast_ty_to_ty, ast_region_to_region}; use astconv; use metadata::csearch; @@ -197,10 +197,10 @@ impl<'a, 'tcx> AstConv<'tcx> for CrateCtxt<'a, 'tcx> { _: Option>, _: ast::DefId, _: ast::DefId) - -> Ty<'tcx> { + -> Option> { self.tcx().sess.span_err(span, "associated types may not be \ referenced here"); - ty::mk_err() + Some(ty::mk_err()) } } @@ -782,7 +782,7 @@ impl<'a,'tcx> AstConv<'tcx> for ImplCtxt<'a,'tcx> { ast::MethodImplItem(_) => {} ast::TypeImplItem(ref typedef) => { if associated_type.name() == typedef.ident.name { - return self.ccx.to_ty(&ExplicitRscope, &*typedef.typ) + return Some(self.ccx.to_ty(&ExplicitRscope, &*typedef.typ)) } } } @@ -943,10 +943,10 @@ impl<'a,'tcx> AstConv<'tcx> for TraitMethodCtxt<'a,'tcx> { fn associated_type_binding(&self, span: Span, - ty: Option>, + self_ty: Option>, trait_id: ast::DefId, associated_type_id: ast::DefId) - -> Ty<'tcx> { + -> Option> { debug!("collect::TraitMethodCtxt::associated_type_binding()"); // If this is one of our own associated types, return it. @@ -957,10 +957,10 @@ impl<'a,'tcx> AstConv<'tcx> for TraitMethodCtxt<'a,'tcx> { ast::RequiredMethod(_) | ast::ProvidedMethod(_) => {} ast::TypeTraitItem(ref item) => { if local_def(item.ty_param.id) == associated_type_id { - return ty::mk_param(self.tcx(), - subst::AssocSpace, - index, - associated_type_id) + return Some(ty::mk_param(self.tcx(), + subst::AssocSpace, + index, + associated_type_id)) } index += 1; } @@ -1142,8 +1142,11 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) { parent_visibility); for trait_ref in opt_trait_ref.iter() { - astconv::instantiate_trait_ref(&icx, &ExplicitRscope, trait_ref, - Some(selfty)); + astconv::instantiate_trait_ref(&icx, + &ExplicitRscope, + trait_ref, + Some(selfty), + AllowEqConstraints::DontAllow); } }, ast::ItemTrait(_, _, _, ref trait_methods) => { @@ -1838,8 +1841,17 @@ fn ty_generics<'tcx,AC>(this: &AC, let trait_def = ty::lookup_trait_def(this.tcx(), trait_def_id); let associated_type_defs = trait_def.generics.types.get_slice(subst::AssocSpace); + // Find any assocaited type bindings in the bound. + let ref segments = ast_trait_ref.trait_ref.path.segments; + let bindings = segments[segments.len() -1].parameters.bindings(); + // Iterate over each associated type `Elem` for associated_type_def in associated_type_defs.iter() { + if bindings.iter().any(|b| associated_type_def.name.ident() == b.ident) { + // Don't add a variable for a bound associated type. + continue; + } + // Create the fresh type parameter `A` let def = ty::TypeParameterDef { name: associated_type_def.name, @@ -1998,10 +2010,11 @@ fn conv_param_bounds<'tcx,AC>(this: &AC, let trait_bounds: Vec> = trait_bounds.into_iter() .map(|bound| { - astconv::instantiate_poly_trait_ref(this, - &ExplicitRscope, - bound, - Some(param_ty.to_ty(this.tcx()))) + astconv::instantiate_trait_ref(this, + &ExplicitRscope, + &bound.trait_ref, + Some(param_ty.to_ty(this.tcx())), + AllowEqConstraints::Allow) }) .collect(); let region_bounds: Vec = @@ -2029,18 +2042,23 @@ fn merge_param_bounds<'a>(tcx: &ty::ctxt, } for predicate in where_clause.predicates.iter() { - let predicate_param_id = - tcx.def_map - .borrow() - .get(&predicate.id) - .expect("compute_bounds(): resolve didn't resolve the type \ - parameter identifier in a `where` clause") - .def_id(); - if param_ty.def_id != predicate_param_id { - continue - } - for bound in predicate.bounds.iter() { - result.push(bound); + match predicate { + &ast::BoundPredicate(ref bound_pred) => { + let predicate_param_id = + tcx.def_map + .borrow() + .get(&bound_pred.id) + .expect("merge_param_bounds(): resolve didn't resolve the \ + type parameter identifier in a `where` clause") + .def_id(); + if param_ty.def_id != predicate_param_id { + continue + } + for bound in bound_pred.bounds.iter() { + result.push(bound); + } + } + &ast::EqPredicate(_) => panic!("not implemented") } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 11af1a43277..ea8de458ce2 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -255,6 +255,7 @@ impl PathParameters { AngleBracketedParameters(AngleBracketedParameterData { lifetimes: Vec::new(), types: OwnedSlice::empty(), + bindings: OwnedSlice::empty(), }) } @@ -307,6 +308,17 @@ impl PathParameters { } } } + + pub fn bindings(&self) -> Vec<&P> { + match *self { + AngleBracketedParameters(ref data) => { + data.bindings.iter().collect() + } + ParenthesizedParameters(_) => { + Vec::new() + } + } + } } /// A path like `Foo<'a, T>` @@ -316,11 +328,14 @@ pub struct AngleBracketedParameterData { pub lifetimes: Vec, /// The type parameters for this path segment, if present. pub types: OwnedSlice>, + /// Bindings (equality constraints) on associated types, if present. + /// E.g., `Foo`. + pub bindings: OwnedSlice>, } impl AngleBracketedParameterData { fn is_empty(&self) -> bool { - self.lifetimes.is_empty() && self.types.is_empty() + self.lifetimes.is_empty() && self.types.is_empty() && self.bindings.is_empty() } } @@ -406,13 +421,27 @@ pub struct WhereClause { } #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] -pub struct WherePredicate { +pub enum WherePredicate { + BoundPredicate(WhereBoundPredicate), + EqPredicate(WhereEqPredicate) +} + +#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] +pub struct WhereBoundPredicate { pub id: NodeId, pub span: Span, pub ident: Ident, pub bounds: OwnedSlice, } +#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] +pub struct WhereEqPredicate { + pub id: NodeId, + pub span: Span, + pub path: Path, + pub ty: P, +} + /// The set of MetaItems that define the compilation environment of the crate, /// used to drive conditional compilation pub type CrateConfig = Vec> ; @@ -1118,6 +1147,16 @@ impl FloatTy { } } +// Bind a type to an associated type: `A=Foo`. +#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] +pub struct TypeBinding { + pub id: NodeId, + pub ident: Ident, + pub ty: P, + pub span: Span, +} + + // NB PartialEq method appears below. #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] pub struct Ty { diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 7dba6a57fc4..eec3f69ee64 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -174,12 +174,28 @@ pub fn ident_to_path(s: Span, identifier: Ident) -> Path { parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData { lifetimes: Vec::new(), types: OwnedSlice::empty(), + bindings: OwnedSlice::empty(), }) } ), } } +// If path is a single segment ident path, return that ident. Otherwise, return +// None. +pub fn path_to_ident(path: &Path) -> Option { + if path.segments.len() != 1 { + return None; + } + + let segment = &path.segments[0]; + if !segment.parameters.is_empty() { + return None; + } + + Some(segment.identifier) +} + pub fn ident_to_pat(id: NodeId, s: Span, i: Ident) -> P { P(Pat { id: id, diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index b4bb1a1a529..84040bcfa9f 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -37,7 +37,8 @@ pub trait AstBuilder { global: bool, idents: Vec , lifetimes: Vec, - types: Vec> ) + types: Vec>, + bindings: Vec> ) -> ast::Path; // types @@ -293,20 +294,21 @@ pub trait AstBuilder { impl<'a> AstBuilder for ExtCtxt<'a> { fn path(&self, span: Span, strs: Vec ) -> ast::Path { - self.path_all(span, false, strs, Vec::new(), Vec::new()) + self.path_all(span, false, strs, Vec::new(), Vec::new(), Vec::new()) } fn path_ident(&self, span: Span, id: ast::Ident) -> ast::Path { self.path(span, vec!(id)) } fn path_global(&self, span: Span, strs: Vec ) -> ast::Path { - self.path_all(span, true, strs, Vec::new(), Vec::new()) + self.path_all(span, true, strs, Vec::new(), Vec::new(), Vec::new()) } fn path_all(&self, sp: Span, global: bool, mut idents: Vec , lifetimes: Vec, - types: Vec> ) + types: Vec>, + bindings: Vec> ) -> ast::Path { let last_identifier = idents.pop().unwrap(); let mut segments: Vec = idents.into_iter() @@ -321,6 +323,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData { lifetimes: lifetimes, types: OwnedSlice::from_vec(types), + bindings: OwnedSlice::from_vec(bindings), }) }); ast::Path { @@ -391,7 +394,8 @@ impl<'a> AstBuilder for ExtCtxt<'a> { self.ident_of("Option") ), Vec::new(), - vec!( ty ))) + vec!( ty ), + Vec::new())) } fn ty_field_imm(&self, span: Span, name: Ident, ty: P) -> ast::TypeField { diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs index d5f472bd827..cf3b3ad9051 100644 --- a/src/libsyntax/ext/deriving/generic/mod.rs +++ b/src/libsyntax/ext/deriving/generic/mod.rs @@ -444,7 +444,7 @@ impl<'a> TraitDef<'a> { // Create the type of `self`. let self_type = cx.ty_path( cx.path_all(self.span, false, vec!( type_ident ), self_lifetimes, - self_ty_params.into_vec())); + self_ty_params.into_vec(), Vec::new())); let attr = cx.attribute( self.span, diff --git a/src/libsyntax/ext/deriving/generic/ty.rs b/src/libsyntax/ext/deriving/generic/ty.rs index 01398273161..56d11c2377f 100644 --- a/src/libsyntax/ext/deriving/generic/ty.rs +++ b/src/libsyntax/ext/deriving/generic/ty.rs @@ -80,7 +80,7 @@ impl<'a> Path<'a> { let lt = mk_lifetimes(cx, span, &self.lifetime); let tys = self.params.iter().map(|t| t.to_ty(cx, span, self_ty, self_generics)).collect(); - cx.path_all(span, self.global, idents, lt, tys) + cx.path_all(span, self.global, idents, lt, tys, Vec::new()) } } @@ -177,7 +177,7 @@ impl<'a> Ty<'a> { .collect(); cx.path_all(span, false, vec!(self_ty), lifetimes, - self_params.into_vec()) + self_params.into_vec(), Vec::new()) } Literal(ref p) => { p.to_path(cx, span, self_ty, self_generics) diff --git a/src/libsyntax/ext/deriving/rand.rs b/src/libsyntax/ext/deriving/rand.rs index 8ad8436906b..c4e64d58c29 100644 --- a/src/libsyntax/ext/deriving/rand.rs +++ b/src/libsyntax/ext/deriving/rand.rs @@ -88,6 +88,7 @@ fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) true, rand_ident.clone(), Vec::new(), + Vec::new(), Vec::new()); let rand_name = cx.expr_path(rand_name); diff --git a/src/libsyntax/ext/env.rs b/src/libsyntax/ext/env.rs index e6a44c57f1b..8c17b31f458 100644 --- a/src/libsyntax/ext/env.rs +++ b/src/libsyntax/ext/env.rs @@ -45,7 +45,8 @@ pub fn expand_option_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenT Some(cx.lifetime(sp, cx.ident_of( "'static").name)), - ast::MutImmutable)))) + ast::MutImmutable)), + Vec::new())) } Some(s) => { cx.expr_call_global(sp, diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index c8fed3dcd16..5d595474e9c 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -530,6 +530,7 @@ impl<'a, 'b> Context<'a, 'b> { self.fmtsp, true, Context::rtpath(self.ecx, "Argument"), vec![static_lifetime], + vec![], vec![] )); lets.push(Context::item_static_array(self.ecx, diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 122f99cabb3..69e311c57f5 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -146,6 +146,10 @@ pub trait Folder { noop_fold_qpath(t, self) } + fn fold_ty_binding(&mut self, t: P) -> P { + noop_fold_ty_binding(t, self) + } + fn fold_mod(&mut self, m: Mod) -> Mod { noop_fold_mod(m, self) } @@ -391,6 +395,15 @@ pub fn noop_fold_decl(d: P, fld: &mut T) -> SmallVector }) } +pub fn noop_fold_ty_binding(b: P, fld: &mut T) -> P { + b.map(|TypeBinding { id, ident, ty, span }| TypeBinding { + id: fld.new_id(id), + ident: ident, + ty: fld.fold_ty(ty), + span: fld.new_span(span), + }) +} + pub fn noop_fold_ty(t: P, fld: &mut T) -> P { t.map(|Ty {id, node, span}| Ty { id: fld.new_id(id), @@ -533,9 +546,10 @@ pub fn noop_fold_angle_bracketed_parameter_data(data: AngleBracketedP fld: &mut T) -> AngleBracketedParameterData { - let AngleBracketedParameterData { lifetimes, types } = data; + let AngleBracketedParameterData { lifetimes, types, bindings } = data; AngleBracketedParameterData { lifetimes: fld.fold_lifetimes(lifetimes), - types: types.move_map(|ty| fld.fold_ty(ty)) } + types: types.move_map(|ty| fld.fold_ty(ty)), + bindings: bindings.move_map(|b| fld.fold_ty_binding(b)) } } pub fn noop_fold_parenthesized_parameter_data(data: ParenthesizedParameterData, @@ -807,14 +821,32 @@ pub fn noop_fold_where_clause( } pub fn noop_fold_where_predicate( - WherePredicate {id, ident, bounds, span}: WherePredicate, + pred: WherePredicate, fld: &mut T) -> WherePredicate { - WherePredicate { - id: fld.new_id(id), - ident: fld.fold_ident(ident), - bounds: bounds.move_map(|x| fld.fold_ty_param_bound(x)), - span: fld.new_span(span) + match pred { + ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate{id, + ident, + bounds, + span}) => { + ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { + id: fld.new_id(id), + ident: fld.fold_ident(ident), + bounds: bounds.move_map(|x| fld.fold_ty_param_bound(x)), + span: fld.new_span(span) + }) + } + ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{id, + path, + ty, + span}) => { + ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ + id: fld.new_id(id), + path: fld.fold_path(path), + ty:fld.fold_ty(ty), + span: fld.new_span(span) + }) + } } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 4929ee885ac..92c7380a61d 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -53,7 +53,7 @@ use ast::{StructVariantKind, BiSub, StrStyle}; use ast::{SelfExplicit, SelfRegion, SelfStatic, SelfValue}; use ast::{Delimited, SequenceRepetition, TokenTree, TraitItem, TraitRef}; use ast::{TtDelimited, TtSequence, TtToken}; -use ast::{TupleVariantKind, Ty, Ty_}; +use ast::{TupleVariantKind, Ty, Ty_, TypeBinding}; use ast::{TypeField, TyFixedLengthVec, TyClosure, TyProc, TyBareFn}; use ast::{TyTypeof, TyInfer, TypeMethod}; use ast::{TyParam, TyParamBound, TyParen, TyPath, TyPolyTraitRef, TyPtr, TyQPath}; @@ -62,7 +62,7 @@ use ast::{TypeImplItem, TypeTraitItem, Typedef, UnboxedClosureKind}; use ast::{UnnamedField, UnsafeBlock}; use ast::{UnsafeFn, ViewItem, ViewItem_, ViewItemExternCrate, ViewItemUse}; use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple}; -use ast::{Visibility, WhereClause, WherePredicate}; +use ast::{Visibility, WhereClause}; use ast; use ast_util::{mod, as_prec, ident_to_path, operator_prec}; use codemap::{mod, Span, BytePos, Spanned, spanned, mk_sp}; @@ -769,13 +769,10 @@ impl<'a> Parser<'a> { } } - /// Parse a sequence bracketed by '<' and '>', stopping - /// before the '>'. - pub fn parse_seq_to_before_gt( - &mut self, - sep: Option, - f: |&mut Parser| -> T) - -> OwnedSlice { + pub fn parse_seq_to_before_gt_or_return(&mut self, + sep: Option, + f: |&mut Parser| -> Option) + -> (OwnedSlice, bool) { let mut v = Vec::new(); // This loop works by alternating back and forth between parsing types // and commas. For example, given a string `A, B,>`, the parser would @@ -792,24 +789,48 @@ impl<'a> Parser<'a> { } if i % 2 == 0 { - v.push(f(self)); + match f(self) { + Some(result) => v.push(result), + None => return (OwnedSlice::from_vec(v), true) + } } else { sep.as_ref().map(|t| self.expect(t)); } } - return OwnedSlice::from_vec(v); + return (OwnedSlice::from_vec(v), false); } - pub fn parse_seq_to_gt( - &mut self, - sep: Option, - f: |&mut Parser| -> T) - -> OwnedSlice { + /// Parse a sequence bracketed by '<' and '>', stopping + /// before the '>'. + pub fn parse_seq_to_before_gt(&mut self, + sep: Option, + f: |&mut Parser| -> T) + -> OwnedSlice { + let (result, returned) = self.parse_seq_to_before_gt_or_return(sep, |p| Some(f(p))); + assert!(!returned); + return result; + } + + pub fn parse_seq_to_gt(&mut self, + sep: Option, + f: |&mut Parser| -> T) + -> OwnedSlice { let v = self.parse_seq_to_before_gt(sep, f); self.expect_gt(); return v; } + pub fn parse_seq_to_gt_or_return(&mut self, + sep: Option, + f: |&mut Parser| -> Option) + -> (OwnedSlice, bool) { + let (v, returned) = self.parse_seq_to_before_gt_or_return(sep, f); + if !returned { + self.expect_gt(); + } + return (v, returned); + } + /// Parse a sequence, including the closing delimiter. The function /// f must consume tokens until reaching the next separator or /// closing bracket. @@ -1842,11 +1863,12 @@ impl<'a> Parser<'a> { // Parse types, optionally. let parameters = if self.eat_lt(false) { - let (lifetimes, types) = self.parse_generic_values_after_lt(); + let (lifetimes, types, bindings) = self.parse_generic_values_after_lt(); ast::AngleBracketedParameters(ast::AngleBracketedParameterData { lifetimes: lifetimes, types: OwnedSlice::from_vec(types), + bindings: OwnedSlice::from_vec(bindings), }) } else if self.eat(&token::OpenDelim(token::Paren)) { let inputs = self.parse_seq_to_end( @@ -1894,6 +1916,7 @@ impl<'a> Parser<'a> { parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData { lifetimes: Vec::new(), types: OwnedSlice::empty(), + bindings: OwnedSlice::empty(), }) }); return segments; @@ -1902,12 +1925,13 @@ impl<'a> Parser<'a> { // Check for a type segment. if self.eat_lt(false) { // Consumed `a::b::<`, go look for types - let (lifetimes, types) = self.parse_generic_values_after_lt(); + let (lifetimes, types, bindings) = self.parse_generic_values_after_lt(); segments.push(ast::PathSegment { identifier: identifier, parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData { lifetimes: lifetimes, types: OwnedSlice::from_vec(types), + bindings: OwnedSlice::from_vec(bindings), }), }); @@ -2435,13 +2459,18 @@ impl<'a> Parser<'a> { let dot = self.last_span.hi; hi = self.span.hi; self.bump(); - let (_, tys) = if self.eat(&token::ModSep) { + let (_, tys, bindings) = if self.eat(&token::ModSep) { self.expect_lt(); self.parse_generic_values_after_lt() } else { - (Vec::new(), Vec::new()) + (Vec::new(), Vec::new(), Vec::new()) }; + if bindings.len() > 0 { + let last_span = self.last_span; + self.span_err(last_span, "type bindings are only permitted on trait paths"); + } + // expr.f() method call match self.token { token::OpenDelim(token::Paren) => { @@ -4041,16 +4070,51 @@ impl<'a> Parser<'a> { } } - fn parse_generic_values_after_lt(&mut self) -> (Vec, Vec> ) { + fn parse_generic_values_after_lt(&mut self) + -> (Vec, Vec>, Vec>) { let lifetimes = self.parse_lifetimes(token::Comma); - let result = self.parse_seq_to_gt( + + // First parse types. + let (types, returned) = self.parse_seq_to_gt_or_return( Some(token::Comma), |p| { p.forbid_lifetime(); - p.parse_ty_sum() + if p.look_ahead(1, |t| t == &token::Eq) { + None + } else { + Some(p.parse_ty_sum()) + } } ); - (lifetimes, result.into_vec()) + + // If we found the `>`, don't continue. + if !returned { + return (lifetimes, types.into_vec(), Vec::new()); + } + + // Then parse type bindings. + let bindings = self.parse_seq_to_gt( + Some(token::Comma), + |p| { + p.forbid_lifetime(); + let lo = p.span.lo; + let ident = p.parse_ident(); + let found_eq = p.eat(&token::Eq); + if !found_eq { + let span = p.span; + p.span_warn(span, "whoops, no =?"); + } + let ty = p.parse_ty(); + let hi = p.span.hi; + let span = mk_sp(lo, hi); + return P(TypeBinding{id: ast::DUMMY_NODE_ID, + ident: ident, + ty: ty, + span: span, + }); + } + ); + (lifetimes, types.into_vec(), bindings.into_vec()) } fn forbid_lifetime(&mut self) { @@ -4070,30 +4134,59 @@ impl<'a> Parser<'a> { let mut parsed_something = false; loop { let lo = self.span.lo; - let ident = match self.token { - token::Ident(..) => self.parse_ident(), + let path = match self.token { + token::Ident(..) => self.parse_path(NoTypesAllowed), _ => break, }; - self.expect(&token::Colon); - let bounds = self.parse_ty_param_bounds(); - let hi = self.span.hi; - let span = mk_sp(lo, hi); + if self.eat(&token::Colon) { + let bounds = self.parse_ty_param_bounds(); + let hi = self.span.hi; + let span = mk_sp(lo, hi); - if bounds.len() == 0 { - self.span_err(span, - "each predicate in a `where` clause must have \ - at least one bound in it"); + if bounds.len() == 0 { + self.span_err(span, + "each predicate in a `where` clause must have \ + at least one bound in it"); + } + + let ident = match ast_util::path_to_ident(&path) { + Some(ident) => ident, + None => { + self.span_err(path.span, "expected a single identifier \ + in bound where clause"); + break; + } + }; + + generics.where_clause.predicates.push( + ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { + id: ast::DUMMY_NODE_ID, + span: span, + ident: ident, + bounds: bounds, + })); + parsed_something = true; + } else if self.eat(&token::Eq) { + let ty = self.parse_ty(); + let hi = self.span.hi; + let span = mk_sp(lo, hi); + generics.where_clause.predicates.push( + ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { + id: ast::DUMMY_NODE_ID, + span: span, + path: path, + ty: ty, + })); + parsed_something = true; + // FIXME(#18433) + self.span_err(span, "equality constraints are not yet supported in where clauses"); + } else { + let last_span = self.last_span; + self.span_err(last_span, + "unexpected token in `where` clause"); } - generics.where_clause.predicates.push(ast::WherePredicate { - id: ast::DUMMY_NODE_ID, - span: span, - ident: ident, - bounds: bounds, - }); - parsed_something = true; - if !self.eat(&token::Comma) { break } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index eab03f73091..26373d00aaf 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1976,6 +1976,18 @@ impl<'a> State<'a> { Inconsistent, data.types.as_slice(), |s, ty| s.print_type(&**ty))); + comma = true; + } + + for binding in data.bindings.iter() { + if comma { + try!(self.word_space(",")) + } + try!(self.print_ident(binding.ident)); + try!(space(&mut self.s)); + try!(self.word_space("=")); + try!(self.print_type(&*binding.ty)); + comma = true; } try!(word(&mut self.s, ">")) @@ -2437,8 +2449,20 @@ impl<'a> State<'a> { try!(self.word_space(",")); } - try!(self.print_ident(predicate.ident)); - try!(self.print_bounds(":", predicate.bounds.as_slice())); + match predicate { + &ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate{ident, + ref bounds, + ..}) => { + try!(self.print_ident(ident)); + try!(self.print_bounds(":", bounds.as_slice())); + } + &ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ref path, ref ty, ..}) => { + try!(self.print_path(path, false)); + try!(space(&mut self.s)); + try!(self.word_space("=")); + try!(self.print_type(&**ty)); + } + } } Ok(()) diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index f5e89dd61ff..a36f8b23ca3 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -573,8 +573,22 @@ pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics } walk_lifetime_decls_helper(visitor, &generics.lifetimes); for predicate in generics.where_clause.predicates.iter() { - visitor.visit_ident(predicate.span, predicate.ident); - walk_ty_param_bounds_helper(visitor, &predicate.bounds); + match predicate { + &ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate{span, + ident, + ref bounds, + ..}) => { + visitor.visit_ident(span, ident); + walk_ty_param_bounds_helper(visitor, bounds); + } + &ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{id, + ref path, + ref ty, + ..}) => { + visitor.visit_path(path, id); + visitor.visit_ty(&**ty); + } + } } } diff --git a/src/test/compile-fail/assoc-eq-1.rs b/src/test/compile-fail/assoc-eq-1.rs new file mode 100644 index 00000000000..4fd53150618 --- /dev/null +++ b/src/test/compile-fail/assoc-eq-1.rs @@ -0,0 +1,27 @@ +// 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 equality constraints on associated types. Check that unsupported syntax +// does not ICE. + +#![feature(associated_types)] + +pub trait Foo { + type A; + fn boo(&self) -> ::A; +} + +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-eq-2.rs b/src/test/compile-fail/assoc-eq-2.rs new file mode 100644 index 00000000000..652bf4fb577 --- /dev/null +++ b/src/test/compile-fail/assoc-eq-2.rs @@ -0,0 +1,30 @@ +// 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 equality constraints on associated types. Check we get an error when an +// equality constraint is used in a qualified path. + +#![feature(associated_types)] + +pub trait Foo { + type A; + fn boo(&self) -> ::A; +} + +struct Bar; + +impl Foo for int { + type A = uint; + fn boo(&self) -> uint { 42 } +} + +fn baz(x: &>::A) {} //~ERROR equality constraints are not allowed in this + +pub fn main() {} diff --git a/src/test/compile-fail/assoc-eq-3.rs b/src/test/compile-fail/assoc-eq-3.rs new file mode 100644 index 00000000000..880b2e9cc4a --- /dev/null +++ b/src/test/compile-fail/assoc-eq-3.rs @@ -0,0 +1,48 @@ +// 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 equality constraints on associated types. Check we get type errors +// where we should. + +#![feature(associated_types)] + +pub trait Foo { + type A; + fn boo(&self) -> ::A; +} + +struct Bar; + +impl Foo for int { + type A = uint; + fn boo(&self) -> uint { + 42 + } +} + +fn foo1>(x: I) { + let _: Bar = x.boo(); +} + +fn foo2(x: I) { + let _: Bar = x.boo(); //~ERROR mismatched types +} + + +pub fn baz(x: &Foo) { + let _: Bar = x.boo(); +} + + +pub fn main() { + let a = 42i; + foo1(a); //~ERROR the trait `Foo` is not implemented for the type `int` + baz(&a); //~ERROR the trait `Foo` is not implemented for the type `int` +} diff --git a/src/test/compile-fail/issue-3973.rs b/src/test/compile-fail/issue-3973.rs index 57bc1137912..e4f7521c333 100644 --- a/src/test/compile-fail/issue-3973.rs +++ b/src/test/compile-fail/issue-3973.rs @@ -31,6 +31,6 @@ impl ToString_ for Point { fn main() { let p = Point::new(0.0, 0.0); //~^ ERROR unresolved name `Point::new` - //~^^ ERROR failed to resolve. Use of undeclared module `Point` + //~^^ ERROR failed to resolve. Use of undeclared type or module `Point` println!("{}", p.to_string()); } diff --git a/src/test/compile-fail/macro-inner-attributes.rs b/src/test/compile-fail/macro-inner-attributes.rs index 3e731a2d2fe..4c4fb5572d6 100644 --- a/src/test/compile-fail/macro-inner-attributes.rs +++ b/src/test/compile-fail/macro-inner-attributes.rs @@ -25,7 +25,7 @@ test!(b, #[qux] fn main() { a::bar(); - //~^ ERROR failed to resolve. Use of undeclared module `a` + //~^ ERROR failed to resolve. Use of undeclared type or module `a` //~^^ ERROR unresolved name `a::bar` b::bar(); } diff --git a/src/test/run-pass/assoc-eq.rs b/src/test/run-pass/assoc-eq.rs new file mode 100644 index 00000000000..f1ba382b42d --- /dev/null +++ b/src/test/run-pass/assoc-eq.rs @@ -0,0 +1,55 @@ +// 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 equality constraints on associated types. + +#![feature(associated_types)] + +pub trait Foo { + type A; + fn boo(&self) -> ::A; +} + +struct Bar; + +impl Foo for int { + type A = uint; + fn boo(&self) -> uint { 42 } +} +impl Foo for Bar { + type A = int; + fn boo(&self) -> int { 43 } +} +impl Foo for char { + type A = Bar; + fn boo(&self) -> Bar { Bar } +} + +fn foo1>(x: I) -> Bar { + x.boo() +} +fn foo2(x: I) -> ::A { + x.boo() +} +fn baz(x: &Foo) -> Bar { + x.boo() +} + +pub fn main() { + let a = 42i; + assert!(foo2(a) == 42u); + + let a = Bar; + assert!(foo2(a) == 43i); + + let a = 'a'; + foo1(a); + baz(&a); +}