Skip to content

Commit 1a1ce83

Browse files
committed
librustc: Introduce a "quick reject" mechanism to quickly throw out
method candidates and prevent method lookup from being invoked at all for overloaded operators. 40%-50% improvement in typechecking time.
1 parent 86509d8 commit 1a1ce83

File tree

5 files changed

+425
-8
lines changed

5 files changed

+425
-8
lines changed

src/librustc/driver/config.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ pub struct Options {
101101
/// An optional name to use as the crate for std during std injection,
102102
/// written `extern crate std = "name"`. Default to "std". Used by
103103
/// out-of-tree drivers.
104-
pub alt_std_name: Option<String>
104+
pub alt_std_name: Option<String>,
105105
}
106106

107107
/// Some reasonable defaults
@@ -841,7 +841,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
841841
color: color,
842842
externs: externs,
843843
crate_name: crate_name,
844-
alt_std_name: None
844+
alt_std_name: None,
845845
}
846846
}
847847

src/librustc/middle/ty.rs

+190
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,11 @@ pub struct ctxt<'tcx> {
586586

587587
/// Caches the representation hints for struct definitions.
588588
pub repr_hint_cache: RefCell<DefIdMap<Rc<Vec<attr::ReprAttr>>>>,
589+
590+
pub overloaded_operator_filter:
591+
RefCell<HashMap<SimplifiedType,OverloadedOperatorFilter>>,
592+
593+
pub smart_pointers: RefCell<DefIdSet>,
589594
}
590595

591596
pub enum tbox_flag {
@@ -1537,6 +1542,8 @@ pub fn mk_ctxt<'tcx>(s: Session,
15371542
trait_associated_types: RefCell::new(DefIdMap::new()),
15381543
selection_cache: traits::SelectionCache::new(),
15391544
repr_hint_cache: RefCell::new(DefIdMap::new()),
1545+
overloaded_operator_filter: RefCell::new(HashMap::new()),
1546+
smart_pointers: RefCell::new(DefIdSet::new()),
15401547
}
15411548
}
15421549

@@ -5598,3 +5605,186 @@ pub fn with_freevars<T>(tcx: &ty::ctxt, fid: ast::NodeId, f: |&[Freevar]| -> T)
55985605
Some(d) => f(d.as_slice())
55995606
}
56005607
}
5608+
5609+
pub enum AllowDerefableFlag<'a> {
5610+
AllowDerefable,
5611+
DisallowDerefable(&'a ParameterEnvironment),
5612+
}
5613+
5614+
#[deriving(Clone, PartialEq, Eq, Hash)]
5615+
pub enum SimplifiedType {
5616+
NilSimplifiedType,
5617+
BoolSimplifiedType,
5618+
CharSimplifiedType,
5619+
IntSimplifiedType(ast::IntTy),
5620+
UintSimplifiedType(ast::UintTy),
5621+
FloatSimplifiedType(ast::FloatTy),
5622+
EnumSimplifiedType(DefId),
5623+
StrSimplifiedType,
5624+
VecSimplifiedType,
5625+
PtrSimplifiedType,
5626+
TupleSimplifiedType(uint),
5627+
TraitSimplifiedType(DefId),
5628+
StructSimplifiedType(DefId),
5629+
UnboxedClosureSimplifiedType(DefId),
5630+
FunctionSimplifiedType(uint),
5631+
ParameterSimplifiedType,
5632+
}
5633+
5634+
impl SimplifiedType {
5635+
pub fn from_type(tcx: &ctxt,
5636+
ty: ty::t,
5637+
can_simplify_params: bool,
5638+
allow_derefable: AllowDerefableFlag)
5639+
-> Option<SimplifiedType> {
5640+
let simplified_type = match get(ty).sty {
5641+
ty_nil => Some(NilSimplifiedType),
5642+
ty_bool => Some(BoolSimplifiedType),
5643+
ty_char => Some(CharSimplifiedType),
5644+
ty_int(int_type) => Some(IntSimplifiedType(int_type)),
5645+
ty_uint(uint_type) => Some(UintSimplifiedType(uint_type)),
5646+
ty_float(float_type) => Some(FloatSimplifiedType(float_type)),
5647+
ty_enum(def_id, _) => Some(EnumSimplifiedType(def_id)),
5648+
ty_str => Some(StrSimplifiedType),
5649+
ty_trait(ref trait_info) => {
5650+
Some(TraitSimplifiedType(trait_info.def_id))
5651+
}
5652+
ty_struct(def_id, _) => {
5653+
Some(StructSimplifiedType(def_id))
5654+
}
5655+
ty_unboxed_closure(def_id, _) => {
5656+
Some(UnboxedClosureSimplifiedType(def_id))
5657+
}
5658+
ty_vec(..) => Some(VecSimplifiedType),
5659+
ty_ptr(_) => Some(PtrSimplifiedType),
5660+
ty_rptr(_, ref mt) => {
5661+
SimplifiedType::from_type(tcx,
5662+
mt.ty,
5663+
can_simplify_params,
5664+
allow_derefable)
5665+
}
5666+
ty_uniq(ref ty) => {
5667+
SimplifiedType::from_type(tcx,
5668+
*ty,
5669+
can_simplify_params,
5670+
allow_derefable)
5671+
}
5672+
ty_tup(ref tys) => Some(TupleSimplifiedType(tys.len())),
5673+
ty_closure(ref f) => {
5674+
Some(FunctionSimplifiedType(f.sig.inputs.len()))
5675+
}
5676+
ty_bare_fn(ref f) => {
5677+
Some(FunctionSimplifiedType(f.sig.inputs.len()))
5678+
}
5679+
ty_param(_) if can_simplify_params => {
5680+
Some(ParameterSimplifiedType)
5681+
}
5682+
ty_bot | ty_param(_) | ty_open(_) | ty_infer(_) | ty_err => None,
5683+
};
5684+
5685+
let simplified_type = match simplified_type {
5686+
None => return None,
5687+
Some(simplified_type) => simplified_type,
5688+
};
5689+
5690+
match allow_derefable {
5691+
AllowDerefable => {}
5692+
DisallowDerefable(param_env) => {
5693+
match tcx.overloaded_operator_filter
5694+
.borrow()
5695+
.find(&simplified_type) {
5696+
Some(ref flags) if flags.contains(
5697+
DEREF_OVERLOADED_OPERATOR_FILTER) => {
5698+
return None
5699+
}
5700+
Some(_) | None => {}
5701+
}
5702+
5703+
match get(ty).sty {
5704+
ty_param(ref param_ty) => {
5705+
let bounds = &param_env.bounds
5706+
.get(param_ty.space,
5707+
param_ty.idx)
5708+
.trait_bounds;
5709+
for bound in bounds.iter() {
5710+
let bad = Some(bound.def_id) ==
5711+
tcx.lang_items.deref_trait() ||
5712+
Some(bound.def_id) ==
5713+
tcx.lang_items.deref_mut_trait();
5714+
debug!("for param {} bound {} deref {} bad {}",
5715+
param_ty.repr(tcx),
5716+
bound.repr(tcx),
5717+
tcx.lang_items.deref_trait(),
5718+
bad);
5719+
if bad {
5720+
return None
5721+
}
5722+
}
5723+
}
5724+
_ => {}
5725+
}
5726+
}
5727+
}
5728+
5729+
Some(simplified_type)
5730+
}
5731+
}
5732+
5733+
pub enum MethodLookupKey {
5734+
SimplifiedTypeLookupKey(SimplifiedType),
5735+
SmartPointerLookupKey(ast::DefId, SimplifiedType),
5736+
}
5737+
5738+
impl MethodLookupKey {
5739+
pub fn from_type(tcx: &ctxt, ty: ty::t, param_env: &ParameterEnvironment)
5740+
-> Option<MethodLookupKey> {
5741+
match get(ty).sty {
5742+
ty_struct(def_id, ref substs) => {
5743+
if tcx.smart_pointers.borrow().contains(&def_id) {
5744+
let simplified_referent = SimplifiedType::from_type(
5745+
tcx,
5746+
*substs.types.get(subst::TypeSpace, 0),
5747+
true,
5748+
DisallowDerefable(param_env));
5749+
match simplified_referent {
5750+
None => return None,
5751+
Some(simplified_referent) => {
5752+
return Some(SmartPointerLookupKey(
5753+
def_id,
5754+
simplified_referent))
5755+
}
5756+
}
5757+
}
5758+
}
5759+
_ => {}
5760+
}
5761+
5762+
match SimplifiedType::from_type(tcx,
5763+
ty,
5764+
true,
5765+
DisallowDerefable(param_env)) {
5766+
None => None,
5767+
Some(simplified_type) => {
5768+
Some(SimplifiedTypeLookupKey(simplified_type))
5769+
}
5770+
}
5771+
}
5772+
5773+
pub fn might_match(&self, other: &SimplifiedType) -> bool {
5774+
match *self {
5775+
SimplifiedTypeLookupKey(ref this) => *this == *other,
5776+
SmartPointerLookupKey(def_id, ref this) => {
5777+
*this == *other || StructSimplifiedType(def_id) == *other
5778+
}
5779+
}
5780+
}
5781+
}
5782+
5783+
bitflags! {
5784+
flags OverloadedOperatorFilter: u8 {
5785+
const INDEX_OVERLOADED_OPERATOR_FILTER = 0x01,
5786+
const DEREF_OVERLOADED_OPERATOR_FILTER = 0x02,
5787+
const CALL_OVERLOADED_OPERATOR_FILTER = 0x04
5788+
}
5789+
}
5790+

src/librustc/middle/typeck/check/method.rs

+60-5
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,15 @@ pub fn lookup<'a, 'tcx>(
161161
lcx.push_inherent_candidates(self_ty);
162162
debug!("searching extension candidates");
163163
lcx.push_bound_candidates(self_ty, None);
164-
lcx.push_extension_candidates(expr.id);
164+
165+
let method_lookup_key = MethodLookupKey::from_type(fcx.tcx(),
166+
self_ty,
167+
&fcx.inh.param_env);
168+
if method_lookup_key.is_none() {
169+
debug!("couldn't simplify {}", lcx.ty_to_string(self_ty));
170+
}
171+
lcx.push_extension_candidates(expr.id, &method_lookup_key);
172+
165173
lcx.search(self_ty)
166174
}
167175

@@ -197,7 +205,14 @@ pub fn lookup_in_trait<'a, 'tcx>(
197205
self_ty.repr(fcx.tcx()), self_expr.map(|e| e.repr(fcx.tcx())));
198206

199207
lcx.push_bound_candidates(self_ty, Some(trait_did));
200-
lcx.push_extension_candidate(trait_did);
208+
209+
let method_lookup_key = MethodLookupKey::from_type(fcx.tcx(),
210+
self_ty,
211+
&fcx.inh.param_env);
212+
if method_lookup_key.is_none() {
213+
debug!("couldn't simplify {}", lcx.ty_to_string(self_ty));
214+
}
215+
lcx.push_extension_candidate(trait_did, &method_lookup_key);
201216
lcx.search(self_ty)
202217
}
203218

@@ -484,7 +499,9 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
484499
});
485500
}
486501

487-
fn push_extension_candidate(&mut self, trait_did: DefId) {
502+
fn push_extension_candidate(&mut self,
503+
trait_did: DefId,
504+
method_lookup_key: &Option<MethodLookupKey>) {
488505
ty::populate_implementations_for_trait_if_necessary(self.tcx(), trait_did);
489506

490507
// Look for explicit implementations.
@@ -494,12 +511,16 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
494511
let items = impl_items.get(impl_did);
495512
self.push_candidates_from_impl(*impl_did,
496513
items.as_slice(),
514+
method_lookup_key,
497515
true);
498516
}
499517
}
500518
}
501519

502-
fn push_extension_candidates(&mut self, expr_id: ast::NodeId) {
520+
fn push_extension_candidates(
521+
&mut self,
522+
expr_id: ast::NodeId,
523+
method_lookup_key: &Option<MethodLookupKey>) {
503524
// If the method being called is associated with a trait, then
504525
// find all the impls of that trait. Each of those are
505526
// candidates.
@@ -512,7 +533,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
512533
} else {
513534
"(external)".to_string()
514535
});
515-
self.push_extension_candidate(*trait_did);
536+
self.push_extension_candidate(*trait_did, method_lookup_key);
516537
}
517538
}
518539
}
@@ -775,6 +796,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
775796
let items = impl_items.get(impl_did);
776797
self.push_candidates_from_impl(*impl_did,
777798
items.as_slice(),
799+
&None,
778800
false);
779801
}
780802
}
@@ -783,6 +805,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
783805
fn push_candidates_from_impl(&mut self,
784806
impl_did: DefId,
785807
impl_items: &[ImplOrTraitItemId],
808+
method_lookup_key: &Option<MethodLookupKey>,
786809
is_extension: bool) {
787810
let did = if self.report_statics == ReportStaticMethods {
788811
// we only want to report each base trait once
@@ -798,6 +821,38 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
798821
return; // already visited
799822
}
800823

824+
// See if we can quickly reject...
825+
match *method_lookup_key {
826+
Some(ref method_lookup_key) => {
827+
let impl_type = ty::lookup_item_type(self.tcx(), impl_did);
828+
let simplified_impl_type =
829+
SimplifiedType::from_type(self.tcx(),
830+
impl_type.ty,
831+
false,
832+
ty::AllowDerefable);
833+
match simplified_impl_type {
834+
Some(ref simplified_impl_type) => {
835+
if !method_lookup_key.might_match(
836+
simplified_impl_type) {
837+
debug!("quick reject succeeded");
838+
return
839+
}
840+
debug!("quick reject failed: could possibly unify");
841+
}
842+
None => {
843+
debug!("quick reject failed: impl type {} was \
844+
unsimplifiable",
845+
self.ty_to_string(impl_type.ty));
846+
}
847+
}
848+
}
849+
None => {
850+
if is_extension {
851+
debug!("quick reject failed: callee is unsimplifiable");
852+
}
853+
}
854+
}
855+
801856
debug!("push_candidates_from_impl: {} {}",
802857
token::get_name(self.m_name),
803858
impl_items.iter()

0 commit comments

Comments
 (0)