summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-12-12 18:57:15 +0000
committerbors <bors@rust-lang.org>2014-12-12 18:57:15 +0000
commit9146a919b616e39e528e4d7100d16eef52f1f852 (patch)
treed6c397956a7905c50e86e85777b417a1f2c5bdfc
parenta5921241a3146cccaffc336a0d1ade1a90e3517f (diff)
parentce4318ad86f2ccd0710247020269ba0ba22c6d59 (diff)
downloadrust-9146a919b616e39e528e4d7100d16eef52f1f852.tar.gz
rust-9146a919b616e39e528e4d7100d16eef52f1f852.zip
auto merge of #19391 : nick29581/rust/assoc-eq, r=nikomatsakis
r? @nikomatsakis 

cc @aturon (I think you were interested in this for some library stuff)

closes #18432
-rw-r--r--src/librustc/diagnostics.rs6
-rw-r--r--src/librustc/metadata/decoder.rs2
-rw-r--r--src/librustc/middle/infer/error_reporting.rs16
-rw-r--r--src/librustc/middle/privacy.rs11
-rw-r--r--src/librustc/middle/resolve.rs136
-rw-r--r--src/librustc/middle/resolve_lifetime.rs25
-rw-r--r--src/librustc/middle/subst.rs9
-rw-r--r--src/librustc/middle/traits/util.rs17
-rw-r--r--src/librustc/middle/ty.rs2
-rw-r--r--src/librustc_typeck/astconv.rs150
-rw-r--r--src/librustc_typeck/check/method/probe.rs22
-rw-r--r--src/librustc_typeck/check/mod.rs15
-rw-r--r--src/librustc_typeck/coherence/mod.rs2
-rw-r--r--src/librustc_typeck/collect.rs141
-rw-r--r--src/librustc_typeck/diagnostics.rs8
-rw-r--r--src/librustdoc/clean/mod.rs13
-rw-r--r--src/libsyntax/ast.rs43
-rw-r--r--src/libsyntax/ast_util.rs16
-rw-r--r--src/libsyntax/ext/build.rs14
-rw-r--r--src/libsyntax/ext/deriving/generic/mod.rs2
-rw-r--r--src/libsyntax/ext/deriving/generic/ty.rs4
-rw-r--r--src/libsyntax/ext/deriving/rand.rs1
-rw-r--r--src/libsyntax/ext/env.rs3
-rw-r--r--src/libsyntax/ext/format.rs1
-rw-r--r--src/libsyntax/fold.rs48
-rw-r--r--src/libsyntax/parse/parser.rs177
-rw-r--r--src/libsyntax/print/pprust.rs28
-rw-r--r--src/libsyntax/visit.rs18
-rw-r--r--src/test/compile-fail-fulldeps/phase-syntax-doesnt-resolve.rs2
-rw-r--r--src/test/compile-fail/assoc-eq-1.rs27
-rw-r--r--src/test/compile-fail/assoc-eq-2.rs30
-rw-r--r--src/test/compile-fail/assoc-eq-3.rs48
-rw-r--r--src/test/compile-fail/assoc-eq-expr-path.rs28
-rw-r--r--src/test/compile-fail/hrtb-precedence-of-plus-error-message.rs6
-rw-r--r--src/test/compile-fail/issue-3973.rs2
-rw-r--r--src/test/compile-fail/macro-inner-attributes.rs2
-rw-r--r--src/test/run-pass/assoc-eq.rs55
37 files changed, 868 insertions, 262 deletions
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index 81209763a0c..0cdf6a68e44 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -148,5 +148,9 @@ register_diagnostics!(
     E0169,
     E0170,
     E0171,
-    E0172
+    E0172,
+    E0173,
+    E0174,
+    E0177,
+    E0178
 )
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index 232a0cc7f54..23bd37486bc 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -435,7 +435,7 @@ pub fn get_symbol(data: &[u8], id: ast::NodeId) -> String {
 }
 
 // Something that a name can resolve to.
