Skip to content
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

Only compute specializes query if (min)specialization is enabled in the crate of the specializing impl #126139

Merged
merged 2 commits into from
Jun 11, 2024
Merged
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
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
})
});

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 17 additions & 25 deletions compiler/rustc_trait_selection/src/traits/specialize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -136,38 +137,29 @@ 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;
/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies
/// 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;
}
}

Expand Down
22 changes: 11 additions & 11 deletions tests/ui/coherence/coherence-impls-copy.stderr
Original file line number Diff line number Diff line change
@@ -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
|
Expand All @@ -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
|
Expand Down
17 changes: 17 additions & 0 deletions tests/ui/specialization/anyid-repro-125197.rs
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/rust-lang/rust/pull/122791>,
// 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;
}
26 changes: 26 additions & 0 deletions tests/ui/specialization/auxiliary/anyid-repro-125197.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::fmt::Display;
use std::sync::Arc;

pub struct AnyId(());

impl PartialEq<Self> for AnyId {
fn eq(&self, _: &Self) -> bool {
todo!()
}
}

impl<T: Identifier> PartialEq<T> for AnyId {
fn eq(&self, _: &T) -> bool {
todo!()
}
}

impl<T: Identifier> From<T> for AnyId {
fn from(_: T) -> Self {
todo!()
}
}

pub trait Identifier: Display + 'static {}

impl<T> Identifier for T where T: PartialEq + Display + 'static {}
Loading