Port a simplified versions of pcwalton's "quick reject" mechanism for quickly throwing out method candidates. Yields a 40%-50% improvement in typechecking time as well as lowering peak memory use from 2.2GB to 1.8GB (due to creating fewer types).

Conflicts:
	src/librustc/driver/config.rs
	src/librustc/middle/ty.rs
	src/librustc/middle/typeck/check/method.rs
	src/librustc/middle/typeck/check/mod.rs
	src/librustc/middle/typeck/coherence/mod.rs
This commit is contained in:
Niko Matsakis 2014-11-16 06:33:31 -05:00
parent 0ed0a4633b
commit d93921b348
3 changed files with 148 additions and 5 deletions

View file

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

View file

@ -0,0 +1,103 @@
// 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;
/** 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,
}
}

View file

@ -16,6 +16,7 @@ use super::MethodIndex;
use super::NoMatch;
use super::TraitSource;
use middle::fast_reject;
use middle::subst;
use middle::subst::Subst;
use middle::traits;
@ -36,6 +37,7 @@ struct ProbeContext<'a, 'tcx:'a> {
span: Span,
method_name: ast::Name,
steps: Rc<Vec<CandidateStep>>,
opt_simplified_steps: Option<Vec<fast_reject::SimplifiedType>>,
inherent_candidates: Vec<Candidate>,
extension_candidates: Vec<Candidate>,
impl_dups: HashSet<ast::DefId>,
@ -44,7 +46,7 @@ struct ProbeContext<'a, 'tcx:'a> {
struct CandidateStep {
self_ty: ty::t,
adjustment: PickAdjustment
adjustment: PickAdjustment,
}
struct Candidate {
@ -123,16 +125,31 @@ pub fn probe(fcx: &FnCtxt,
// take place in the `fcx.infcx().probe` below.
let steps = create_steps(fcx, span, self_ty);
// Create a list of simplified self types, if we can.
let mut simplified_steps = Vec::new();
for step in steps.iter() {
match fast_reject::simplify_type(fcx.tcx(), step.self_ty, true) {
None => { break; }
Some(simplified_type) => { simplified_steps.push(simplified_type); }
}
}
let opt_simplified_steps =
if simplified_steps.len() < steps.len() {
None // failed to convert at least one of the steps
} else {
Some(simplified_steps)
};
debug!("ProbeContext: steps for self_ty={} are {}",
self_ty.repr(fcx.tcx()),
steps.repr(fcx.tcx()));
// this creates one big transaction so that all type variables etc
// that we create during the probe process are removed later
let mut steps = Some(steps); // FIXME(#18101) need once closures
let mut dummy = Some((steps, opt_simplified_steps)); // FIXME(#18101) need once closures
fcx.infcx().probe(|| {
let steps = steps.take().unwrap();
let mut probe_cx = ProbeContext::new(fcx, span, method_name, steps);
let (steps, opt_simplified_steps) = dummy.take().unwrap();
let mut probe_cx = ProbeContext::new(fcx, span, method_name, steps, opt_simplified_steps);
probe_cx.assemble_inherent_candidates();
probe_cx.assemble_extension_candidates_for_traits_in_scope(call_expr_id);
probe_cx.pick()
@ -177,7 +194,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
fn new(fcx: &'a FnCtxt<'a,'tcx>,
span: Span,
method_name: ast::Name,
steps: Vec<CandidateStep>)
steps: Vec<CandidateStep>,
opt_simplified_steps: Option<Vec<fast_reject::SimplifiedType>>)
-> ProbeContext<'a,'tcx>
{
ProbeContext {
@ -188,6 +206,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
extension_candidates: Vec::new(),
impl_dups: HashSet::new(),
steps: Rc::new(steps),
opt_simplified_steps: opt_simplified_steps,
static_candidates: Vec::new(),
}
}
@ -473,6 +492,10 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
trait_def_id.repr(self.tcx()),
impl_def_id.repr(self.tcx()));
if !self.impl_can_possibly_match(impl_def_id) {
continue;
}
let impl_pty = check::impl_self_ty(self.fcx, self.span, impl_def_id);
let impl_substs = impl_pty.substs;
@ -499,6 +522,22 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
}
}
fn impl_can_possibly_match(&self, impl_def_id: ast::DefId) -> bool {
let simplified_steps = match self.opt_simplified_steps {
Some(ref simplified_steps) => simplified_steps,
None => { return true; }
};
let impl_type = ty::lookup_item_type(self.tcx(), impl_def_id);
let impl_simplified_type =
match fast_reject::simplify_type(self.tcx(), impl_type.ty, false) {
Some(simplified_type) => simplified_type,
None => { return true; }
};
simplified_steps.contains(&impl_simplified_type)
}
fn assemble_unboxed_closure_candidates(&mut self,
trait_def_id: ast::DefId,
method_ty: Rc<ty::Method>,