diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs index f5423b22fe2ef..d66af98129c2b 100644 --- a/src/librustc/driver/config.rs +++ b/src/librustc/driver/config.rs @@ -101,7 +101,7 @@ pub struct Options { /// An optional name to use as the crate for std during std injection, /// written `extern crate std = "name"`. Default to "std". Used by /// out-of-tree drivers. - pub alt_std_name: Option + pub alt_std_name: Option, } /// Some reasonable defaults @@ -841,7 +841,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { color: color, externs: externs, crate_name: crate_name, - alt_std_name: None + alt_std_name: None, } } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index cea74c6573d54..2c05d51799a3b 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -586,6 +586,11 @@ pub struct ctxt<'tcx> { /// Caches the representation hints for struct definitions. pub repr_hint_cache: RefCell>>>, + + pub overloaded_operator_filter: + RefCell>, + + pub smart_pointers: RefCell, } pub enum tbox_flag { @@ -1537,6 +1542,8 @@ pub fn mk_ctxt<'tcx>(s: Session, trait_associated_types: RefCell::new(DefIdMap::new()), selection_cache: traits::SelectionCache::new(), repr_hint_cache: RefCell::new(DefIdMap::new()), + overloaded_operator_filter: RefCell::new(HashMap::new()), + smart_pointers: RefCell::new(DefIdSet::new()), } } @@ -5598,3 +5605,186 @@ pub fn with_freevars(tcx: &ty::ctxt, fid: ast::NodeId, f: |&[Freevar]| -> T) Some(d) => f(d.as_slice()) } } + +pub enum AllowDerefableFlag<'a> { + AllowDerefable, + DisallowDerefable(&'a ParameterEnvironment), +} + +#[deriving(Clone, PartialEq, Eq, Hash)] +pub enum SimplifiedType { + NilSimplifiedType, + BoolSimplifiedType, + CharSimplifiedType, + IntSimplifiedType(ast::IntTy), + UintSimplifiedType(ast::UintTy), + FloatSimplifiedType(ast::FloatTy), + EnumSimplifiedType(DefId), + StrSimplifiedType, + VecSimplifiedType, + PtrSimplifiedType, + TupleSimplifiedType(uint), + TraitSimplifiedType(DefId), + StructSimplifiedType(DefId), + UnboxedClosureSimplifiedType(DefId), + FunctionSimplifiedType(uint), + ParameterSimplifiedType, +} + +impl SimplifiedType { + pub fn from_type(tcx: &ctxt, + ty: ty::t, + can_simplify_params: bool, + allow_derefable: AllowDerefableFlag) + -> Option { + let simplified_type = match get(ty).sty { + ty_nil => Some(NilSimplifiedType), + ty_bool => Some(BoolSimplifiedType), + ty_char => Some(CharSimplifiedType), + ty_int(int_type) => Some(IntSimplifiedType(int_type)), + ty_uint(uint_type) => Some(UintSimplifiedType(uint_type)), + ty_float(float_type) => Some(FloatSimplifiedType(float_type)), + ty_enum(def_id, _) => Some(EnumSimplifiedType(def_id)), + ty_str => Some(StrSimplifiedType), + ty_trait(ref trait_info) => { + Some(TraitSimplifiedType(trait_info.def_id)) + } + ty_struct(def_id, _) => { + Some(StructSimplifiedType(def_id)) + } + ty_unboxed_closure(def_id, _) => { + Some(UnboxedClosureSimplifiedType(def_id)) + } + ty_vec(..) => Some(VecSimplifiedType), + ty_ptr(_) => Some(PtrSimplifiedType), + ty_rptr(_, ref mt) => { + SimplifiedType::from_type(tcx, + mt.ty, + can_simplify_params, + allow_derefable) + } + ty_uniq(ref ty) => { + SimplifiedType::from_type(tcx, + *ty, + can_simplify_params, + allow_derefable) + } + ty_tup(ref tys) => Some(TupleSimplifiedType(tys.len())), + ty_closure(ref f) => { + Some(FunctionSimplifiedType(f.sig.inputs.len())) + } + ty_bare_fn(ref f) => { + Some(FunctionSimplifiedType(f.sig.inputs.len())) + } + ty_param(_) if can_simplify_params => { + Some(ParameterSimplifiedType) + } + ty_bot | ty_param(_) | ty_open(_) | ty_infer(_) | ty_err => None, + }; + + let simplified_type = match simplified_type { + None => return None, + Some(simplified_type) => simplified_type, + }; + + match allow_derefable { + AllowDerefable => {} + DisallowDerefable(param_env) => { + match tcx.overloaded_operator_filter + .borrow() + .find(&simplified_type) { + Some(ref flags) if flags.contains( + DEREF_OVERLOADED_OPERATOR_FILTER) => { + return None + } + Some(_) | None => {} + } + + match get(ty).sty { + ty_param(ref param_ty) => { + let bounds = ¶m_env.bounds + .get(param_ty.space, + param_ty.idx) + .trait_bounds; + for bound in bounds.iter() { + let bad = Some(bound.def_id) == + tcx.lang_items.deref_trait() || + Some(bound.def_id) == + tcx.lang_items.deref_mut_trait(); + debug!("for param {} bound {} deref {} bad {}", + param_ty.repr(tcx), + bound.repr(tcx), + tcx.lang_items.deref_trait(), + bad); + if bad { + return None + } + } + } + _ => {} + } + } + } + + Some(simplified_type) + } +} + +pub enum MethodLookupKey { + SimplifiedTypeLookupKey(SimplifiedType), + SmartPointerLookupKey(ast::DefId, SimplifiedType), +} + +impl MethodLookupKey { + pub fn from_type(tcx: &ctxt, ty: ty::t, param_env: &ParameterEnvironment) + -> Option { + match get(ty).sty { + ty_struct(def_id, ref substs) => { + if tcx.smart_pointers.borrow().contains(&def_id) { + let simplified_referent = SimplifiedType::from_type( + tcx, + *substs.types.get(subst::TypeSpace, 0), + true, + DisallowDerefable(param_env)); + match simplified_referent { + None => return None, + Some(simplified_referent) => { + return Some(SmartPointerLookupKey( + def_id, + simplified_referent)) + } + } + } + } + _ => {} + } + + match SimplifiedType::from_type(tcx, + ty, + true, + DisallowDerefable(param_env)) { + None => None, + Some(simplified_type) => { + Some(SimplifiedTypeLookupKey(simplified_type)) + } + } + } + + pub fn might_match(&self, other: &SimplifiedType) -> bool { + match *self { + SimplifiedTypeLookupKey(ref this) => *this == *other, + SmartPointerLookupKey(def_id, ref this) => { + *this == *other || StructSimplifiedType(def_id) == *other + } + } + } +} + +bitflags! { + flags OverloadedOperatorFilter: u8 { + const INDEX_OVERLOADED_OPERATOR_FILTER = 0x01, + const DEREF_OVERLOADED_OPERATOR_FILTER = 0x02, + const CALL_OVERLOADED_OPERATOR_FILTER = 0x04 + } +} + diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 8711f4de512e6..08beb0ad7f5e5 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -161,7 +161,15 @@ pub fn lookup<'a, 'tcx>( lcx.push_inherent_candidates(self_ty); debug!("searching extension candidates"); lcx.push_bound_candidates(self_ty, None); - lcx.push_extension_candidates(expr.id); + + let method_lookup_key = MethodLookupKey::from_type(fcx.tcx(), + self_ty, + &fcx.inh.param_env); + if method_lookup_key.is_none() { + debug!("couldn't simplify {}", lcx.ty_to_string(self_ty)); + } + lcx.push_extension_candidates(expr.id, &method_lookup_key); + lcx.search(self_ty) } @@ -197,7 +205,14 @@ pub fn lookup_in_trait<'a, 'tcx>( self_ty.repr(fcx.tcx()), self_expr.map(|e| e.repr(fcx.tcx()))); lcx.push_bound_candidates(self_ty, Some(trait_did)); - lcx.push_extension_candidate(trait_did); + + let method_lookup_key = MethodLookupKey::from_type(fcx.tcx(), + self_ty, + &fcx.inh.param_env); + if method_lookup_key.is_none() { + debug!("couldn't simplify {}", lcx.ty_to_string(self_ty)); + } + lcx.push_extension_candidate(trait_did, &method_lookup_key); lcx.search(self_ty) } @@ -484,7 +499,9 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { }); } - fn push_extension_candidate(&mut self, trait_did: DefId) { + fn push_extension_candidate(&mut self, + trait_did: DefId, + method_lookup_key: &Option) { ty::populate_implementations_for_trait_if_necessary(self.tcx(), trait_did); // Look for explicit implementations. @@ -494,12 +511,16 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { let items = impl_items.get(impl_did); self.push_candidates_from_impl(*impl_did, items.as_slice(), + method_lookup_key, true); } } } - fn push_extension_candidates(&mut self, expr_id: ast::NodeId) { + fn push_extension_candidates( + &mut self, + expr_id: ast::NodeId, + method_lookup_key: &Option) { // If the method being called is associated with a trait, then // find all the impls of that trait. Each of those are // candidates. @@ -512,7 +533,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { } else { "(external)".to_string() }); - self.push_extension_candidate(*trait_did); + self.push_extension_candidate(*trait_did, method_lookup_key); } } } @@ -775,6 +796,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { let items = impl_items.get(impl_did); self.push_candidates_from_impl(*impl_did, items.as_slice(), + &None, false); } } @@ -783,6 +805,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { fn push_candidates_from_impl(&mut self, impl_did: DefId, impl_items: &[ImplOrTraitItemId], + method_lookup_key: &Option, is_extension: bool) { let did = if self.report_statics == ReportStaticMethods { // we only want to report each base trait once @@ -798,6 +821,38 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { return; // already visited } + // See if we can quickly reject... + match *method_lookup_key { + Some(ref method_lookup_key) => { + let impl_type = ty::lookup_item_type(self.tcx(), impl_did); + let simplified_impl_type = + SimplifiedType::from_type(self.tcx(), + impl_type.ty, + false, + ty::AllowDerefable); + match simplified_impl_type { + Some(ref simplified_impl_type) => { + if !method_lookup_key.might_match( + simplified_impl_type) { + debug!("quick reject succeeded"); + return + } + debug!("quick reject failed: could possibly unify"); + } + None => { + debug!("quick reject failed: impl type {} was \ + unsimplifiable", + self.ty_to_string(impl_type.ty)); + } + } + } + None => { + if is_extension { + debug!("quick reject failed: callee is unsimplifiable"); + } + } + } + debug!("push_candidates_from_impl: {} {}", token::get_name(self.m_name), impl_items.iter() diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 68bb3fcf94544..ad4c95fc7a3b1 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -2140,6 +2140,41 @@ pub fn autoderef(fcx: &FnCtxt, sp: Span, base_ty: ty::t, (ty::mk_err(), 0, None) } +/// Returns true if the cache hit (and therefore we should bail out). +fn check_overloaded_operator_cache(fcx: &FnCtxt, + callee_type: ty::t, + flag: ty::OverloadedOperatorFilter) + -> bool { + let allow_derefable = if flag == ty::DEREF_OVERLOADED_OPERATOR_FILTER { + ty::DisallowDerefable(&fcx.inh.param_env) + } else { + ty::AllowDerefable + }; + let result = match ty::SimplifiedType::from_type(fcx.tcx(), + callee_type, + true, + allow_derefable) { + None => false, + Some(simplified_callee_type) => { + match fcx.tcx() + .overloaded_operator_filter + .borrow() + .find(&simplified_callee_type) { + None => true, + Some(flags) if !flags.contains(flag) => true, + _ => false + } + } + }; + debug!("check_overloaded_operator_cache: cache {}", + if result { + "hit" + } else { + "miss" + }); + result +} + /// Attempts to resolve a call expression as an overloaded call. fn try_overloaded_call<'a>(fcx: &FnCtxt, call_expression: &ast::Expr, @@ -2154,6 +2189,13 @@ fn try_overloaded_call<'a>(fcx: &FnCtxt, _ => {} } + // Bail out if the cache tells us to. + if check_overloaded_operator_cache(fcx, + callee_type, + ty::CALL_OVERLOADED_OPERATOR_FILTER) { + return false + } + // Try `FnOnce`, then `FnMut`, then `Fn`. for &(maybe_function_trait, method_name) in [ (fcx.tcx().lang_items.fn_once_trait(), token::intern("call_once")), @@ -2209,6 +2251,13 @@ fn try_overloaded_deref(fcx: &FnCtxt, base_ty: ty::t, lvalue_pref: LvaluePreference) -> Option { + // Bail out if the cache tells us to. + if check_overloaded_operator_cache(fcx, + base_ty, + ty::DEREF_OVERLOADED_OPERATOR_FILTER) { + return None + } + // Try DerefMut first, if preferred. let method = match (lvalue_pref, fcx.tcx().lang_items.deref_mut_trait()) { (PreferMutLvalue, Some(trait_did)) => { @@ -2359,6 +2408,13 @@ fn try_overloaded_index(fcx: &FnCtxt, index_expr: &P, lvalue_pref: LvaluePreference) -> Option { + // Bail out if the cache tells us to. + if check_overloaded_operator_cache(fcx, + base_ty, + ty::INDEX_OVERLOADED_OPERATOR_FILTER) { + return None + } + // Try `IndexMut` first, if preferred. let method = match (lvalue_pref, fcx.tcx().lang_items.index_mut_trait()) { (PreferMutLvalue, Some(trait_did)) => { diff --git a/src/librustc/middle/typeck/coherence/mod.rs b/src/librustc/middle/typeck/coherence/mod.rs index f6ac0e1666ca9..024005d4fcc80 100644 --- a/src/librustc/middle/typeck/coherence/mod.rs +++ b/src/librustc/middle/typeck/coherence/mod.rs @@ -18,11 +18,12 @@ use metadata::csearch::{each_impl, get_impl_trait}; use metadata::csearch; +use middle::lang_items; use middle::subst; use middle::subst::{Substs}; use middle::ty::get; use middle::ty::{ImplContainer, ImplOrTraitItemId, MethodTraitItemId}; -use middle::ty::{TypeTraitItemId, lookup_item_type}; +use middle::ty::{SimplifiedType, TypeTraitItemId, lookup_item_type}; use middle::ty::{t, ty_bool, ty_char, ty_bot, ty_enum, ty_err}; use middle::ty::{ty_str, ty_vec, ty_float, ty_infer, ty_int, ty_nil, ty_open}; use middle::ty::{ty_param, Polytype, ty_ptr}; @@ -550,6 +551,119 @@ fn subst_receiver_types_in_method_ty(tcx: &ty::ctxt, ) } +fn populate_overloaded_operator_filter(tcx: &ty::ctxt) { + fn filter(tcx: &ty::ctxt, + lang_item: lang_items::LangItem, + flag: ty::OverloadedOperatorFilter) { + let trait_id = match *tcx.lang_items.items.get(lang_item as uint) { + Some(trait_id) => trait_id, + None => return, + }; + + ty::populate_implementations_for_trait_if_necessary(tcx, trait_id); + + for impl_infos in tcx.trait_impls.borrow().find(&trait_id).iter() { + for impl_did in impl_infos.borrow().iter() { + let impl_type = ty::lookup_item_type(tcx, *impl_did); + let simplified_impl_type = + SimplifiedType::from_type(tcx, + impl_type.ty, + false, + ty::AllowDerefable); + match simplified_impl_type { + None => { + tcx.sess.span_err(tcx.map.span(impl_did.node), + "type is unsimplifiable"); + } + Some(simplified_impl_type) => { + let mut filter = + match tcx.overloaded_operator_filter + .borrow() + .find(&simplified_impl_type) { + None => ty::OverloadedOperatorFilter::empty(), + Some(filter) => *filter, + }; + filter.insert(flag); + tcx.overloaded_operator_filter + .borrow_mut() + .insert(simplified_impl_type, filter); + } + } + } + } + } + + filter(tcx, + lang_items::IndexTraitLangItem, + ty::INDEX_OVERLOADED_OPERATOR_FILTER); + filter(tcx, + lang_items::IndexMutTraitLangItem, + ty::INDEX_OVERLOADED_OPERATOR_FILTER); + filter(tcx, + lang_items::DerefTraitLangItem, + ty::DEREF_OVERLOADED_OPERATOR_FILTER); + filter(tcx, + lang_items::DerefMutTraitLangItem, + ty::DEREF_OVERLOADED_OPERATOR_FILTER); + filter(tcx, + lang_items::FnTraitLangItem, + ty::CALL_OVERLOADED_OPERATOR_FILTER); + filter(tcx, + lang_items::FnMutTraitLangItem, + ty::CALL_OVERLOADED_OPERATOR_FILTER); + filter(tcx, + lang_items::FnOnceTraitLangItem, + ty::CALL_OVERLOADED_OPERATOR_FILTER); +} + +fn populate_smart_pointer_filter(tcx: &ty::ctxt) { + fn filter(tcx: &ty::ctxt, lang_item: lang_items::LangItem) { + let trait_id = match *tcx.lang_items.items.get(lang_item as uint) { + Some(trait_id) => trait_id, + None => return, + }; + + ty::populate_implementations_for_trait_if_necessary(tcx, trait_id); + + for impl_infos in tcx.trait_impls.borrow().find(&trait_id).iter() { + for impl_did in impl_infos.borrow().iter() { + let impl_polytype = ty::lookup_item_type(tcx, *impl_did); + let struct_did = match ty::get(impl_polytype.ty).sty { + ty::ty_struct(struct_did, _) => { + debug!("not smart pointer: not a struct"); + struct_did + } + _ => continue, + }; + if impl_polytype.generics.types.len(subst::TypeSpace) != 1 { + continue + } + let impl_trait_ref = match ty::impl_trait_ref(tcx, + *impl_did) { + None => { + debug!("not smart pointer: no trait ref"); + continue + } + Some(impl_trait_ref) => impl_trait_ref, + }; + match ty::get(*impl_trait_ref.substs + .types + .get(subst::TypeSpace, 0)).sty { + ty::ty_param(..) => { + debug!("not smart pointer: no type param in subst"); + } + _ => continue, + } + debug!("found smart pointer: {}", impl_polytype.repr(tcx)); + tcx.smart_pointers.borrow_mut().insert(struct_did); + } + } + } + + filter(tcx, lang_items::DerefTraitLangItem); + filter(tcx, lang_items::DerefMutTraitLangItem); +} + pub fn check_coherence(crate_context: &CrateCtxt) { CoherenceChecker { crate_context: crate_context, @@ -558,4 +672,6 @@ pub fn check_coherence(crate_context: &CrateCtxt) { }.check(crate_context.tcx.map.krate()); orphan::check(crate_context.tcx); overlap::check(crate_context.tcx); + populate_overloaded_operator_filter(crate_context.tcx); + populate_smart_pointer_filter(crate_context.tcx); }