1
Fork 0

Get associated consts working in match patterns.

This commit is contained in:
Sean Patrick Santos 2015-03-25 10:53:28 -06:00
parent 91ae5e31ab
commit 29eb550ee6
25 changed files with 611 additions and 199 deletions

View file

@ -105,6 +105,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
match pat.node { match pat.node {
ast::PatIdent(_, _, None) | ast::PatIdent(_, _, None) |
ast::PatEnum(_, None) | ast::PatEnum(_, None) |
ast::PatQPath(..) |
ast::PatLit(..) | ast::PatLit(..) |
ast::PatRange(..) | ast::PatRange(..) |
ast::PatWild(_) => { ast::PatWild(_) => {

View file

@ -439,7 +439,7 @@ impl<'map> ast_util::IdVisitingOperation for RenamingRecorder<'map> {
impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> { impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> {
fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> { fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
return match pat.node { 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()); let def = self.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def());
match def { match def {
Some(DefAssociatedConst(did, _)) | Some(DefAssociatedConst(did, _)) |
@ -762,6 +762,9 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat,
Some(DefVariant(_, id, _)) => vec!(Variant(id)), Some(DefVariant(_, id, _)) => vec!(Variant(id)),
_ => vec!(Single) _ => vec!(Single)
}, },
ast::PatQPath(..) =>
cx.tcx.sess.span_bug(pat.span, "const pattern should've \
been rewritten"),
ast::PatStruct(..) => ast::PatStruct(..) =>
match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) { match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) {
Some(DefConst(..)) | Some(DefAssociatedConst(..)) => 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, _) => { ast::PatStruct(_, ref pattern_fields, _) => {
// Is this a struct or an enum variant? // Is this a struct or an enum variant?
let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def(); let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def();

View file

@ -1147,7 +1147,8 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> {
let tcx = typer.tcx(); let tcx = typer.tcx();
match pat.node { 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()) { match def_map.get(&pat.id).map(|d| d.full_def()) {
None => { None => {
// no definition found: pat is not a // no definition found: pat is not a

View file

@ -1299,6 +1299,10 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
} }
} }
ast::PatQPath(..) => {
// Lone constant: ignore
}
ast::PatIdent(_, _, Some(ref subpat)) => { ast::PatIdent(_, _, Some(ref subpat)) => {
try!(self.cat_pattern_(cmt, &**subpat, op)); try!(self.cat_pattern_(cmt, &**subpat, op));
} }

View file

@ -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 { pub fn pat_is_refutable(dm: &DefMap, pat: &ast::Pat) -> bool {
match pat.node { match pat.node {
ast::PatLit(_) | ast::PatRange(_, _) => true, ast::PatLit(_) | ast::PatRange(_, _) | ast::PatQPath(..) => true,
ast::PatEnum(_, _) | ast::PatEnum(_, _) |
ast::PatIdent(_, _, None) | ast::PatIdent(_, _, None) |
ast::PatStruct(..) => { 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 { pub fn pat_is_const(dm: &DefMap, pat: &ast::Pat) -> bool {
match pat.node { 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()) { match dm.borrow().get(&pat.id).map(|d| d.full_def()) {
Some(DefConst(..)) | Some(DefAssociatedConst(..)) => true, Some(DefConst(..)) | Some(DefAssociatedConst(..)) => true,
_ => false _ => 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 { pub fn pat_is_binding(dm: &DefMap, pat: &ast::Pat) -> bool {
match pat.node { match pat.node {
ast::PatIdent(..) => { ast::PatIdent(..) => {

View file

@ -1091,7 +1091,6 @@ impl LintPass for NonUpperCaseGlobals {
fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { fn check_pat(&mut self, cx: &Context, p: &ast::Pat) {
// Lint for constants that look like binding identifiers (#7526) // 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())) { 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(..))) => { (&ast::PatIdent(_, ref path1, _), Some(def::DefConst(..))) => {
NonUpperCaseGlobals::check_upper_case(cx, "constant in pattern", NonUpperCaseGlobals::check_upper_case(cx, "constant in pattern",
path1.node, p.span); path1.node, p.span);

View file

@ -41,6 +41,7 @@ use self::TypeParameters::*;
use self::RibKind::*; use self::RibKind::*;
use self::UseLexicalScopeFlag::*; use self::UseLexicalScopeFlag::*;
use self::ModulePrefixResult::*; use self::ModulePrefixResult::*;
use self::AssocItemResolveResult::*;
use self::NameSearchType::*; use self::NameSearchType::*;
use self::BareIdentifierPatternResolution::*; use self::BareIdentifierPatternResolution::*;
use self::ParentLink::*; 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::{ItemFn, ItemForeignMod, ItemImpl, ItemMac, ItemMod, ItemStatic, ItemDefaultImpl};
use syntax::ast::{ItemStruct, ItemTrait, ItemTy, ItemUse}; use syntax::ast::{ItemStruct, ItemTrait, ItemTy, ItemUse};
use syntax::ast::{Local, MethodImplItem, Name, NodeId}; 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::{PatRange, PatStruct, Path, PrimTy};
use syntax::ast::{TraitRef, Ty, TyBool, TyChar, TyF32}; use syntax::ast::{TraitRef, Ty, TyBool, TyChar, TyF32};
use syntax::ast::{TyF64, TyFloat, TyIs, TyI8, TyI16, TyI32, TyI64, TyInt}; use syntax::ast::{TyF64, TyFloat, TyIs, TyI8, TyI16, TyI32, TyI64, TyInt};
@ -331,6 +332,15 @@ enum ModulePrefixResult {
PrefixFound(Rc<Module>, usize) 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)] #[derive(Copy, Clone, PartialEq)]
enum NameSearchType { enum NameSearchType {
/// We're doing a name search in order to resolve a `use` directive. /// We're doing a name search in order to resolve a `use` directive.
@ -2305,32 +2315,22 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
fn resolve_type(&mut self, ty: &Ty) { fn resolve_type(&mut self, ty: &Ty) {
match ty.node { match ty.node {
// `<T>::a::b::c` is resolved by typeck alone.
TyPath(Some(ast::QSelf { position: 0, .. }), _) => {}
TyPath(ref maybe_qself, ref path) => { TyPath(ref maybe_qself, ref path) => {
let max_assoc_types = if let Some(ref qself) = *maybe_qself { let resolution =
// Make sure the trait is valid. match self.resolve_possibly_assoc_item(ty.id,
let _ = self.resolve_trait_reference(ty.id, path, 1); maybe_qself.as_ref(),
path.segments.len() - qself.position path,
} else { TypeNS,
path.segments.len() true) {
// `<T>::a::b::c` is resolved by typeck alone.
TypecheckRequired => {
// Resolve embedded types.
visit::walk_ty(self, ty);
return;
}
ResolveAttempt(resolution) => resolution,
}; };
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;
}
// This is a path in the type namespace. Walk through scopes // This is a path in the type namespace. Walk through scopes
// looking for it. // looking for it.
match resolution { match resolution {
@ -2489,10 +2489,24 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
PatEnum(ref path, _) => { PatEnum(ref path, _) => {
// This must be an enum variant, struct or const. // 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 { match path_res.base_def {
DefVariant(..) | DefStruct(..) | DefConst(..) | DefVariant(..) | DefStruct(..) | DefConst(..) => {
DefAssociatedConst(..) => {
self.record_def(pattern.id, path_res); self.record_def(pattern.id, path_res);
} }
DefStatic(..) => { DefStatic(..) => {
@ -2502,10 +2516,20 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
use a `const` instead"); use a `const` instead");
} }
_ => { _ => {
self.resolve_error(path.span, // 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", &format!("`{}` is not an enum variant, struct or const",
token::get_ident( token::get_ident(
path.segments.last().unwrap().identifier))); path.segments.last().unwrap().identifier)));
} else {
self.record_def(pattern.id, path_res);
}
} }
} }
} else { } else {
@ -2516,6 +2540,47 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
visit::walk_path(self, path); 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 associated const",
token::get_ident(
path.segments.last().unwrap().identifier)));
}
}
} else {
self.resolve_error(path.span,
&format!("unresolved associated const `{}`",
token::get_ident(path.segments.last().unwrap().identifier)));
}
visit::walk_pat(self, pattern);
}
PatStruct(ref path, _, _) => { PatStruct(ref path, _, _) => {
match self.resolve_path(pat_id, path, 0, TypeNS, false) { match self.resolve_path(pat_id, path, 0, TypeNS, false) {
Some(definition) => { Some(definition) => {
@ -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. /// If `check_ribs` is true, checks the local definitions first; i.e.
/// doesn't skip straight to the containing module. /// doesn't skip straight to the containing module.
/// Skips `path_depth` trailing segments, which is also reflected in the /// Skips `path_depth` trailing segments, which is also reflected in the
@ -3119,39 +3225,24 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// Next, resolve the node. // Next, resolve the node.
match expr.node { match expr.node {
ExprPath(ref maybe_qself, ref path) => {
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. // `<T>::a::b::c` is resolved by typeck alone.
ExprPath(Some(ast::QSelf { position: 0, .. }), ref path) => { TypecheckRequired => {
let method_name = path.segments.last().unwrap().identifier.name; 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); self.trait_map.insert(expr.id, traits);
visit::walk_expr(self, expr); visit::walk_expr(self, expr);
return;
} }
ResolveAttempt(resolution) => resolution,
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;
}
// This is a local path in the value namespace. Walk through // This is a local path in the value namespace. Walk through
// scopes looking for it. // scopes looking for it.
if let Some(path_res) = resolution { if let Some(path_res) = resolution {
@ -3181,7 +3272,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// so they can be completed during typeck. // so they can be completed during typeck.
if path_res.depth != 0 { if path_res.depth != 0 {
let method_name = path.segments.last().unwrap().identifier.name; 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); 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 // field, we need to add any trait methods we find that match
// the field name so that we can do some nice error reporting // the field name so that we can do some nice error reporting
// later on in typeck. // 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); self.trait_map.insert(expr.id, traits);
} }
ExprMethodCall(ident, _, _) => { ExprMethodCall(ident, _, _) => {
debug!("(recording candidate traits for expr) recording \ debug!("(recording candidate traits for expr) recording \
traits for {}", traits for {}",
expr.id); 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); 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> { fn get_traits_containing_item(&mut self, name: Name) -> Vec<DefId> {
debug!("(searching for traits containing method) looking for '{}'", debug!("(getting traits containing item) looking for '{}'",
token::get_name(name)); token::get_name(name));
fn add_trait_info(found_traits: &mut Vec<DefId>, fn add_trait_info(found_traits: &mut Vec<DefId>,

View file

@ -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)); self.collected_paths.push((p.id, path.clone(), false, recorder::VarRef));
visit::walk_pat(self, p); visit::walk_pat(self, p);
} }

View file

@ -1809,7 +1809,8 @@ fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
ast::PatMac(..) => { ast::PatMac(..) => {
bcx.sess().span_bug(pat.span, "unexpanded macro"); bcx.sess().span_bug(pat.span, "unexpanded macro");
} }
ast::PatWild(_) | ast::PatLit(_) | ast::PatRange(_, _) => () ast::PatQPath(..) | ast::PatWild(_) | ast::PatLit(_) |
ast::PatRange(_, _) => ()
} }
return bcx; return bcx;
} }

View file

@ -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, _) => { ast::PatStruct(_, ref field_pats, _) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);

View file

@ -11,12 +11,14 @@
use middle::const_eval; use middle::const_eval;
use middle::def; use middle::def;
use middle::infer; 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::subst::Substs;
use middle::ty::{self, Ty}; use middle::ty::{self, Ty};
use check::{check_expr, check_expr_has_type, check_expr_with_expectation}; use check::{check_expr, check_expr_has_type, check_expr_with_expectation};
use check::{check_expr_coercable_to_type, demand, FnCtxt, 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 require_same_types;
use util::nodemap::FnvHashMap; use util::nodemap::FnvHashMap;
use util::ppaux::Repr; 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 // subtyping doesn't matter here, as the value is some kind of scalar
demand::eqtype(fcx, pat.span, expected, lhs_ty); 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_did = tcx.def_map.borrow().get(&pat.id).unwrap().def_id();
let const_scheme = ty::lookup_item_type(tcx, const_did); let const_scheme = ty::lookup_item_type(tcx, const_did);
assert!(const_scheme.generics.is_empty()); 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[..]); let subpats = subpats.as_ref().map(|v| &v[..]);
check_pat_enum(pcx, pat, path, subpats, expected); 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) => { ast::PatStruct(ref path, ref fields, etc) => {
check_pat_struct(pcx, pat, path, fields, etc, expected); 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. // 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>, pub fn check_dereferencable<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
span: Span, expected: Ty<'tcx>, span: Span, expected: Ty<'tcx>,
inner: &ast::Pat) -> bool { 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 fcx = pcx.fcx;
let tcx = pcx.fcx.ccx.tcx; 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() let enum_def = def.variant_def_ids()
.map_or_else(|| def.def_id(), |(enum_def, _)| enum_def); .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 { } else {
ctor_scheme ctor_scheme
}; };
instantiate_path(pcx.fcx, &path.segments, instantiate_path(pcx.fcx, segments,
path_scheme, &ctor_predicates, 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); let pat_ty = fcx.node_ty(pat.id);
demand::eqtype(fcx, pat.span, expected, pat_ty); demand::eqtype(fcx, pat.span, expected, pat_ty);
let real_path_ty = fcx.node_ty(pat.id); let real_path_ty = fcx.node_ty(pat.id);
let (arg_tys, kind_name): (Vec<_>, &'static str) = match real_path_ty.sty { let (arg_tys, kind_name): (Vec<_>, &'static str) = match real_path_ty.sty {
ty::ty_enum(enum_def_id, expected_substs) ty::ty_enum(enum_def_id, expected_substs)

View file

@ -3230,53 +3230,20 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
&format!("unbound path {}", expr.repr(tcx))) &format!("unbound path {}", expr.repr(tcx)))
}; };
let def = path_res.base_def; if let Some((opt_ty, segments, def)) =
if path_res.depth == 0 { 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, let (scheme, predicates) = type_scheme_and_predicates_for_def(fcx,
expr.span, expr.span,
def); def);
instantiate_path(fcx, instantiate_path(fcx,
&path.segments, segments,
scheme, scheme,
&predicates, &predicates,
opt_self_ty, opt_ty,
def, def,
expr.span, expr.span,
id); 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 // 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(); 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, fn constrain_path_type_parameters(fcx: &FnCtxt,
expr: &ast::Expr) expr: &ast::Expr)
{ {

View file

@ -180,6 +180,7 @@ register_diagnostics! {
E0324, // implemented a method when another trait item expected E0324, // implemented a method when another trait item expected
E0325, // implemented an associated type when another trait item expected E0325, // implemented an associated type when another trait item expected
E0326, // associated const implemented with different type from trait 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 E0366, // dropck forbid specialization to concrete type or region
E0367, // dropck forbid specialization to predicate not in struct/enum E0367, // dropck forbid specialization to predicate not in struct/enum
E0368, // binary operation `<op>=` cannot be applied to types E0368, // binary operation `<op>=` cannot be applied to types

View file

@ -2522,6 +2522,8 @@ fn name_from_pat(p: &ast::Pat) -> String {
PatWild(PatWildMulti) => "..".to_string(), PatWild(PatWildMulti) => "..".to_string(),
PatIdent(_, ref p, _) => token::get_ident(p.node).to_string(), PatIdent(_, ref p, _) => token::get_ident(p.node).to_string(),
PatEnum(ref p, _) => path_to_string(p), 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) => { PatStruct(ref name, ref fields, etc) => {
format!("{} {{ {}{} }}", path_to_string(name), format!("{} {{ {}{} }}", path_to_string(name),
fields.iter().map(|&Spanned { node: ref fp, .. }| fields.iter().map(|&Spanned { node: ref fp, .. }|

View file

@ -599,6 +599,12 @@ pub enum Pat_ {
/// "None" means a * pattern where we don't bind the fields to names. /// "None" means a * pattern where we don't bind the fields to names.
PatEnum(Path, Option<Vec<P<Pat>>>), 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, ..}` /// Destructuring of a struct, e.g. `Foo {x, y, ..}`
/// The `bool` is `true` in the presence of a `..` /// The `bool` is `true` in the presence of a `..`
PatStruct(Path, Vec<Spanned<FieldPat>>, bool), PatStruct(Path, Vec<Spanned<FieldPat>>, bool),

View file

@ -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"), PatMac(_) => panic!("attempted to analyze unexpanded pattern"),
PatWild(_) | PatLit(_) | PatRange(_, _) | PatIdent(_, _, _) | PatWild(_) | PatLit(_) | PatRange(_, _) | PatIdent(_, _, _) |
PatEnum(_, _) => { PatEnum(_, _) | PatQPath(_, _) => {
true true
} }
} }

View file

@ -1134,6 +1134,10 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
PatEnum(folder.fold_path(pth), PatEnum(folder.fold_path(pth),
pats.map(|pats| pats.move_map(|x| folder.fold_pat(x)))) 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) => { PatStruct(pth, fields, etc) => {
let pth = folder.fold_path(pth); let pth = folder.fold_path(pth);
let fs = fields.move_map(|f| { let fs = fields.move_map(|f| {

View file

@ -40,8 +40,9 @@ use ast::{MacStmtWithBraces, MacStmtWithSemicolon, MacStmtWithoutBraces};
use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, MatchSource}; use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, MatchSource};
use ast::{MutTy, BiMul, Mutability}; use ast::{MutTy, BiMul, Mutability};
use ast::{MethodImplItem, NamedField, UnNeg, NoReturn, UnNot}; use ast::{MethodImplItem, NamedField, UnNeg, NoReturn, UnNot};
use ast::{Pat, PatBox, PatEnum, PatIdent, PatLit, PatMac, PatRange, PatRegion}; use ast::{Pat, PatBox, PatEnum, PatIdent, PatLit, PatQPath, PatMac, PatRange};
use ast::{PatStruct, PatTup, PatVec, PatWild, PatWildMulti, PatWildSingle}; use ast::{PatRegion, PatStruct, PatTup, PatVec, PatWild, PatWildMulti};
use ast::PatWildSingle;
use ast::{PolyTraitRef, QSelf}; use ast::{PolyTraitRef, QSelf};
use ast::{Return, BiShl, BiShr, Stmt, StmtDecl}; use ast::{Return, BiShl, BiShr, Stmt, StmtDecl};
use ast::{StmtExpr, StmtSemi, StmtMac, StructDef, StructField}; use ast::{StmtExpr, StmtSemi, StmtMac, StructDef, StructField};
@ -109,6 +110,15 @@ pub enum PathParsingMode {
LifetimeAndTypesWithColons, 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 `?`. /// How to parse a bound, whether to allow bound modifiers such as `?`.
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub enum BoundParsingMode { pub enum BoundParsingMode {
@ -1345,36 +1355,9 @@ impl<'a> Parser<'a> {
try!(self.expect(&token::CloseDelim(token::Paren))); try!(self.expect(&token::CloseDelim(token::Paren)));
TyTypeof(e) TyTypeof(e)
} else if try!(self.eat_lt()) { } 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) ){ let (qself, path) =
try!(self.parse_path(LifetimeAndTypesWithoutColons)) try!(self.parse_qualified_path(QPathParsingMode::NoParameters));
} 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;
TyPath(Some(qself), path) TyPath(Some(qself), path)
} else if self.check(&token::ModSep) || } 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 /// Parses a path and optional type parameter bounds, depending on the
/// mode. The `mode` parameter determines whether lifetimes, types, and/or /// mode. The `mode` parameter determines whether lifetimes, types, and/or
/// bounds are permitted and whether `::` must precede type parameter /// bounds are permitted and whether `::` must precede type parameter
@ -2054,49 +2092,10 @@ impl<'a> Parser<'a> {
} }
_ => { _ => {
if try!(self.eat_lt()){ 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 (qself, path) =
let parameters = if try!(self.eat(&token::ModSep) ){ try!(self.parse_qualified_path(QPathParsingMode::MaybeParameters));
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 hi = self.span.hi;
return Ok(self.mk_expr(lo, hi, ExprPath(Some(qself), path))); return Ok(self.mk_expr(lo, hi, ExprPath(Some(qself), path)));
} }
if try!(self.eat_keyword(keywords::Move) ){ 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>> { fn parse_pat_range_end(&mut self) -> PResult<P<Expr>> {
if self.is_path_start() { if self.is_path_start() {
let lo = self.span.lo; 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; 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 { } else {
self.parse_literal_maybe_minus() self.parse_literal_maybe_minus()
} }
} }
fn is_path_start(&self) -> bool { 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) && !self.token.is_keyword(keywords::True) && !self.token.is_keyword(keywords::False)
} }
@ -3252,18 +3260,31 @@ impl<'a> Parser<'a> {
pat = try!(self.parse_pat_ident(BindByValue(MutImmutable))); pat = try!(self.parse_pat_ident(BindByValue(MutImmutable)));
} }
} else { } else {
// Parse as a general path let (qself, path) = if try!(self.eat_lt()) {
let path = try!(self.parse_path(LifetimeAndTypesWithColons)); // 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 { match self.token {
token::DotDotDot => { token::DotDotDot => {
// Parse range // Parse range
let hi = self.last_span.hi; 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()); try!(self.bump());
let end = try!(self.parse_pat_range_end()); let end = try!(self.parse_pat_range_end());
pat = PatRange(begin, end); pat = PatRange(begin, end);
} }
token::OpenDelim(token::Brace) => { token::OpenDelim(token::Brace) => {
if qself.is_some() {
let span = self.span;
self.span_err(span,
"unexpected `{` after qualified path");
self.abort_if_errors();
}
// Parse struct pattern // Parse struct pattern
try!(self.bump()); try!(self.bump());
let (fields, etc) = try!(self.parse_pat_fields()); let (fields, etc) = try!(self.parse_pat_fields());
@ -3271,6 +3292,12 @@ impl<'a> Parser<'a> {
pat = PatStruct(path, fields, etc); pat = PatStruct(path, fields, etc);
} }
token::OpenDelim(token::Paren) => { 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 // Parse tuple struct or enum pattern
if self.look_ahead(1, |t| *t == token::DotDot) { if self.look_ahead(1, |t| *t == token::DotDot) {
// This is a "top constructor only" pat // This is a "top constructor only" pat
@ -3287,6 +3314,10 @@ impl<'a> Parser<'a> {
pat = PatEnum(path, Some(args)); pat = PatEnum(path, Some(args));
} }
} }
_ if qself.is_some() => {
// Parse qualified path
pat = PatQPath(qself.unwrap(), path);
}
_ => { _ => {
// Parse nullary enum // Parse nullary enum
pat = PatEnum(path, Some(vec![])); pat = PatEnum(path, Some(vec![]));

View file

@ -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) => { ast::PatStruct(ref path, ref fields, etc) => {
try!(self.print_path(path, true, 0)); try!(self.print_path(path, true, 0));
try!(self.nbsp()); try!(self.nbsp());

View file

@ -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, _) => { PatStruct(ref path, ref fields, _) => {
visitor.visit_path(path, pattern.id); visitor.visit_path(path, pattern.id);
for field in fields { for field in fields {

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
_ => (),
}
}

View file

@ -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
_ => (),
}
}

View file

@ -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,
});
}