Skip to content

return NoSolution for default assoc items #111994

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

Closed
wants to merge 1 commit into from
Closed
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
13 changes: 12 additions & 1 deletion compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use rustc_middle::ty::{
self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt, TypeVisitor,
};
use rustc_span::DUMMY_SP;
use rustc_span::{ErrorGuaranteed, DUMMY_SP};
use std::ops::ControlFlow;

use crate::traits::specialization_graph;
Expand Down Expand Up @@ -561,6 +561,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
}

pub(super) fn term_error_of_kind(
&self,
kind: ty::Term<'tcx>,
guar: ErrorGuaranteed,
) -> ty::Term<'tcx> {
match kind.unpack() {
ty::TermKind::Ty(_) => self.tcx().ty_error(guar).into(),
ty::TermKind::Const(ct) => self.tcx().const_error(ct.ty(), guar).into(),
}
}

/// Is the projection predicate is of the form `exists<T> <Ty as Trait>::Assoc = T`.
///
/// This is the case if the `term` is an inference variable in the innermost universe
Expand Down
65 changes: 47 additions & 18 deletions compiler/rustc_trait_selection/src/solve/project_goals.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::traits::specialization_graph;

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

// 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 Some(assoc_def) = fetch_eligible_assoc_item_def(
let assoc_def = match fetch_eligible_assoc_item_def(
ecx,
goal.param_env,
goal_trait_ref,
goal.predicate.def_id(),
impl_def_id
)? else {
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
impl_def_id,
) {
Ok(assoc_def) => assoc_def,
Err(NotAvailableReason::ErrorGuaranteed(guar)) => {
let error_term = ecx.term_error_of_kind(goal.predicate.term, guar);
ecx.eq(goal.param_env, goal.predicate.term, error_term)
.expect("expected goal term to be fully unconstrained");
return 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 during coherence as it would otherwise be incomplete,
// resulting in unsoundness (#105782).
//
// Outside of coherence we want to fail here as we want to treat defaulted
// associated items as opaque.
Err(NotAvailableReason::DefaultItem) => match ecx.solver_mode() {
SolverMode::Normal => return Err(NoSolution),
SolverMode::Coherence => {
return ecx.evaluate_added_goals_and_make_canonical_response(
Certainty::AMBIGUOUS,
);
}
},
};

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

#[derive(Debug)]
enum NotAvailableReason {
ErrorGuaranteed(ErrorGuaranteed),
DefaultItem,
}

/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.
///
/// FIXME: We should merge these 3 implementations as it's likely that they otherwise
Expand All @@ -585,26 +608,32 @@ fn fetch_eligible_assoc_item_def<'tcx>(
goal_trait_ref: ty::TraitRef<'tcx>,
trait_assoc_def_id: DefId,
impl_def_id: DefId,
) -> Result<Option<LeafDef>, NoSolution> {
) -> Result<LeafDef, NotAvailableReason> {
let node_item = specialization_graph::assoc_def(ecx.tcx(), impl_def_id, trait_assoc_def_id)
.map_err(|ErrorGuaranteed { .. }| NoSolution)?;
.map_err(NotAvailableReason::ErrorGuaranteed)?;

let eligible = if node_item.is_final() {
if node_item.is_final() {
// Non-specializable items are always projectable.
true
Ok(node_item)
} else {
// Only reveal a specializable default if we're past type-checking
// and the obligation is monomorphic, otherwise passes such as
// transmute checking and polymorphic MIR optimizations could
// get a result which isn't correct for all monomorphizations.
if param_env.reveal() == Reveal::All {
let poly_trait_ref = ecx.resolve_vars_if_possible(goal_trait_ref);
!poly_trait_ref.still_further_specializable()
if poly_trait_ref.still_further_specializable() {
// We'd have to deal with inference variables and return
// ambiguity or something, that's annoying so I am going to
// just ICE here until there's a need to actually implement
// this.
assert!(!poly_trait_ref.has_infer());
Err(NotAvailableReason::DefaultItem)
} else {
Ok(node_item)
}
} else {
debug!(?node_item.item.def_id, "not eligible due to default");
false
Err(NotAvailableReason::DefaultItem)
}
};

if eligible { Ok(Some(node_item)) } else { Ok(None) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// check-pass
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

want a second test:

normalizes-to is NoSolution if there is no specializing impl, projection remains rigid

normalizes-to chooses the specializing impl over the default impl where applicable

// compile-flags: -Ztrait-solver=next
#![feature(specialization)]
#![allow(incomplete_features)]
trait Trait {
type Assoc;
}

impl<T> Trait for T {
default type Assoc = u32;
}

impl Trait for u32 {
type Assoc = u32;
}

fn generic<T: Trait<Assoc = u32>>(_: T) {}

fn main() {
generic(1)
// We want `normalizes-to(<{integer} as Trait>::Assoc, u32)`
// to succeed as there is only one impl that can be used for
// this function to compile, even if the default impl would
// also satisfy this. This is different from coherence where
// doing so would be unsound.
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl<T> Default for T {

fn intu(&self) -> &Self::Id {
self
//~^ ERROR cannot satisfy `T <: <T as Default>::Id`
//~^ ERROR mismatched types
}
}

Expand All @@ -25,6 +25,6 @@ fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U {
use std::num::NonZeroU8;
fn main() {
let s = transmute::<u8, Option<NonZeroU8>>(0);
//~^ ERROR cannot satisfy `<u8 as Default>::Id == Option<NonZeroU8>
//~^ ERROR type mismatch resolving `<u8 as Default>::Id == Option<NonZeroU8>`
assert_eq!(s, None);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,30 @@ 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 `T <: <T as Default>::Id`
error[E0308]: mismatched types
--> $DIR/specialization-transmute.rs:16:9
|
LL | fn intu(&self) -> &Self::Id {
| --------- expected `&<T as Default>::Id` because of return type
LL | self
| ^^^^ cannot satisfy `T <: <T as Default>::Id`
| ^^^^ types differ
|
= note: expected reference `&<T as Default>::Id`
found reference `&T`

error[E0284]: type annotations needed: cannot satisfy `<u8 as Default>::Id == Option<NonZeroU8>`
--> $DIR/specialization-transmute.rs:27:13
error[E0271]: type mismatch resolving `<u8 as Default>::Id == Option<NonZeroU8>`
--> $DIR/specialization-transmute.rs:27:48
|
LL | let s = transmute::<u8, Option<NonZeroU8>>(0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `<u8 as Default>::Id == Option<NonZeroU8>`
| ---------------------------------- ^ type mismatch resolving `<u8 as Default>::Id == Option<NonZeroU8>`
| |
| required by a bound introduced by this call
|
note: types differ
--> $DIR/specialization-transmute.rs:13:22
|
LL | default type Id = T;
| ^
note: required by a bound in `transmute`
--> $DIR/specialization-transmute.rs:21:25
|
Expand All @@ -28,4 +40,5 @@ LL | fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U {

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

For more information about this error, try `rustc --explain E0284`.
Some errors have detailed explanations: E0271, E0308.
For more information about an error, try `rustc --explain E0271`.
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ fn test<T: Default<Id = U>, U>() {}

fn main() {
test::<u32, ()>();
//~^ ERROR cannot satisfy `<u32 as Default>::Id == ()`
//~^ ERROR type mismatch resolving `<u32 as Default>::Id == ()`
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@ 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 == ()`
--> $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 == ()`
| ^^^ type mismatch resolving `<u32 as Default>::Id == ()`
|
note: types differ
--> $DIR/specialization-unconstrained.rs:14:22
|
LL | default type Id = T;
| ^
note: required by a bound in `test`
--> $DIR/specialization-unconstrained.rs:17:20
|
Expand All @@ -22,4 +27,4 @@ LL | fn test<T: Default<Id = U>, U>() {}

error: aborting due to 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`.