Get associated consts working in match patterns.
This commit is contained in:
parent
91ae5e31ab
commit
29eb550ee6
25 changed files with 611 additions and 199 deletions
|
@ -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(_) => {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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(..) => {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,32 +2315,22 @@ 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 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,
|
||||
};
|
||||
|
||||
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
|
||||
// looking for it.
|
||||
match resolution {
|
||||
|
@ -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,10 +2516,20 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
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",
|
||||
token::get_ident(
|
||||
path.segments.last().unwrap().identifier)));
|
||||
} else {
|
||||
self.record_def(pattern.id, path_res);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -2516,6 +2540,47 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
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, _, _) => {
|
||||
match self.resolve_path(pat_id, path, 0, TypeNS, false) {
|
||||
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.
|
||||
/// doesn't skip straight to the containing module.
|
||||
/// 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.
|
||||
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.
|
||||
ExprPath(Some(ast::QSelf { position: 0, .. }), ref path) => {
|
||||
TypecheckRequired => {
|
||||
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);
|
||||
visit::walk_expr(self, expr);
|
||||
return;
|
||||
}
|
||||
|
||||
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()
|
||||
ResolveAttempt(resolution) => resolution,
|
||||
};
|
||||
|
||||
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
|
||||
// scopes looking for it.
|
||||
if let Some(path_res) = resolution {
|
||||
|
@ -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>,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, .. }|
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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
|
||||
});
|
||||
let (qself, path) =
|
||||
try!(self.parse_qualified_path(QPathParsingMode::MaybeParameters));
|
||||
|
||||
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)));
|
||||
}
|
||||
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,18 +3260,31 @@ 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) => {
|
||||
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());
|
||||
|
@ -3271,6 +3292,12 @@ impl<'a> Parser<'a> {
|
|||
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![]));
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 {
|
||||
|
|
35
src/test/compile-fail/method-path-in-pattern.rs
Normal file
35
src/test/compile-fail/method-path-in-pattern.rs
Normal 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
|
||||
}
|
||||
}
|
26
src/test/compile-fail/method-resolvable-path-in-pattern.rs
Normal file
26
src/test/compile-fail/method-resolvable-path-in-pattern.rs
Normal 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
|
||||
}
|
||||
}
|
17
src/test/parse-fail/brace-after-qualified-path-in-match.rs
Normal file
17
src/test/parse-fail/brace-after-qualified-path-in-match.rs
Normal 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
|
||||
_ => (),
|
||||
}
|
||||
}
|
17
src/test/parse-fail/paren-after-qualified-path-in-match.rs
Normal file
17
src/test/parse-fail/paren-after-qualified-path-in-match.rs
Normal 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
|
||||
_ => (),
|
||||
}
|
||||
}
|
52
src/test/run-pass/associated-const-match-patterns.rs
Normal file
52
src/test/run-pass/associated-const-match-patterns.rs
Normal 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,
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue