diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index afee8d5646c85..1cef35f082b98 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -314,6 +314,7 @@ provide! { tcx, def_id, other, cdata, extern_crate => { cdata.extern_crate.map(|c| &*tcx.arena.alloc(c)) } is_no_builtins => { cdata.root.no_builtins } symbol_mangling_version => { cdata.root.symbol_mangling_version } + specialization_enabled_in => { cdata.root.specialization_enabled_in } reachable_non_generics => { let reachable_non_generics = tcx .exported_symbols(cdata.cnum) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 67c5bc8c786bc..89da0df8575e3 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -741,6 +741,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { expn_data, expn_hashes, def_path_hash_map, + specialization_enabled_in: tcx.specialization_enabled_in(LOCAL_CRATE), }) }); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index c2cf5b6b7125b..87900c23d8daf 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -290,6 +290,8 @@ pub(crate) struct CrateRoot { panic_runtime: bool, profiler_runtime: bool, symbol_mangling_version: SymbolManglingVersion, + + specialization_enabled_in: bool, } /// On-disk representation of `DefId`. diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0af32a6a85781..a8bf735fa5a93 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1494,6 +1494,11 @@ rustc_queries! { separate_provide_extern } + query specialization_enabled_in(cnum: CrateNum) -> bool { + desc { "checking whether the crate enabled `specialization`/`min_specialization`" } + separate_provide_extern + } + query specializes(_: (DefId, DefId)) -> bool { desc { "computing whether impls specialize one another" } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 7be2c4a85c563..eae2f9d1792f6 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -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 diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index c2727ae6bfd9a..c9bb0d330e1fd 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -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; } } diff --git a/tests/ui/coherence/coherence-impls-copy.stderr b/tests/ui/coherence/coherence-impls-copy.stderr index 2d2c5064043a9..f529a056b0fd5 100644 --- a/tests/ui/coherence/coherence-impls-copy.stderr +++ b/tests/ui/coherence/coherence-impls-copy.stderr @@ -1,14 +1,3 @@ -error[E0117]: only traits defined in the current crate can be implemented for primitive types - --> $DIR/coherence-impls-copy.rs:5:1 - | -LL | impl Copy for i32 {} - | ^^^^^^^^^^^^^^--- - | | | - | | `i32` is not defined in the current crate - | impl doesn't use only types from inside the current crate - | - = note: define and implement a trait or new type instead - error[E0119]: conflicting implementations of trait `Copy` for type `&NotSync` --> $DIR/coherence-impls-copy.rs:28:1 | @@ -30,6 +19,17 @@ LL | impl Copy for &'static [NotSync] {} | = note: define and implement a trait or new type instead +error[E0117]: only traits defined in the current crate can be implemented for primitive types + --> $DIR/coherence-impls-copy.rs:5:1 + | +LL | impl Copy for i32 {} + | ^^^^^^^^^^^^^^--- + | | | + | | `i32` is not defined in the current crate + | impl doesn't use only types from inside the current crate + | + = note: define and implement a trait or new type instead + error[E0206]: the trait `Copy` cannot be implemented for this type --> $DIR/coherence-impls-copy.rs:21:15 | diff --git a/tests/ui/specialization/anyid-repro-125197.rs b/tests/ui/specialization/anyid-repro-125197.rs new file mode 100644 index 0000000000000..9428d8dc2d419 --- /dev/null +++ b/tests/ui/specialization/anyid-repro-125197.rs @@ -0,0 +1,17 @@ +//@ aux-build: anyid-repro-125197.rs +//@ check-pass + +// Makes sure that we don't check `specializes(impl1, impl2)` for a pair of impls that don't +// actually participate in specialization. Since , +// we don't treat inductive cycles as errors -- so we may need to winnow more pairs of impls, and +// we try to winnow impls in favor of other impls. However, if we're *inside* the `specializes` +// query, then may have a query cycle if we call `specializes` again! + +extern crate anyid_repro_125197; +use anyid_repro_125197::AnyId; + +fn main() { + let x = "hello, world"; + let y: AnyId = x.into(); + let _ = y == x; +} diff --git a/tests/ui/specialization/auxiliary/anyid-repro-125197.rs b/tests/ui/specialization/auxiliary/anyid-repro-125197.rs new file mode 100644 index 0000000000000..c2794959740e6 --- /dev/null +++ b/tests/ui/specialization/auxiliary/anyid-repro-125197.rs @@ -0,0 +1,26 @@ +use std::fmt::Display; +use std::sync::Arc; + +pub struct AnyId(()); + +impl PartialEq for AnyId { + fn eq(&self, _: &Self) -> bool { + todo!() + } +} + +impl PartialEq for AnyId { + fn eq(&self, _: &T) -> bool { + todo!() + } +} + +impl From for AnyId { + fn from(_: T) -> Self { + todo!() + } +} + +pub trait Identifier: Display + 'static {} + +impl Identifier for T where T: PartialEq + Display + 'static {}