Skip to content

[CRATER] probe fallout of dyn overlap #140824

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

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
@@ -391,6 +391,12 @@ fn check_trait_item<'tcx>(
// Check that an item definition in a subtrait is shadowing a supertrait item.
lint_item_shadowing_supertrait_item(tcx, def_id);

for blanket_impl_def_id in tcx.all_impls(def_id.to_def_id()) {
if !blanket_impl_def_id.is_local() {
tcx.ensure_ok().lint_object_blanket_impl((blanket_impl_def_id, def_id.to_def_id()));
}
}

let mut res = check_associated_item(tcx, def_id, span, method_sig);

if matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) {
148 changes: 144 additions & 4 deletions compiler/rustc_hir_analysis/src/impl_wf_check.rs
Original file line number Diff line number Diff line change
@@ -10,16 +10,23 @@

use std::assert_matches::debug_assert_matches;

use min_specialization::check_min_specialization;
use itertools::Itertools;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::codes::*;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
use rustc_span::ErrorGuaranteed;
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint_defs::builtin::DYN_OVERLAP;
use rustc_middle::ty::{
self, ExistentialPredicateStableCmpExt, Ty, TyCtxt, TypeVisitableExt, TypingMode, Upcast,
elaborate,
};
use rustc_span::{DUMMY_SP, ErrorGuaranteed, sym};
use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt};

use crate::constrained_generic_params as cgp;
use crate::errors::UnconstrainedGenericParameter;
use crate::impl_wf_check::min_specialization::check_min_specialization;

mod min_specialization;

@@ -68,6 +75,22 @@ pub(crate) fn check_impl_wf(
if tcx.features().min_specialization() {
res = res.and(check_min_specialization(tcx, impl_def_id));
}

if let Some(trait_def_id) = tcx.trait_id_of_impl(impl_def_id.to_def_id()) {
for &subtrait_def_id in tcx
.crates(())
.into_iter()
.copied()
.chain([LOCAL_CRATE])
.flat_map(|cnum| tcx.traits(cnum))
{
if ty::elaborate::supertrait_def_ids(tcx, subtrait_def_id).contains(&trait_def_id) {
tcx.ensure_ok()
.lint_object_blanket_impl((impl_def_id.to_def_id(), subtrait_def_id));
}
}
}

res
}

@@ -234,3 +257,120 @@ pub(crate) fn enforce_impl_non_lifetime_params_are_constrained(
}
res
}

pub(crate) fn lint_object_blanket_impl<'tcx>(
tcx: TyCtxt<'tcx>,
(impl_def_id, trait_def_id): (DefId, DefId),
) {
if tcx.is_diagnostic_item(sym::Any, trait_def_id) {
return;
}

if !tcx.is_dyn_compatible(trait_def_id) {
return;
}

let infcx = tcx.infer_ctxt().with_next_trait_solver(true).build(TypingMode::CheckObjectOverlap);

let principal_trait_args = infcx.fresh_args_for_item(DUMMY_SP, trait_def_id);
let principal_trait = ty::TraitRef::new_from_args(tcx, trait_def_id, principal_trait_args);

let mut needed_associated_types = vec![];
let clause: ty::Clause<'tcx> = ty::TraitRef::identity(tcx, trait_def_id).upcast(tcx);
for clause in elaborate::elaborate(tcx, [clause]).filter_only_self() {
let clause = clause.instantiate_supertrait(tcx, ty::Binder::dummy(principal_trait));

let bound_predicate = clause.kind();
match bound_predicate.skip_binder() {
ty::ClauseKind::Trait(pred) => {
// FIXME(negative_bounds): Handle this correctly...
let trait_ref = tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref));
needed_associated_types.extend(
tcx.associated_items(pred.trait_ref.def_id)
.in_definition_order()
// We only care about associated types.
.filter(|item| item.is_type())
// No RPITITs -- they're not dyn-compatible for now.
.filter(|item| !item.is_impl_trait_in_trait())
// If the associated type has a `where Self: Sized` bound,
// we do not need to constrain the associated type.
.filter(|item| !tcx.generics_require_sized_self(item.def_id))
.map(|item| (item.def_id, trait_ref)),
);
}
_ => (),
}
}

