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:
parent
242ed0b7c0
commit
70042cff97
5 changed files with 119 additions and 33 deletions
|
@ -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};
|
||||||
|
|
|
@ -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>`.
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
58
src/test/run-pass/issue-23485.rs
Normal file
58
src/test/run-pass/issue-23485.rs
Normal 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);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue