Rollup merge of #140306 - lcnr:specialization-new, r=compiler-errors

handle specialization in the new trait solver

fixes https://github.com/rust-lang/trait-system-refactor-initiative/issues/187
also fixes the regression in `plonky2_field` from https://github.com/rust-lang/trait-system-refactor-initiative/issues/188

cc https://github.com/rust-lang/rust/pull/111994

r? ```@compiler-errors```
This commit is contained in:
Matthias Krüger 2025-04-26 07:13:09 +02:00 committed by GitHub
commit 54eae599e0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 290 additions and 51 deletions

View file

@ -590,6 +590,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.defaultness(def_id).has_value()
}
fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool {
self.specializes((impl_def_id, victim_def_id))
}
fn impl_is_default(self, impl_def_id: DefId) -> bool {
self.defaultness(impl_def_id).is_default()
}

View file

@ -10,6 +10,7 @@ use rustc_type_ir::{
};
use tracing::{debug, instrument};
use super::has_only_region_constraints;
use super::trait_goals::TraitGoalProvenVia;
use crate::delegate::SolverDelegate;
use crate::solve::inspect::ProbeKind;
@ -771,6 +772,69 @@ where
}
})
}
}
pub(super) enum AllowInferenceConstraints {
Yes,
No,
}
impl<D, I> EvalCtxt<'_, D>
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
/// Check whether we can ignore impl candidates due to specialization.
///
/// This is only necessary for `feature(specialization)` and seems quite ugly.
pub(super) fn filter_specialized_impls(
&mut self,
allow_inference_constraints: AllowInferenceConstraints,
candidates: &mut Vec<Candidate<I>>,
) {
match self.typing_mode() {
TypingMode::Coherence => return,
TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => {}
}
let mut i = 0;
'outer: while i < candidates.len() {
let CandidateSource::Impl(victim_def_id) = candidates[i].source else {
i += 1;
continue;
};
for (j, c) in candidates.iter().enumerate() {
if i == j {
continue;
}
let CandidateSource::Impl(other_def_id) = c.source else {
continue;
};
// See if we can toss out `victim` based on specialization.
//
// While this requires us to know *for sure* that the `lhs` impl applies
// we still use modulo regions here. This is fine as specialization currently
// assumes that specializing impls have to be always applicable, meaning that
// the only allowed region constraints may be constraints also present on the default impl.
if matches!(allow_inference_constraints, AllowInferenceConstraints::Yes)
|| has_only_region_constraints(c.result)
{
if self.cx().impl_specializes(other_def_id, victim_def_id) {
candidates.remove(i);
continue 'outer;
}
}
}
i += 1;
}
}
/// Assemble and merge candidates for goals which are related to an underlying trait
/// goal. Right now, this is normalizes-to and host effect goals.
@ -857,7 +921,7 @@ where
}
}
TraitGoalProvenVia::Misc => {
let candidates =
let mut candidates =
self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
// Prefer "orphaned" param-env normalization predicates, which are used
@ -871,6 +935,13 @@ where
return Ok(response);
}
// We drop specialized impls to allow normalization via a final impl here. In case
// the specializing impl has different inference constraints from the specialized
// impl, proving the trait goal is already ambiguous, so we never get here. This
// means we can just ignore inference constraints and don't have to special-case
// constraining the normalized-to `term`.
self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates);
let responses: Vec<_> = candidates.iter().map(|c| c.result).collect();
if let Some(response) = self.try_merge_responses(&responses) {
Ok(response)

View file

@ -16,6 +16,7 @@ use rustc_type_ir::{
};
use tracing::{instrument, trace};
use super::has_only_region_constraints;
use crate::coherence;
use crate::delegate::SolverDelegate;
use crate::solve::inspect::{self, ProofTreeBuilder};
@ -476,13 +477,8 @@ where
Ok(response) => response,
};
let has_changed = if !response.value.var_values.is_identity_modulo_regions()
|| !response.value.external_constraints.opaque_types.is_empty()
{
HasChanged::Yes
} else {
HasChanged::No
};
let has_changed =
if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No };
let (normalization_nested_goals, certainty) =
self.instantiate_and_apply_query_response(goal.param_env, orig_values, response);

View file

