From 29eb550ee6a9fd6961bb00e2680a5735aab95de1 Mon Sep 17 00:00:00 2001 From: Sean Patrick Santos Date: Wed, 25 Mar 2015 10:53:28 -0600 Subject: [PATCH] Get associated consts working in match patterns. --- src/librustc/middle/cfg/construct.rs | 1 + src/librustc/middle/check_match.rs | 10 +- src/librustc/middle/expr_use_visitor.rs | 3 +- src/librustc/middle/mem_categorization.rs | 4 + src/librustc/middle/pat_util.rs | 20 +- src/librustc_lint/builtin.rs | 1 - src/librustc_resolve/lib.rs | 225 ++++++++++++------ src/librustc_trans/save/mod.rs | 3 +- src/librustc_trans/trans/_match.rs | 3 +- src/librustc_trans/trans/debuginfo.rs | 4 + src/librustc_typeck/check/_match.rs | 87 ++++++- src/librustc_typeck/check/mod.rs | 89 ++++--- src/librustc_typeck/diagnostics.rs | 1 + src/librustdoc/clean/mod.rs | 2 + src/libsyntax/ast.rs | 6 + src/libsyntax/ast_util.rs | 2 +- src/libsyntax/fold.rs | 4 + src/libsyntax/parse/parser.rs | 189 +++++++++------ src/libsyntax/print/pprust.rs | 3 + src/libsyntax/visit.rs | 4 + .../compile-fail/method-path-in-pattern.rs | 35 +++ .../method-resolvable-path-in-pattern.rs | 26 ++ .../brace-after-qualified-path-in-match.rs | 17 ++ .../paren-after-qualified-path-in-match.rs | 17 ++ .../associated-const-match-patterns.rs | 52 ++++ 25 files changed, 610 insertions(+), 198 deletions(-) create mode 100644 src/test/compile-fail/method-path-in-pattern.rs create mode 100644 src/test/compile-fail/method-resolvable-path-in-pattern.rs create mode 100644 src/test/parse-fail/brace-after-qualified-path-in-match.rs create mode 100644 src/test/parse-fail/paren-after-qualified-path-in-match.rs create mode 100644 src/test/run-pass/associated-const-match-patterns.rs diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index 359a1a486c9da..a7950a701f8c4 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 13be6d0cb7d9b..a5ea3629abc84 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) -> P { 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 87379bd48f0c4..d740d24e23672 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 6db55baf48350..587194bafada4 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 7a0f58947fe70..27a30f5cf253c 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 902e9ffca1f64..1d5c5fb86cbd5 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 41a6f4adfe038..61eab4ce9b285 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, usize) } +#[derive(Copy, Clone)] +enum AssocItemResolveResult { + /// Syntax such as `::item`, which can't be resolved until type + /// checking. + TypecheckRequired, + /// We should have been able to resolve the associated item. + ResolveAttempt(Option), +} + #[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 { - // `::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) { + // `::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(..) => { @@ -2501,19 +2515,70 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { referenced in a pattern, \ 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 `::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 `::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 { - // `::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) { + // `::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 { - debug!("(searching for traits containing method) looking for '{}'", + fn get_traits_containing_item(&mut self, name: Name) -> Vec { + debug!("(getting traits containing item) looking for '{}'", token::get_name(name)); fn add_trait_info(found_traits: &mut Vec, diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index dc14ef3696f5f..39cfac42011ab 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 744ec5a616841..b93068c88c8be 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 69cd57d1bab70..516ff443dacb9 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 0959f9d1b9154..1f4d6cc2fd471 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 d04205666f2ea..6ba341f62f58e 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>, + path: &'a ast::Path, + span: Span, + node_id: ast::NodeId) + -> Option<(Option>, + &'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 a4814b36fe5b1..46cc4628e2eeb 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 `=` cannot be applied to types diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 73fbfe29224ee..1e6e9a7562a7c 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 3f2e7c765a5e4..4307abe417453 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>>), + /// An associated const named using the qualified path `::CONST` or + /// `::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>, bool), diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 78f06ce5fd5d0..fc4d73210ea98 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -579,7 +579,7 @@ pub fn walk_pat(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 22bc3a198e231..8898dc7e3597e 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1134,6 +1134,10 @@ pub fn noop_fold_pat(p: P, folder: &mut T) -> P { 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 9a1c963b8eb0b..abeee2a1c7489 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. `::Item` + NoParameters, + /// Optional parameters, e.g. `::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 `::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 `::IDENT[::]` + // 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 `::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> { 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 4cfb9e4147a95..27682bc6fec6a 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 2ab353676251e..6cf791b10be67 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 0000000000000..1d83f901cdb62 --- /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 or the MIT license +// , 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 { + ::bar => {} //~ ERROR E0327 + } + match 0u32 { + ::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 0000000000000..f3e93537203b3 --- /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 or the MIT license +// , 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 { + ::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 0000000000000..66f462df05ac9 --- /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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn foo() { + match x { + ::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 0000000000000..d06fd2bb4e704 --- /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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn foo() { + match x { + ::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 0000000000000..0085f89822edf --- /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 or the MIT license +// , 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 { + ::MYBAR => true, + _ => false, + }); + // Trait impl + assert!(match Bar::Var1 { + ::THEBAR => true, + _ => false, + }); + assert!(match Bar::Var1 { + ::THEBAR => true, + _ => false, + }); +}