Skip to content

Commit be4a817

Browse files
committed
return NoSolution for default assoc items
still return ambiguity in coherence as this would otherwise be unsound.
1 parent b5e51db commit be4a817

File tree

7 files changed

+116
-32
lines changed

7 files changed

+116
-32
lines changed

compiler/rustc_trait_selection/src/solve/eval_ctxt.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use rustc_middle::ty::{
1919
self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
2020
TypeVisitableExt, TypeVisitor,
2121
};
22-
use rustc_span::DUMMY_SP;
22+
use rustc_span::{ErrorGuaranteed, DUMMY_SP};
2323
use std::ops::ControlFlow;
2424

2525
use crate::traits::specialization_graph;
@@ -561,6 +561,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
561561
}
562562
}
563563

564+
pub(super) fn term_error_of_kind(
565+
&self,
566+
kind: ty::Term<'tcx>,
567+
guar: ErrorGuaranteed,
568+
) -> ty::Term<'tcx> {
569+
match kind.unpack() {
570+
ty::TermKind::Ty(_) => self.tcx().ty_error(guar).into(),
571+
ty::TermKind::Const(ct) => self.tcx().const_error(ct.ty(), guar).into(),
572+
}
573+
}
574+
564575
/// Is the projection predicate is of the form `exists<T> <Ty as Trait>::Assoc = T`.
565576
///
566577
/// This is the case if the `term` is an inference variable in the innermost universe

compiler/rustc_trait_selection/src/solve/project_goals.rs

+47-18
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::traits::specialization_graph;
22

33
use super::assembly::{self, structural_traits};
4-
use super::EvalCtxt;
4+
use super::{EvalCtxt, SolverMode};
55
use rustc_errors::ErrorGuaranteed;
66
use rustc_hir::def::DefKind;
77
use rustc_hir::def_id::DefId;
@@ -167,17 +167,34 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
167167
.map(|pred| goal.with(tcx, pred));
168168
ecx.add_goals(where_clause_bounds);
169169

170-
// In case the associated item is hidden due to specialization, we have to
171-
// return ambiguity this would otherwise be incomplete, resulting in
172-
// unsoundness during coherence (#105782).
173-
let Some(assoc_def) = fetch_eligible_assoc_item_def(
170+
let assoc_def = match fetch_eligible_assoc_item_def(
174171
ecx,
175172
goal.param_env,
176173
goal_trait_ref,
177174
goal.predicate.def_id(),
178-
impl_def_id
179-
)? else {
180-
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
175+
impl_def_id,
176+
) {
177+
Ok(assoc_def) => assoc_def,
178+
Err(NotAvailableReason::ErrorGuaranteed(guar)) => {
179+
let error_term = ecx.term_error_of_kind(goal.predicate.term, guar);
180+
ecx.eq(goal.param_env, goal.predicate.term, error_term)
181+
.expect("expected goal term to be fully unconstrained");
182+
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
183+
}
184+
// In case the associated item is hidden due to specialization, we have to
185+
// return ambiguity during coherence as it would otherwise be incomplete,
186+
// resulting in unsoundness (#105782).
187+
//
188+
// Outside of coherence we want to fail here as we want to treat defaulted
189+
// associated items as opaque.
190+
Err(NotAvailableReason::DefaultItem) => match ecx.solver_mode() {
191+
SolverMode::Normal => return Err(NoSolution),
192+
SolverMode::Coherence => {
193+
return ecx.evaluate_added_goals_and_make_canonical_response(
194+
Certainty::AMBIGUOUS,
195+
);
196+
}
197+
},
181198
};
182199

183200
if !assoc_def.item.defaultness(tcx).has_value() {
@@ -574,6 +591,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
574591
}
575592
}
576593

594+
#[derive(Debug)]
595+
enum NotAvailableReason {
596+
ErrorGuaranteed(ErrorGuaranteed),
597+
DefaultItem,
598+
}
599+
577600
/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.
578601
///
579602
/// FIXME: We should merge these 3 implementations as it's likely that they otherwise
@@ -585,26 +608,32 @@ fn fetch_eligible_assoc_item_def<'tcx>(
585608
goal_trait_ref: ty::TraitRef<'tcx>,
586609
trait_assoc_def_id: DefId,
587610
impl_def_id: DefId,
588-
) -> Result<Option<LeafDef>, NoSolution> {
611+
) -> Result<LeafDef, NotAvailableReason> {
589612
let node_item = specialization_graph::assoc_def(ecx.tcx(), impl_def_id, trait_assoc_def_id)
590-
.map_err(|ErrorGuaranteed { .. }| NoSolution)?;
613+
.map_err(NotAvailableReason::ErrorGuaranteed)?;
591614

592-
let eligible = if node_item.is_final() {
615+
if node_item.is_final() {
593616
// Non-specializable items are always projectable.
594-
true
617+
Ok(node_item)
595618
} else {
596619
// Only reveal a specializable default if we're past type-checking
597620
// and the obligation is monomorphic, otherwise passes such as
598621
// transmute checking and polymorphic MIR optimizations could
599622
// get a result which isn't correct for all monomorphizations.
600623
if param_env.reveal() == Reveal::All {
601624
let poly_trait_ref = ecx.resolve_vars_if_possible(goal_trait_ref);
602-
!poly_trait_ref.still_further_specializable()
625+
if poly_trait_ref.still_further_specializable() {
626+
// We'd have to deal with inference variables and return
627+
// ambiguity or something, that's annoying so I am going to
628+
// just ICE here until there's a need to actually implement
629+
// this.
630+
assert!(!poly_trait_ref.has_infer());
631+
Err(NotAvailableReason::DefaultItem)
632+
} else {
633+
Ok(node_item)
634+
}
603635
} else {
604-
debug!(?node_item.item.def_id, "not eligible due to default");
605-
false
636+
Err(NotAvailableReason::DefaultItem)
606637
}
607-
};
608-
609-
if eligible { Ok(Some(node_item)) } else { Ok(None) }
638+
}
610639
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// check-pass
2+
// compile-flags: -Ztrait-solver=next
3+
#![feature(specialization)]
4+
#![allow(incomplete_features)]
5+
trait Trait {
6+
type Assoc;
7+
}
8+
9+
impl<T> Trait for T {
10+
default type Assoc = u32;
11+
}
12+
13+
impl Trait for u32 {
14+
type Assoc = u32;
15+
}
16+
17+
fn generic<T: Trait<Assoc = u32>>(_: T) {}
18+
19+
fn main() {
20+
generic(1)
21+
// We want `normalizes-to(<{integer} as Trait>::Assoc, u32)`
22+
// to succeed as there is only one impl that can be used for
23+
// this function to compile, even if the default impl would
24+
// also satisfy this. This is different from coherence where
25+
// doing so would be unsound.
26+
}

tests/ui/traits/new-solver/specialization-transmute.rs renamed to tests/ui/traits/new-solver/specialization/specialization-transmute.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ impl<T> Default for T {
1414

1515
fn intu(&self) -> &Self::Id {
1616
self
17-
//~^ ERROR cannot satisfy `T <: <T as Default>::Id`
17+
//~^ ERROR mismatched types
1818
}
1919
}
2020

@@ -25,6 +25,6 @@ fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U {
2525
use std::num::NonZeroU8;
2626
fn main() {
2727
let s = transmute::<u8, Option<NonZeroU8>>(0);
28-
//~^ ERROR cannot satisfy `<u8 as Default>::Id == Option<NonZeroU8>
28+
//~^ ERROR type mismatch resolving `<u8 as Default>::Id == Option<NonZeroU8>`
2929
assert_eq!(s, None);
3030
}

tests/ui/traits/new-solver/specialization-transmute.stderr renamed to tests/ui/traits/new-solver/specialization/specialization-transmute.stderr

+19-6
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,30 @@ LL | #![feature(specialization)]
88
= help: consider using `min_specialization` instead, which is more stable and complete
99
= note: `#[warn(incomplete_features)]` on by default
1010

11-
error[E0284]: type annotations needed: cannot satisfy `T <: <T as Default>::Id`
11+
error[E0308]: mismatched types
1212
--> $DIR/specialization-transmute.rs:16:9
1313
|
14+
LL | fn intu(&self) -> &Self::Id {
15+
| --------- expected `&<T as Default>::Id` because of return type
1416
LL | self
15-
| ^^^^ cannot satisfy `T <: <T as Default>::Id`
17+
| ^^^^ types differ
18+
|
19+
= note: expected reference `&<T as Default>::Id`
20+
found reference `&T`
1621

17-
error[E0284]: type annotations needed: cannot satisfy `<u8 as Default>::Id == Option<NonZeroU8>`
18-
--> $DIR/specialization-transmute.rs:27:13
22+
error[E0271]: type mismatch resolving `<u8 as Default>::Id == Option<NonZeroU8>`
23+
--> $DIR/specialization-transmute.rs:27:48
1924
|
2025
LL | let s = transmute::<u8, Option<NonZeroU8>>(0);
21-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `<u8 as Default>::Id == Option<NonZeroU8>`
26+
| ---------------------------------- ^ type mismatch resolving `<u8 as Default>::Id == Option<NonZeroU8>`
27+
| |
28+
| required by a bound introduced by this call
29+
|
30+
note: types differ
31+
--> $DIR/specialization-transmute.rs:13:22
2232
|
33+
LL | default type Id = T;
34+
| ^
2335
note: required by a bound in `transmute`
2436
--> $DIR/specialization-transmute.rs:21:25
2537
|
@@ -28,4 +40,5 @@ LL | fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U {
2840

2941
error: aborting due to 2 previous errors; 1 warning emitted
3042

31-
For more information about this error, try `rustc --explain E0284`.
43+
Some errors have detailed explanations: E0271, E0308.
44+
For more information about an error, try `rustc --explain E0271`.

tests/ui/traits/new-solver/specialization-unconstrained.rs renamed to tests/ui/traits/new-solver/specialization/specialization-unconstrained.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ fn test<T: Default<Id = U>, U>() {}
1818

1919
fn main() {
2020
test::<u32, ()>();
21-
//~^ ERROR cannot satisfy `<u32 as Default>::Id == ()`
21+
//~^ ERROR type mismatch resolving `<u32 as Default>::Id == ()`
2222
}

tests/ui/traits/new-solver/specialization-unconstrained.stderr renamed to tests/ui/traits/new-solver/specialization/specialization-unconstrained.stderr

+9-4
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,17 @@ LL | #![feature(specialization)]
88
= help: consider using `min_specialization` instead, which is more stable and complete
99
= note: `#[warn(incomplete_features)]` on by default
1010

11-
error[E0284]: type annotations needed: cannot satisfy `<u32 as Default>::Id == ()`
12-
--> $DIR/specialization-unconstrained.rs:20:5
11+
error[E0271]: type mismatch resolving `<u32 as Default>::Id == ()`
12+
--> $DIR/specialization-unconstrained.rs:20:12
1313
|
1414
LL | test::<u32, ()>();
15-
| ^^^^^^^^^^^^^^^ cannot satisfy `<u32 as Default>::Id == ()`
15+
| ^^^ type mismatch resolving `<u32 as Default>::Id == ()`
1616
|
17+
note: types differ
18+
--> $DIR/specialization-unconstrained.rs:14:22
19+
|
20+
LL | default type Id = T;
21+
| ^
1722
note: required by a bound in `test`
1823
--> $DIR/specialization-unconstrained.rs:17:20
1924
|
@@ -22,4 +27,4 @@ LL | fn test<T: Default<Id = U>, U>() {}
2227

2328
error: aborting due to previous error; 1 warning emitted
2429

25-
For more information about this error, try `rustc --explain E0284`.
30+
For more information about this error, try `rustc --explain E0271`.

0 commit comments

Comments
 (0)