@ -70,6 +70,17 @@ fn has_no_inference_or_external_constraints<I: Interner>(
&& normalization_nested_goals.is_empty()
}
fn has_only_region_constraints<I: Interner>(response: ty::Canonical<I, Response<I>>) -> bool {
let ExternalConstraintsData {
region_constraints: _,
ref opaque_types,
ref normalization_nested_goals,
} = *response.value.external_constraints;
response.value.var_values.is_identity_modulo_regions()
&& opaque_types.is_empty()
&& normalization_nested_goals.is_empty()
}
impl<'a, D, I> EvalCtxt<'a, D>
where
D: SolverDelegate<Interner = I>,

View file

@ -213,9 +213,6 @@ where
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
};
// In case the associated item is hidden due to specialization, we have to
// return ambiguity this would otherwise be incomplete, resulting in
// unsoundness during coherence (#105782).
let target_item_def_id = match ecx.fetch_eligible_assoc_item(
goal_trait_ref,
goal.predicate.def_id(),
@ -223,8 +220,28 @@ where
) {
Ok(Some(target_item_def_id)) => target_item_def_id,
Ok(None) => {
return ecx
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
match ecx.typing_mode() {
// In case the associated item is hidden due to specialization, we have to
// return ambiguity this would otherwise be incomplete, resulting in
// unsoundness during coherence (#105782).
ty::TypingMode::Coherence => {
return ecx.evaluate_added_goals_and_make_canonical_response(
Certainty::AMBIGUOUS,
);
}
// Outside of coherence, we treat the associated item as rigid instead.
ty::TypingMode::Analysis { .. }
| ty::TypingMode::Borrowck { .. }
| ty::TypingMode::PostBorrowckAnalysis { .. }
| ty::TypingMode::PostAnalysis => {
ecx.structurally_instantiate_normalizes_to_term(
goal,
goal.predicate.alias,
);
return ecx
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
}
};
}
Err(guar) => return error_response(ecx, guar),
};

View file

@ -13,7 +13,7 @@ use tracing::{instrument, trace};
use crate::delegate::SolverDelegate;
use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
use crate::solve::assembly::{self, AssembleCandidatesFrom, Candidate};
use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidatesFrom, Candidate};
use crate::solve::inspect::ProbeKind;
use crate::solve::{
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
@ -1338,6 +1338,8 @@ where
};
}
self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates);
// If there are *only* global where bounds, then make sure to return that this
// is still reported as being proven-via the param-env so that rigid projections
// operate correctly. Otherwise, drop all global where-bounds before merging the

View file

@ -286,6 +286,8 @@ pub trait Interner:
fn has_item_definition(self, def_id: Self::DefId) -> bool;
fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool;
fn impl_is_default(self, impl_def_id: Self::DefId) -> bool;
fn impl_trait_ref(self, impl_def_id: Self::DefId) -> ty::EarlyBinder<Self, ty::TraitRef<Self>>;

View file

@ -0,0 +1,12 @@
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/prefer-specializing-impl-over-default.rs:5:12
|
LL | #![feature(specialization)]
| ^^^^^^^^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: consider using `min_specialization` instead, which is more stable and complete
= note: `#[warn(incomplete_features)]` on by default
warning: 1 warning emitted

View file

@ -0,0 +1,12 @@
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/prefer-specializing-impl-over-default.rs:5:12
|
LL | #![feature(specialization)]
| ^^^^^^^^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: consider using `min_specialization` instead, which is more stable and complete
= note: `#[warn(incomplete_features)]` on by default
warning: 1 warning emitted

View file

@ -0,0 +1,29 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@ check-pass
#![feature(specialization)]
//~^ WARN the feature `specialization` is incomplete
trait WithAssoc: 'static {
type Assoc;
}
impl<T: 'static> WithAssoc for (T,) {
type Assoc = ();
}
struct GenericArray<U: WithAssoc>(U::Assoc);
trait AbiExample {
fn example();
}
impl<U: WithAssoc> AbiExample for GenericArray<U> {
fn example() {}
}
impl<T> AbiExample for T {
default fn example() {}
}
fn main() {
let _ = GenericArray::<((),)>::example();
}

View file