let mut data: Vec<_> = [ty::Binder::dummy(ty::ExistentialPredicate::Trait(
ty::ExistentialTraitRef::erase_self_ty(tcx, principal_trait),
))]
.into_iter()
.chain(needed_associated_types.into_iter().map(|(def_id, trait_ref)| {
trait_ref.map_bound(|trait_ref| {
ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty(
tcx,
ty::ProjectionPredicate {
projection_term: ty::AliasTerm::new_from_args(tcx, def_id, trait_ref.args),
term: infcx.next_ty_var(DUMMY_SP).into(),
},
))
})
}))
.collect();
data.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));

let self_ty = Ty::new_dynamic(
tcx,
tcx.mk_poly_existential_predicates(&data),
tcx.lifetimes.re_erased,
ty::Dyn,
);

let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args);

let ocx = ObligationCtxt::new(&infcx);
let Ok(()) =
ocx.eq(&ObligationCause::dummy(), ty::ParamEnv::empty(), principal_trait, impl_trait_ref)
else {
return;
};
let Ok(()) =
ocx.eq(&ObligationCause::dummy(), ty::ParamEnv::empty(), self_ty, impl_trait_ref.self_ty())
else {
return;
};

ocx.register_obligations(
tcx.predicates_of(impl_def_id).instantiate(tcx, impl_args).into_iter().map(
|(clause, _)| {
Obligation::new(tcx, ObligationCause::dummy(), ty::ParamEnv::empty(), clause)
},
),
);

if !ocx.select_where_possible().is_empty() {
return;
}

let local_def_id = if let Some(impl_def_id) = impl_def_id.as_local() {
impl_def_id
} else if let Some(trait_def_id) = trait_def_id.as_local() {
trait_def_id
} else {
panic!()
};
let hir_id = tcx.local_def_id_to_hir_id(local_def_id);

let self_ty = infcx.resolve_vars_if_possible(self_ty);

