-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Disqualify built-in trait impl if it seems likely to overlap in an unsound way with a blanket impl #132289
Disqualify built-in trait impl if it seems likely to overlap in an unsound way with a blanket impl #132289
Changes from all commits
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 | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -11,9 +11,10 @@ | |||||||||
|
||||||||||
pub mod specialization_graph; | ||||||||||
|
||||||||||
use rustc_data_structures::fx::FxIndexSet; | ||||||||||
use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; | ||||||||||
use rustc_errors::codes::*; | ||||||||||
use rustc_errors::{Diag, EmissionGuarantee}; | ||||||||||
use rustc_hir::LangItem; | ||||||||||
use rustc_hir::def_id::{DefId, LocalDefId}; | ||||||||||
use rustc_infer::traits::Obligation; | ||||||||||
use rustc_middle::bug; | ||||||||||
|
@@ -22,6 +23,8 @@ use rustc_middle::ty::print::PrintTraitRefExt as _; | |||||||||
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; | ||||||||||
use rustc_session::lint::builtin::{COHERENCE_LEAK_CHECK, ORDER_DEPENDENT_TRAIT_OBJECTS}; | ||||||||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, sym}; | ||||||||||
use rustc_type_ir::elaborate; | ||||||||||
use rustc_type_ir::fast_reject::{SimplifiedType, TreatParams, simplify_type}; | ||||||||||
use rustc_type_ir::solve::NoSolution; | ||||||||||
use specialization_graph::GraphExt; | ||||||||||
use tracing::{debug, instrument}; | ||||||||||
|
@@ -595,3 +598,128 @@ fn report_conflicting_impls<'tcx>( | |||||||||
} | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
pub(super) fn trait_has_impl_which_may_shadow_dyn<'tcx>( | ||||||||||
tcx: TyCtxt<'tcx>, | ||||||||||
(target_trait_def_id, principal_def_id): (DefId, Option<DefId>), | ||||||||||
) -> bool { | ||||||||||
// We only care about trait objects which have associated types. | ||||||||||
if !tcx | ||||||||||
.associated_items(target_trait_def_id) | ||||||||||
.in_definition_order() | ||||||||||
.any(|item| item.kind == ty::AssocKind::Type) | ||||||||||
{ | ||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
let target_self_ty = | ||||||||||
principal_def_id.map_or(SimplifiedType::MarkerTraitObject, SimplifiedType::Trait); | ||||||||||
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.
Suggested change
do we get here with auto-traits? I don't think so, as we'd always return |
||||||||||
|
||||||||||
let elaborated_supertraits = | ||||||||||
principal_def_id.into_iter().flat_map(|def_id| tcx.supertrait_def_ids(def_id)).collect(); | ||||||||||
|
||||||||||
trait_has_impl_inner( | ||||||||||
tcx, | ||||||||||
target_trait_def_id, | ||||||||||
target_self_ty, | ||||||||||
&elaborated_supertraits, | ||||||||||
&mut Default::default(), | ||||||||||
false, | ||||||||||
) | ||||||||||
} | ||||||||||
|
||||||||||
fn trait_has_impl_inner<'tcx>( | ||||||||||
tcx: TyCtxt<'tcx>, | ||||||||||
target_trait_def_id: DefId, | ||||||||||
target_self_ty: SimplifiedType<DefId>, | ||||||||||
elaborated_supertraits: &FxHashSet<DefId>, | ||||||||||
seen_traits: &mut FxHashSet<DefId>, | ||||||||||
for_nested_impl: bool, | ||||||||||
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. pls change to an or wait: alternative: if we duplicate the "is |
||||||||||
) -> bool { | ||||||||||
if tcx.is_lang_item(target_trait_def_id, LangItem::Sized) { | ||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
// If we've encountered a trait in a cycle, then let's just | ||||||||||
// consider it to be implemented defensively. | ||||||||||
if !seen_traits.insert(target_trait_def_id) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
// Since we don't pass in the set of auto traits, and just the principal, | ||||||||||
// consider all auto traits implemented. | ||||||||||
if tcx.trait_is_auto(target_trait_def_id) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
// When checking the outermost call of this recursive function, we don't care | ||||||||||
// about elaborated supertraits or built-in impls. This is intuitively because | ||||||||||
// `dyn Trait: Trait` doesn't overlap with itself, and all built-in impls have | ||||||||||
// `rustc_deny_explicit_impl(implement_via_object = true)`. | ||||||||||
if for_nested_impl { | ||||||||||
if elaborated_supertraits.contains(&target_trait_def_id) { | ||||||||||
return true; | ||||||||||
} | ||||||||||
// Lazy heuristic that any lang item is potentially a built-in impl. | ||||||||||
if tcx.as_lang_item(target_trait_def_id).is_some() { | ||||||||||
return true; | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
let mut has_offending_impl = false; | ||||||||||
tcx.for_each_impl(target_trait_def_id, |impl_def_id| { | ||||||||||
if has_offending_impl { | ||||||||||
return; | ||||||||||
} | ||||||||||
|
||||||||||
let self_ty = tcx | ||||||||||
.impl_trait_ref(impl_def_id) | ||||||||||
.expect("impl must have trait ref") | ||||||||||
.instantiate_identity() | ||||||||||
.self_ty(); | ||||||||||
|
||||||||||
if simplify_type(tcx, self_ty, TreatParams::InstantiateWithInfer) | ||||||||||
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.
Suggested change
|
||||||||||
.is_some_and(|simp| simp != target_self_ty) | ||||||||||
{ | ||||||||||
return; | ||||||||||
} | ||||||||||
|
||||||||||
for (pred, _) in | ||||||||||
elaborate::elaborate(tcx, tcx.predicates_of(impl_def_id).instantiate_identity(tcx)) | ||||||||||
{ | ||||||||||
if let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() | ||||||||||
&& trait_pred.self_ty() == self_ty | ||||||||||
&& !trait_has_impl_inner( | ||||||||||
tcx, | ||||||||||
trait_pred.def_id(), | ||||||||||
target_self_ty, | ||||||||||
elaborated_supertraits, | ||||||||||
seen_traits, | ||||||||||
true, | ||||||||||
) | ||||||||||
{ | ||||||||||
return; | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
if let ty::Alias(ty::Projection, alias_ty) = self_ty.kind() { | ||||||||||
for pred in tcx.item_super_predicates(alias_ty.def_id).iter_identity() { | ||||||||||
if let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() | ||||||||||
&& trait_pred.self_ty() == self_ty | ||||||||||
&& !trait_has_impl_inner( | ||||||||||
tcx, | ||||||||||
trait_pred.def_id(), | ||||||||||
target_self_ty, | ||||||||||
elaborated_supertraits, | ||||||||||
seen_traits, | ||||||||||
false, | ||||||||||
) | ||||||||||
{ | ||||||||||
return; | ||||||||||
} | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
has_offending_impl = true; | ||||||||||
}); | ||||||||||
|
||||||||||
has_offending_impl | ||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
//@ check-pass | ||
|
||
// Make sure that if we don't disqualify a built-in object impl | ||
// due to a blanket with a trait bound that will never apply to | ||
// the object. | ||
|
||
pub trait SimpleService { | ||
type Resp; | ||
} | ||
|
||
trait Service { | ||
type Resp; | ||
} | ||
|
||
impl<S> Service for S where S: SimpleService + ?Sized { | ||
type Resp = <S as SimpleService>::Resp; | ||
} | ||
|
||
fn implements_service(x: &(impl Service<Resp = ()> + ?Sized)) {} | ||
|
||
fn test(x: &dyn Service<Resp = ()>) { | ||
implements_service(x); | ||
} | ||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
trait Trait { | ||
type Assoc; | ||
fn generate(&self) -> Self::Assoc; | ||
} | ||
|
||
trait Other {} | ||
|
||
impl<S> Trait for S where S: Other + ?Sized { | ||
type Assoc = &'static str; | ||
fn generate(&self) -> Self::Assoc { "hi" } | ||
} | ||
|
||
trait Downstream: Trait<Assoc = usize> {} | ||
impl<T> Other for T where T: ?Sized + Downstream + OnlyDyn {} | ||
|
||
trait OnlyDyn {} | ||
impl OnlyDyn for dyn Downstream {} | ||
|
||
struct Concrete; | ||
impl Trait for Concrete { | ||
type Assoc = usize; | ||
fn generate(&self) -> Self::Assoc { 42 } | ||
} | ||
impl Downstream for Concrete {} | ||
|
||
fn test<T: ?Sized + Other>(x: &T) { | ||
let s: &str = x.generate(); | ||
println!("{s}"); | ||
} | ||
|
||
fn impl_downstream<T: ?Sized + Downstream>(x: &T) {} | ||
|
||
fn main() { | ||
let x: &dyn Downstream = &Concrete; | ||
|
||
test(x); // This call used to segfault. | ||
//~^ ERROR type mismatch resolving | ||
|
||
// This no longer holds since `Downstream: Trait<Assoc = usize>`, | ||
// but the `Trait<Assoc = &'static str>` blanket impl now shadows. | ||
impl_downstream(x); | ||
//~^ ERROR type mismatch resolving | ||
} |
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.