@ -1,5 +1,5 @@
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/specialization-default-projection.rs:1:12
--> $DIR/specialization-default-projection.rs:5:12
|
LL | #![feature(specialization)]
| ^^^^^^^^^^^^^^
@ -9,7 +9,7 @@ LL | #![feature(specialization)]
= note: `#[warn(incomplete_features)]` on by default
error[E0308]: mismatched types
--> $DIR/specialization-default-projection.rs:21:5
--> $DIR/specialization-default-projection.rs:25:5
|
LL | fn generic<T>() -> <T as Foo>::Assoc {
| ----------------- expected `<T as Foo>::Assoc` because of return type
@ -23,7 +23,7 @@ LL | ()
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
error[E0308]: mismatched types
--> $DIR/specialization-default-projection.rs:28:5
--> $DIR/specialization-default-projection.rs:32:5
|
LL | fn monomorphic() -> () {
| -- expected `()` because of return type

View file

@ -0,0 +1,43 @@
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/specialization-default-projection.rs:5:12
|
LL | #![feature(specialization)]
| ^^^^^^^^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: consider using `min_specialization` instead, which is more stable and complete
= note: `#[warn(incomplete_features)]` on by default
error[E0308]: mismatched types
--> $DIR/specialization-default-projection.rs:25:5
|
LL | fn generic<T>() -> <T as Foo>::Assoc {
| ----------------- expected `<T as Foo>::Assoc` because of return type
...
LL | ()
| ^^ types differ
|
= note: expected associated type `<T as Foo>::Assoc`
found unit type `()`
= help: consider constraining the associated type `<T as Foo>::Assoc` to `()` or calling a method that returns `<T as Foo>::Assoc`
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
error[E0308]: mismatched types
--> $DIR/specialization-default-projection.rs:32:5
|
LL | fn monomorphic() -> () {
| -- expected `()` because of return type
...
LL | generic::<()>()
| ^^^^^^^^^^^^^^^- help: consider using a semicolon here: `;`
| |
| types differ
|
= note: expected unit type `()`
found associated type `<() as Foo>::Assoc`
= help: consider constraining the associated type `<() as Foo>::Assoc` to `()`
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
error: aborting due to 2 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0308`.

View file

@ -1,3 +1,7 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
#![feature(specialization)] //~ WARN the feature `specialization` is incomplete
// Make sure we can't project defaulted associated types

View file

@ -1,5 +1,5 @@
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/specialization-default-types.rs:5:12
--> $DIR/specialization-default-types.rs:9:12
|
LL | #![feature(specialization)]
| ^^^^^^^^^^^^^^
@ -9,7 +9,7 @@ LL | #![feature(specialization)]
= note: `#[warn(incomplete_features)]` on by default
error[E0308]: mismatched types
--> $DIR/specialization-default-types.rs:15:9
--> $DIR/specialization-default-types.rs:19:9
|
LL | default type Output = Box<T>;
| ----------------------------- associated type is `default` and may be overridden
@ -22,7 +22,7 @@ LL | Box::new(self)
found struct `Box<T>`
error[E0308]: mismatched types
--> $DIR/specialization-default-types.rs:25:5
--> $DIR/specialization-default-types.rs:29:5
|
LL | fn trouble<T>(t: T) -> Box<T> {
| ------ expected `Box<T>` because of return type

View file

@ -0,0 +1,39 @@
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/specialization-default-types.rs:9:12
|
LL | #![feature(specialization)]
| ^^^^^^^^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: consider using `min_specialization` instead, which is more stable and complete
= note: `#[warn(incomplete_features)]` on by default
error[E0308]: mismatched types
--> $DIR/specialization-default-types.rs:19:9
|
LL | default type Output = Box<T>;
| ----------------------------- associated type is `default` and may be overridden
LL | default fn generate(self) -> Self::Output {
| ------------ expected `<T as Example>::Output` because of return type
LL | Box::new(self)
| ^^^^^^^^^^^^^^ types differ
|
= note: expected associated type `<T as Example>::Output`
found struct `Box<T>`
error[E0308]: mismatched types
--> $DIR/specialization-default-types.rs:29:5
|
LL | fn trouble<T>(t: T) -> Box<T> {
| ------ expected `Box<T>` because of return type
LL | Example::generate(t)
| ^^^^^^^^^^^^^^^^^^^^ types differ
|
= note: expected struct `Box<T>`
found associated type `<T as Example>::Output`
= help: consider constraining the associated type `<T as Example>::Output` to `Box<T>`
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
error: aborting due to 2 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0308`.

View file

@ -1,3 +1,7 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
// It should not be possible to use the concrete value of a defaulted
// associated type in the impl defining it -- otherwise, what happens
// if it's overridden?

View file

@ -10,11 +10,8 @@ trait Default {
impl<T> Default for T {
default type Id = T;
// This will be fixed by #111994
fn intu(&self) -> &Self::Id {
//~^ ERROR type annotations needed
//~| ERROR cannot normalize `<T as Default>::Id: '_`
self //~ ERROR cannot satisfy
self //~ ERROR mismatched types
}
}
@ -25,6 +22,7 @@ fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U {
use std::num::NonZero;
fn main() {
let s = transmute::<u8, Option<NonZero<u8>>>(0); //~ ERROR cannot satisfy
let s = transmute::<u8, Option<NonZero<u8>>>(0);
//~^ ERROR type mismatch resolving `<u8 as Default>::Id == Option<NonZero<u8>>`
assert_eq!(s, None);
}

View file

@ -8,37 +8,32 @@ LL | #![feature(specialization)]
= help: consider using `min_specialization` instead, which is more stable and complete
= note: `#[warn(incomplete_features)]` on by default
error: cannot normalize `<T as Default>::Id: '_`
--> $DIR/specialization-transmute.rs:14:5
error[E0308]: mismatched types
--> $DIR/specialization-transmute.rs:14:9
|
LL | fn intu(&self) -> &Self::Id {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0282]: type annotations needed
--> $DIR/specialization-transmute.rs:14:23
|
LL | fn intu(&self) -> &Self::Id {
| ^^^^^^^^^ cannot infer type for reference `&<T as Default>::Id`
error[E0284]: type annotations needed: cannot satisfy `<T as Default>::Id normalizes-to T`
--> $DIR/specialization-transmute.rs:17:9
|
| --------- expected `&<T as Default>::Id` because of return type
LL | self
| ^^^^ cannot satisfy `<T as Default>::Id normalizes-to T`
| ^^^^ types differ
|
= note: expected reference `&<T as Default>::Id`
found reference `&T`
error[E0284]: type annotations needed: cannot satisfy `<u8 as Default>::Id normalizes-to Option<NonZero<u8>>`
--> $DIR/specialization-transmute.rs:28:13
error[E0271]: type mismatch resolving `<u8 as Default>::Id == Option<NonZero<u8>>`
--> $DIR/specialization-transmute.rs:25:50
|
LL | let s = transmute::<u8, Option<NonZero<u8>>>(0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `<u8 as Default>::Id normalizes-to Option<NonZero<u8>>`
| ------------------------------------ ^ types differ
| |
| required by a bound introduced by this call
|
note: required by a bound in `transmute`
--> $DIR/specialization-transmute.rs:21:25
--> $DIR/specialization-transmute.rs:18:25
|
LL | fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U {
| ^^^^^^ required by this bound in `transmute`
error: aborting due to 4 previous errors; 1 warning emitted
error: aborting due to 2 previous errors; 1 warning emitted
Some errors have detailed explanations: E0282, E0284.
For more information about an error, try `rustc --explain E0282`.
Some errors have detailed explanations: E0271, E0308.
For more information about an error, try `rustc --explain E0271`.

View file

@ -18,5 +18,5 @@ fn test<T: Default<Id = U>, U>() {}
fn main() {
test::<u32, ()>();
//~^ ERROR cannot satisfy `<u32 as Default>::Id normalizes-to ()`
//~^ ERROR type mismatch resolving `<u32 as Default>::Id == ()`
}

View file

@ -8,11 +8,11 @@ LL | #![feature(specialization)]
= help: consider using `min_specialization` instead, which is more stable and complete
= note: `#[warn(incomplete_features)]` on by default
error[E0284]: type annotations needed: cannot satisfy `<u32 as Default>::Id normalizes-to ()`
--> $DIR/specialization-unconstrained.rs:20:5
error[E0271]: type mismatch resolving `<u32 as Default>::Id == ()`
--> $DIR/specialization-unconstrained.rs:20:12
|
LL | test::<u32, ()>();
| ^^^^^^^^^^^^^^^^^ cannot satisfy `<u32 as Default>::Id normalizes-to ()`
| ^^^ types differ
|
note: required by a bound in `test`
--> $DIR/specialization-unconstrained.rs:17:20
@ -22,4 +22,4 @@ LL | fn test<T: Default<Id = U>, U>() {}
error: aborting due to 1 previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0284`.
For more information about this error, try `rustc --explain E0271`.