Skip to content

Commit ac24a51

Browse files
committed
rollup merge of rust-lang#23486: nikomatsakis/issue-23485
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 rust-lang#23485. r? @nrc (I tried to address your nit from before as well)
2 parents 3f1d57f + 70042cf commit ac24a51

File tree

5 files changed

+119
-33
lines changed

5 files changed

+119
-33
lines changed

src/librustc/middle/traits/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub use self::object_safety::is_object_safe;
3939
pub use self::object_safety::object_safety_violations;
4040
pub use self::object_safety::ObjectSafetyViolation;
4141
pub use self::object_safety::MethodViolationCode;
42+
pub use self::object_safety::is_vtable_safe_method;
4243
pub use self::select::SelectionContext;
4344
pub use self::select::SelectionCache;
4445
pub use self::select::{MethodMatchResult, MethodMatched, MethodAmbiguous, MethodDidNotMatch};

src/librustc/middle/traits/object_safety.rs

+30-5
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ fn object_safety_violations_for_trait<'tcx>(tcx: &ty::ctxt<'tcx>,
9696
.flat_map(|item| {
9797
match *item {
9898
ty::MethodTraitItem(ref m) => {
99-
object_safety_violations_for_method(tcx, trait_def_id, &**m)
99+
object_safety_violation_for_method(tcx, trait_def_id, &**m)
100100
.map(|code| ObjectSafetyViolation::Method(m.clone(), code))
101101
.into_iter()
102102
}
@@ -193,17 +193,42 @@ fn generics_require_sized_self<'tcx>(tcx: &ty::ctxt<'tcx>,
193193
})
194194
}
195195

196-
fn object_safety_violations_for_method<'tcx>(tcx: &ty::ctxt<'tcx>,
197-
trait_def_id: ast::DefId,
198-
method: &ty::Method<'tcx>)
199-
-> Option<MethodViolationCode>
196+
/// Returns `Some(_)` if this method makes the containing trait not object safe.
197+
fn object_safety_violation_for_method<'tcx>(tcx: &ty::ctxt<'tcx>,
198+
trait_def_id: ast::DefId,
199+
method: &ty::Method<'tcx>)
200+
-> Option<MethodViolationCode>
200201
{
201202
// Any method that has a `Self : Sized` requisite is otherwise
202203
// exempt from the regulations.
203204
if generics_require_sized_self(tcx, &method.generics, &method.predicates) {
204205
return None;
205206
}
206207

208+
virtual_call_violation_for_method(tcx, trait_def_id, method)
209+
}
210+
211+
/// We say a method is *vtable safe* if it can be invoked on a trait
212+
/// object. Note that object-safe traits can have some
213+
/// non-vtable-safe methods, so long as they require `Self:Sized` or
214+
/// otherwise ensure that they cannot be used when `Self=Trait`.
215+
pub fn is_vtable_safe_method<'tcx>(tcx: &ty::ctxt<'tcx>,
216+
trait_def_id: ast::DefId,
217+
method: &ty::Method<'tcx>)
218+
-> bool
219+
{
220+
virtual_call_violation_for_method(tcx, trait_def_id, method).is_none()
221+
}
222+
223+
/// Returns `Some(_)` if this method cannot be called on a trait
224+
/// object; this does not necessarily imply that the enclosing trait
225+
/// is not object safe, because the method might have a where clause
226+
/// `Self:Sized`.
227+
fn virtual_call_violation_for_method<'tcx>(tcx: &ty::ctxt<'tcx>,
228+
trait_def_id: ast::DefId,
229+
method: &ty::Method<'tcx>)
230+
-> Option<MethodViolationCode>
231+
{
207232
// The method's first parameter must be something that derefs (or
208233
// autorefs) to `&self`. For now, we only accept `self`, `&self`
209234
// and `Box<Self>`.

src/librustc_trans/trans/common.rs

+19-6
Original file line numberDiff line numberDiff line change
@@ -1069,17 +1069,30 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
10691069
vtable
10701070
}
10711071

1072-
pub fn predicates_hold<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
1073-
predicates: Vec<ty::Predicate<'tcx>>)
1074-
-> bool
1072+
/// Normalizes the predicates and checks whether they hold. If this
1073+
/// returns false, then either normalize encountered an error or one
1074+
/// of the predicates did not hold. Used when creating vtables to
1075+
/// check for unsatisfiable methods.
1076+
pub fn normalize_and_test_predicates<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
1077+
predicates: Vec<ty::Predicate<'tcx>>)
1078+
-> bool
10751079
{
1076-
debug!("predicates_hold(predicates={})",
1080+
debug!("normalize_and_test_predicates(predicates={})",
10771081
predicates.repr(ccx.tcx()));
10781082

1079-
let infcx = infer::new_infer_ctxt(ccx.tcx());
1083+
let tcx = ccx.tcx();
1084+
let infcx = infer::new_infer_ctxt(tcx);
1085+
let typer = NormalizingClosureTyper::new(tcx);
1086+
let mut selcx = traits::SelectionContext::new(&infcx, &typer);
10801087
let mut fulfill_cx = traits::FulfillmentContext::new();
1088+
let cause = traits::ObligationCause::dummy();
1089+
let traits::Normalized { value: predicates, obligations } =
1090+
traits::normalize(&mut selcx, cause.clone(), &predicates);
1091+
for obligation in obligations {
1092+
fulfill_cx.register_predicate_obligation(&infcx, obligation);
1093+
}
10811094
for predicate in predicates {
1082-
let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), predicate);
1095+
let obligation = traits::Obligation::new(cause.clone(), predicate);
10831096
fulfill_cx.register_predicate_obligation(&infcx, obligation);
10841097
}
10851098
drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()).is_ok()

