Skip to content

Commit

Permalink
auto merge of #18694 : nikomatsakis/rust/issue-18208-method-dispatch-…
Browse files Browse the repository at this point in the history
…2, r=nrc

This is a pretty major refactoring of the method dispatch infrastructure. It is intended to avoid gross inefficiencies and enable caching and other optimizations (e.g. #17995), though it itself doesn't seem to execute particularly faster yet. It also solves some cases where we were failing to resolve methods that we theoretically should have succeeded with.

Fixes #18674.

cc #18208
  • Loading branch information
bors committed Nov 17, 2014
2 parents f092793 + 99fbd34 commit 336349c
Show file tree
Hide file tree
Showing 25 changed files with 2,643 additions and 2,074 deletions.
3 changes: 2 additions & 1 deletion src/librustc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ This API is completely unstable and subject to change.
html_root_url = "http://doc.rust-lang.org/nightly/")]

#![feature(default_type_params, globs, if_let, import_shadowing, macro_rules, phase, quote)]
#![feature(slicing_syntax, struct_variant, unsafe_destructor)]
#![feature(slicing_syntax, struct_variant, tuple_indexing, unsafe_destructor)]
#![feature(rustc_diagnostic_macros)]

extern crate arena;
Expand Down Expand Up @@ -87,6 +87,7 @@ pub mod middle {
pub mod effect;
pub mod entry;
pub mod expr_use_visitor;
pub mod fast_reject;
pub mod graph;
pub mod intrinsicck;
pub mod lang_items;
Expand Down
105 changes: 105 additions & 0 deletions src/librustc/middle/fast_reject.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// 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 middle::ty;
use syntax::ast;

use self::SimplifiedType::*;

/** See `simplify_type */
#[deriving(Clone, PartialEq, Eq, Hash)]
pub enum SimplifiedType {
BoolSimplifiedType,
CharSimplifiedType,
IntSimplifiedType(ast::IntTy),
UintSimplifiedType(ast::UintTy),
FloatSimplifiedType(ast::FloatTy),
EnumSimplifiedType(ast::DefId),
StrSimplifiedType,
VecSimplifiedType,
PtrSimplifiedType,
TupleSimplifiedType(uint),
TraitSimplifiedType(ast::DefId),
StructSimplifiedType(ast::DefId),
UnboxedClosureSimplifiedType(ast::DefId),
FunctionSimplifiedType(uint),
ParameterSimplifiedType,
}

pub fn simplify_type(tcx: &ty::ctxt,
ty: ty::t,
can_simplify_params: bool)
-> Option<SimplifiedType>
{
/*!
* Tries to simplify a type by dropping type parameters, deref'ing
* away any reference types, etc. The idea is to get something
* simple that we can use to quickly decide if two types could
* unify during method lookup.
*
* If `can_simplify_params` is false, then we will fail to
* simplify type parameters entirely. This is useful when those
* type parameters would be instantiated with fresh type
* variables, since then we can't say much about whether two types
* would unify. Put another way, `can_simplify_params` should be
* true if type parameters appear free in `ty` and `false` if they
* are to be considered bound.
*/

match ty::get(ty).sty {
ty::ty_bool => Some(BoolSimplifiedType),
ty::ty_char => Some(CharSimplifiedType),
ty::ty_int(int_type) => Some(IntSimplifiedType(int_type)),
ty::ty_uint(uint_type) => Some(UintSimplifiedType(uint_type)),
ty::ty_float(float_type) => Some(FloatSimplifiedType(float_type)),
ty::ty_enum(def_id, _) => Some(EnumSimplifiedType(def_id)),
ty::ty_str => Some(StrSimplifiedType),
ty::ty_vec(..) => Some(VecSimplifiedType),
ty::ty_ptr(_) => Some(PtrSimplifiedType),
ty::ty_trait(ref trait_info) => {
Some(TraitSimplifiedType(trait_info.principal.def_id))
}
ty::ty_struct(def_id, _) => {
Some(StructSimplifiedType(def_id))
}
ty::ty_rptr(_, mt) => {
// since we introduce auto-refs during method lookup, we
// just treat &T and T as equivalent from the point of
// view of possibly unifying
simplify_type(tcx, mt.ty, can_simplify_params)
}
ty::ty_uniq(_) => {
// treat like we would treat `Box`
let def_id = tcx.lang_items.owned_box().unwrap();
Some(StructSimplifiedType(def_id))
}
ty::ty_unboxed_closure(def_id, _, _) => {
Some(UnboxedClosureSimplifiedType(def_id))
}
ty::ty_tup(ref tys) => {
Some(TupleSimplifiedType(tys.len()))
}
ty::ty_closure(ref f) => {
Some(FunctionSimplifiedType(f.sig.inputs.len()))
}
ty::ty_bare_fn(ref f) => {
Some(FunctionSimplifiedType(f.sig.inputs.len()))
}
ty::ty_param(_) => {
if can_simplify_params {
Some(ParameterSimplifiedType)
} else {
None
}
}
ty::ty_open(_) | ty::ty_infer(_) | ty::ty_err => None,
}
}

10 changes: 10 additions & 0 deletions src/librustc/middle/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,16 @@ pub fn overlapping_impls(infcx: &InferCtxt,
coherence::impl_can_satisfy(infcx, impl2_def_id, impl1_def_id)
}

pub fn impl_obligations(tcx: &ty::ctxt,
cause: ObligationCause,
impl_def_id: ast::DefId,
impl_substs: &subst::Substs)
-> subst::VecPerParamSpace<Obligation>
{
let impl_generics = ty::lookup_item_type(tcx, impl_def_id).generics;
obligations_for_generics(tcx, cause, &impl_generics, impl_substs)
}

pub fn obligations_for_generics(tcx: &ty::ctxt,
cause: ObligationCause,
generics: &ty::Generics,
Expand Down
40 changes: 37 additions & 3 deletions src/librustc/middle/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ use super::{VtableBuiltin, VtableImpl, VtableParam, VtableUnboxedClosure};
use super::{VtableImplData, VtableParamData, VtableBuiltinData};
use super::{util};

use middle::fast_reject;
use middle::mem_categorization::Typer;
use middle::subst::{Subst, Substs, VecPerParamSpace};
use middle::ty;
use middle::typeck::check::regionmanip;
use middle::typeck::infer;
use middle::typeck::infer::LateBoundRegionConversionTime::*;
use middle::typeck::infer::{InferCtxt, TypeSkolemizer};
use middle::ty_fold::TypeFoldable;
use std::cell::RefCell;
Expand Down Expand Up @@ -1714,7 +1716,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
closure_type.sig.binder_id,
&closure_type.sig,
|br| self.infcx.next_region_var(
infer::LateBoundRegion(obligation.cause.span, br)));
infer::LateBoundRegion(obligation.cause.span, br,
infer::FnCall)));