-#[deriving(Clone)]
+#[deriving(Clone,Show)]
 pub enum DefLike {
     DlDef(def::Def),
     DlImpl(ast::DefId),
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..79bb19a1e53 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::WherePredicate::BoundPredicate(ref bound_pred) => {
+                    for bound in bound_pred.bounds.iter() {
+                        self.check_ty_param_bound(bound_pred.span, bound)
+                    }
+                }
+                &ast::WherePredicate::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..2899f60f736 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -108,7 +108,7 @@ pub type ExportMap2 = NodeMap<Vec<Export2>>;
 
 pub struct Export2 {
     pub name: String,        // The name of the target.
-    pub def_id: DefId,     // The definition of the target.
+    pub def_id: DefId,       // The definition of the target.
 }
 
 // This set contains all exported definitions from external crates. The set does
@@ -314,7 +314,7 @@ impl<'a> Copy for TypeParameters<'a> {}
 
 // The rib kind controls the translation of local
 // definitions (`DefLocal`) to upvars (`DefUpvar`).
-
+#[deriving(Show)]
 enum RibKind {
     // No translation needs to be applied.
     NormalRibKind,
@@ -340,6 +340,7 @@ enum RibKind {
 impl Copy for RibKind {}
 
 // Methods can be required or provided. RequiredMethod methods only occur in traits.
+#[deriving(Show)]
 enum MethodSort {
     RequiredMethod,
     ProvidedMethod(NodeId)
@@ -414,6 +415,7 @@ enum DuplicateCheckingMode {
 impl Copy for DuplicateCheckingMode {}
 
 /// One local scope.
+#[deriving(Show)]
 struct Rib {
     bindings: HashMap<Name, DefLike>,
     kind: RibKind,
@@ -728,8 +730,11 @@ impl NameBindings {
         let type_def = self.type_def.borrow().clone();
         match type_def {
             None => {
-                let module = Module::new(parent_link, def_id, kind,
-                                         external, is_public);
+                let module = Module::new(parent_link,
+                                         def_id,
+                                         kind,
+                                         external,
+                                         is_public);
                 *self.type_def.borrow_mut() = Some(TypeNsDef {
                     modifiers: modifiers,
                     module_def: Some(Rc::new(module)),
@@ -774,9 +779,9 @@ impl NameBindings {
             }
             Some(type_def) => {
                 *self.type_def.borrow_mut() = Some(TypeNsDef {
+                    module_def: type_def.module_def,
                     type_def: Some(def),
                     type_span: Some(sp),
-                    module_def: type_def.module_def,
                     modifiers: modifiers,
                 });
             }
@@ -1286,7 +1291,7 @@ impl<'a> Resolver<'a> {
     }
 
     fn get_parent_link(&mut self, parent: ReducedGraphParent, name: Name)
-                           -> ParentLink {
+                       -> ParentLink {
         match parent {
             ModuleReducedGraphParent(module_) => {
                 return ModuleParentLink(module_.downgrade(), name);
@@ -1578,14 +1583,14 @@ impl<'a> Resolver<'a> {
 
             ItemImpl(_, Some(_), _, _) => parent,
 
-            ItemTrait(_, _, _, ref methods) => {
+            ItemTrait(_, _, _, ref items) => {
                 let name_bindings =
                     self.add_child(name,
                                    parent.clone(),
                                    ForbidDuplicateTypesAndModules,
                                    sp);
 
-                // Add all the methods within to a new module.
+                // Add all the items within to a new module.
                 let parent_link = self.get_parent_link(parent.clone(), name);
                 name_bindings.define_module(parent_link,
                                             Some(local_def(item.id)),
@@ -1598,13 +1603,12 @@ impl<'a> Resolver<'a> {
 
                 let def_id = local_def(item.id);
 
-                // Add the names of all the methods to the trait info.
-                for method in methods.iter() {
-                    let (name, kind) = match *method {
+                // Add the names of all the items to the trait info.
+                for trait_item in items.iter() {
+                    let (name, kind) = match *trait_item {
                         ast::RequiredMethod(_) |
                         ast::ProvidedMethod(_) => {
-                            let ty_m =
-                                ast_util::trait_item_to_ty_method(method);
+                            let ty_m = ast_util::trait_item_to_ty_method(trait_item);
 
                             let name = ty_m.ident.name;
 
@@ -3353,7 +3357,7 @@ impl<'a> Resolver<'a> {
                            use_lexical_scope: UseLexicalScopeFlag,
                            span: Span,
                            name_search_type: NameSearchType)
-                               -> ResolveResult<(Rc<Module>, LastPrivate)> {
+                           -> ResolveResult<(Rc<Module>, LastPrivate)> {
         let module_path_len = module_path.len();
         assert!(module_path_len > 0);
 
@@ -3382,7 +3386,9 @@ impl<'a> Resolver<'a> {
                                             mpath.slice_to(idx - 1));
                         return Failed(Some((span, msg)));
                     },
-                    None => return Failed(None),
+                    None => {
+                        return Failed(None)
+                    }
                 }
             }
             Failed(err) => return Failed(err),
@@ -3407,9 +3413,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) \
@@ -3576,8 +3581,7 @@ impl<'a> Resolver<'a> {
                                 -> ResolveResult<Rc<Module>> {
         // If this module is an anonymous module, resolve the item in the
         // lexical scope. Otherwise, resolve the item from the crate root.
-        let resolve_result = self.resolve_item_in_lexical_scope(
-            module_, name, TypeNS);
+        let resolve_result = self.resolve_item_in_lexical_scope(module_, name, TypeNS);
         match resolve_result {
             Success((target, _)) => {
                 let bindings = &*target.bindings;
@@ -4590,25 +4594,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::WherePredicate::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 bound_pred.bounds.iter() {
+                        self.resolve_type_parameter_bound(bound_pred.id, bound,
+                                                          TraitBoundingTypeParameter);
+                    }
                 }
-            }
+                &ast::WherePredicate::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");
+                        }
+                    }
 
-            for bound in predicate.bounds.iter() {
-                self.resolve_type_parameter_bound(predicate.id, bound,
-                                                  TraitBoundingTypeParameter);
+                    self.resolve_type(&*eq_pred.ty);
+                }
             }
         }
     }
@@ -5269,15 +5290,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()
@@ -5307,15 +5332,15 @@ impl<'a> Resolver<'a> {
 
     // resolve a single identifier (used as a varref)
     fn resolve_identifier(&mut self,
-                              identifier: Ident,
-                              namespace: Namespace,
-                              check_ribs: bool,
-                              span: Span)
-                              -> Option<(Def, LastPrivate)> {
+                          identifier: Ident,
+                          namespace: Namespace,
+                          check_ribs: bool,
+                          span: Span)
+                          -> Option<(Def, LastPrivate)> {
         if check_ribs {
             match self.resolve_identifier_in_local_ribs(identifier,
-                                                      namespace,
-                                                      span) {
+                                                        namespace,
+                                                        span) {
                 Some(def) => {
                     return Some((def, LastMod(AllPublic)));
                 }
@@ -5333,7 +5358,7 @@ impl<'a> Resolver<'a> {
                                             containing_module: Rc<Module>,
                                             name: Name,
                                             namespace: Namespace)
-                                                -> NameDefinition {
+                                            -> NameDefinition {
         // First, search children.
         self.populate_module_if_necessary(&containing_module);
 
@@ -5403,9 +5428,9 @@ impl<'a> Resolver<'a> {
 
     // resolve a "module-relative" path, e.g. a::b::c
     fn resolve_module_relative_path(&mut self,
-                                        path: &Path,
-                                        namespace: Namespace)
-                                        -> Option<(Def, LastPrivate)> {
+                                    path: &Path,
+                                    namespace: Namespace)
+                                    -> Option<(Def, LastPrivate)> {
         let module_path = path.segments.init().iter()
                                               .map(|ps| ps.identifier.name)
                                               .collect::<Vec<_>>();
@@ -5422,9 +5447,8 @@ impl<'a> Resolver<'a> {
                 let (span, msg) = match err {
                     Some((span, msg)) => (span, msg),
                     None => {
-                        let msg = format!("Use of undeclared module `{}`",
-                                          self.names_to_string(
-                                               module_path.as_slice()));
+                        let msg = format!("Use of undeclared type or module `{}`",
+                                          self.names_to_string(module_path.as_slice()));
                         (path.span, msg)
                     }
                 };
@@ -5518,10 +5542,10 @@ impl<'a> Resolver<'a> {
     }
 
     fn resolve_identifier_in_local_ribs(&mut self,
-                                            ident: Ident,
-                                            namespace: Namespace,
-                                            span: Span)
-                                            -> Option<Def> {
+                                        ident: Ident,
+                                        namespace: Namespace,
+                                        span: Span)
+                                        -> Option<Def> {
         // Check the local set of ribs.
         let search_result = match namespace {
             ValueNS => {
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 2ba9ba5631d..3ab94d3ca66 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -210,8 +210,22 @@ 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::WherePredicate::BoundPredicate(ast::WhereBoundPredicate{ ident,
+                                                                               ref bounds,
+                                                                               span,
+                                                                               .. }) => {
+                    self.visit_ident(span, ident);
+                    visit::walk_ty_param_bounds_helper(self, bounds);
+                }
+                &ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ id,
+                                                                         ref path,
+                                                                         ref ty,
+                                                                         .. }) => {
+                    self.visit_path(path, id);
+                    self.visit_ty(&**ty);
+                }
+            }
         }
     }
 
@@ -486,7 +500,12 @@ fn early_bound_lifetime_names(generics: &ast::Generics) -> Vec<ast::Name> {
             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::WherePredicate::BoundPredicate(ast::WhereBoundPredicate{ref bounds, ..}) => {
+                    visit::walk_ty_param_bounds_helper(&mut collector, bounds);
+                }
+                &ast::WherePredicate::EqPredicate(_) => unimplemented!()
+            }
         }
     }
 
diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs
index bcc762a9640..d3b1c2d2afc 100644
--- a/src/librustc/middle/subst.rs
+++ b/src/librustc/middle/subst.rs
@@ -112,7 +112,7 @@ impl<'tcx> Substs<'tcx> {
         }
     }
 
-pub fn self_ty(&self) -> Option<Ty<'tcx>> {
+    pub fn self_ty(&self) -> Option<Ty<'tcx>> {
         self.types.get_self().map(|&t| t)
     }
 
@@ -123,6 +123,13 @@ pub fn self_ty(&self) -> Option<Ty<'tcx>> {
         s
     }
 
+    pub fn with_assoc_tys(&self, assoc_tys: Vec<Ty<'tcx>>) -> 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..159b6961782 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;
@@ -139,7 +139,7 @@ impl<'cx, 'tcx> Iterator<Rc<ty::TraitRef<'tcx>>> for Supertraits<'cx, 'tcx> {
 }
 
 // determine the `self` type, using fresh variables for all variables
-// declared on the impl declaration e.g., `impl<A,B> for ~[(A,B)]`
+// declared on the impl declaration e.g., `impl<A,B> for Box<[(A,B)]>`
 // would return ($0, $1) where $0 and $1 are freshly instantiated type
 // variables.
 pub fn fresh_substs_for_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
@@ -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/middle/ty.rs b/src/librustc/middle/ty.rs
index 4c4b5d07f50..3f555ec5c4c 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -1613,7 +1613,7 @@ pub struct RegionParameterDef {
     pub bounds: Vec<ty::Region>,
 }
 
-/// Information about the type/lifetime parameters associated with an
+/// Information about the formal type/lifetime parameters associated with an
 /// item or method. Analogous to ast::Generics.
 #[deriving(Clone, Show)]
 pub struct Generics<'tcx> {
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 790d882f836..e4edfadb3aa 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -84,13 +84,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<Ty<'tcx>>,
+                               self_ty: Option<Ty<'tcx>>,
+                               // 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<Ty<'tcx>>;
 }
 
 pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &ast::Lifetime)
@@ -208,7 +213,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<Ty<'tcx>>,
     path: &ast::Path)
     -> Substs<'tcx>
     where AC: AstConv<'tcx>, RS: RegionScope
@@ -226,19 +230,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>(
@@ -249,7 +260,8 @@ fn create_substs_for_ast_path<'tcx,AC,RS>(
     decl_generics: &ty::Generics<'tcx>,
     self_ty: Option<Ty<'tcx>>,
     types: Vec<Ty<'tcx>>,
-    regions: Vec<ty::Region>)
+    regions: Vec<ty::Region>,
+    assoc_bindings: Vec<(ast::Ident, Ty<'tcx>)>)
     -> Substs<'tcx>
     where AC: AstConv<'tcx>, RS: RegionScope
 {
@@ -356,13 +368,41 @@ 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));
+    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);
+                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);
+                }
+                None => {
+                    substs.types.push(AssocSpace, ty::mk_err());
+                    span_err!(this.tcx().sess, span, E0171,
+                              "missing type for associated type `{}`",
+                              token::get_ident(formal_assoc.name.ident()));
+                }
+            }
+        }
+    }
+
+    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;
@@ -371,7 +411,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<ty::Region>, Vec<Ty<'tcx>>)
+                                                    -> (Vec<ty::Region>,
+                                                        Vec<Ty<'tcx>>,
+                                                        Vec<(ast::Ident, Ty<'tcx>)>)
     where AC: AstConv<'tcx>, RS: RegionScope
 {
     let regions: Vec<_> =
@@ -384,7 +426,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
@@ -468,16 +515,6 @@ fn convert_parenthesized_parameters<'tcx,AC>(this: &AC,
     vec![input_ty, output]
 }
 
-pub fn instantiate_poly_trait_ref<'tcx,AC,RS>(
-    this: &AC,
-    rscope: &RS,
-    ast_trait_ref: &ast::PolyTraitRef,
-    self_ty: Option<Ty<'tcx>>)
-    -> Rc<ty::TraitRef<'tcx>>
-    where AC: AstConv<'tcx>, RS: RegionScope
-{
-    instantiate_trait_ref(this, rscope, &ast_trait_ref.trait_ref, self_ty)
-}
 
 /// 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
@@ -485,7 +522,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<Ty<'tcx>>)
+                                         self_ty: Option<Ty<'tcx>>,
+                                         allow_eq: AllowEqConstraints)
                                          -> Rc<ty::TraitRef<'tcx>>
                                          where AC: AstConv<'tcx>,
                                                RS: RegionScope
@@ -494,8 +532,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
@@ -508,15 +550,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<Ty<'tcx>>,
-    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
@@ -526,15 +576,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,
@@ -542,7 +597,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)
 }
@@ -564,7 +620,6 @@ pub fn ast_path_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
                                         rscope,
                                         did,
                                         &generics,
-                                        None,
                                         path);
     let ty = decl_ty.subst(tcx, &substs);
     TypeAndSubsts { substs: substs, ty: ty }
@@ -605,7 +660,7 @@ pub fn ast_path_to_ty_relaxed<'tcx,AC,RS>(
         Substs::new(VecPerParamSpace::params_from_type(type_params),
                     VecPerParamSpace::params_from_type(region_params))
     } else {
-        ast_path_substs_for_ty(this, rscope, did, &generics, None, path)
+        ast_path_substs_for_ty(this, rscope, did, &generics, path)
     };
 
     let ty = decl_ty.subst(tcx, &substs);
@@ -694,7 +749,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");
@@ -703,7 +759,7 @@ fn ast_ty_to_trait_ref<'tcx,AC,RS>(this: &AC,
             }
         }
         _ => {
-            span_err!(this.tcx().sess, ty.span, E0171,
+            span_err!(this.tcx().sess, ty.span, E0178,
                       "expected a path on the left-hand side of `+`, not `{}`",
                       pprust::ty_to_string(ty));
             match ty.node {
@@ -714,8 +770,7 @@ fn ast_ty_to_trait_ref<'tcx,AC,RS>(this: &AC,
                                pprust::ty_to_string(&*mut_ty.ty),
                                pprust::bounds_to_string(bounds));
                 }
-
-                ast::TyRptr(Some(ref lt), ref mut_ty) => {
+               ast::TyRptr(Some(ref lt), ref mut_ty) => {
                     span_note!(this.tcx().sess, ty.span,
                                "perhaps you meant `&{} {}({} +{})`? (per RFC 248)",
                                pprust::lifetime_to_string(lt),
@@ -732,7 +787,6 @@ fn ast_ty_to_trait_ref<'tcx,AC,RS>(this: &AC,
             Err(ErrorReported)
         }
     }
-
 }
 
 fn trait_ref_to_object_type<'tcx,AC,RS>(this: &AC,
@@ -773,7 +827,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()));
 
@@ -917,7 +972,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) => {
@@ -1341,7 +1397,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 18ab4f79b0b..4d23161f6de 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -310,7 +310,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);
@@ -357,7 +357,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);
 
@@ -394,6 +394,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
     fn elaborate_bounds(
         &mut self,
         bounds: &[Rc<ty::TraitRef<'tcx>>],
+        num_includes_types: bool,
         mk_cand: for<'a> |this: &mut ProbeContext<'a, 'tcx>,
                           tr: Rc<ty::TraitRef<'tcx>>,
                           m: Rc<ty::Method<'tcx>>,
@@ -407,7 +408,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; }
             };
@@ -1003,12 +1007,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<ty::Method<'tcx>>)>
 {
     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)))
@@ -1025,7 +1035,7 @@ fn get_method_index<'tcx>(tcx: &ty::ctxt<'tcx>,
     // iterating down the supertraits of the object's trait until
     // we find the trait the method came from, counting up the
     // methods from them.
-    let mut method_count = 0;
+    let mut method_count = n_method;
     ty::each_bound_trait_and_supertraits(tcx, &[subtrait], |bound_ref| {
         if bound_ref.def_id == trait_ref.def_id {
             false
@@ -1040,7 +1050,7 @@ fn get_method_index<'tcx>(tcx: &ty::ctxt<'tcx>,
             true
         }
     });
-    method_count + n_method
+    method_count
 }
 
 impl<'tcx> Candidate<'tcx> {
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 1a8b06ec12d..7e29e7078d4 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<Ty<'tcx>>,
                                _: ast::DefId,
                                _: ast::DefId)
-                               -> Ty<'tcx> {
+                               -> Option<Ty<'tcx>> {
         self.tcx().sess.span_err(span, "unsupported associated type binding");
-        ty::mk_err()
+        Some(ty::mk_err())
     }
 }
 
