Auto merge of #126139 - compiler-errors:specializes, r=lcnr
Only compute `specializes` query if (min)specialization is enabled in the crate of the specializing impl Fixes (after backport) https://github.com/rust-lang/rust/issues/125197 ### What https://github.com/rust-lang/rust/pull/122791 makes it so that inductive cycles are no longer hard errors. That means that when we are testing, for example, whether these impls overlap: ```rust impl PartialEq<Self> for AnyId { fn eq(&self, _: &Self) -> bool { todo!() } } impl<T: Identifier> PartialEq<T> for AnyId { fn eq(&self, _: &T) -> bool { todo!() } } ``` ...given... ```rust pub trait Identifier: Display + 'static {} impl<T> Identifier for T where T: PartialEq + Display + 'static {} ``` Then we try to see if the second impl holds given `T = AnyId`. That requires `AnyId: Identifier`, which requires that `AnyId: PartialEq`, which is satisfied by these two impl candidates... The `PartialEq<T>` impl is a cycle, and we used to winnow it when we used to treat inductive cycles as errors. However, now that we don't winnow it, this means that we *now* try calling `candidate_should_be_dropped_in_favor_of`, which tries to check whether one of the impls specializes the other: the `specializes` query. In that query, we currently bail early if the impl is local. However, in a foreign crate, we try to compute if the two impls specialize each other by doing trait solving. This may itself lead to the same situation where we call `specializes`, which will lead to a query cycle. ### How does this fix the problem We now record whether specialization is enabled in foreign crates, and extend this early-return behavior to foreign impls too. This means that we can only encounter these cycles if we truly have a specializing impl from a crate with specialization enabled. ----- r? `@oli-obk` or `@lcnr`
This commit is contained in:
commit
336e6ab3b3
9 changed files with 81 additions and 36 deletions
|
@ -623,6 +623,7 @@ pub fn provide(providers: &mut Providers) {
|
|||
*providers = Providers {
|
||||
specialization_graph_of: specialize::specialization_graph_provider,
|
||||
specializes: specialize::specializes,
|
||||
specialization_enabled_in: specialize::specialization_enabled_in,
|
||||
instantiate_and_check_impossible_predicates,
|
||||
is_impossible_associated_item,
|
||||
..*providers
|
||||
|
|
|
@ -24,6 +24,7 @@ use rustc_data_structures::fx::FxIndexSet;
|
|||
use rustc_errors::{codes::*, Diag, EmissionGuarantee};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::query::LocalCrate;
|
||||
use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::ty::{GenericArgs, GenericArgsRef};
|
||||
use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
|
||||
|
@ -136,6 +137,10 @@ pub fn translate_args_with_cause<'tcx>(
|
|||
source_args.rebase_onto(infcx.tcx, source_impl, target_args)
|
||||
}
|
||||
|
||||
pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool {
|
||||
tcx.features().specialization || tcx.features().min_specialization
|
||||
}
|
||||
|
||||
/// Is `impl1` a specialization of `impl2`?
|
||||
///
|
||||
/// Specialization is determined by the sets of types to which the impls apply;
|
||||
|
@ -143,31 +148,18 @@ pub fn translate_args_with_cause<'tcx>(
|
|||
/// to.
|
||||
#[instrument(skip(tcx), level = "debug")]
|
||||
pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool {
|
||||
// The feature gate should prevent introducing new specializations, but not
|
||||
// taking advantage of upstream ones.
|
||||
// If specialization is enabled for this crate then no extra checks are needed.
|
||||
// If it's not, and either of the `impl`s is local to this crate, then this definitely
|
||||
// isn't specializing - unless specialization is enabled for the `impl` span,
|
||||
// e.g. if it comes from an `allow_internal_unstable` macro
|
||||
let features = tcx.features();
|
||||
let specialization_enabled = features.specialization || features.min_specialization;
|
||||
if !specialization_enabled {
|
||||
if impl1_def_id.is_local() {
|
||||
let span = tcx.def_span(impl1_def_id);
|
||||
if !span.allows_unstable(sym::specialization)
|
||||
&& !span.allows_unstable(sym::min_specialization)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if impl2_def_id.is_local() {
|
||||
let span = tcx.def_span(impl2_def_id);
|
||||
if !span.allows_unstable(sym::specialization)
|
||||
&& !span.allows_unstable(sym::min_specialization)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// We check that the specializing impl comes from a crate that has specialization enabled,
|
||||
// or if the specializing impl is marked with `allow_internal_unstable`.
|
||||
//
|
||||
// We don't really care if the specialized impl (the parent) is in a crate that has
|
||||
// specialization enabled, since it's not being specialized, and it's already been checked
|
||||
// for coherence.
|
||||
if !tcx.specialization_enabled_in(impl1_def_id.krate) {
|
||||
let span = tcx.def_span(impl1_def_id);
|
||||
if !span.allows_unstable(sym::specialization)
|
||||
&& !span.allows_unstable(sym::min_specialization)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue