1
Fork 0

When testing whether a default method predicates are satisfiable,

combine normalization with this check so that we also skip the
default method if normalization fails. Fixes #23485.
This commit is contained in:
Niko Matsakis 2015-03-18 15:26:38 -04:00
parent 242ed0b7c0
commit 70042cff97
5 changed files with 119 additions and 33 deletions

View file

@ -39,6 +39,7 @@ pub use self::object_safety::is_object_safe;
pub use self::object_safety::object_safety_violations; pub use self::object_safety::object_safety_violations;
pub use self::object_safety::ObjectSafetyViolation; pub use self::object_safety::ObjectSafetyViolation;
pub use self::object_safety::MethodViolationCode; pub use self::object_safety::MethodViolationCode;
pub use self::object_safety::is_vtable_safe_method;
pub use self::select::SelectionContext; pub use self::select::SelectionContext;
pub use self::select::SelectionCache; pub use self::select::SelectionCache;
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch}; pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};

View file

@ -96,7 +96,7 @@ fn object_safety_violations_for_trait<'tcx>(tcx: &ty::ctxt<'tcx>,
.flat_map(|item| { .flat_map(|item| {
match *item { match *item {
ty::MethodTraitItem(ref m) => { ty::MethodTraitItem(ref m) => {
object_safety_violations_for_method(tcx, trait_def_id, &**m) object_safety_violation_for_method(tcx, trait_def_id, &**m)
.map(|code| ObjectSafetyViolation::Method(m.clone(), code)) .map(|code| ObjectSafetyViolation::Method(m.clone(), code))
.into_iter() .into_iter()
} }
@ -193,10 +193,11 @@ fn generics_require_sized_self<'tcx>(tcx: &ty::ctxt<'tcx>,
}) })
} }
fn object_safety_violations_for_method<'tcx>(tcx: &ty::ctxt<'tcx>, /// Returns `Some(_)` if this method makes the containing trait not object safe.
trait_def_id: ast::DefId, fn object_safety_violation_for_method<'tcx>(tcx: &ty::ctxt<'tcx>,
method: &ty::Method<'tcx>) trait_def_id: ast::DefId,
-> Option<MethodViolationCode> method: &ty::Method<'tcx>)
-> Option<MethodViolationCode>
{ {
// Any method that has a `Self : Sized` requisite is otherwise // Any method that has a `Self : Sized` requisite is otherwise
// exempt from the regulations. // exempt from the regulations.
@ -204,6 +205,30 @@ fn object_safety_violations_for_method<'tcx>(tcx: &ty::ctxt<'tcx>,
return None; return None;
} }
virtual_call_violation_for_method(tcx, trait_def_id, method)
}
/// We say a method is *vtable safe* if it can be invoked on a trait
/// object. Note that object-safe traits can have some
/// non-vtable-safe methods, so long as they require `Self:Sized` or
/// otherwise ensure that they cannot be used when `Self=Trait`.
pub fn is_vtable_safe_method<'tcx>(tcx: &ty::ctxt<'tcx>,
trait_def_id: ast::DefId,
method: &ty::Method<'tcx>)
-> bool
{
virtual_call_violation_for_method(tcx, trait_def_id, method).is_none()
}
/// Returns `Some(_)` if this method cannot be called on a trait
/// object; this does not necessarily imply that the enclosing trait
/// is not object safe, because the method might have a where clause
/// `Self:Sized`.
fn virtual_call_violation_for_method<'tcx>(tcx: &ty::ctxt<'tcx>,
trait_def_id: ast::DefId,
method: &ty::Method<'tcx>)
-> Option<MethodViolationCode>
{
// The method's first parameter must be something that derefs (or // The method's first parameter must be something that derefs (or
// autorefs) to `&self`. For now, we only accept `self`, `&self` // autorefs) to `&self`. For now, we only accept `self`, `&self`
// and `Box<Self>`. // and `Box<Self>`.

View file

@ -1069,17 +1069,30 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
vtable vtable
} }
pub fn predicates_hold<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, /// Normalizes the predicates and checks whether they hold. If this
predicates: Vec<ty::Predicate<'tcx>>) /// returns false, then either normalize encountered an error or one
-> bool /// of the predicates did not hold. Used when creating vtables to
/// check for unsatisfiable methods.
pub fn normalize_and_test_predicates<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
predicates: Vec<ty::Predicate<'tcx>>)
-> bool
{ {
debug!("predicates_hold(predicates={})", debug!("normalize_and_test_predicates(predicates={})",
predicates.repr(ccx.tcx())); predicates.repr(ccx.tcx()));
let infcx = infer::new_infer_ctxt(ccx.tcx()); let tcx = ccx.tcx();
let infcx = infer::new_infer_ctxt(tcx);
let typer = NormalizingClosureTyper::new(tcx);
let mut selcx = traits::SelectionContext::new(&infcx, &typer);
let mut fulfill_cx = traits::FulfillmentContext::new(); let mut fulfill_cx = traits::FulfillmentContext::new();
let cause = traits::ObligationCause::dummy();
let traits::Normalized { value: predicates, obligations } =
traits::normalize(&mut selcx, cause.clone(), &predicates);
for obligation in obligations {
fulfill_cx.register_predicate_obligation(&infcx, obligation);
}
for predicate in predicates { for predicate in predicates {
let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), predicate); let obligation = traits::Obligation::new(cause.clone(), predicate);
fulfill_cx.register_predicate_obligation(&infcx, obligation); fulfill_cx.register_predicate_obligation(&infcx, obligation);
} }
drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()).is_ok() drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()).is_ok()

View file

@ -13,7 +13,7 @@ use back::abi;
use back::link; use back::link;
use llvm::{ValueRef, get_param}; use llvm::{ValueRef, get_param};
use metadata::csearch; use metadata::csearch;
use middle::subst::Substs; use middle::subst::{Subst, Substs};
use middle::subst::VecPerParamSpace; use middle::subst::VecPerParamSpace;
use middle::subst; use middle::subst;
use middle::traits; use middle::traits;
@ -784,6 +784,7 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
ty::populate_implementations_for_trait_if_necessary(tcx, trt_id); ty::populate_implementations_for_trait_if_necessary(tcx, trt_id);
let nullptr = C_null(Type::nil(ccx).ptr_to());
let trait_item_def_ids = ty::trait_item_def_ids(tcx, trt_id); let trait_item_def_ids = ty::trait_item_def_ids(tcx, trt_id);
trait_item_def_ids trait_item_def_ids
.iter() .iter()
@ -809,6 +810,12 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
}; };
let name = trait_method_type.name; let name = trait_method_type.name;
// Some methods cannot be called on an object; skip those.
if !traits::is_vtable_safe_method(tcx, trt_id, &trait_method_type) {
debug!("emit_vtable_methods: not vtable safe");
return nullptr;
}
debug!("emit_vtable_methods: trait_method_type={}", debug!("emit_vtable_methods: trait_method_type={}",
trait_method_type.repr(tcx)); trait_method_type.repr(tcx));
@ -820,35 +827,17 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
ty::TypeTraitItem(_) => ccx.sess().bug("should be a method, not assoc type") ty::TypeTraitItem(_) => ccx.sess().bug("should be a method, not assoc type")
}; };
debug!("emit_vtable_methods: m={}", debug!("emit_vtable_methods: impl_method_type={}",
impl_method_type.repr(tcx)); impl_method_type.repr(tcx));
let nullptr = C_null(Type::nil(ccx).ptr_to());
if impl_method_type.generics.has_type_params(subst::FnSpace) {
debug!("emit_vtable_methods: generic");
return nullptr;
}
let bare_fn_ty =
ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(impl_method_type.fty.clone()));
if ty::type_has_self(bare_fn_ty) {
debug!("emit_vtable_methods: type_has_self {}",
bare_fn_ty.repr(tcx));
return nullptr;
}
// If this is a default method, it's possible that it // If this is a default method, it's possible that it
// relies on where clauses that do not hold for this // relies on where clauses that do not hold for this
// particular set of type parameters. Note that this // particular set of type parameters. Note that this
// method could then never be called, so we do not want to // method could then never be called, so we do not want to
// try and trans it, in that case. Issue #23435. // try and trans it, in that case. Issue #23435.
if ty::provided_source(tcx, impl_method_def_id).is_some() { if ty::provided_source(tcx, impl_method_def_id).is_some() {
let predicates = let predicates = impl_method_type.predicates.predicates.subst(tcx, &substs);
monomorphize::apply_param_substs(tcx, if !normalize_and_test_predicates(ccx, predicates.into_vec()) {
&substs,
&impl_method_type.predicates.predicates);
if !predicates_hold(ccx, predicates.into_vec()) {
debug!("emit_vtable_methods: predicates do not hold"); debug!("emit_vtable_methods: predicates do not hold");
return nullptr; return nullptr;
} }

View file

@ -0,0 +1,58 @@
// 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.
// Test for an ICE that occured when a default method implementation
// was applied to a type that did not meet the prerequisites. The
// problem occurred specifically because normalizing
// `Self::Item::Target` was impossible in this case.
use std::boxed::Box;
use std::marker::Sized;
use std::clone::Clone;
use std::ops::Deref;
use std::option::Option;
use std::option::Option::{Some,None};
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
fn clone_first(mut self) -> Option<<Self::Item as Deref>::Target> where
Self: Sized,
Self::Item: Deref,
<Self::Item as Deref>::Target: Clone,
{
self.next().cloned()
}
}
struct Counter {
value: i32
}
struct Token {
value: i32
}
impl Iterator for Counter {
type Item = Token;
fn next(&mut self) -> Option<Token> {
let x = self.value;
self.value += 1;
Some(Token { value: x })
}
}
fn main() {
let mut x: Box<Iterator<Item=Token>> = Box::new(Counter { value: 22 });
assert_eq!(x.next().unwrap().value, 22);
}