-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Skip default methods whose predicates are not satisfied when constructing vtables #23438
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
88b65c9
99a508b
bd2096c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -771,9 +771,15 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, | |
impl_id: ast::DefId, | ||
substs: subst::Substs<'tcx>, | ||
param_substs: &'tcx subst::Substs<'tcx>) | ||
-> Vec<ValueRef> { | ||
-> Vec<ValueRef> | ||
{ | ||
let tcx = ccx.tcx(); | ||
|
||
debug!("emit_vtable_methods(impl_id={}, substs={}, param_substs={})", | ||
impl_id.repr(tcx), | ||
substs.repr(tcx), | ||
param_substs.repr(tcx)); | ||
|
||
let trt_id = match ty::impl_trait_ref(tcx, impl_id) { | ||
Some(t_id) => t_id.def_id, | ||
None => ccx.sess().bug("make_impl_vtable: don't know how to \ | ||
|
@@ -783,41 +789,82 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, | |
ty::populate_implementations_for_trait_if_necessary(tcx, trt_id); | ||
|
||
let trait_item_def_ids = ty::trait_item_def_ids(tcx, trt_id); | ||
trait_item_def_ids.iter().flat_map(|method_def_id| { | ||
let method_def_id = method_def_id.def_id(); | ||
let name = ty::impl_or_trait_item(tcx, method_def_id).name(); | ||
// The substitutions we have are on the impl, so we grab | ||
// the method type from the impl to substitute into. | ||
let m_id = method_with_name(ccx, impl_id, name); | ||
let ti = ty::impl_or_trait_item(tcx, m_id); | ||
match ti { | ||
ty::MethodTraitItem(m) => { | ||
debug!("(making impl vtable) emitting method {} at subst {}", | ||
m.repr(tcx), | ||
substs.repr(tcx)); | ||
if m.generics.has_type_params(subst::FnSpace) || | ||
ty::type_has_self(ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(m.fty.clone()))) | ||
{ | ||
debug!("(making impl vtable) method has self or type \ | ||
params: {}", | ||
token::get_name(name)); | ||
Some(C_null(Type::nil(ccx).ptr_to())).into_iter() | ||
} else { | ||
let fn_ref = trans_fn_ref_with_substs( | ||
ccx, | ||
m_id, | ||
ExprId(0), | ||
param_substs, | ||
substs.clone()).val; | ||
|
||
Some(fn_ref).into_iter() | ||
} | ||
trait_item_def_ids | ||
.iter() | ||
|
||
// Filter out the associated types. | ||
.filter_map(|item_def_id| { | ||
match *item_def_id { | ||
ty::MethodTraitItemId(def_id) => Some(def_id), | ||
ty::TypeTraitItemId(_) => None, | ||
} | ||
}) | ||
|
||
// Now produce pointers for each remaining method. If the | ||
// method could never be called from this object, just supply | ||
// null. | ||
.map(|trait_method_def_id| { | ||
debug!("emit_vtable_methods: trait_method_def_id={}", | ||
trait_method_def_id.repr(tcx)); | ||
|
||
let trait_method_type = match ty::impl_or_trait_item(tcx, trait_method_def_id) { | ||
ty::MethodTraitItem(m) => m, | ||
ty::TypeTraitItem(_) => ccx.sess().bug("should be a method, not assoc type") | ||
}; | ||
let name = trait_method_type.name; | ||
|
||
debug!("emit_vtable_methods: trait_method_type={}", | ||
trait_method_type.repr(tcx)); | ||
|
||
// The substitutions we have are on the impl, so we grab | ||
// the method type from the impl to substitute into. | ||
let impl_method_def_id = method_with_name(ccx, impl_id, name); | ||
let impl_method_type = match ty::impl_or_trait_item(tcx, impl_method_def_id) { | ||
ty::MethodTraitItem(m) => m, | ||
ty::TypeTraitItem(_) => ccx.sess().bug("should be a method, not assoc type") | ||
}; | ||
|
||
debug!("emit_vtable_methods: m={}", | ||
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This approach of, effectively, re-checking methods for inclusion in the vtables seems not very DRY. It would be nicer if we could do these checks just once in type checking and not repeat them here. I suppose there are monomorphisation issues or something? Any way to avoid the repeated checks? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With respect to the new check I added, it's not really "re-checking" since there is no equivalent check in the typeck phase. (That is, no check for whether the where clauses on a default method impl are satisfied for any given implementor type.) I agree that the existing code that checks for generic methods and such is somewhat non-DRY in that it overlaps with the object safety checks -- probably it would in any case suffice (and perhaps be better...) to check for whether |
||
} | ||
ty::TypeTraitItem(_) => { | ||
None.into_iter() | ||
|
||
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; | ||
} | ||
} | ||
}).collect() | ||
|
||
// If this is a default method, it's possible that it | ||
// relies on where clauses that do not hold for this | ||
// particular set of type parameters. Note that this | ||
// method could then never be called, so we do not want to | ||
// try and trans it, in that case. Issue #23435. | ||
if ty::provided_source(tcx, impl_method_def_id).is_some() { | ||
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), | ||
param_substs, | ||
substs.clone()).val | ||
}) | ||
.collect() | ||
} | ||
|
||
/// Generates the code to convert from a pointer (`Box<T>`, `&T`, etc) into an object | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 }; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you explain "freshens" in this comment please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind, I see it is explained further down