@@ -5281,10 +5281,18 @@ 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 data.bindings.len() > 0 {
+            span_err!(fcx.tcx().sess, data.bindings[0].span, E0182,
+                      "unexpected binding of associated item in expression path \
+                       (only allowed in type paths)");
+            substs.types.truncate(subst::ParamSpace::AssocSpace, 0);
+        }
+
         {
             let region_count = region_defs.len(space);
             assert_eq!(substs.regions().len(space), 0);
@@ -5299,6 +5307,7 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                         region_count,
                         data.lifetimes.len());
                     substs.mut_regions().truncate(space, 0);
+                    break;
                 }
             }
         }
diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs
index c4e1f6fe8eb..defad95f749 100644
--- a/src/librustc_typeck/coherence/mod.rs
+++ b/src/librustc_typeck/coherence/mod.rs
@@ -562,7 +562,7 @@ fn enforce_trait_manually_implementable(tcx: &ty::ctxt, sp: Span, trait_def_id:
     } else {
         return // everything OK
     };
-    span_err!(tcx.sess, sp, E0173, "manual implementations of `{}` are experimental", trait_name);
+    span_err!(tcx.sess, sp, E0183, "manual implementations of `{}` are experimental", trait_name);
     span_help!(tcx.sess, sp,
                "add `#![feature(unboxed_closures)]` to the crate attributes to enable");
 }
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 4ad6dc292a7..6b7f08e8104 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<Ty<'tcx>>,
                                _: ast::DefId,
                                _: ast::DefId)
-                               -> Ty<'tcx> {
+                               -> Option<Ty<'tcx>> {
         self.tcx().sess.span_err(span, "associated types may not be \
                                         referenced here");
-        ty::mk_err()
+        Some(ty::mk_err())
     }
 }
 
@@ -663,48 +663,43 @@ fn is_associated_type_valid_for_param(ty: Ty,
 
 fn find_associated_type_in_generics<'tcx>(tcx: &ty::ctxt<'tcx>,
                                           span: Span,
-                                          ty: Option<Ty<'tcx>>,
+                                          self_ty: Option<Ty<'tcx>>,
                                           associated_type_id: ast::DefId,
                                           generics: &ty::Generics<'tcx>)
-                                          -> Ty<'tcx>
+                                          -> Option<Ty<'tcx>>
 {
     debug!("find_associated_type_in_generics(ty={}, associated_type_id={}, generics={}",
-           ty.repr(tcx), associated_type_id.repr(tcx), generics.repr(tcx));
+           self_ty.repr(tcx), associated_type_id.repr(tcx), generics.repr(tcx));
 
-    let ty = match ty {
+    let self_ty = match self_ty {
         None => {
-            tcx.sess.span_bug(span,
-                              "find_associated_type_in_generics(): no self \
-                               type")
+            return None;
         }
         Some(ty) => ty,
     };
 
-    match ty.sty {
+    match self_ty.sty {
         ty::ty_param(ref param_ty) => {
-            /*let type_parameter = generics.types.get(param_ty.space,
-                                                    param_ty.idx);
-            let param_id = type_parameter.def_id;*/
             let param_id = param_ty.def_id;
             for type_parameter in generics.types.iter() {
                 if type_parameter.def_id == associated_type_id
                     && type_parameter.associated_with == Some(param_id) {
-                    return ty::mk_param_from_def(tcx, type_parameter);
+                    return Some(ty::mk_param_from_def(tcx, type_parameter));
                 }
             }
 
             tcx.sess.span_err(
                 span,
                 format!("no suitable bound on `{}`",
-                        ty.user_string(tcx))[]);
-            ty::mk_err()
+                        self_ty.user_string(tcx))[]);
+            Some(ty::mk_err())
         }
         _ => {
             tcx.sess.span_err(
                 span,
                 "it is currently unsupported to access associated types except \
                  through a type parameter; this restriction will be lifted in time");
-            ty::mk_err()
+            Some(ty::mk_err())
         }
     }
 }
@@ -762,16 +757,16 @@ impl<'a,'tcx> AstConv<'tcx> for ImplCtxt<'a,'tcx> {
 
     fn associated_type_binding(&self,
                                span: Span,
-                               ty: Option<Ty<'tcx>>,
+                               self_ty: Option<Ty<'tcx>>,
                                trait_id: ast::DefId,
                                associated_type_id: ast::DefId)
-                               -> Ty<'tcx>
+                               -> Option<Ty<'tcx>>
     {
-        let trait_def = ty::lookup_trait_def(self.tcx(), trait_id);
         match self.opt_trait_ref_id {
+            // It's an associated type on the trait that we're
+            // implementing.
             Some(trait_ref_id) if trait_ref_id == trait_id => {
-                // It's an associated type on the trait that we're
-                // implementing.
+                let trait_def = ty::lookup_trait_def(self.tcx(), trait_id);
                 assert!(trait_def.generics.types
                         .get_slice(subst::AssocSpace)
                         .iter()
@@ -782,7 +777,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))
                             }
                         }
                     }
@@ -801,7 +796,7 @@ impl<'a,'tcx> AstConv<'tcx> for ImplCtxt<'a,'tcx> {
         // our bounds.
         find_associated_type_in_generics(self.ccx.tcx,
                                          span,
-                                         ty,
+                                         self_ty,
                                          associated_type_id,
                                          self.impl_generics)
     }
@@ -840,17 +835,17 @@ impl<'a,'tcx> AstConv<'tcx> for FnCtxt<'a,'tcx> {
 
     fn associated_type_binding(&self,
                                span: Span,
-                               ty: Option<Ty<'tcx>>,
+                               self_ty: Option<Ty<'tcx>>,
                                _: ast::DefId,
                                associated_type_id: ast::DefId)
-                               -> Ty<'tcx> {
+                               -> Option<Ty<'tcx>> {
         debug!("collect::FnCtxt::associated_type_binding()");
 
         // The ID should map to an associated type on one of the traits in
         // our bounds.
         find_associated_type_in_generics(self.ccx.tcx,
                                          span,
-                                         ty,
+                                         self_ty,
                                          associated_type_id,
                                          self.generics)
     }
@@ -887,17 +882,17 @@ impl<'a,'tcx> AstConv<'tcx> for ImplMethodCtxt<'a,'tcx> {
 
     fn associated_type_binding(&self,
                                span: Span,
-                               ty: Option<Ty<'tcx>>,
+                               self_ty: Option<Ty<'tcx>>,
                                _: ast::DefId,
                                associated_type_id: ast::DefId)
-                               -> Ty<'tcx> {
+                               -> Option<Ty<'tcx>> {
         debug!("collect::ImplMethodCtxt::associated_type_binding()");
 
         // The ID should map to an associated type on one of the traits in
         // our bounds.
         find_associated_type_in_generics(self.ccx.tcx,
                                          span,
-                                         ty,
+                                         self_ty,
                                          associated_type_id,
                                          self.method_generics)
     }
@@ -943,10 +938,10 @@ impl<'a,'tcx> AstConv<'tcx> for TraitMethodCtxt<'a,'tcx> {
 
     fn associated_type_binding(&self,
                                span: Span,
-                               ty: Option<Ty<'tcx>>,
+                               self_ty: Option<Ty<'tcx>>,
                                trait_id: ast::DefId,
                                associated_type_id: ast::DefId)
-                               -> Ty<'tcx> {
+                               -> Option<Ty<'tcx>> {
         debug!("collect::TraitMethodCtxt::associated_type_binding()");
 
         // If this is one of our own associated types, return it.
@@ -957,10 +952,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;
                     }
@@ -979,7 +974,7 @@ impl<'a,'tcx> AstConv<'tcx> for TraitMethodCtxt<'a,'tcx> {
         // our bounds.
         find_associated_type_in_generics(self.ccx.tcx,
                                          span,
-                                         ty,
+                                         self_ty,
                                          associated_type_id,
                                          self.method_generics)
     }
@@ -1020,17 +1015,17 @@ impl<'a,'tcx,AC:AstConv<'tcx>> AstConv<'tcx> for GenericsCtxt<'a,'tcx,AC> {
 
     fn associated_type_binding(&self,
                                span: Span,
-                               ty: Option<Ty<'tcx>>,
+                               self_ty: Option<Ty<'tcx>>,
                                _: ast::DefId,
                                associated_type_id: ast::DefId)
-                               -> Ty<'tcx> {
+                               -> Option<Ty<'tcx>> {
         debug!("collect::GenericsCtxt::associated_type_binding()");
 
         // The ID should map to an associated type on one of the traits in
         // our bounds.
         find_associated_type_in_generics(self.chain.tcx(),
                                          span,
-                                         ty,
+                                         self_ty,
                                          associated_type_id,
                                          self.associated_types_generics)
     }
