Skip to content

Commit 54eae59

Browse files
authored
Rollup merge of rust-lang#140306 - lcnr:specialization-new, r=compiler-errors
handle specialization in the new trait solver fixes rust-lang/trait-system-refactor-initiative#187 also fixes the regression in `plonky2_field` from rust-lang/trait-system-refactor-initiative#188 cc rust-lang#111994 r? ```@compiler-errors```
2 parents f2f4152 + 009db53 commit 54eae59

20 files changed

+290
-51
lines changed

compiler/rustc_middle/src/ty/context.rs

+4
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
590590
self.defaultness(def_id).has_value()
591591
}
592592

593+
fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool {
594+
self.specializes((impl_def_id, victim_def_id))
595+
}
596+
593597
fn impl_is_default(self, impl_def_id: DefId) -> bool {
594598
self.defaultness(impl_def_id).is_default()
595599
}

compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs

+72-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_type_ir::{
1010
};
1111
use tracing::{debug, instrument};
1212

13+
use super::has_only_region_constraints;
1314
use super::trait_goals::TraitGoalProvenVia;
1415
use crate::delegate::SolverDelegate;
1516
use crate::solve::inspect::ProbeKind;
@@ -771,6 +772,69 @@ where
771772
}
772773
})
773774
}
775+
}
776+
777+
pub(super) enum AllowInferenceConstraints {
778+
Yes,
779+
No,
780+
}
781+
782+
impl<D, I> EvalCtxt<'_, D>
783+
where
784+
D: SolverDelegate<Interner = I>,
785+
I: Interner,
786+
{
787+
/// Check whether we can ignore impl candidates due to specialization.
788+
///
789+
/// This is only necessary for `feature(specialization)` and seems quite ugly.
790+
pub(super) fn filter_specialized_impls(
791+
&mut self,
792+
allow_inference_constraints: AllowInferenceConstraints,
793+
candidates: &mut Vec<Candidate<I>>,
794+
) {
795+
match self.typing_mode() {
796+
TypingMode::Coherence => return,
797+
TypingMode::Analysis { .. }
798+
| TypingMode::Borrowck { .. }
799+
| TypingMode::PostBorrowckAnalysis { .. }
800+
| TypingMode::PostAnalysis => {}
801+
}
802+
803+
let mut i = 0;
804+
'outer: while i < candidates.len() {
805+
let CandidateSource::Impl(victim_def_id) = candidates[i].source else {
806+
i += 1;
807+
continue;
808+
};
809+
810+
for (j, c) in candidates.iter().enumerate() {
811+
if i == j {
812+
continue;
813+
}
814+
815+
let CandidateSource::Impl(other_def_id) = c.source else {
816+
continue;
817+
};
818+
819+
// See if we can toss out `victim` based on specialization.
820+
//
821+
// While this requires us to know *for sure* that the `lhs` impl applies
822+
// we still use modulo regions here. This is fine as specialization currently
823+
// assumes that specializing impls have to be always applicable, meaning that
824+
// the only allowed region constraints may be constraints also present on the default impl.
825+
if matches!(allow_inference_constraints, AllowInferenceConstraints::Yes)
826+
|| has_only_region_constraints(c.result)
827+
{
828+
if self.cx().impl_specializes(other_def_id, victim_def_id) {
829+
candidates.remove(i);
830+
continue 'outer;
831+
}
832+
}
833+
}
834+
835+
i += 1;
836+
}
837+
}
774838

775839
/// Assemble and merge candidates for goals which are related to an underlying trait
776840
/// goal. Right now, this is normalizes-to and host effect goals.
@@ -857,7 +921,7 @@ where
857921
}
858922
}
859923
TraitGoalProvenVia::Misc => {
860-
let candidates =
924+
let mut candidates =
861925
self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
862926

863927
// Prefer "orphaned" param-env normalization predicates, which are used
@@ -871,6 +935,13 @@ where
871935
return Ok(response);
872936
}
873937

938+
// We drop specialized impls to allow normalization via a final impl here. In case
939+
// the specializing impl has different inference constraints from the specialized
940+
// impl, proving the trait goal is already ambiguous, so we never get here. This
941+
// means we can just ignore inference constraints and don't have to special-case
942+
// constraining the normalized-to `term`.
943+
self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates);
944+
874945
let responses: Vec<_> = candidates.iter().map(|c| c.result).collect();
875946
if let Some(response) = self.try_merge_responses(&responses) {
876947
Ok(response)

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

+3-7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc_type_ir::{
1616
};
1717
use tracing::{instrument, trace};
1818

19+
use super::has_only_region_constraints;
1920
use crate::coherence;
2021
use crate::delegate::SolverDelegate;
2122
use crate::solve::inspect::{self, ProofTreeBuilder};
@@ -476,13 +477,8 @@ where
476477
Ok(response) => response,
477478
};
478479

