rustc: implement fully qualified UFCS expressions.
This commit is contained in:
parent
b51026e09c
commit
85ba8178e2
7 changed files with 130 additions and 38 deletions
|
|
@ -61,7 +61,7 @@ use rustc::util::lev_distance::lev_distance;
|
|||
use syntax::ast::{Arm, BindByRef, BindByValue, BindingMode, Block, Crate, CrateNum};
|
||||
use syntax::ast::{DefId, Expr, ExprAgain, ExprBreak, ExprField};
|
||||
use syntax::ast::{ExprClosure, ExprForLoop, ExprLoop, ExprWhile, ExprMethodCall};
|
||||
use syntax::ast::{ExprPath, ExprStruct, FnDecl};
|
||||
use syntax::ast::{ExprPath, ExprQPath, ExprStruct, FnDecl};
|
||||
use syntax::ast::{ForeignItemFn, ForeignItemStatic, Generics};
|
||||
use syntax::ast::{Ident, ImplItem, Item, ItemConst, ItemEnum, ItemFn};
|
||||
use syntax::ast::{ItemForeignMod, ItemImpl, ItemMac, ItemMod, ItemStatic};
|
||||
|
|
@ -3169,7 +3169,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
TraitImplementation => "implement",
|
||||
TraitDerivation => "derive",
|
||||
TraitObject => "reference",
|
||||
TraitQPath => "extract an associated type from",
|
||||
TraitQPath => "extract an associated item from",
|
||||
};
|
||||
|
||||
let msg = format!("attempt to {} a nonexistent trait `{}`", usage_str, path_str);
|
||||
|
|
@ -3565,31 +3565,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
match result_def {
|
||||
None => {
|
||||
match self.resolve_path(ty.id, path, TypeNS, true) {
|
||||
Some(def) => {
|
||||
debug!("(resolving type) resolved `{:?}` to \
|
||||
type {:?}",
|
||||
token::get_ident(path.segments.last().unwrap() .identifier),
|
||||
def);
|
||||
result_def = Some(def);
|
||||
}
|
||||
None => {
|
||||
result_def = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(_) => {} // Continue.
|
||||
if let None = result_def {
|
||||
result_def = self.resolve_path(ty.id, path, TypeNS, true);
|
||||
}
|
||||
|
||||
match result_def {
|
||||
Some(def) => {
|
||||
// Write the result into the def map.
|
||||
debug!("(resolving type) writing resolution for `{}` \
|
||||
(id {})",
|
||||
(id {}) = {:?}",
|
||||
self.path_names_to_string(path),
|
||||
path_id);
|
||||
path_id, def);
|
||||
self.record_def(path_id, def);
|
||||
}
|
||||
None => {
|
||||
|
|
@ -3609,6 +3595,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
TyQPath(ref qpath) => {
|
||||
self.resolve_type(&*qpath.self_type);
|
||||
self.resolve_trait_reference(ty.id, &*qpath.trait_ref, TraitQPath);
|
||||
for ty in qpath.item_path.parameters.types().into_iter() {
|
||||
self.resolve_type(&**ty);
|
||||
}
|
||||
for binding in qpath.item_path.parameters.bindings().into_iter() {
|
||||
self.resolve_type(&*binding.ty);
|
||||
}
|
||||
}
|
||||
|
||||
TyPolyTraitRef(ref bounds) => {
|
||||
|
|
@ -4400,15 +4392,25 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
// The interpretation of paths depends on whether the path has
|
||||
// multiple elements in it or not.
|
||||
|
||||
ExprPath(ref path) => {
|
||||
ExprPath(_) | ExprQPath(_) => {
|
||||
let mut path_from_qpath;
|
||||
let path = match expr.node {
|
||||
ExprPath(ref path) => path,
|
||||
ExprQPath(ref qpath) => {
|
||||
self.resolve_type(&*qpath.self_type);
|
||||
self.resolve_trait_reference(expr.id, &*qpath.trait_ref, TraitQPath);
|
||||
path_from_qpath = qpath.trait_ref.path.clone();
|
||||
path_from_qpath.segments.push(qpath.item_path.clone());
|
||||
&path_from_qpath
|
||||
}
|
||||
_ => unreachable!()
|
||||
};
|
||||
// This is a local path in the value namespace. Walk through
|
||||
// scopes looking for it.
|
||||
|
||||
let path_name = self.path_names_to_string(path);
|
||||
|
||||
match self.resolve_path(expr.id, path, ValueNS, true) {
|
||||
// Check if struct variant
|
||||
Some((DefVariant(_, _, true), _)) => {
|
||||
let path_name = self.path_names_to_string(path);
|
||||
self.resolve_error(expr.span,
|
||||
format!("`{}` is a struct variant name, but \
|
||||
this expression \
|
||||
|
|
@ -4423,7 +4425,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
Some(def) => {
|
||||
// Write the result into the def map.
|
||||
debug!("(resolving expr) resolved `{}`",
|
||||
path_name);
|
||||
self.path_names_to_string(path));
|
||||
|
||||
self.record_def(expr.id, def);
|
||||
}
|
||||
|
|
@ -4432,6 +4434,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
// (The pattern matching def_tys where the id is in self.structs
|
||||
// matches on regular structs while excluding tuple- and enum-like
|
||||
// structs, which wouldn't result in this error.)
|
||||
let path_name = self.path_names_to_string(path);
|
||||
match self.with_no_errors(|this|
|
||||
this.resolve_path(expr.id, path, TypeNS, false)) {
|
||||
Some((DefTy(struct_id, _), _))
|
||||
|
|
|
|||
|
|
@ -767,7 +767,7 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> {
|
|||
span: Span,
|
||||
path: &ast::Path,
|
||||
ref_kind: Option<recorder::Row>) {
|
||||
if generated_code(path.span) {
|
||||
if generated_code(span) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -1307,9 +1307,15 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
|
|||
visit::walk_expr(self, ex);
|
||||
},
|
||||
ast::ExprPath(ref path) => {
|
||||
self.process_path(ex.id, ex.span, path, None);
|
||||
self.process_path(ex.id, path.span, path, None);
|
||||
visit::walk_path(self, path);
|
||||
}
|
||||
ast::ExprQPath(ref qpath) => {
|
||||
let mut path = qpath.trait_ref.path.clone();
|
||||
path.segments.push(qpath.item_path.clone());
|
||||
self.process_path(ex.id, ex.span, &path, None);
|
||||
visit::walk_qpath(self, ex.span, &**qpath);
|
||||
}
|
||||
ast::ExprStruct(ref path, ref fields, ref base) =>
|
||||
self.process_struct_lit(ex, path, fields, base),
|
||||
ast::ExprMethodCall(_, _, ref args) => self.process_method_call(ex, args),
|
||||
|
|
@ -1439,7 +1445,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
|
|||
"")
|
||||
}
|
||||
def::DefVariant(..) => {
|
||||
paths_to_process.push((id, p.span, p.clone(), Some(ref_kind)))
|
||||
paths_to_process.push((id, p.clone(), Some(ref_kind)))
|
||||
}
|
||||
// FIXME(nrc) what are these doing here?
|
||||
def::DefStatic(_, _) => {}
|
||||
|
|
@ -1448,8 +1454,8 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
|
|||
*def)
|
||||
}
|
||||
}
|
||||
for &(id, span, ref path, ref_kind) in paths_to_process.iter() {
|
||||
self.process_path(id, span, path, ref_kind);
|
||||
for &(id, ref path, ref_kind) in paths_to_process.iter() {
|
||||
self.process_path(id, path.span, path, ref_kind);
|
||||
}
|
||||
self.collecting = false;
|
||||
self.collected_paths.clear();
|
||||
|
|
|
|||
|
|
@ -467,7 +467,7 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat,
|
|||
};
|
||||
|
||||
instantiate_path(pcx.fcx, path, ty::lookup_item_type(tcx, enum_def_id),
|
||||
def, pat.span, pat.id);
|
||||
None, def, pat.span, pat.id);
|
||||
|
||||
let pat_ty = fcx.node_ty(pat.id);
|
||||
demand::eqtype(fcx, pat.span, expected, pat_ty);
|
||||
|
|
@ -505,7 +505,7 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &ast::Pat,
|
|||
} else {
|
||||
ctor_scheme
|
||||
};
|
||||
instantiate_path(pcx.fcx, path, path_scheme, def, pat.span, pat.id);
|
||||
instantiate_path(pcx.fcx, path, path_scheme, None, def, pat.span, pat.id);
|
||||
|
||||
let pat_ty = fcx.node_ty(pat.id);
|
||||
demand::eqtype(fcx, pat.span, expected, pat_ty);
|
||||
|
|
|
|||
|
|
@ -3553,10 +3553,25 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
|
|||
};
|
||||
fcx.write_ty(id, oprnd_t);
|
||||
}
|
||||
ast::ExprPath(ref pth) => {
|
||||
let defn = lookup_def(fcx, pth.span, id);
|
||||
ast::ExprPath(ref path) => {
|
||||
let defn = lookup_def(fcx, path.span, id);
|
||||
let pty = type_scheme_for_def(fcx, expr.span, defn);
|
||||
instantiate_path(fcx, pth, pty, defn, expr.span, expr.id);
|
||||
instantiate_path(fcx, path, pty, None, defn, expr.span, expr.id);
|
||||
|
||||
// We always require that the type provided as the value for
|
||||
// a type parameter outlives the moment of instantiation.
|
||||
constrain_path_type_parameters(fcx, expr);
|
||||
}
|
||||
ast::ExprQPath(ref qpath) => {
|
||||
// Require explicit type params for the trait.
|
||||
let self_ty = fcx.to_ty(&*qpath.self_type);
|
||||
astconv::instantiate_trait_ref(fcx, fcx, &*qpath.trait_ref, Some(self_ty), None);
|
||||
|
||||
let defn = lookup_def(fcx, expr.span, id);
|
||||
let pty = type_scheme_for_def(fcx, expr.span, defn);
|
||||
let mut path = qpath.trait_ref.path.clone();
|
||||
path.segments.push(qpath.item_path.clone());
|
||||
instantiate_path(fcx, &path, pty, Some(self_ty), defn, expr.span, expr.id);
|
||||
|
||||
// We always require that the type provided as the value for
|
||||
// a type parameter outlives the moment of instantiation.
|
||||
|
|
@ -4619,6 +4634,7 @@ pub fn type_scheme_for_def<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
|||
pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
||||
path: &ast::Path,
|
||||
type_scheme: TypeScheme<'tcx>,
|
||||
opt_self_ty: Option<Ty<'tcx>>,
|
||||
def: def::Def,
|
||||
span: Span,
|
||||
node_id: ast::NodeId) {
|
||||
|
|
@ -4776,6 +4792,11 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
|||
}
|
||||
}
|
||||
}
|
||||
if let Some(self_ty) = opt_self_ty {
|
||||
// `<T as Trait>::foo` shouldn't have resolved to a `Self`-less item.
|
||||
assert_eq!(type_defs.len(subst::SelfSpace), 1);
|
||||
substs.types.push(subst::SelfSpace, self_ty);
|
||||
}
|
||||
|
||||
// Now we have to compare the types that the user *actually*
|
||||
// provided against the types that were *expected*. If the user
|
||||
|
|
|
|||
17
src/test/compile-fail/ufcs-qpath-missing-params.rs
Normal file
17
src/test/compile-fail/ufcs-qpath-missing-params.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2014 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::borrow::IntoCow;
|
||||
|
||||
fn main() {
|
||||
<String as IntoCow>::into_cow("foo".to_string());
|
||||
//~^ ERROR wrong number of type arguments: expected 2, found 0
|
||||
}
|
||||
|
||||
21
src/test/compile-fail/ufcs-qpath-self-mismatch.rs
Normal file
21
src/test/compile-fail/ufcs-qpath-self-mismatch.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2014 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::ops::Add;
|
||||
|
||||
fn main() {
|
||||
<i32 as Add<u32>>::add(1, 2);
|
||||
//~^ ERROR the trait `core::ops::Add<u32>` is not implemented for the type `i32`
|
||||
<i32 as Add<i32>>::add(1u32, 2);
|
||||
//~^ ERROR mismatched types
|
||||
<i32 as Add<i32>>::add(1, 2u32);
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
|
|
@ -10,9 +10,11 @@
|
|||
|
||||
#![feature(macro_rules)]
|
||||
|
||||
use std::borrow::{Cow, IntoCow};
|
||||
use std::collections::Bitv;
|
||||
use std::default::Default;
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::Add;
|
||||
use std::option::IntoIter as OptionIter;
|
||||
use std::rand::Rand;
|
||||
use std::rand::XorShiftRng as DummyRng;
|
||||
|
|
@ -28,6 +30,11 @@ fn u8_as_i8(x: u8) -> i8 { x as i8 }
|
|||
fn odd(x: uint) -> bool { x % 2 == 1 }
|
||||
fn dummy_rng() -> DummyRng { DummyRng::new_unseeded() }
|
||||
|
||||
trait Size: Sized {
|
||||
fn size() -> uint { std::mem::size_of::<Self>() }
|
||||
}
|
||||
impl<T> Size for T {}
|
||||
|
||||
macro_rules! tests {
|
||||
($($expr:expr, $ty:ty, ($($test:expr),*);)+) => (pub fn main() {$({
|
||||
const C: $ty = $expr;
|
||||
|
|
@ -70,14 +77,31 @@ tests! {
|
|||
// , (vec![b'f', b'o', b'o'], u8_as_i8);
|
||||
|
||||
// Trait static methods.
|
||||
// FIXME qualified path expressions aka UFCS i.e. <T as Trait>::method.
|
||||
<bool as Size>::size, fn() -> uint, ();
|
||||
Default::default, fn() -> int, ();
|
||||
<int as Default>::default, fn() -> int, ();
|
||||
Rand::rand, fn(&mut DummyRng) -> int, (&mut dummy_rng());
|
||||
<int as Rand>::rand, fn(&mut DummyRng) -> int, (&mut dummy_rng());
|
||||
Rand::rand::<DummyRng>, fn(&mut DummyRng) -> int, (&mut dummy_rng());
|
||||
<int as Rand>::rand::<DummyRng>, fn(&mut DummyRng) -> int, (&mut dummy_rng());
|
||||
|
||||
// Trait non-static methods.
|
||||
Clone::clone, fn(&int) -> int, (&5);
|
||||
<int as Clone>::clone, fn(&int) -> int, (&5);
|
||||
FromIterator::from_iter, fn(OptionIter<int>) -> Vec<int>, (Some(5).into_iter());
|
||||
FromIterator::from_iter::<OptionIter<int>>, fn(OptionIter<int>) -> Vec<int>
|
||||
, (Some(5).into_iter());
|
||||
<Vec<_> as FromIterator<_>>::from_iter, fn(OptionIter<int>) -> Vec<int>,
|
||||
(Some(5).into_iter());
|
||||
<Vec<int> as FromIterator<_>>::from_iter, fn(OptionIter<int>) -> Vec<int>,
|
||||
(Some(5).into_iter());
|
||||
FromIterator::from_iter::<OptionIter<int>>, fn(OptionIter<int>) -> Vec<int>,
|
||||
(Some(5).into_iter());
|
||||
<Vec<int> as FromIterator<_>>::from_iter::<OptionIter<int>>, fn(OptionIter<int>) -> Vec<int>,
|
||||
(Some(5).into_iter());
|
||||
Add::add, fn(i32, i32) -> i32, (5, 6);
|
||||
<i32 as Add<_>>::add, fn(i32, i32) -> i32, (5, 6);
|
||||
<i32 as Add<i32>>::add, fn(i32, i32) -> i32, (5, 6);
|
||||
<String as IntoCow<_, _>>::into_cow, fn(String) -> Cow<'static, String, str>,
|
||||
("foo".to_string());
|
||||
<String as IntoCow<'static, _, _>>::into_cow, fn(String) -> Cow<'static, String, str>,
|
||||
("foo".to_string());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue