Skip to content

Commit

Permalink
rustc: implement fully qualified UFCS expressions.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Jan 15, 2015
1 parent b51026e commit 85ba817
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 38 deletions.
53 changes: 28 additions & 25 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 => {
Expand All @@ -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) => {
Expand Down Expand Up @@ -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 \
Expand All @@ -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);
}
Expand All @@ -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, _), _))
Expand Down
16 changes: 11 additions & 5 deletions src/librustc_trans/save/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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(_, _) => {}
Expand All @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_typeck/check/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
27 changes: 24 additions & 3 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions src/test/compile-fail/ufcs-qpath-missing-params.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 21 additions & 0 deletions src/test/compile-fail/ufcs-qpath-self-mismatch.rs
Original file line number Diff line number Diff line change
@@ -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
}

30 changes: 27 additions & 3 deletions src/test/run-pass/const-polymorphic-paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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());
}

7 comments on commit 85ba817

@bors
Copy link
Contributor

@bors bors commented on 85ba817 Jan 15, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from nikomatsakis
at eddyb@85ba817

@bors
Copy link
Contributor

@bors bors commented on 85ba817 Jan 15, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging eddyb/rust/expr-qpath = 85ba817 into auto

@bors
Copy link
Contributor

@bors bors commented on 85ba817 Jan 15, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

status: {"merge_sha": "9ade482b3bdc7a967ca98f1f1138b596ef45191e"}

@bors
Copy link
Contributor

@bors bors commented on 85ba817 Jan 15, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eddyb/rust/expr-qpath = 85ba817 merged ok, testing candidate = 9ade482

@bors
Copy link
Contributor

@bors bors commented on 85ba817 Jan 15, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding master to auto = 9ade482

@bors
Copy link
Contributor

@bors bors commented on 85ba817 Jan 15, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding master to auto = 9ade482

Please sign in to comment.