@@ -1142,8 +1137,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) => {
@@ -1361,8 +1359,12 @@ pub fn trait_def_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
 
     let self_param_ty = ty::ParamTy::for_self(def_id);
 
-    let bounds = compute_bounds(ccx, token::SELF_KEYWORD_NAME, self_param_ty,
-                                bounds.as_slice(), unbound, it.span,
+    let bounds = compute_bounds(ccx,
+                                token::SELF_KEYWORD_NAME,
+                                self_param_ty,
+                                bounds.as_slice(),
+                                unbound,
+                                it.span,
                                 &generics.where_clause);
 
     let substs = mk_item_substs(ccx, &ty_generics);
@@ -1806,7 +1808,7 @@ fn ty_generics<'tcx,AC>(this: &AC,
 
     return result;
 
-    fn create_type_parameters_for_associated_types<'tcx,AC>(
+    fn create_type_parameters_for_associated_types<'tcx, AC>(
         this: &AC,
         space: subst::ParamSpace,
         types: &[ast::TyParam],
@@ -1856,8 +1858,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 associated 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,
@@ -2016,10 +2027,11 @@ fn conv_param_bounds<'tcx,AC>(this: &AC,
     let trait_bounds: Vec<Rc<ty::TraitRef>> =
         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<ty::Region> =
@@ -2047,18 +2059,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::WherePredicate::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::WherePredicate::EqPredicate(_) => panic!("not implemented")
         }
     }
 
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index e026fbd05c7..ecd3cafd91f 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -149,5 +149,11 @@ register_diagnostics!(
     E0171,
     E0172,
     E0173, // manual implementations of unboxed closure traits are experimental
-    E0174 // explicit use of unboxed closure methods are experimental
+    E0174, // explicit use of unboxed closure methods are experimental
+    E0177,
+    E0178,
+    E0180,
+    E0181,
+    E0182,
+    E0183
 )
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index df7b922bd1a..630d41fa7e2 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -693,9 +693,16 @@ pub struct WherePredicate {
 
 impl Clean<WherePredicate> for ast::WherePredicate {
     fn clean(&self, cx: &DocContext) -> WherePredicate {
-        WherePredicate {
-            name: self.ident.clean(cx),
-            bounds: self.bounds.clean(cx)
+        match *self {
+            ast::WherePredicate::BoundPredicate(ref wbp) => {
+                WherePredicate {
+                    name: wbp.ident.clean(cx),
+                    bounds: wbp.bounds.clean(cx)
+                }
+            }
+            ast::WherePredicate::EqPredicate(_) => {
+                unimplemented!();
+            }
         }
     }
 }
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<TypeBinding>> {
+        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<Lifetime>,
     /// The type parameters for this path segment, if present.
     pub types: OwnedSlice<P<Ty>>,
+    /// Bindings (equality constraints) on associated types, if present.
+    /// E.g., `Foo<A=Bar>`.
+    pub bindings: OwnedSlice<P<TypeBinding>>,
 }
 
 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<TyParamBound>,
 }
 
+#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
+pub struct WhereEqPredicate {
+    pub id: NodeId,
+    pub span: Span,
+    pub path: Path,
+    pub ty: P<Ty>,
+}
+
 /// The set of MetaItems that define the compilation environment of the crate,
 /// used to drive conditional compilation
 pub type CrateConfig = Vec<P<MetaItem>> ;
@@ -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<Ty>,
+    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<Ident> {
+    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<Pat> {
     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<ast::Ident> ,
                 lifetimes: Vec<ast::Lifetime>,
-                types: Vec<P<ast::Ty>> )
+                types: Vec<P<ast::Ty>>,
+                bindings: Vec<P<ast::TypeBinding>> )
         -> ast::Path;
 
     // types
@@ -293,20 +294,21 @@ pub trait AstBuilder {
 
 impl<'a> AstBuilder for ExtCtxt<'a> {
     fn path(&self, span: Span, strs: Vec<ast::Ident> ) -> 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::Ident> ) -> 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<ast::Ident> ,
                 lifetimes: Vec<ast::Lifetime>,
-                types: Vec<P<ast::Ty>> )
+                types: Vec<P<ast::Ty>>,
+                bindings: Vec<P<ast::TypeBinding>> )
                 -> ast::Path {
         let last_identifier = idents.pop().unwrap();
         let mut segments: Vec<ast::PathSegment> = 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::Ty>) -> 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<TypeBinding>) -> P<TypeBinding> {
+        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<T: Folder>(d: P<Decl>, fld: &mut T) -> SmallVector<P<Decl>
     })
 }
 