tcx.node_span_lint(DYN_OVERLAP, hir_id, tcx.def_span(local_def_id), |diag| {
diag.primary_message("hi");
diag.span_label(
tcx.def_span(trait_def_id),
format!("built-in `{self_ty}` implementation for this trait"),
);
diag.span_label(tcx.def_span(impl_def_id), "overlaps with this blanket impl");
});
}
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/lib.rs
Original file line number Diff line number Diff line change
@@ -171,6 +171,7 @@ pub fn provide(providers: &mut Providers) {
inherit_sig_for_delegation_item: delegation::inherit_sig_for_delegation_item,
enforce_impl_non_lifetime_params_are_constrained:
impl_wf_check::enforce_impl_non_lifetime_params_are_constrained,
lint_object_blanket_impl: impl_wf_check::lint_object_blanket_impl,
..*providers
};
}
2 changes: 2 additions & 0 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
@@ -968,6 +968,7 @@ impl<'tcx> InferCtxt<'tcx> {
// and post-borrowck analysis mode. We may need to modify its uses
// to support PostBorrowckAnalysis in the old solver as well.
TypingMode::Coherence
| TypingMode::CheckObjectOverlap
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => false,
}
@@ -1260,6 +1261,7 @@ impl<'tcx> InferCtxt<'tcx> {
TypingMode::non_body_analysis()
}
mode @ (ty::TypingMode::Coherence
| TypingMode::CheckObjectOverlap
| ty::TypingMode::PostBorrowckAnalysis { .. }
| ty::TypingMode::PostAnalysis) => mode,
};
6 changes: 4 additions & 2 deletions compiler/rustc_infer/src/infer/opaque_types/mod.rs
Original file line number Diff line number Diff line change
@@ -87,7 +87,9 @@ impl<'tcx> InferCtxt<'tcx> {
let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() {
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) if def_id.is_local() => {
let def_id = def_id.expect_local();
if let ty::TypingMode::Coherence = self.typing_mode() {
if let ty::TypingMode::Coherence | ty::TypingMode::CheckObjectOverlap =
self.typing_mode()
{
// See comment on `insert_hidden_type` for why this is sufficient in coherence
return Some(self.register_hidden_type(
OpaqueTypeKey { def_id, args },
@@ -226,7 +228,7 @@ impl<'tcx> InferCtxt<'tcx> {
// these are the same span, but not in cases like `-> (impl
// Foo, impl Bar)`.
match self.typing_mode() {
ty::TypingMode::Coherence => {
ty::TypingMode::Coherence | ty::TypingMode::CheckObjectOverlap => {
// During intercrate we do not define opaque types but instead always
// force ambiguity unless the hidden type is known to not implement
// our trait.
10 changes: 8 additions & 2 deletions compiler/rustc_infer/src/infer/relate/generalize.rs
Original file line number Diff line number Diff line change
@@ -519,7 +519,10 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Generalizer<'_, 'tcx> {
//
// cc trait-system-refactor-initiative#108
if self.infcx.next_trait_solver()
&& !matches!(self.infcx.typing_mode(), TypingMode::Coherence)
&& !matches!(
self.infcx.typing_mode(),
TypingMode::Coherence | TypingMode::CheckObjectOverlap
)
&& self.in_alias
{
inner.type_variables().equate(vid, new_var_id);
@@ -650,7 +653,10 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Generalizer<'_, 'tcx> {
// See the comment for type inference variables
// for more details.
if self.infcx.next_trait_solver()
&& !matches!(self.infcx.typing_mode(), TypingMode::Coherence)
&& !matches!(
self.infcx.typing_mode(),
TypingMode::Coherence | TypingMode::CheckObjectOverlap
)
&& self.in_alias
{
variable_table.union(vid, new_var_id);
12 changes: 12 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
@@ -37,6 +37,7 @@ declare_lint_pass! {
DEPRECATED_SAFE_2024,
DEPRECATED_WHERE_CLAUSE_LOCATION,
DUPLICATE_MACRO_ATTRIBUTES,
DYN_OVERLAP,
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
ELIDED_LIFETIMES_IN_PATHS,
ELIDED_NAMED_LIFETIMES,
@@ -5067,3 +5068,14 @@ declare_lint! {
reference: "issue #138762 <https://github.com/rust-lang/rust/issues/138762>",
};
}

declare_lint! {
/// The `dyn_overlap` lint is one of the lints of all time.
///
/// ### Uwu
///
/// Owo
pub DYN_OVERLAP,
Forbid,
"owo",
}
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
@@ -527,6 +527,10 @@ rustc_queries! {
anon
}

query lint_object_blanket_impl(_: (DefId, DefId)) {
desc { "u if wu" }
}

/// Set of param indexes for type params that are in the type's representation
query params_in_repr(key: DefId) -> &'tcx rustc_index::bit_set::DenseBitSet<u32> {
desc { "finding type parameters in the representation" }
4 changes: 2 additions & 2 deletions compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
Original file line number Diff line number Diff line change
@@ -380,7 +380,7 @@ where

let mut candidates = vec![];

if let TypingMode::Coherence = self.typing_mode() {
if let TypingMode::Coherence | TypingMode::CheckObjectOverlap = self.typing_mode() {
if let Ok(candidate) = self.consider_coherence_unknowable_candidate(goal) {
return vec![candidate];
}
@@ -831,7 +831,7 @@ where
candidates: &mut Vec<Candidate<I>>,
) {
match self.typing_mode() {
TypingMode::Coherence => return,
TypingMode::Coherence | TypingMode::CheckObjectOverlap => return,
TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::PostBorrowckAnalysis { .. }
3 changes: 2 additions & 1 deletion compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
@@ -948,7 +948,8 @@ where
GoalSource::TypeRelating
}
// FIXME(-Znext-solver=coinductive): should these WF goals also be unproductive?
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => GoalSource::Misc,
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_))
| ty::PredicateKind::Ambiguous => GoalSource::Misc,
p => unreachable!("unexpected nested goal in `relate`: {p:?}"),
};
self.add_goal(source, goal);
4 changes: 3 additions & 1 deletion compiler/rustc_next_trait_solver/src/solve/mod.rs
Original file line number Diff line number Diff line change
@@ -339,7 +339,9 @@ where
fn opaque_type_is_rigid(&self, def_id: I::DefId) -> bool {
match self.typing_mode() {
// Opaques are never rigid outside of analysis mode.
TypingMode::Coherence | TypingMode::PostAnalysis => false,
TypingMode::Coherence | TypingMode::CheckObjectOverlap | TypingMode::PostAnalysis => {
false
}
// During analysis, opaques are rigid unless they may be defined by
// the current body.
TypingMode::Analysis { defining_opaque_types_and_generators: non_rigid_opaques }
Original file line number Diff line number Diff line change
@@ -240,7 +240,7 @@ where
// would be relevant if any of the nested goals refer to the `term`.
// This is not the case here and we only prefer adding an ambiguous
// nested goal for consistency.
ty::TypingMode::Coherence => {
ty::TypingMode::Coherence | ty::TypingMode::CheckObjectOverlap => {
ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous));
return ecx
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
@@ -280,7 +280,7 @@ where
// would be relevant if any of the nested goals refer to the `term`.
// This is not the case here and we only prefer adding an ambiguous
// nested goal for consistency.
ty::TypingMode::Coherence => {
ty::TypingMode::Coherence | ty::TypingMode::CheckObjectOverlap => {
ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous));
return ecx
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ where
let expected = goal.predicate.term.as_type().expect("no such thing as an opaque const");

match self.typing_mode() {
TypingMode::Coherence => {
TypingMode::Coherence | TypingMode::CheckObjectOverlap => {
// An impossible opaque type bound is the only way this goal will fail
// e.g. assigning `impl Copy := NotCopy`
self.add_item_bounds_for_hidden_type(
2 changes: 1 addition & 1 deletion compiler/rustc_next_trait_solver/src/solve/search_graph.rs
Original file line number Diff line number Diff line change
@@ -58,7 +58,7 @@ where
// example where this would matter. We likely should change these cycles to `NoSolution`
// even in coherence once this is a bit more settled.
PathKind::Inductive => match input.typing_mode {
TypingMode::Coherence => {
TypingMode::Coherence | TypingMode::CheckObjectOverlap => {
response_no_constraints(cx, input, Certainty::overflow(false))
}
TypingMode::Analysis { .. }
5 changes: 3 additions & 2 deletions compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
Original file line number Diff line number Diff line change
@@ -70,7 +70,7 @@ where
// In intercrate mode, this is ambiguous. But outside of intercrate,
// it's not a real impl.
(ty::ImplPolarity::Reservation, _) => match ecx.typing_mode() {
TypingMode::Coherence => Certainty::AMBIGUOUS,
TypingMode::Coherence | TypingMode::CheckObjectOverlap => Certainty::AMBIGUOUS,
TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::PostBorrowckAnalysis { .. }
@@ -1257,7 +1257,7 @@ where
&mut self,
mut candidates: Vec<Candidate<I>>,
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
if let TypingMode::Coherence = self.typing_mode() {
if let TypingMode::Coherence | TypingMode::CheckObjectOverlap = self.typing_mode() {
let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect();
return if let Some(response) = self.try_merge_responses(&all_candidates) {
Ok((response, Some(TraitGoalProvenVia::Misc)))
@@ -1360,6 +1360,7 @@ where
}
}
TypingMode::Coherence
| TypingMode::CheckObjectOverlap
| TypingMode::PostAnalysis
| TypingMode::Borrowck { defining_opaque_types: _ }
| TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => {}
2 changes: 1 addition & 1 deletion compiler/rustc_pattern_analysis/src/lib.rs
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ use crate::constructor::{Constructor, ConstructorSet, IntRange};
use crate::pat::DeconstructedPat;

pub trait Captures<'a> {}
impl<'a, T: ?Sized> Captures<'a> for T {}
impl<'a, T> Captures<'a> for T {}

/// `bool` newtype that indicates whether this is a privately uninhabited field that we should skip
/// during analysis.
Loading