src/librustc_trans/trans/meth.rs

+11-22
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use back::abi;
1313
use back::link;
1414
use llvm::{ValueRef, get_param};
1515
use metadata::csearch;
16-
use middle::subst::Substs;
16+
use middle::subst::{Subst, Substs};
1717
use middle::subst::VecPerParamSpace;
1818
use middle::subst;
1919
use middle::traits;
@@ -784,6 +784,7 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
784784

785785
ty::populate_implementations_for_trait_if_necessary(tcx, trt_id);
786786

787+
let nullptr = C_null(Type::nil(ccx).ptr_to());
787788
let trait_item_def_ids = ty::trait_item_def_ids(tcx, trt_id);
788789
trait_item_def_ids
789790
.iter()
@@ -809,6 +810,12 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
809810
};
810811
let name = trait_method_type.name;
811812

813+
// Some methods cannot be called on an object; skip those.
814+
if !traits::is_vtable_safe_method(tcx, trt_id, &trait_method_type) {
815+
debug!("emit_vtable_methods: not vtable safe");
816+
return nullptr;
817+
}
818+
812819
debug!("emit_vtable_methods: trait_method_type={}",
813820
trait_method_type.repr(tcx));
814821

@@ -820,35 +827,17 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
820827
ty::TypeTraitItem(_) => ccx.sess().bug("should be a method, not assoc type")
821828
};
822829

823-
debug!("emit_vtable_methods: m={}",
830+
debug!("emit_vtable_methods: impl_method_type={}",
824831
impl_method_type.repr(tcx));
825832

826-
let nullptr = C_null(Type::nil(ccx).ptr_to());
827-
828-
if impl_method_type.generics.has_type_params(subst::FnSpace) {
829-
debug!("emit_vtable_methods: generic");
830-
return nullptr;
831-
}
832-
833-
let bare_fn_ty =
834-
ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(impl_method_type.fty.clone()));
835-
if ty::type_has_self(bare_fn_ty) {
836-
debug!("emit_vtable_methods: type_has_self {}",
837-
bare_fn_ty.repr(tcx));
838-
return nullptr;
839-
}
840-
841833
// If this is a default method, it's possible that it
842834
// relies on where clauses that do not hold for this
843835
// particular set of type parameters. Note that this
844836
// method could then never be called, so we do not want to
845837
// try and trans it, in that case. Issue #23435.
846838
if ty::provided_source(tcx, impl_method_def_id).is_some() {
847-
let predicates =
848-
monomorphize::apply_param_substs(tcx,
849-
&substs,
850-
&impl_method_type.predicates.predicates);
851-
if !predicates_hold(ccx, predicates.into_vec()) {
839+
let predicates = impl_method_type.predicates.predicates.subst(tcx, &substs);
840+
if !normalize_and_test_predicates(ccx, predicates.into_vec()) {
852841
debug!("emit_vtable_methods: predicates do not hold");
853842
return nullptr;
854843
}

src/test/run-pass/issue-23485.rs

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test for an ICE that occured when a default method implementation
12+
// was applied to a type that did not meet the prerequisites. The
13+
// problem occurred specifically because normalizing
14+
// `Self::Item::Target` was impossible in this case.
15+
16+
use std::boxed::Box;
17+
use std::marker::Sized;
18+
use std::clone::Clone;
19+
use std::ops::Deref;
20+
use std::option::Option;
21+
use std::option::Option::{Some,None};
22+
23+
trait Iterator {
24+
type Item;
25+
26+
fn next(&mut self) -> Option<Self::Item>;
27+
28+
fn clone_first(mut self) -> Option<<Self::Item as Deref>::Target> where
29+
Self: Sized,
30+
Self::Item: Deref,
31+
<Self::Item as Deref>::Target: Clone,
32+
{
33+
self.next().cloned()
34+
}
35+
}
36+
37+
struct Counter {
38+
value: i32
39+
}
40+
41+
struct Token {
42+
value: i32
43+
}
44+
45+
impl Iterator for Counter {
46+
type Item = Token;
47+
48+
fn next(&mut self) -> Option<Token> {
49+
let x = self.value;
50+
self.value += 1;
51+
Some(Token { value: x })
52+
}
53+
}
54+
55+
fn main() {
56+
let mut x: Box<Iterator<Item=Token>> = Box::new(Counter { value: 22 });
57+
assert_eq!(x.next().unwrap().value, 22);
58+
}

0 commit comments

Comments
 (0)