479-
let has_changed = if !response.value.var_values.is_identity_modulo_regions()
480-
|| !response.value.external_constraints.opaque_types.is_empty()
481-
{
482-
HasChanged::Yes
483-
} else {
484-
HasChanged::No
485-
};
480+
let has_changed =
481+
if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No };
486482

487483
let (normalization_nested_goals, certainty) =
488484
self.instantiate_and_apply_query_response(goal.param_env, orig_values, response);

compiler/rustc_next_trait_solver/src/solve/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ fn has_no_inference_or_external_constraints<I: Interner>(
7070
&& normalization_nested_goals.is_empty()
7171
}
7272

73+
fn has_only_region_constraints<I: Interner>(response: ty::Canonical<I, Response<I>>) -> bool {
74+
let ExternalConstraintsData {
75+
region_constraints: _,
76+
ref opaque_types,
77+
ref normalization_nested_goals,
78+
} = *response.value.external_constraints;
79+
response.value.var_values.is_identity_modulo_regions()
80+
&& opaque_types.is_empty()
81+
&& normalization_nested_goals.is_empty()
82+
}
83+
7384
impl<'a, D, I> EvalCtxt<'a, D>
7485
where
7586
D: SolverDelegate<Interner = I>,

compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs

+22-5
Original file line numberDiff line numberDiff line change
@@ -213,18 +213,35 @@ where
213213
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
214214
};
215215

216-
// In case the associated item is hidden due to specialization, we have to
217-
// return ambiguity this would otherwise be incomplete, resulting in
218-
// unsoundness during coherence (#105782).
219216
let target_item_def_id = match ecx.fetch_eligible_assoc_item(
220217
goal_trait_ref,
221218
goal.predicate.def_id(),
222219
impl_def_id,
223220
) {
224221
Ok(Some(target_item_def_id)) => target_item_def_id,
225222
Ok(None) => {
226-
return ecx
227-
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
223+
match ecx.typing_mode() {
224+
// In case the associated item is hidden due to specialization, we have to
225+
// return ambiguity this would otherwise be incomplete, resulting in
226+
// unsoundness during coherence (#105782).
227+
ty::TypingMode::Coherence => {
228+
return ecx.evaluate_added_goals_and_make_canonical_response(
229+
Certainty::AMBIGUOUS,
230+
);
231+
}
232+
// Outside of coherence, we treat the associated item as rigid instead.
233+
ty::TypingMode::Analysis { .. }
234+
| ty::TypingMode::Borrowck { .. }
235+
| ty::TypingMode::PostBorrowckAnalysis { .. }
236+
| ty::TypingMode::PostAnalysis => {
237+
ecx.structurally_instantiate_normalizes_to_term(
238+
goal,
239+
goal.predicate.alias,
240+
);
241+
return ecx
242+
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
243+
}
244+
};
228245
}
229246
Err(guar) => return error_response(ecx, guar),
230247
};

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use tracing::{instrument, trace};
1313

1414
use crate::delegate::SolverDelegate;
1515
use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
16-
use crate::solve::assembly::{self, AssembleCandidatesFrom, Candidate};
16+
use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidatesFrom, Candidate};
1717
use crate::solve::inspect::ProbeKind;
1818
use crate::solve::{
1919
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
@@ -1338,6 +1338,8 @@ where
13381338
};
13391339
}
13401340

1341+
self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates);
1342+
13411343
// If there are *only* global where bounds, then make sure to return that this
13421344
// is still reported as being proven-via the param-env so that rigid projections
13431345
// operate correctly. Otherwise, drop all global where-bounds before merging the

compiler/rustc_type_ir/src/interner.rs

+2
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@ pub trait Interner:
286286

287287
fn has_item_definition(self, def_id: Self::DefId) -> bool;
288288

289+
fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool;
290+
289291
fn impl_is_default(self, impl_def_id: Self::DefId) -> bool;
290292

291293
fn impl_trait_ref(self, impl_def_id: Self::DefId) -> ty::EarlyBinder<Self, ty::TraitRef<Self>>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/prefer-specializing-impl-over-default.rs:5:12
3+
|
4+
LL | #![feature(specialization)]
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
8+
= help: consider using `min_specialization` instead, which is more stable and complete
9+
= note: `#[warn(incomplete_features)]` on by default
10+
11+
warning: 1 warning emitted
12+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/prefer-specializing-impl-over-default.rs:5:12
3+
|
4+
LL | #![feature(specialization)]
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
8+
= help: consider using `min_specialization` instead, which is more stable and complete
9+
= note: `#[warn(incomplete_features)]` on by default
10+
11+
warning: 1 warning emitted
12+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//@ revisions: current next
2+
//@ ignore-compare-mode-next-solver (explicit revisions)
3+
//@[next] compile-flags: -Znext-solver
4+
//@ check-pass
5+
#![feature(specialization)]
6+
//~^ WARN the feature `specialization` is incomplete
7+
8+
trait WithAssoc: 'static {
9+
type Assoc;
10+
}
11+
impl<T: 'static> WithAssoc for (T,) {
12+
type Assoc = ();
13+
}
14+
15+
struct GenericArray<U: WithAssoc>(U::Assoc);
16+
17+
trait AbiExample {
18+
fn example();
19+
}
20+
impl<U: WithAssoc> AbiExample for GenericArray<U> {
21+
fn example() {}
22+
}
23+
impl<T> AbiExample for T {
24+
default fn example() {}
25+
}
26+
27+
fn main() {
28+
let _ = GenericArray::<((),)>::example();
29+
}

tests/ui/specialization/specialization-default-projection.stderr renamed to tests/ui/specialization/specialization-default-projection.current.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
2-
--> $DIR/specialization-default-projection.rs:1:12
2+
--> $DIR/specialization-default-projection.rs:5:12
33
|
44
LL | #![feature(specialization)]
55
| ^^^^^^^^^^^^^^
@@ -9,7 +9,7 @@ LL | #![feature(specialization)]
99
= note: `#[warn(incomplete_features)]` on by default
1010

1111
error[E0308]: mismatched types
12-
--> $DIR/specialization-default-projection.rs:21:5
12+
--> $DIR/specialization-default-projection.rs:25:5
1313
|
1414
LL | fn generic<T>() -> <T as Foo>::Assoc {
1515
| ----------------- expected `<T as Foo>::Assoc` because of return type
@@ -23,7 +23,7 @@ LL | ()
2323
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
2424

2525
error[E0308]: mismatched types
26-
--> $DIR/specialization-default-projection.rs:28:5
26+
--> $DIR/specialization-default-projection.rs:32:5
2727
|
2828
LL | fn monomorphic() -> () {
2929
| -- expected `()` because of return type
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/specialization-default-projection.rs:5:12
3+
|
4+
LL | #![feature(specialization)]
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
8+
= help: consider using `min_specialization` instead, which is more stable and complete
9+
= note: `#[warn(incomplete_features)]` on by default
10+
11+
error[E0308]: mismatched types
12+
--> $DIR/specialization-default-projection.rs:25:5
13+
|
14+
LL | fn generic<T>() -> <T as Foo>::Assoc {
15+
| ----------------- expected `<T as Foo>::Assoc` because of return type
16+
...
17+
LL | ()
18+
| ^^ types differ
19+
|
20+
= note: expected associated type `<T as Foo>::Assoc`
21+
found unit type `()`
22+
= help: consider constraining the associated type `<T as Foo>::Assoc` to `()` or calling a method that returns `<T as Foo>::Assoc`
23+
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
24+
25+
error[E0308]: mismatched types
26+
--> $DIR/specialization-default-projection.rs:32:5
27+
|
28+
LL | fn monomorphic() -> () {
29+
| -- expected `()` because of return type
30+
...
31+
LL | generic::<()>()
32+
| ^^^^^^^^^^^^^^^- help: consider using a semicolon here: `;`
33+
| |
34+
| types differ
35+
|
36+
= note: expected unit type `()`
37+
found associated type `<() as Foo>::Assoc`
38+
= help: consider constraining the associated type `<() as Foo>::Assoc` to `()`
39+
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
40+
41+
error: aborting due to 2 previous errors; 1 warning emitted
42+
43+
For more information about this error, try `rustc --explain E0308`.

tests/ui/specialization/specialization-default-projection.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
//@ revisions: current next
2+
//@ ignore-compare-mode-next-solver (explicit revisions)
3+
//@[next] compile-flags: -Znext-solver
4+
15
#![feature(specialization)] //~ WARN the feature `specialization` is incomplete
26

37
// Make sure we can't project defaulted associated types

tests/ui/specialization/specialization-default-types.stderr renamed to tests/ui/specialization/specialization-default-types.current.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
2-
--> $DIR/specialization-default-types.rs:5:12
2+
--> $DIR/specialization-default-types.rs:9:12
33
|
44
LL | #![feature(specialization)]
55
| ^^^^^^^^^^^^^^
@@ -9,7 +9,7 @@ LL | #![feature(specialization)]
99
= note: `#[warn(incomplete_features)]` on by default
1010

1111
error[E0308]: mismatched types
12-
--> $DIR/specialization-default-types.rs:15:9
12+
--> $DIR/specialization-default-types.rs:19:9
1313
|
1414
LL | default type Output = Box<T>;
1515
| ----------------------------- associated type is `default` and may be overridden
@@ -22,7 +22,7 @@ LL | Box::new(self)
2222
found struct `Box<T>`
2323

2424
error[E0308]: mismatched types
25-
--> $DIR/specialization-default-types.rs:25:5
25+
--> $DIR/specialization-default-types.rs:29:5
2626
|
2727
LL | fn trouble<T>(t: T) -> Box<T> {
2828
| ------ expected `Box<T>` because of return type

0 commit comments

Comments
 (0)