Skip to content

Commit

Permalink
Check that predicates hold before emitting an entry for the vtable.
Browse files Browse the repository at this point in the history
Fixes #23435.
  • Loading branch information
nikomatsakis committed Mar 17, 2015
1 parent 88b65c9 commit 99a508b
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 16 deletions.
3 changes: 2 additions & 1 deletion src/librustc/util/ppaux.rs
Expand Up @@ -1053,10 +1053,11 @@ impl<'tcx> Repr<'tcx> for ty::Variance {

impl<'tcx> Repr<'tcx> for ty::Method<'tcx> {
fn repr(&self, tcx: &ctxt<'tcx>) -> String {
format!("method(name: {}, generics: {}, fty: {}, \
format!("method(name: {}, generics: {}, predicates: {}, fty: {}, \
explicit_self: {}, vis: {}, def_id: {})",
self.name.repr(tcx),
self.generics.repr(tcx),
self.predicates.repr(tcx),
self.fty.repr(tcx),
self.explicit_self.repr(tcx),
self.vis.repr(tcx),
Expand Down
67 changes: 53 additions & 14 deletions src/librustc_trans/trans/common.rs
Expand Up @@ -46,6 +46,7 @@ use arena::TypedArena;
use libc::{c_uint, c_char};
use std::ffi::CString;
use std::cell::{Cell, RefCell};
use std::result::Result as StdResult;
use std::vec::Vec;
use syntax::ast::Ident;
use syntax::ast;
Expand Down Expand Up @@ -1006,9 +1007,9 @@ pub fn expr_ty_adjusted<'blk, 'tcx>(bcx: &BlockS<'blk, 'tcx>, ex: &ast::Expr) ->
/// do not (necessarily) resolve all nested obligations on the impl. Note that type check should
/// guarantee to us that all nested obligations *could be* resolved if we wanted to.
pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
span: Span,
trait_ref: ty::PolyTraitRef<'tcx>)
-> traits::Vtable<'tcx, ()>
span: Span,
trait_ref: ty::PolyTraitRef<'tcx>)
-> traits::Vtable<'tcx, ()>
{
let tcx = ccx.tcx();

Expand Down Expand Up @@ -1067,7 +1068,7 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
let vtable = selection.map_move_nested(|predicate| {
fulfill_cx.register_predicate_obligation(&infcx, predicate);
});
let vtable = drain_fulfillment_cx(span, &infcx, &mut fulfill_cx, &vtable);
let vtable = drain_fulfillment_cx_or_panic(span, &infcx, &mut fulfill_cx, &vtable);

info!("Cache miss: {}", trait_ref.repr(ccx.tcx()));
ccx.trait_cache().borrow_mut().insert(trait_ref,
Expand All @@ -1076,6 +1077,22 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
vtable
}

pub fn predicates_hold<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
predicates: Vec<ty::Predicate<'tcx>>)
-> bool
{
debug!("predicates_hold(predicates={})",
predicates.repr(ccx.tcx()));

let infcx = infer::new_infer_ctxt(ccx.tcx());
let mut fulfill_cx = traits::FulfillmentContext::new();
for predicate in predicates {
let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), predicate);
fulfill_cx.register_predicate_obligation(&infcx, obligation);
}
drain_fulfillment_cx(DUMMY_SP, &infcx, &mut fulfill_cx, &()).is_ok()
}

pub struct NormalizingClosureTyper<'a,'tcx:'a> {
param_env: ty::ParameterEnvironment<'a, 'tcx>
}
Expand Down Expand Up @@ -1123,11 +1140,36 @@ impl<'a,'tcx> ty::ClosureTyper<'tcx> for NormalizingClosureTyper<'a,'tcx> {
}
}