let arguments_tuple = new_signature.inputs[0];
let trait_ref = Rc::new(ty::TraitRef {
Expand Down Expand Up @@ -1766,12 +1769,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation: &Obligation)
-> Result<Substs, ()>
{
let impl_trait_ref = ty::impl_trait_ref(self.tcx(),
impl_def_id).unwrap();

// Before we create the substitutions and everything, first
// consider a "quick reject". This avoids creating more types
// and so forth that we need to.
if self.fast_reject_trait_refs(obligation, &*impl_trait_ref) {
return Err(());
}

let impl_substs = util::fresh_substs_for_impl(self.infcx,
obligation.cause.span,
impl_def_id);

let impl_trait_ref = ty::impl_trait_ref(self.tcx(),
impl_def_id).unwrap();
let impl_trait_ref = impl_trait_ref.subst(self.tcx(),
&impl_substs);

Expand All @@ -1781,6 +1792,29 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}

fn fast_reject_trait_refs(&mut self,
obligation: &Obligation,
impl_trait_ref: &ty::TraitRef)
-> bool
{
// We can avoid creating type variables and doing the full
// substitution if we find that any of the input types, when
// simplified, do not match.

obligation.trait_ref.input_types().iter()
.zip(impl_trait_ref.input_types().iter())
.any(|(&obligation_ty, &impl_ty)| {
let simplified_obligation_ty =
fast_reject::simplify_type(self.tcx(), obligation_ty, true);
let simplified_impl_ty =
fast_reject::simplify_type(self.tcx(), impl_ty, false);

simplified_obligation_ty.is_some() &&
simplified_impl_ty.is_some() &&
simplified_obligation_ty != simplified_impl_ty
})
}

fn match_trait_refs(&mut self,
obligation: &Obligation,
trait_ref: Rc<ty::TraitRef>)
Expand Down
58 changes: 30 additions & 28 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3491,43 +3491,45 @@ pub fn adjust_ty(cx: &ctxt,
}
}

match adj.autoref {
None => adjusted_ty,
Some(ref autoref) => adjust_for_autoref(cx, span, adjusted_ty, autoref)
}
adjust_ty_for_autoref(cx, span, adjusted_ty, adj.autoref.as_ref())
}
}
}
None => unadjusted_ty
};
}

fn adjust_for_autoref(cx: &ctxt,
span: Span,
ty: ty::t,
autoref: &AutoRef) -> ty::t{
match *autoref {
AutoPtr(r, m, ref a) => {
let adjusted_ty = match a {
&Some(box ref a) => adjust_for_autoref(cx, span, ty, a),
&None => ty
};
mk_rptr(cx, r, mt {
ty: adjusted_ty,
mutbl: m
})
}
pub fn adjust_ty_for_autoref(cx: &ctxt,
span: Span,
ty: ty::t,
autoref: Option<&AutoRef>)
-> ty::t
{
match autoref {
None => ty,

AutoUnsafe(m, ref a) => {
let adjusted_ty = match a {
&Some(box ref a) => adjust_for_autoref(cx, span, ty, a),
&None => ty
};
mk_ptr(cx, mt {ty: adjusted_ty, mutbl: m})
}
Some(&AutoPtr(r, m, ref a)) => {
let adjusted_ty = match a {
&Some(box ref a) => adjust_ty_for_autoref(cx, span, ty, Some(a)),
&None => ty
};
mk_rptr(cx, r, mt {
ty: adjusted_ty,
mutbl: m
})
}

AutoUnsize(ref k) => unsize_ty(cx, ty, k, span),
AutoUnsizeUniq(ref k) => ty::mk_uniq(cx, unsize_ty(cx, ty, k, span)),
Some(&AutoUnsafe(m, ref a)) => {
let adjusted_ty = match a {
&Some(box ref a) => adjust_ty_for_autoref(cx, span, ty, Some(a)),
&None => ty
};
mk_ptr(cx, mt {ty: adjusted_ty, mutbl: m})
}

Some(&AutoUnsize(ref k)) => unsize_ty(cx, ty, k, span),

Some(&AutoUnsizeUniq(ref k)) => ty::mk_uniq(cx, unsize_ty(cx, ty, k, span)),
}
}

Expand Down
Loading

0 comments on commit 336349c

Please sign in to comment.