+pub fn noop_fold_ty_binding<T: Folder>(b: P<TypeBinding>, fld: &mut T) -> P<TypeBinding> {
+    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: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> {
     t.map(|Ty {id, node, span}| Ty {
         id: fld.new_id(id),
@@ -533,9 +546,10 @@ pub fn noop_fold_angle_bracketed_parameter_data<T: Folder>(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<T: Folder>(data: ParenthesizedParameterData,
@@ -807,14 +821,32 @@ pub fn noop_fold_where_clause<T: Folder>(
 }
 
 pub fn noop_fold_where_predicate<T: Folder>(
-                                 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<T>(
-                                  &mut self,
-                                  sep: Option<token::Token>,
-                                  f: |&mut Parser| -> T)
-                                  -> OwnedSlice<T> {
+    pub fn parse_seq_to_before_gt_or_return<T>(&mut self,
+                                               sep: Option<token::Token>,
+                                               f: |&mut Parser| -> Option<T>)
+                                               -> (OwnedSlice<T>, 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);
+    }
+
+    /// Parse a sequence bracketed by '<' and '>', stopping
+    /// before the '>'.
+    pub fn parse_seq_to_before_gt<T>(&mut self,
+                                     sep: Option<token::Token>,
+                                     f: |&mut Parser| -> T)
+                                     -> OwnedSlice<T> {
+        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<T>(
-                           &mut self,
-                           sep: Option<token::Token>,
-                           f: |&mut Parser| -> T)
-                           -> OwnedSlice<T> {
+    pub fn parse_seq_to_gt<T>(&mut self,
+                              sep: Option<token::Token>,
+                              f: |&mut Parser| -> T)
+                              -> OwnedSlice<T> {
         let v = self.parse_seq_to_before_gt(sep, f);
         self.expect_gt();
         return v;
     }
 
+    pub fn parse_seq_to_gt_or_return<T>(&mut self,
+                                        sep: Option<token::Token>,
+                                        f: |&mut Parser| -> Option<T>)
+                                        -> (OwnedSlice<T>, 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<ast::Lifetime>, Vec<P<Ty>> ) {
+    fn parse_generic_values_after_lt(&mut self)
+                                     -> (Vec<ast::Lifetime>, Vec<P<Ty>>, Vec<P<TypeBinding>>) {
         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();
+                if p.look_ahead(1, |t| t == &token::Eq) {
+                    None
+                } else {
+                    Some(p.parse_ty_sum())
+                }
+            }
+        );
+
+        // 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();
-                p.parse_ty_sum()
+                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, result.into_vec())
+        (lifetimes, types.into_vec(), bindings.into_vec())
     }
 
     fn forbid_lifetime(&mut self) {
@@ -4070,29 +4134,58 @@ 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");
+                }
 
-            generics.where_clause.predicates.push(ast::WherePredicate {
-                id: ast::DUMMY_NODE_ID,
-                span: span,
-                ident: ident,
-                bounds: bounds,
-            });
-            parsed_something = true;
+                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");
+            }
 
             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-fulldeps/phase-syntax-doesnt-resolve.rs b/src/test/compile-fail-fulldeps/phase-syntax-doesnt-resolve.rs
index 3972d01850e..00aeb1c1bae 100644
--- a/src/test/compile-fail-fulldeps/phase-syntax-doesnt-resolve.rs
+++ b/src/test/compile-fail-fulldeps/phase-syntax-doesnt-resolve.rs
@@ -19,6 +19,6 @@ extern crate macro_crate_test;
 
 fn main() {
     macro_crate_test::foo();
-    //~^ ERROR failed to resolve. Use of undeclared module `macro_crate_test`
+    //~^ ERROR failed to resolve. Use of undeclared type or module `macro_crate_test`
     //~^^ ERROR unresolved name `macro_crate_test::foo`
 }
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 <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 equality constraints on associated types. Check that unsupported syntax
+// does not ICE.
+
+#![feature(associated_types)]
+
+pub trait Foo {
+    type A;
+    fn boo(&self) -> <Self as Foo>::A;
+}
+
+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() {}
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 <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 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) -> <Self as Foo>::A;
+}
+
+struct Bar;
+
+impl Foo for int {
+    type A = uint;
+    fn boo(&self) -> uint { 42 }
+}
+
+fn baz<I: Foo>(x: &<I as Foo<A=Bar>>::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 <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 equality constraints on associated types. Check we get type errors
+// where we should.
+
+#![feature(associated_types)]
+
+pub trait Foo {
+    type A;
+    fn boo(&self) -> <Self as Foo>::A;
+}
+
+struct Bar;
+
+impl Foo for int {
+    type A = uint;
+    fn boo(&self) -> uint {
+        42
+    }
+}
+
+fn foo1<I: Foo<A=Bar>>(x: I) {
+    let _: Bar = x.boo();
+}
+
+fn foo2<I: Foo>(x: I) {
+    let _: Bar = x.boo(); //~ERROR mismatched types
+}
+
+
+pub fn baz(x: &Foo<A=Bar>) {
+    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/assoc-eq-expr-path.rs b/src/test/compile-fail/assoc-eq-expr-path.rs
new file mode 100644
index 00000000000..1a96b0ca681
--- /dev/null
+++ b/src/test/compile-fail/assoc-eq-expr-path.rs
@@ -0,0 +1,28 @@
+// 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.
+
+// Check that an associated type cannot be bound in an expression path.
+
+#![feature(associated_types)]
+
+trait Foo {
+    type A;
+    fn bar() -> int;
+}
+
+impl Foo for int {
+    type A = uint;
+    fn bar() -> int { 42 }
+}
+
+pub fn main() {
+    let x: int = Foo::<A=uint>::bar();
+    //~^ERROR unexpected binding of associated item in expression path
+}
diff --git a/src/test/compile-fail/hrtb-precedence-of-plus-error-message.rs b/src/test/compile-fail/hrtb-precedence-of-plus-error-message.rs
index ff3512ad8e7..41a0be37add 100644
--- a/src/test/compile-fail/hrtb-precedence-of-plus-error-message.rs
+++ b/src/test/compile-fail/hrtb-precedence-of-plus-error-message.rs
@@ -18,17 +18,17 @@ trait Bar {
 
 struct Foo<'a> {
     a: &'a Bar+'a,
-        //~^ ERROR E0171
+        //~^ ERROR E0178
         //~^^ NOTE perhaps you meant `&'a (Bar + 'a)`?
 
     b: &'a mut Bar+'a,
-        //~^ ERROR E0171
+        //~^ ERROR E0178
         //~^^ NOTE perhaps you meant `&'a mut (Bar + 'a)`?
 
     c: Box<Bar+'a>, // OK, no paren needed in this context
 
     d: fn() -> Bar+'a,
-        //~^ ERROR E0171
+        //~^ ERROR E0178
         //~^^ NOTE perhaps you forgot parentheses
         //~^^^ WARN deprecated syntax
 }
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 <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 equality constraints on associated types.
+
+#![feature(associated_types)]
+
+pub trait Foo {
+    type A;
+    fn boo(&self) -> <Self as Foo>::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<I: Foo<A=Bar>>(x: I) -> Bar {
+    x.boo()
+}
+fn foo2<I: Foo>(x: I) -> <I as Foo>::A {
+    x.boo()
+}
+fn baz(x: &Foo<A=Bar>) -> 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);
+}