pub fn drain_fulfillment_cx_or_panic<'a,'tcx,T>(span: Span,
infcx: &infer::InferCtxt<'a,'tcx>,
fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
result: &T)
-> T
where T : TypeFoldable<'tcx> + Repr<'tcx>
{
match drain_fulfillment_cx(span, infcx, fulfill_cx, result) {
Ok(v) => v,
Err(errors) => {
infcx.tcx.sess.span_bug(
span,
&format!("Encountered errors `{}` fulfilling during trans",
errors.repr(infcx.tcx)));
}
}
}

/// Finishes processes any obligations that remain in the fulfillment
/// context, and then "freshens" and returns `result`. This is
/// primarily used during normalization and other cases where
/// processing the obligations in `fulfill_cx` may cause type
/// inference variables that appear in `result` to be unified, and
/// hence we need to process those obligations to get the complete
/// picture of the type.
pub fn drain_fulfillment_cx<'a,'tcx,T>(span: Span,
infcx: &infer::InferCtxt<'a,'tcx>,
fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
result: &T)
-> T
infcx: &infer::InferCtxt<'a,'tcx>,
fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
result: &T)
-> StdResult<T,Vec<traits::FulfillmentError<'tcx>>>
where T : TypeFoldable<'tcx> + Repr<'tcx>
{
debug!("drain_fulfillment_cx(result={})",
Expand All @@ -1140,16 +1182,13 @@ pub fn drain_fulfillment_cx<'a,'tcx,T>(span: Span,
match fulfill_cx.select_all_or_error(infcx, &typer) {
Ok(()) => { }
Err(errors) => {
// We always want to surface any overflow errors, no matter what.
if errors.iter().all(|e| e.is_overflow()) {
// See Ok(None) case above.
infcx.tcx.sess.span_fatal(
span,
"reached the recursion limit during monomorphization");
} else {
infcx.tcx.sess.span_bug(
span,
&format!("Encountered errors `{}` fulfilling during trans",
errors.repr(infcx.tcx)));
return Err(errors);
}
}
}
Expand All @@ -1159,7 +1198,7 @@ pub fn drain_fulfillment_cx<'a,'tcx,T>(span: Span,
// sort of overkill because we do not expect there to be any
// unbound type variables, hence no `TyFresh` types should ever be
// inserted.
result.fold_with(&mut infcx.freshener())
Ok(result.fold_with(&mut infcx.freshener()))
}

// Key used to lookup values supplied for type parameters in an expr.
Expand Down
9 changes: 9 additions & 0 deletions src/librustc_trans/trans/meth.rs
Expand Up @@ -842,6 +842,15 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
return nullptr;
}

let predicates =
monomorphize::apply_param_substs(tcx,
&substs,
&impl_method_type.predicates.predicates);
if !predicates_hold(ccx, predicates.into_vec()) {
debug!("emit_vtable_methods: predicates do not hold");
return nullptr;
}

trans_fn_ref_with_substs(ccx,
impl_method_def_id,
ExprId(0),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_trans/trans/monomorphize.rs
Expand Up @@ -339,7 +339,7 @@ pub fn normalize_associated_type<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> T
for obligation in obligations {
fulfill_cx.register_predicate_obligation(&infcx, obligation);
}
let result = drain_fulfillment_cx(DUMMY_SP, &infcx, &mut fulfill_cx, &result);
let result = drain_fulfillment_cx_or_panic(DUMMY_SP, &infcx, &mut fulfill_cx, &result);

result
}
35 changes: 35 additions & 0 deletions src/test/run-pass/issue-23435.rs
@@ -0,0 +1,35 @@
// Copyright 2015 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 that we do not ICE when a default method implementation has
// requirements (in this case, `Self : Baz`) that do not hold for some
// specific impl (in this case, `Foo : Bar`). This causes problems
// only when building a vtable, because that goes along and
// instantiates all the methods, even those that could not otherwise
// be called.

struct Foo {
x: i32
}

trait Bar {
fn bar(&self) where Self : Baz { self.baz(); }
}

trait Baz {
fn baz(&self);
}

impl Bar for Foo {
}

fn main() {
let x: &Bar = &Foo { x: 22 };
}

0 comments on commit 99a508b

Please sign in to comment.