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
Show file tree
Hide file tree
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
Expand Up @@ -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(..)) {
Expand Down
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
Expand Up @@ -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;

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
Expand Up @@ -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
};
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down Expand Up @@ -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,
};
Expand Down
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
Expand Up @@ -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 },
Expand Down Expand Up @@ -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.
Expand Down
10 changes: 8 additions & 2 deletions compiler/rustc_infer/src/infer/relate/generalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Up @@ -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" }
Expand Down
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
Expand Up @@ -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];
}
Expand Down Expand Up @@ -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 { .. }
Expand Down
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
Expand Up @@ -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);
Expand Down
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
Expand Up @@ -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 }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
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
Expand Up @@ -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 { .. }
Expand Down
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
Expand Up @@ -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 { .. }
Expand Down Expand Up @@ -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)))
Expand Down Expand Up @@ -1360,6 +1360,7 @@ where
}
}
TypingMode::Coherence
| TypingMode::CheckObjectOverlap
| TypingMode::PostAnalysis
| TypingMode::Borrowck { defining_opaque_types: _ }
| TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => {}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_pattern_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading
Loading