about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSean Patrick Santos <SeanPatrickSantos@gmail.com>2015-03-25 10:53:28 -0600
committerSean Patrick Santos <SeanPatrickSantos@gmail.com>2015-04-23 21:02:29 -0600
commit29eb550ee6a9fd6961bb00e2680a5735aab95de1 (patch)
tree05443f6ba31ac17d54592e96317bca1166230dea
parent91ae5e31ab57de15ef2855c700ad4b012ea00234 (diff)
downloadrust-29eb550ee6a9fd6961bb00e2680a5735aab95de1.tar.gz
rust-29eb550ee6a9fd6961bb00e2680a5735aab95de1.zip
Get associated consts working in match patterns.
-rw-r--r--src/librustc/middle/cfg/construct.rs1
-rw-r--r--src/librustc/middle/check_match.rs10
-rw-r--r--src/librustc/middle/expr_use_visitor.rs3
-rw-r--r--src/librustc/middle/mem_categorization.rs4
-rw-r--r--src/librustc/middle/pat_util.rs20
-rw-r--r--src/librustc_lint/builtin.rs1
-rw-r--r--src/librustc_resolve/lib.rs225
-rw-r--r--src/librustc_trans/save/mod.rs3
-rw-r--r--src/librustc_trans/trans/_match.rs3
-rw-r--r--src/librustc_trans/trans/debuginfo.rs4
-rw-r--r--src/librustc_typeck/check/_match.rs87
-rw-r--r--src/librustc_typeck/check/mod.rs89
-rw-r--r--src/librustc_typeck/diagnostics.rs1
-rw-r--r--src/librustdoc/clean/mod.rs2
-rw-r--r--src/libsyntax/ast.rs6
-rw-r--r--src/libsyntax/ast_util.rs2
-rw-r--r--src/libsyntax/fold.rs4
-rw-r--r--src/libsyntax/parse/parser.rs189
-rw-r--r--src/libsyntax/print/pprust.rs3
-rw-r--r--src/libsyntax/visit.rs4
-rw-r--r--src/test/compile-fail/method-path-in-pattern.rs35
-rw-r--r--src/test/compile-fail/method-resolvable-path-in-pattern.rs26
-rw-r--r--src/test/parse-fail/brace-after-qualified-path-in-match.rs17
-rw-r--r--src/test/parse-fail/paren-after-qualified-path-in-match.rs17
-rw-r--r--src/test/run-pass/associated-const-match-patterns.rs52
25 files changed, 610 insertions, 198 deletions
diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs
index 359a1a486c9..a7950a701f8 100644
--- a/src/librustc/middle/cfg/construct.rs
+++ b/src/librustc/middle/cfg/construct.rs
@@ -105,6 +105,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
         match pat.node {
             ast::PatIdent(_, _, None) |
             ast::PatEnum(_, None) |
+            ast::PatQPath(..) |
             ast::PatLit(..) |
             ast::PatRange(..) |
             ast::PatWild(_) => {
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index 13be6d0cb7d..a5ea3629abc 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -439,7 +439,7 @@ impl<'map> ast_util::IdVisitingOperation for RenamingRecorder<'map> {
 impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> {
     fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
         return match pat.node {
-            ast::PatIdent(..) | ast::PatEnum(..) => {
+            ast::PatIdent(..) | ast::PatEnum(..) | ast::PatQPath(..) => {
                 let def = self.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def());
                 match def {
                     Some(DefAssociatedConst(did, _)) |
@@ -762,6 +762,9 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat,
                 Some(DefVariant(_, id, _)) => vec!(Variant(id)),
                 _ => vec!(Single)
             },
+        ast::PatQPath(..) =>
+            cx.tcx.sess.span_bug(pat.span, "const pattern should've \
+                                            been rewritten"),
         ast::PatStruct(..) =>
             match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) {
                 Some(DefConst(..)) | Some(DefAssociatedConst(..)) =>
@@ -891,6 +894,11 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
             }
         }
 
+        ast::PatQPath(_, _) => {
+            cx.tcx.sess.span_bug(pat_span, "const pattern should've \
+                                            been rewritten")
+        }
+
         ast::PatStruct(_, ref pattern_fields, _) => {
             // Is this a struct or an enum variant?
             let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def();
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index 87379bd48f0..d740d24e236 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -1147,7 +1147,8 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
             let tcx = typer.tcx();
 
             match pat.node {
-                ast::PatEnum(_, _) | ast::PatIdent(_, _, None) | ast::PatStruct(..) => {
+                ast::PatEnum(_, _) | ast::PatQPath(..) |
+                ast::PatIdent(_, _, None) | ast::PatStruct(..) => {
                     match def_map.get(&pat.id).map(|d| d.full_def()) {
                         None => {
                             // no definition found: pat is not a
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 6db55baf483..587194bafad 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -1299,6 +1299,10 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
             }
           }
 
+          ast::PatQPath(..) => {
+              // Lone constant: ignore
+          }
+
           ast::PatIdent(_, _, Some(ref subpat)) => {
               try!(self.cat_pattern_(cmt, &**subpat, op));
           }
diff --git a/src/librustc/middle/pat_util.rs b/src/librustc/middle/pat_util.rs
index 7a0f58947fe..27a30f5cf25 100644
--- a/src/librustc/middle/pat_util.rs
+++ b/src/librustc/middle/pat_util.rs
@@ -30,7 +30,7 @@ pub fn pat_id_map(dm: &DefMap, pat: &ast::Pat) -> PatIdMap {
 
 pub fn pat_is_refutable(dm: &DefMap, pat: &ast::Pat) -> bool {
     match pat.node {
-        ast::PatLit(_) | ast::PatRange(_, _) => true,
+        ast::PatLit(_) | ast::PatRange(_, _) | ast::PatQPath(..) => true,
         ast::PatEnum(_, _) |
         ast::PatIdent(_, _, None) |
         ast::PatStruct(..) => {
@@ -60,7 +60,7 @@ pub fn pat_is_variant_or_struct(dm: &DefMap, pat: &ast::Pat) -> bool {
 
 pub fn pat_is_const(dm: &DefMap, pat: &ast::Pat) -> bool {
     match pat.node {
-        ast::PatIdent(_, _, None) | ast::PatEnum(..) => {
+        ast::PatIdent(_, _, None) | ast::PatEnum(..) | ast::PatQPath(..) => {
             match dm.borrow().get(&pat.id).map(|d| d.full_def()) {
                 Some(DefConst(..)) | Some(DefAssociatedConst(..)) => true,
                 _ => false
@@ -70,6 +70,22 @@ pub fn pat_is_const(dm: &DefMap, pat: &ast::Pat) -> bool {
     }
 }
 
+// Same as above, except that partially-resolved defs cause `false` to be
+// returned instead of a panic.
+pub fn pat_is_resolved_const(dm: &DefMap, pat: &ast::Pat) -> bool {
+    match pat.node {
+        ast::PatIdent(_, _, None) | ast::PatEnum(..) | ast::PatQPath(..) => {
+            match dm.borrow().get(&pat.id)
+                    .and_then(|d| if d.depth == 0 { Some(d.base_def) }
+                                  else { None } ) {
+                Some(DefConst(..)) | Some(DefAssociatedConst(..)) => true,
+                _ => false
+            }
+        }
+        _ => false
+    }
+}
+
 pub fn pat_is_binding(dm: &DefMap, pat: &ast::Pat) -> bool {
     match pat.node {
         ast::PatIdent(..) => {
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 902e9ffca1f..1d5c5fb86cb 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -1091,7 +1091,6 @@ impl LintPass for NonUpperCaseGlobals {
     fn check_pat(&mut self, cx: &Context, p: &ast::Pat) {
         // Lint for constants that look like binding identifiers (#7526)
         match (&p.node, cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def())) {
-            (&ast::PatIdent(_, ref path1, _), Some(def::DefAssociatedConst(..))) |
             (&ast::PatIdent(_, ref path1, _), Some(def::DefConst(..))) => {
                 NonUpperCaseGlobals::check_upper_case(cx, "constant in pattern",
                                                       path1.node, p.span);
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 41a6f4adfe0..61eab4ce9b2 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -41,6 +41,7 @@ use self::TypeParameters::*;
 use self::RibKind::*;
 use self::UseLexicalScopeFlag::*;
 use self::ModulePrefixResult::*;
+use self::AssocItemResolveResult::*;
 use self::NameSearchType::*;
 use self::BareIdentifierPatternResolution::*;
 use self::ParentLink::*;
@@ -70,7 +71,7 @@ use syntax::ast::{Ident, ImplItem, Item, ItemConst, ItemEnum, ItemExternCrate};
 use syntax::ast::{ItemFn, ItemForeignMod, ItemImpl, ItemMac, ItemMod, ItemStatic, ItemDefaultImpl};
 use syntax::ast::{ItemStruct, ItemTrait, ItemTy, ItemUse};
 use syntax::ast::{Local, MethodImplItem, Name, NodeId};
-use syntax::ast::{Pat, PatEnum, PatIdent, PatLit};
+use syntax::ast::{Pat, PatEnum, PatIdent, PatLit, PatQPath};
 use syntax::ast::{PatRange, PatStruct, Path, PrimTy};
 use syntax::ast::{TraitRef, Ty, TyBool, TyChar, TyF32};
 use syntax::ast::{TyF64, TyFloat, TyIs, TyI8, TyI16, TyI32, TyI64, TyInt};
@@ -331,6 +332,15 @@ enum ModulePrefixResult {
     PrefixFound(Rc<Module>, usize)
 }
 
+#[derive(Copy, Clone)]
+enum AssocItemResolveResult {
+    /// Syntax such as `<T>::item`, which can't be resolved until type
+    /// checking.
+    TypecheckRequired,
+    /// We should have been able to resolve the associated item.
+    ResolveAttempt(Option<PathResolution>),
+}
+
 #[derive(Copy, Clone, PartialEq)]
 enum NameSearchType {
     /// We're doing a name search in order to resolve a `use` directive.
@@ -2305,31 +2315,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
     fn resolve_type(&mut self, ty: &Ty) {
         match ty.node {
-            // `<T>::a::b::c` is resolved by typeck alone.
-            TyPath(Some(ast::QSelf { position: 0, .. }), _) => {}
-
             TyPath(ref maybe_qself, ref path) => {
-                let max_assoc_types = if let Some(ref qself) = *maybe_qself {
-                    // Make sure the trait is valid.
-                    let _ = self.resolve_trait_reference(ty.id, path, 1);
-                    path.segments.len() - qself.position
-                } else {
-                    path.segments.len()
-                };
-
-                let mut resolution = None;
-                for depth in 0..max_assoc_types {
-                    self.with_no_errors(|this| {
-                        resolution = this.resolve_path(ty.id, path, depth, TypeNS, true);
-                    });
-                    if resolution.is_some() {
-                        break;
-                    }
-                }
-                if let Some(DefMod(_)) = resolution.map(|r| r.base_def) {
-                    // A module is not a valid type.
-                    resolution = None;
-                }
+                let resolution =
+                    match self.resolve_possibly_assoc_item(ty.id,
+                                                           maybe_qself.as_ref(),
+                                                           path,
+                                                           TypeNS,
+                                                           true) {
+                        // `<T>::a::b::c` is resolved by typeck alone.
+                        TypecheckRequired => {
+                            // Resolve embedded types.
+                            visit::walk_ty(self, ty);
+                            return;
+                        }
+                        ResolveAttempt(resolution) => resolution,
+                    };
 
                 // This is a path in the type namespace. Walk through scopes
                 // looking for it.
@@ -2489,10 +2489,24 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
                 PatEnum(ref path, _) => {
                     // This must be an enum variant, struct or const.
-                    if let Some(path_res) = self.resolve_path(pat_id, path, 0, ValueNS, false) {
+                    let resolution =
+                        match self.resolve_possibly_assoc_item(pat_id, None,
+                                                               path, ValueNS,
+                                                               false) {
+                            // The below shouldn't happen because all
+                            // qualified paths should be in PatQPath.
+                            TypecheckRequired =>
+                                self.session.span_bug(
+                                    path.span,
+                                    "resolve_possibly_assoc_item claimed
+                                     that a path in PatEnum requires typecheck
+                                     to resolve, but qualified paths should be
+                                     PatQPath"),
+                            ResolveAttempt(resolution) => resolution,
+                        };
+                    if let Some(path_res) = resolution {
                         match path_res.base_def {
-                            DefVariant(..) | DefStruct(..) | DefConst(..) |
-                            DefAssociatedConst(..) => {
+                            DefVariant(..) | DefStruct(..) | DefConst(..) => {
                                 self.record_def(pattern.id, path_res);
                             }
                             DefStatic(..) => {
@@ -2502,18 +2516,69 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                                     use a `const` instead");
                             }
                             _ => {
+                                // If anything ends up here entirely resolved,
+                                // it's an error. If anything ends up here
+                                // partially resolved, that's OK, because it may
+                                // be a `T::CONST` that typeck will resolve to
+                                // an inherent impl.
+                                if path_res.depth == 0 {
+                                    self.resolve_error(
+                                        path.span,
+                                        &format!("`{}` is not an enum variant, struct or const",
+                                                 token::get_ident(
+                                                     path.segments.last().unwrap().identifier)));
+                                } else {
+                                    self.record_def(pattern.id, path_res);
+                                }
+                            }
+                        }
+                    } else {
+                        self.resolve_error(path.span,
+                            &format!("unresolved enum variant, struct or const `{}`",
+                                token::get_ident(path.segments.last().unwrap().identifier)));
+                    }
+                    visit::walk_path(self, path);
+                }
+
+                PatQPath(ref qself, ref path) => {
+                    // Associated constants only.
+                    let resolution =
+                        match self.resolve_possibly_assoc_item(pat_id, Some(qself),
+                                                               path, ValueNS,
+                                                               false) {
+                            TypecheckRequired => {
+                                // All `<T>::CONST` should end up here, and will
+                                // require use of the trait map to resolve
+                                // during typechecking.
+                                let const_name = path.segments.last().unwrap()
+                                                     .identifier.name;
+                                let traits = self.get_traits_containing_item(const_name);
+                                self.trait_map.insert(pattern.id, traits);
+                                visit::walk_pat(self, pattern);
+                                return true;
+                            }
+                            ResolveAttempt(resolution) => resolution,
+                        };
+                    if let Some(path_res) = resolution {
+                        match path_res.base_def {
+                            // All `<T as Trait>::CONST` should end up here, and
+                            // have the trait already selected.
+                            DefAssociatedConst(..) => {
+                                self.record_def(pattern.id, path_res);
+                            }
+                            _ => {
                                 self.resolve_error(path.span,
-                                    &format!("`{}` is not an enum variant, struct or const",
+                                    &format!("`{}` is not an associated const",
                                         token::get_ident(
                                             path.segments.last().unwrap().identifier)));
                             }
                         }
                     } else {
                         self.resolve_error(path.span,
-                            &format!("unresolved enum variant, struct or const `{}`",
+                            &format!("unresolved associated const `{}`",
                                 token::get_ident(path.segments.last().unwrap().identifier)));
                     }
-                    visit::walk_path(self, path);
+                    visit::walk_pat(self, pattern);
                 }
 
                 PatStruct(ref path, _, _) => {
@@ -2605,6 +2670,47 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
     }
 
+    /// Handles paths that may refer to associated items
+    fn resolve_possibly_assoc_item(&mut self,
+                                   id: NodeId,
+                                   maybe_qself: Option<&ast::QSelf>,
+                                   path: &Path,
+                                   namespace: Namespace,
+                                   check_ribs: bool)
+                                   -> AssocItemResolveResult
+    {
+        match maybe_qself {
+            Some(&ast::QSelf { position: 0, .. }) =>
+                return TypecheckRequired,
+            _ => {}
+        }
+        let max_assoc_types = if let Some(qself) = maybe_qself {
+            // Make sure the trait is valid.
+            let _ = self.resolve_trait_reference(id, path, 1);
+            path.segments.len() - qself.position
+        } else {
+            path.segments.len()
+        };
+
+        let mut resolution = self.with_no_errors(|this| {
+            this.resolve_path(id, path, 0, namespace, check_ribs)
+        });
+        for depth in 1..max_assoc_types {
+            if resolution.is_some() {
+                break;
+            }
+            self.with_no_errors(|this| {
+                resolution = this.resolve_path(id, path, depth,
+                                               TypeNS, true);
+            });
+        }
+        if let Some(DefMod(_)) = resolution.map(|r| r.base_def) {
+            // A module is not a valid type or value.
+            resolution = None;
+        }
+        ResolveAttempt(resolution)
+    }
+
     /// If `check_ribs` is true, checks the local definitions first; i.e.
     /// doesn't skip straight to the containing module.
     /// Skips `path_depth` trailing segments, which is also reflected in the
@@ -3119,38 +3225,23 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
         // Next, resolve the node.
         match expr.node {
-            // `<T>::a::b::c` is resolved by typeck alone.
-            ExprPath(Some(ast::QSelf { position: 0, .. }), ref path) => {
-                let method_name = path.segments.last().unwrap().identifier.name;
-                let traits = self.search_for_traits_containing_method(method_name);
-                self.trait_map.insert(expr.id, traits);
-                visit::walk_expr(self, expr);
-            }
-
             ExprPath(ref maybe_qself, ref path) => {
-                let max_assoc_types = if let Some(ref qself) = *maybe_qself {
-                    // Make sure the trait is valid.
-                    let _ = self.resolve_trait_reference(expr.id, path, 1);
-                    path.segments.len() - qself.position
-                } else {
-                    path.segments.len()
-                };
-
-                let mut resolution = self.with_no_errors(|this| {
-                    this.resolve_path(expr.id, path, 0, ValueNS, true)
-                });
-                for depth in 1..max_assoc_types {
-                    if resolution.is_some() {
-                        break;
-                    }
-                    self.with_no_errors(|this| {
-                        resolution = this.resolve_path(expr.id, path, depth, TypeNS, true);
-                    });
-                }
-                if let Some(DefMod(_)) = resolution.map(|r| r.base_def) {
-                    // A module is not a valid type or value.
-                    resolution = None;
-                }
+                let resolution =
+                    match self.resolve_possibly_assoc_item(expr.id,
+                                                           maybe_qself.as_ref(),
+                                                           path,
+                                                           ValueNS,
+                                                           true) {
+                        // `<T>::a::b::c` is resolved by typeck alone.
+                        TypecheckRequired => {
+                            let method_name = path.segments.last().unwrap().identifier.name;
+                            let traits = self.get_traits_containing_item(method_name);
+                            self.trait_map.insert(expr.id, traits);
+                            visit::walk_expr(self, expr);
+                            return;
+                        }
+                        ResolveAttempt(resolution) => resolution,
+                    };
 
                 // This is a local path in the value namespace. Walk through
                 // scopes looking for it.
@@ -3181,7 +3272,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                         // so they can be completed during typeck.
                         if path_res.depth != 0 {
                             let method_name = path.segments.last().unwrap().identifier.name;
-                            let traits = self.search_for_traits_containing_method(method_name);
+                            let traits = self.get_traits_containing_item(method_name);
                             self.trait_map.insert(expr.id, traits);
                         }
 
@@ -3339,14 +3430,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 // field, we need to add any trait methods we find that match
                 // the field name so that we can do some nice error reporting
                 // later on in typeck.
-                let traits = self.search_for_traits_containing_method(ident.node.name);
+                let traits = self.get_traits_containing_item(ident.node.name);
                 self.trait_map.insert(expr.id, traits);
             }
             ExprMethodCall(ident, _, _) => {
                 debug!("(recording candidate traits for expr) recording \
                         traits for {}",
                        expr.id);
-                let traits = self.search_for_traits_containing_method(ident.node.name);
+                let traits = self.get_traits_containing_item(ident.node.name);
                 self.trait_map.insert(expr.id, traits);
             }
             _ => {
@@ -3355,8 +3446,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
     }
 
-    fn search_for_traits_containing_method(&mut self, name: Name) -> Vec<DefId> {
-        debug!("(searching for traits containing method) looking for '{}'",
+    fn get_traits_containing_item(&mut self, name: Name) -> Vec<DefId> {
+        debug!("(getting traits containing item) looking for '{}'",
                token::get_name(name));
 
         fn add_trait_info(found_traits: &mut Vec<DefId>,
diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs
index dc14ef3696f..39cfac42011 100644
--- a/src/librustc_trans/save/mod.rs
+++ b/src/librustc_trans/save/mod.rs
@@ -1044,7 +1044,8 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> {
                     }
                 }
             }
-            ast::PatEnum(ref path, _) => {
+            ast::PatEnum(ref path, _) |
+            ast::PatQPath(_, ref path) => {
                 self.collected_paths.push((p.id, path.clone(), false, recorder::VarRef));
                 visit::walk_pat(self, p);
             }
diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs
index 744ec5a6168..b93068c88c8 100644
--- a/src/librustc_trans/trans/_match.rs
+++ b/src/librustc_trans/trans/_match.rs
@@ -1809,7 +1809,8 @@ fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         ast::PatMac(..) => {
             bcx.sess().span_bug(pat.span, "unexpanded macro");
         }
-        ast::PatWild(_) | ast::PatLit(_) | ast::PatRange(_, _) => ()
+        ast::PatQPath(..) | ast::PatWild(_) | ast::PatLit(_) |
+        ast::PatRange(_, _) => ()
     }
     return bcx;
 }
diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs
index 69cd57d1bab..516ff443dac 100644
--- a/src/librustc_trans/trans/debuginfo.rs
+++ b/src/librustc_trans/trans/debuginfo.rs
@@ -3437,6 +3437,10 @@ fn create_scope_map(cx: &CrateContext,
                 }
             }
 
+            ast::PatQPath(..) => {
+                scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
+            }
+
             ast::PatStruct(_, ref field_pats, _) => {
                 scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
 
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 0959f9d1b91..1f4d6cc2fd4 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -11,12 +11,14 @@
 use middle::const_eval;
 use middle::def;
 use middle::infer;
-use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding, pat_is_const};
+use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding};
+use middle::pat_util::pat_is_resolved_const;
+use middle::privacy::{AllPublic, LastMod};
 use middle::subst::Substs;
 use middle::ty::{self, Ty};
 use check::{check_expr, check_expr_has_type, check_expr_with_expectation};
 use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation};
-use check::{instantiate_path, structurally_resolved_type};
+use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type};
 use require_same_types;
 use util::nodemap::FnvHashMap;
 use util::ppaux::Repr;
@@ -118,7 +120,7 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
             // subtyping doesn't matter here, as the value is some kind of scalar
             demand::eqtype(fcx, pat.span, expected, lhs_ty);
         }
-        ast::PatEnum(..) | ast::PatIdent(..) if pat_is_const(&tcx.def_map, pat) => {
+        ast::PatEnum(..) | ast::PatIdent(..) if pat_is_resolved_const(&tcx.def_map, pat) => {
             let const_did = tcx.def_map.borrow().get(&pat.id).unwrap().def_id();
             let const_scheme = ty::lookup_item_type(tcx, const_did);
             assert!(const_scheme.generics.is_empty());
@@ -181,6 +183,37 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
             let subpats = subpats.as_ref().map(|v| &v[..]);
             check_pat_enum(pcx, pat, path, subpats, expected);
         }
+        ast::PatQPath(ref qself, ref path) => {
+            let self_ty = fcx.to_ty(&qself.ty);
+            let path_res = if let Some(&d) = tcx.def_map.borrow().get(&pat.id) {
+                d
+            } else if qself.position == 0 {
+                def::PathResolution {
+                    // This is just a sentinel for finish_resolving_def_to_ty.
+                    base_def: def::DefMod(ast_util::local_def(ast::CRATE_NODE_ID)),
+                    last_private: LastMod(AllPublic),
+                    depth: path.segments.len()
+                }
+            } else {
+                tcx.sess.span_bug(pat.span,
+                                  &format!("unbound path {}", pat.repr(tcx)))
+            };
+            if let Some((opt_ty, segments, def)) =
+                    resolve_ty_and_def_ufcs(fcx, path_res, Some(self_ty),
+                                            path, pat.span, pat.id) {
+                if check_assoc_item_is_const(pcx, def, pat.span) {
+                    let scheme = ty::lookup_item_type(tcx, def.def_id());
+                    let predicates = ty::lookup_predicates(tcx, def.def_id());
+                    instantiate_path(fcx, segments,
+                                     scheme, &predicates,
+                                     opt_ty, def, pat.span, pat.id);
+                    let const_ty = fcx.node_ty(pat.id);
+                    demand::suptype(fcx, pat.span, expected, const_ty);
+                } else {
+                    fcx.write_error(pat.id)
+                }
+            }
+        }
         ast::PatStruct(ref path, ref fields, etc) => {
             check_pat_struct(pcx, pat, path, fields, etc, expected);
         }
@@ -331,6 +364,21 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
     // subtyping.
 }
 
+fn check_assoc_item_is_const(pcx: &pat_ctxt, def: def::Def, span: Span) -> bool {
+    match def {
+        def::DefAssociatedConst(..) => true,
+        def::DefMethod(..) => {
+            span_err!(pcx.fcx.ccx.tcx.sess, span, E0327,
+                      "associated items in match patterns must be constants");
+            false
+        }
+        _ => {
+            pcx.fcx.ccx.tcx.sess.span_bug(span, "non-associated item in
+                                                 check_assoc_item_is_const");
+        }
+    }
+}
+
 pub fn check_dereferencable<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
                                       span: Span, expected: Ty<'tcx>,
                                       inner: &ast::Pat) -> bool {
@@ -532,7 +580,24 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
     let fcx = pcx.fcx;
     let tcx = pcx.fcx.ccx.tcx;
 
-    let def = tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
+    let path_res = *tcx.def_map.borrow().get(&pat.id).unwrap();
+
+    let (opt_ty, segments, def) = match resolve_ty_and_def_ufcs(fcx, path_res,
+                                                                None, path,
+                                                                pat.span, pat.id) {
+        Some(resolution) => resolution,
+        // Error handling done inside resolve_ty_and_def_ufcs, so if
+        // resolution fails just return.
+        None => {return;}
+    };
+
+    // Items that were partially resolved before should have been resolved to
+    // associated constants (i.e. not methods).
+    if path_res.depth != 0 && !check_assoc_item_is_const(pcx, def, pat.span) {
+        fcx.write_error(pat.id);
+        return;
+    }
+
     let enum_def = def.variant_def_ids()
         .map_or_else(|| def.def_id(), |(enum_def, _)| enum_def);
 
@@ -547,13 +612,23 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
     } else {
         ctor_scheme
     };
-    instantiate_path(pcx.fcx, &path.segments,
+    instantiate_path(pcx.fcx, segments,
                      path_scheme, &ctor_predicates,
-                     None, def, pat.span, pat.id);
+                     opt_ty, def, pat.span, pat.id);
+
+    // If we didn't have a fully resolved path to start with, we had an
+    // associated const, and we should quit now, since the rest of this
+    // function uses checks specific to structs and enums.
+    if path_res.depth != 0 {
+        let pat_ty = fcx.node_ty(pat.id);
+        demand::suptype(fcx, pat.span, expected, pat_ty);
+        return;
+    }
 
     let pat_ty = fcx.node_ty(pat.id);
     demand::eqtype(fcx, pat.span, expected, pat_ty);
 
+
     let real_path_ty = fcx.node_ty(pat.id);
     let (arg_tys, kind_name): (Vec<_>, &'static str) = match real_path_ty.sty {
         ty::ty_enum(enum_def_id, expected_substs)
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index d04205666f2..6ba341f62f5 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3230,53 +3230,20 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                 &format!("unbound path {}", expr.repr(tcx)))
           };
 
-          let def = path_res.base_def;
-          if path_res.depth == 0 {
+          if let Some((opt_ty, segments, def)) =
+                  resolve_ty_and_def_ufcs(fcx, path_res, opt_self_ty, path,
+                                          expr.span, expr.id) {
               let (scheme, predicates) = type_scheme_and_predicates_for_def(fcx,
                                                                             expr.span,
                                                                             def);
               instantiate_path(fcx,
-                               &path.segments,
+                               segments,
                                scheme,
                                &predicates,
-                               opt_self_ty,
+                               opt_ty,
                                def,
                                expr.span,
                                id);
-          } else {
-              let ty_segments = path.segments.init();
-              let base_ty_end = path.segments.len() - path_res.depth;
-              let ty = astconv::finish_resolving_def_to_ty(fcx,
-                                                           fcx,
-                                                           expr.span,
-                                                           PathParamMode::Optional,
-                                                           &def,
-                                                           opt_self_ty,
-                                                           &ty_segments[..base_ty_end],
-                                                           &ty_segments[base_ty_end..]);
-              let method_segment = path.segments.last().unwrap();
-              let method_name = method_segment.identifier.name;
-              match method::resolve_ufcs(fcx, expr.span, method_name, ty, id) {
-                  Ok((def, lp)) => {
-                      // Write back the new resolution.
-                      tcx.def_map.borrow_mut().insert(id, def::PathResolution {
-                          base_def: def,
-                          last_private: path_res.last_private.or(lp),
-                          depth: 0
-                      });
-
-                      let (scheme, predicates) =
-                          type_scheme_and_predicates_for_def(fcx, expr.span, def);
-                      instantiate_path(fcx, slice::ref_slice(method_segment),
-                                       scheme, &predicates,
-                                       Some(ty), def, expr.span, id);
-                  }
-                  Err(error) => {
-                      method::report_error(fcx, expr.span, ty,
-                                           method_name, None, error);
-                      fcx.write_error(id);
-                  }
-              }
           }
 
           // We always require that the type provided as the value for
@@ -3738,6 +3705,52 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
     unifier();
 }
 
+pub fn resolve_ty_and_def_ufcs<'a, 'b, 'tcx>(fcx: &FnCtxt<'b, 'tcx>,
+                                             path_res: def::PathResolution,
+                                             opt_self_ty: Option<Ty<'tcx>>,
+                                             path: &'a ast::Path,
+                                             span: Span,
+                                             node_id: ast::NodeId)
+                                             -> Option<(Option<Ty<'tcx>>,
+                                                        &'a [ast::PathSegment],
+                                                        def::Def)>
+{
+    // If fully resolved already, we don't have to do anything.
+    if path_res.depth == 0 {
+        Some((opt_self_ty, &path.segments, path_res.base_def))
+    } else {
+        let mut def = path_res.base_def;
+        let ty_segments = path.segments.init();
+        let base_ty_end = path.segments.len() - path_res.depth;
+        let ty = astconv::finish_resolving_def_to_ty(fcx, fcx, span,
+                                                     PathParamMode::Optional,
+                                                     &mut def,
+                                                     opt_self_ty,
+                                                     &ty_segments[..base_ty_end],
+                                                     &ty_segments[base_ty_end..]);
+        let item_segment = path.segments.last().unwrap();
+        let item_name = item_segment.identifier.name;
+        match method::resolve_ufcs(fcx, span, item_name, ty, node_id) {
+            Ok((def, lp)) => {
+                // Write back the new resolution.
+                fcx.ccx.tcx.def_map.borrow_mut()
+                       .insert(node_id, def::PathResolution {
+                   base_def: def,
+                   last_private: path_res.last_private.or(lp),
+                   depth: 0
+                });
+                Some((Some(ty), slice::ref_slice(item_segment), def))
+            }
+            Err(error) => {
+                method::report_error(fcx, span, ty,
+                                     item_name, None, error);
+                fcx.write_error(node_id);
+                None
+            }
+        }
+    }
+}
+
 fn constrain_path_type_parameters(fcx: &FnCtxt,
                                   expr: &ast::Expr)
 {
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index a4814b36fe5..46cc4628e2e 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -180,6 +180,7 @@ register_diagnostics! {
     E0324, // implemented a method when another trait item expected
     E0325, // implemented an associated type when another trait item expected
     E0326, // associated const implemented with different type from trait
+    E0327, // referred to method instead of constant in match pattern
     E0366, // dropck forbid specialization to concrete type or region
     E0367, // dropck forbid specialization to predicate not in struct/enum
     E0368, // binary operation `<op>=` cannot be applied to types
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 73fbfe29224..1e6e9a7562a 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -2522,6 +2522,8 @@ fn name_from_pat(p: &ast::Pat) -> String {
         PatWild(PatWildMulti) => "..".to_string(),
         PatIdent(_, ref p, _) => token::get_ident(p.node).to_string(),
         PatEnum(ref p, _) => path_to_string(p),
+        PatQPath(..) => panic!("tried to get argument name from PatQPath, \
+                                which is not allowed in function arguments"),
         PatStruct(ref name, ref fields, etc) => {
             format!("{} {{ {}{} }}", path_to_string(name),
                 fields.iter().map(|&Spanned { node: ref fp, .. }|
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 3f2e7c765a5..4307abe4174 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -599,6 +599,12 @@ pub enum Pat_ {
     /// "None" means a * pattern where we don't bind the fields to names.
     PatEnum(Path, Option<Vec<P<Pat>>>),
 
+    /// An associated const named using the qualified path `<T>::CONST` or
+    /// `<T as Trait>::CONST`. Associated consts from inherent impls can be
+    /// refered to as simply `T::CONST`, in which case they will end up as
+    /// PatEnum, and the resolver will have to sort that out.
+    PatQPath(QSelf, Path),
+
     /// Destructuring of a struct, e.g. `Foo {x, y, ..}`
     /// The `bool` is `true` in the presence of a `..`
     PatStruct(Path, Vec<Spanned<FieldPat>>, bool),
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index 78f06ce5fd5..fc4d73210ea 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -579,7 +579,7 @@ pub fn walk_pat<F>(pat: &Pat, mut it: F) -> bool where F: FnMut(&Pat) -> bool {
             }
             PatMac(_) => panic!("attempted to analyze unexpanded pattern"),
             PatWild(_) | PatLit(_) | PatRange(_, _) | PatIdent(_, _, _) |
-            PatEnum(_, _) => {
+            PatEnum(_, _) | PatQPath(_, _) => {
                 true
             }
         }
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 22bc3a198e2..8898dc7e359 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -1134,6 +1134,10 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
                 PatEnum(folder.fold_path(pth),
                         pats.map(|pats| pats.move_map(|x| folder.fold_pat(x))))
             }
+            PatQPath(qself, pth) => {
+                let qself = QSelf {ty: folder.fold_ty(qself.ty), .. qself};
+                PatQPath(qself, folder.fold_path(pth))
+            }
             PatStruct(pth, fields, etc) => {
                 let pth = folder.fold_path(pth);
                 let fs = fields.move_map(|f| {
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 9a1c963b8eb..abeee2a1c74 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -40,8 +40,9 @@ use ast::{MacStmtWithBraces, MacStmtWithSemicolon, MacStmtWithoutBraces};
 use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, MatchSource};
 use ast::{MutTy, BiMul, Mutability};
 use ast::{MethodImplItem, NamedField, UnNeg, NoReturn, UnNot};
-use ast::{Pat, PatBox, PatEnum, PatIdent, PatLit, PatMac, PatRange, PatRegion};
-use ast::{PatStruct, PatTup, PatVec, PatWild, PatWildMulti, PatWildSingle};
+use ast::{Pat, PatBox, PatEnum, PatIdent, PatLit, PatQPath, PatMac, PatRange};
+use ast::{PatRegion, PatStruct, PatTup, PatVec, PatWild, PatWildMulti};
+use ast::PatWildSingle;
 use ast::{PolyTraitRef, QSelf};
 use ast::{Return, BiShl, BiShr, Stmt, StmtDecl};
 use ast::{StmtExpr, StmtSemi, StmtMac, StructDef, StructField};
@@ -109,6 +110,15 @@ pub enum PathParsingMode {
     LifetimeAndTypesWithColons,
 }
 
+/// How to parse a qualified path, whether to allow trailing parameters.
+#[derive(Copy, Clone, PartialEq)]
+pub enum QPathParsingMode {
+    /// No trailing parameters, e.g. `<T as Trait>::Item`
+    NoParameters,
+    /// Optional parameters, e.g. `<T as Trait>::item::<'a, U>`
+    MaybeParameters,
+}
+
 /// How to parse a bound, whether to allow bound modifiers such as `?`.
 #[derive(Copy, Clone, PartialEq)]
 pub enum BoundParsingMode {
@@ -1345,36 +1355,9 @@ impl<'a> Parser<'a> {
             try!(self.expect(&token::CloseDelim(token::Paren)));
             TyTypeof(e)
         } else if try!(self.eat_lt()) {
-            // QUALIFIED PATH `<TYPE as TRAIT_REF>::item`
-            let self_type = try!(self.parse_ty_sum());
-
-            let mut path = if try!(self.eat_keyword(keywords::As) ){
-                try!(self.parse_path(LifetimeAndTypesWithoutColons))
-            } else {
-                ast::Path {
-                    span: self.span,
-                    global: false,
-                    segments: vec![]
-                }
-            };
 
-            let qself = QSelf {
-                ty: self_type,
-                position: path.segments.len()
-            };
-
-            try!(self.expect(&token::Gt));
-            try!(self.expect(&token::ModSep));
-
-            path.segments.push(ast::PathSegment {
-                identifier: try!(self.parse_ident()),
-                parameters: ast::PathParameters::none()
-            });
-
-            if path.segments.len() == 1 {
-                path.span.lo = self.last_span.lo;
-            }
-            path.span.hi = self.last_span.hi;
+            let (qself, path) =
+                 try!(self.parse_qualified_path(QPathParsingMode::NoParameters));
 
             TyPath(Some(qself), path)
         } else if self.check(&token::ModSep) ||
@@ -1591,6 +1574,61 @@ impl<'a> Parser<'a> {
         }
     }
 
+    // QUALIFIED PATH `<TYPE [as TRAIT_REF]>::IDENT[::<PARAMS>]`
+    // Assumes that the leading `<` has been parsed already.
+    pub fn parse_qualified_path(&mut self, mode: QPathParsingMode)
+                                -> PResult<(QSelf, ast::Path)> {
+        let self_type = try!(self.parse_ty_sum());
+        let mut path = if try!(self.eat_keyword(keywords::As)) {
+            try!(self.parse_path(LifetimeAndTypesWithoutColons))
+        } else {
+            ast::Path {
+                span: self.span,
+                global: false,
+                segments: vec![]
+            }
+        };
+
+        let qself = QSelf {
+            ty: self_type,
+            position: path.segments.len()
+        };
+
+        try!(self.expect(&token::Gt));
+        try!(self.expect(&token::ModSep));
+
+        let item_name = try!(self.parse_ident());
+        let parameters = match mode {
+            QPathParsingMode::NoParameters => ast::PathParameters::none(),
+            QPathParsingMode::MaybeParameters => {
+                if try!(self.eat(&token::ModSep)) {
+                    try!(self.expect_lt());
+                    // Consumed `item::<`, go look for types
+                    let (lifetimes, types, bindings) =
+                        try!(self.parse_generic_values_after_lt());
+                    ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
+                        lifetimes: lifetimes,
+                        types: OwnedSlice::from_vec(types),
+                        bindings: OwnedSlice::from_vec(bindings),
+                    })
+                } else {
+                    ast::PathParameters::none()
+                }
+            }
+        };
+        path.segments.push(ast::PathSegment {
+            identifier: item_name,
+            parameters: parameters
+        });
+
+        if path.segments.len() == 1 {
+            path.span.lo = self.last_span.lo;
+        }
+        path.span.hi = self.last_span.hi;
+
+        Ok((qself, path))
+    }
+
     /// Parses a path and optional type parameter bounds, depending on the
     /// mode. The `mode` parameter determines whether lifetimes, types, and/or
     /// bounds are permitted and whether `::` must precede type parameter
@@ -2054,49 +2092,10 @@ impl<'a> Parser<'a> {
             }
             _ => {
                 if try!(self.eat_lt()){
-                    // QUALIFIED PATH `<TYPE as TRAIT_REF>::item::<'a, T>`
-                    let self_type = try!(self.parse_ty_sum());
-                    let mut path = if try!(self.eat_keyword(keywords::As) ){
-                        try!(self.parse_path(LifetimeAndTypesWithoutColons))
-                    } else {
-                        ast::Path {
-                            span: self.span,
-                            global: false,
-                            segments: vec![]
-                        }
-                    };
-                    let qself = QSelf {
-                        ty: self_type,
-                        position: path.segments.len()
-                    };
-                    try!(self.expect(&token::Gt));
-                    try!(self.expect(&token::ModSep));
-
-                    let item_name = try!(self.parse_ident());
-                    let parameters = if try!(self.eat(&token::ModSep) ){
-                        try!(self.expect_lt());
-                        // Consumed `item::<`, go look for types
-                        let (lifetimes, types, bindings) =
-                            try!(self.parse_generic_values_after_lt());
-                        ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
-                            lifetimes: lifetimes,
-                            types: OwnedSlice::from_vec(types),
-                            bindings: OwnedSlice::from_vec(bindings),
-                        })
-                    } else {
-                        ast::PathParameters::none()
-                    };
-                    path.segments.push(ast::PathSegment {
-                        identifier: item_name,
-                        parameters: parameters
-                    });
 
-                    if path.segments.len() == 1 {
-                        path.span.lo = self.last_span.lo;
-                    }
-                    path.span.hi = self.last_span.hi;
+                    let (qself, path) =
+                        try!(self.parse_qualified_path(QPathParsingMode::MaybeParameters));
 
-                    let hi = self.span.hi;
                     return Ok(self.mk_expr(lo, hi, ExprPath(Some(qself), path)));
                 }
                 if try!(self.eat_keyword(keywords::Move) ){
@@ -3167,16 +3166,25 @@ impl<'a> Parser<'a> {
     fn parse_pat_range_end(&mut self) -> PResult<P<Expr>> {
         if self.is_path_start() {
             let lo = self.span.lo;
-            let path = try!(self.parse_path(LifetimeAndTypesWithColons));
+            let (qself, path) = if try!(self.eat_lt()) {
+                // Parse a qualified path
+                let (qself, path) =
+                    try!(self.parse_qualified_path(QPathParsingMode::NoParameters));
+                (Some(qself), path)
+            } else {
+                // Parse an unqualified path
+                (None, try!(self.parse_path(LifetimeAndTypesWithColons)))
+            };
             let hi = self.last_span.hi;
-            Ok(self.mk_expr(lo, hi, ExprPath(None, path)))
+            Ok(self.mk_expr(lo, hi, ExprPath(qself, path)))
         } else {
             self.parse_literal_maybe_minus()
         }
     }
 
     fn is_path_start(&self) -> bool {
-        (self.token == token::ModSep || self.token.is_ident() || self.token.is_path())
+        (self.token == token::Lt || self.token == token::ModSep
+            || self.token.is_ident() || self.token.is_path())
             && !self.token.is_keyword(keywords::True) && !self.token.is_keyword(keywords::False)
     }
 
@@ -3252,25 +3260,44 @@ impl<'a> Parser<'a> {
                         pat = try!(self.parse_pat_ident(BindByValue(MutImmutable)));
                     }
                 } else {
-                    // Parse as a general path
-                    let path = try!(self.parse_path(LifetimeAndTypesWithColons));
+                    let (qself, path) = if try!(self.eat_lt()) {
+                        // Parse a qualified path
+                        let (qself, path) =
+                            try!(self.parse_qualified_path(QPathParsingMode::NoParameters));
+                        (Some(qself), path)
+                    } else {
+                        // Parse an unqualified path
+                        (None, try!(self.parse_path(LifetimeAndTypesWithColons)))
+                    };
                     match self.token {
                       token::DotDotDot => {
                         // Parse range
                         let hi = self.last_span.hi;
-                        let begin = self.mk_expr(lo, hi, ExprPath(None, path));
+                        let begin = self.mk_expr(lo, hi, ExprPath(qself, path));
                         try!(self.bump());
                         let end = try!(self.parse_pat_range_end());
                         pat = PatRange(begin, end);
                       }
                       token::OpenDelim(token::Brace) => {
-                        // Parse struct pattern
+                         if qself.is_some() {
+                            let span = self.span;
+                            self.span_err(span,
+                                          "unexpected `{` after qualified path");
+                            self.abort_if_errors();
+                        }
+                       // Parse struct pattern
                         try!(self.bump());
                         let (fields, etc) = try!(self.parse_pat_fields());
                         try!(self.bump());
                         pat = PatStruct(path, fields, etc);
                       }
                       token::OpenDelim(token::Paren) => {
+                        if qself.is_some() {
+                            let span = self.span;
+                            self.span_err(span,
+                                          "unexpected `(` after qualified path");
+                            self.abort_if_errors();
+                        }
                         // Parse tuple struct or enum pattern
                         if self.look_ahead(1, |t| *t == token::DotDot) {
                             // This is a "top constructor only" pat
@@ -3287,6 +3314,10 @@ impl<'a> Parser<'a> {
                             pat = PatEnum(path, Some(args));
                         }
                       }
+                      _ if qself.is_some() => {
+                        // Parse qualified path
+                        pat = PatQPath(qself.unwrap(), path);
+                      }
                       _ => {
                         // Parse nullary enum
                         pat = PatEnum(path, Some(vec![]));
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 4cfb9e4147a..27682bc6fec 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -2220,6 +2220,9 @@ impl<'a> State<'a> {
                     }
                 }
             }
+            ast::PatQPath(ref qself, ref path) => {
+                try!(self.print_qpath(path, qself, false));
+            }
             ast::PatStruct(ref path, ref fields, etc) => {
                 try!(self.print_path(path, true, 0));
                 try!(self.nbsp());
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 2ab35367625..6cf791b10be 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -464,6 +464,10 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
                 }
             }
         }
+        PatQPath(ref qself, ref path) => {
+            visitor.visit_ty(&qself.ty);
+            visitor.visit_path(path, pattern.id)
+        }
         PatStruct(ref path, ref fields, _) => {
             visitor.visit_path(path, pattern.id);
             for field in fields {
diff --git a/src/test/compile-fail/method-path-in-pattern.rs b/src/test/compile-fail/method-path-in-pattern.rs
new file mode 100644
index 00000000000..1d83f901cdb
--- /dev/null
+++ b/src/test/compile-fail/method-path-in-pattern.rs
@@ -0,0 +1,35 @@
+// Copyright 2015 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.
+
+use std::marker::MarkerTrait;
+
+struct Foo;
+
+impl Foo {
+    fn bar(&self) {}
+}
+
+trait MyTrait: MarkerTrait {
+    fn trait_bar() {}
+}
+
+impl MyTrait for Foo {}
+
+fn main() {
+    match 0u32 {
+        Foo::bar => {} //~ ERROR E0327
+    }
+    match 0u32 {
+        <Foo>::bar => {} //~ ERROR E0327
+    }
+    match 0u32 {
+        <Foo>::trait_bar => {} //~ ERROR E0327
+    }
+}
diff --git a/src/test/compile-fail/method-resolvable-path-in-pattern.rs b/src/test/compile-fail/method-resolvable-path-in-pattern.rs
new file mode 100644
index 00000000000..f3e93537203
--- /dev/null
+++ b/src/test/compile-fail/method-resolvable-path-in-pattern.rs
@@ -0,0 +1,26 @@
+// Copyright 2015 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.
+
+use std::marker::MarkerTrait;
+
+struct Foo;
+
+trait MyTrait: MarkerTrait {
+    fn trait_bar() {}
+}
+
+impl MyTrait for Foo {}
+
+fn main() {
+    match 0u32 {
+        <Foo as MyTrait>::trait_bar => {}
+        //~^ ERROR `trait_bar` is not an associated const
+    }
+}
diff --git a/src/test/parse-fail/brace-after-qualified-path-in-match.rs b/src/test/parse-fail/brace-after-qualified-path-in-match.rs
new file mode 100644
index 00000000000..66f462df05a
--- /dev/null
+++ b/src/test/parse-fail/brace-after-qualified-path-in-match.rs
@@ -0,0 +1,17 @@
+// Copyright 2015 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.
+
+fn foo() {
+    match x {
+        <T as Trait>::Type{key: value} => (),
+        //~^ ERROR unexpected `{` after qualified path
+        _ => (),
+    }
+}
diff --git a/src/test/parse-fail/paren-after-qualified-path-in-match.rs b/src/test/parse-fail/paren-after-qualified-path-in-match.rs
new file mode 100644
index 00000000000..d06fd2bb4e7
--- /dev/null
+++ b/src/test/parse-fail/paren-after-qualified-path-in-match.rs
@@ -0,0 +1,17 @@
+// Copyright 2015 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.
+
+fn foo() {
+    match x {
+        <T as Trait>::Type(2) => (),
+        //~^ ERROR unexpected `(` after qualified path
+        _ => (),
+    }
+}
diff --git a/src/test/run-pass/associated-const-match-patterns.rs b/src/test/run-pass/associated-const-match-patterns.rs
new file mode 100644
index 00000000000..0085f89822e
--- /dev/null
+++ b/src/test/run-pass/associated-const-match-patterns.rs
@@ -0,0 +1,52 @@
+// Copyright 2015 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.
+
+use std::marker::MarkerTrait;
+
+struct Foo;
+
+enum Bar {
+    Var1,
+    Var2,
+}
+
+// Use inherent and trait impls to test UFCS syntax.
+impl Foo {
+    const MYBAR: Bar = Bar::Var2;
+}
+
+trait HasBar: MarkerTrait {
+    const THEBAR: Bar;
+}
+
+impl HasBar for Foo {
+    const THEBAR: Bar = Bar::Var1;
+}
+
+fn main() {
+    // Inherent impl
+    assert!(match Bar::Var2 {
+        Foo::MYBAR => true,
+        _ => false,
+    });
+    assert!(match Bar::Var2 {
+        <Foo>::MYBAR => true,
+        _ => false,
+    });
+    // Trait impl
+    assert!(match Bar::Var1 {
+        <Foo>::THEBAR => true,
+        _ => false,
+    });
+    assert!(match Bar::Var1 {
+        <Foo as HasBar>::THEBAR => true,
+        _ => false,
+    });
+}