diff --git a/src/doc/reference.md b/src/doc/reference.md
index 9851e1c28fbf0..d6a4375536b34 100644
--- a/src/doc/reference.md
+++ b/src/doc/reference.md
@@ -2382,6 +2382,8 @@ The currently implemented features of the reference compiler are:
           semantics are likely to change, so this macro usage must be opted
           into.
 
+* `associated_types` - Allows type aliases in traits. Experimental.
+
 * `concat_idents` - Allows use of the `concat_idents` macro, which is in many
                     ways insufficient for concatenating identifiers, and may be
                     removed entirely for something more wholesome.
@@ -2389,6 +2391,9 @@ The currently implemented features of the reference compiler are:
 * `default_type_params` - Allows use of default type parameters. The future of
                           this feature is uncertain.
 
+* `equality_constraints` - Allows the use of `=` in where clauses. This
+  allows a user to demand that two type's normal forms are equal.
+
 * `intrinsics` - Allows use of the "rust-intrinsics" ABI. Compiler intrinsics
                  are inherently unstable and no promise about them is made.
 
@@ -2465,8 +2470,6 @@ The currently implemented features of the reference compiler are:
                    Such items should not be allowed by the compiler to exist,
                    so if you need this there probably is a compiler bug.
 
-* `associated_types` - Allows type aliases in traits. Experimental.
-
 If a feature is promoted to a language feature, then all existing programs will
 start to receive compilation warnings about #[feature] directives which enabled
 the new feature (because the directive is no longer necessary). However, if a
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 5e27023e026fb..1ffacb4e9d477 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -211,12 +211,11 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
                         self.visit_lifetime_ref(bound);
                     }
                 }
-                &ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ id,
-                                                                         ref path,
-                                                                         ref ty,
+                &ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ ref ty_left,
+                                                                         ref ty_right,
                                                                          .. }) => {
-                    self.visit_path(path, id);
-                    self.visit_ty(&**ty);
+                    self.visit_ty(&**ty_left);
+                    self.visit_ty(&**ty_right);
                 }
             }
         }
@@ -555,7 +554,12 @@ fn early_bound_lifetime_names(generics: &ast::Generics) -> Vec<ast::Name> {
                         collector.visit_lifetime_ref(bound);
                     }
                 }
-                &ast::WherePredicate::EqPredicate(_) => unimplemented!()
+                &ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ref ty_left,
+                                                                        ref ty_right,
+                                                                        ..}) => {
+                    collector.visit_ty(&**ty_left);
+                    collector.visit_ty(&**ty_right);
+                }
             }
         }
     }
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index aa9b6c479bb78..dd96957f5aeca 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -1453,7 +1453,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for VisiblePrivateTypesVisitor<'a, 'tcx> {
                 }
                 &ast::WherePredicate::RegionPredicate(_) => {}
                 &ast::WherePredicate::EqPredicate(ref eq_pred) => {
-                    self.visit_ty(&*eq_pred.ty);
+                    self.visit_ty(&*eq_pred.ty_left);
+                    self.visit_ty(&*eq_pred.ty_right);
                 }
             }
         }
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index a261599a70656..54c7fdcb2ae09 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -3205,17 +3205,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 }
                 &ast::WherePredicate::RegionPredicate(_) => {}
                 &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");
-                        }
-                    }
-
-                    self.resolve_type(&*eq_pred.ty);
+                    self.resolve_type(&*eq_pred.ty_left);
+                    self.resolve_type(&*eq_pred.ty_right);
                 }
             }
         }
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index ce26658cf4b93..f582664a6cbf0 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1293,10 +1293,11 @@ fn ty_generics<'a,'tcx>(ccx: &CollectCtxt<'a,'tcx>,
             }
 
             &ast::WherePredicate::EqPredicate(ref eq_pred) => {
-                // FIXME(#20041)
-                ccx.tcx.sess.span_bug(eq_pred.span,
-                                         "Equality constraints are not yet \
-                                            implemented (#20041)")
+                let ty_left = ast_ty_to_ty(ccx, &ExplicitRscope, &*eq_pred.ty_left);
+                let ty_right = ast_ty_to_ty(ccx, &ExplicitRscope, &*eq_pred.ty_right);
+                result.predicates.push(space, ty::Predicate::Equate(
+                    ty::Binder(ty::EquatePredicate(ty_left, ty_right))
+                ));
             }
         }
     }
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 7e1bf7a2230b8..36f407014f9fa 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -458,8 +458,8 @@ pub struct WhereRegionPredicate {
 pub struct WhereEqPredicate {
     pub id: NodeId,
     pub span: Span,
-    pub path: Path,
-    pub ty: P<Ty>,
+    pub ty_left: P<Ty>,
+    pub ty_right: P<Ty>,
 }
 
 /// The set of MetaItems that define the compilation environment of the crate,
diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs
index 28573ef757b12..1c3cc22a69d24 100644
--- a/src/libsyntax/ext/deriving/generic/mod.rs
+++ b/src/libsyntax/ext/deriving/generic/mod.rs
@@ -458,8 +458,8 @@ impl<'a> TraitDef<'a> {
                     ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
                         id: ast::DUMMY_NODE_ID,
                         span: self.span,
-                        path: we.path.clone(),
-                        ty: we.ty.clone()
+                        ty_right: we.ty_right.clone(),
+                        ty_left: we.ty_left.clone()
                     })
                 }
             }
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 12efd959918e1..6948813800507 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -122,7 +122,10 @@ static KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
     ("staged_api", "1.0.0", Active),
 
     // Allows using items which are missing stability attributes
-    ("unmarked_api", "1.0.0", Active)
+    ("unmarked_api", "1.0.0", Active),
+
+    // Enables equality constraints in where clauses.
+    ("equality_constraints", "1.0.0", Active)
 ];
 
 enum Status {
@@ -503,6 +506,20 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
         }
         visit::walk_fn(self, fn_kind, fn_decl, block, span);
     }
+
+    fn visit_generics(&mut self, generics: &'v ast::Generics) {
+        for pred in generics.where_clause.predicates.iter() {
+            match pred {
+                &ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{span, ..}) => {
+                    self.gate_feature("equality_constraints",
+                                       span, "equality constraints are experimental")
+                },
+                _ => {}
+            }
+        }
+
+        visit::walk_generics(self, generics);
+    }
 }
 
 fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate,
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index b0ddb655882a8..fb660f7ed4b70 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -825,13 +825,13 @@ pub fn noop_fold_where_predicate<T: Folder>(
             })
         }
         ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{id,
-                                                               path,
-                                                               ty,
+                                                               ty_left,
+                                                               ty_right,
                                                                span}) => {
             ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{
                 id: fld.new_id(id),
-                path: fld.fold_path(path),
-                ty:fld.fold_ty(ty),
+                ty_left: fld.fold_ty(ty_left),
+                ty_right: fld.fold_ty(ty_right),
                 span: fld.new_span(span)
             })
         }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 3107f47de7859..ee5043b7d598b 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -4245,21 +4245,20 @@ impl<'a> Parser<'a> {
 
                         parsed_something = true;
                     } else if self.eat(&token::Eq) {
-                        // let ty = self.parse_ty();
+                        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: panic!("NYI"), //bounded_ty,
-                        //         ty: ty,
-                        // }));
-                        // parsed_something = true;
-                        // // FIXME(#18433)
-                        self.span_err(span,
-                                     "equality constraints are not yet supported \
-                                     in where clauses (#20041)");
+                        generics.where_clause.predicates.push(
+                            ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
+                                id: ast::DUMMY_NODE_ID,
+                                span: span,
+                                ty_left: bounded_ty,
+                                ty_right: ty,
+                        }));
+                        parsed_something = true;
+                         // self.span_err(span,
+                         //          "equality constraints are not yet supported \
+                         //             in where clauses (#20041)");
                     } else {
                         let last_span = self.last_span;
                         self.span_err(last_span,
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index c177cd1fafa55..8105d9876090e 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -2516,11 +2516,13 @@ impl<'a> State<'a> {
                         }
                     }
                 }
-                &ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ref path, ref ty, ..}) => {
-                    try!(self.print_path(path, false));
+                &ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ref ty_left,
+                                                                        ref ty_right,
+                                                                        ..}) => {
+                    try!(self.print_type(&**ty_left));
                     try!(space(&mut self.s));
                     try!(self.word_space("="));
-                    try!(self.print_type(&**ty));
+                    try!(self.print_type(&**ty_right));
                 }
             }
         }
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 21cb62b0a0c11..1c7ce992120ca 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -597,12 +597,11 @@ pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics
                     visitor.visit_lifetime_ref(bound);
                 }
             }
-            &ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{id,
-                                                                    ref path,
-                                                                    ref ty,
+            &ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ref ty_left,
+                                                                    ref ty_right,
                                                                     ..}) => {
-                visitor.visit_path(path, id);
-                visitor.visit_ty(&**ty);
+                visitor.visit_ty(&**ty_left);
+                visitor.visit_ty(&**ty_right);
             }
         }
     }
diff --git a/src/test/compile-fail/where-clause-equality-constraint.rs b/src/test/compile-fail/where-clause-equality-constraint.rs
new file mode 100644
index 0000000000000..58b76f71e910c
--- /dev/null
+++ b/src/test/compile-fail/where-clause-equality-constraint.rs
@@ -0,0 +1,30 @@
+// Copyright 2012 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.
+
+#![feature(equality_constraints)]
+
+trait Foo<A> where A = isize {
+    type Inner;
+
+    fn foo_bar(&self) where Self::Inner = String {}
+}
+
+// Failure to meet equality constraint on a trait definiton.
+impl Foo<String> for () {
+    //~^ the requirement `collections::string::String == isize` is not satisfied
+    type Inner = bool;
+
+    fn foo_bar(&self) {}
+}
+
+fn main() {
+    // Failure to meet associated type constraint on a method
+    ().foo_bar()
+}
diff --git a/src/test/run-pass/where-clause-equality-constraint.rs b/src/test/run-pass/where-clause-equality-constraint.rs
new file mode 100644
index 0000000000000..b802d2ab8c0d2
--- /dev/null
+++ b/src/test/run-pass/where-clause-equality-constraint.rs
@@ -0,0 +1,27 @@
+// Copyright 2012 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.
+
+#![feature(equality_constraints)]
+
+trait Foo<A> where A = isize {
+    type Inner;
+
+    fn foo_bar(&self) where Self::Inner = String {}
+}
+
+impl Foo<isize> for () {
+    type Inner = String;
+
+    fn foo_bar(&self) {}
+}
+
+fn main() {
+    ().foo_bar()
+}