diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 6aca40b8dbd98..f0ae4dfdeb0fb 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -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; @@ -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 ::Assoc = T`. /// /// This is the case if the `term` is an inference variable in the innermost universe diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index cbea8009f0248..f65c6892b6e5a 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -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; @@ -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() { @@ -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 @@ -585,13 +608,13 @@ fn fetch_eligible_assoc_item_def<'tcx>( goal_trait_ref: ty::TraitRef<'tcx>, trait_assoc_def_id: DefId, impl_def_id: DefId, -) -> Result, NoSolution> { +) -> Result { 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 @@ -599,12 +622,18 @@ fn fetch_eligible_assoc_item_def<'tcx>( // 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) } + } } diff --git a/tests/ui/traits/new-solver/specialization/specialization-default-no-solution.rs b/tests/ui/traits/new-solver/specialization/specialization-default-no-solution.rs new file mode 100644 index 0000000000000..aa8ab863c70ce --- /dev/null +++ b/tests/ui/traits/new-solver/specialization/specialization-default-no-solution.rs @@ -0,0 +1,26 @@ +// check-pass +// compile-flags: -Ztrait-solver=next +#![feature(specialization)] +#![allow(incomplete_features)] +trait Trait { + type Assoc; +} + +impl Trait for T { + default type Assoc = u32; +} + +impl Trait for u32 { + type Assoc = u32; +} + +fn generic>(_: 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. +} diff --git a/tests/ui/traits/new-solver/specialization-transmute.rs b/tests/ui/traits/new-solver/specialization/specialization-transmute.rs similarity index 78% rename from tests/ui/traits/new-solver/specialization-transmute.rs rename to tests/ui/traits/new-solver/specialization/specialization-transmute.rs index a54701df4ef7e..83a2592741038 100644 --- a/tests/ui/traits/new-solver/specialization-transmute.rs +++ b/tests/ui/traits/new-solver/specialization/specialization-transmute.rs @@ -14,7 +14,7 @@ impl Default for T { fn intu(&self) -> &Self::Id { self - //~^ ERROR cannot satisfy `T <: ::Id` + //~^ ERROR mismatched types } } @@ -25,6 +25,6 @@ fn transmute, U: Copy>(t: T) -> U { use std::num::NonZeroU8; fn main() { let s = transmute::>(0); - //~^ ERROR cannot satisfy `::Id == Option + //~^ ERROR type mismatch resolving `::Id == Option` assert_eq!(s, None); } diff --git a/tests/ui/traits/new-solver/specialization-transmute.stderr b/tests/ui/traits/new-solver/specialization/specialization-transmute.stderr similarity index 51% rename from tests/ui/traits/new-solver/specialization-transmute.stderr rename to tests/ui/traits/new-solver/specialization/specialization-transmute.stderr index e67c56afc0d0c..2e3dde3126795 100644 --- a/tests/ui/traits/new-solver/specialization-transmute.stderr +++ b/tests/ui/traits/new-solver/specialization/specialization-transmute.stderr @@ -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 <: ::Id` +error[E0308]: mismatched types --> $DIR/specialization-transmute.rs:16:9 | +LL | fn intu(&self) -> &Self::Id { + | --------- expected `&::Id` because of return type LL | self - | ^^^^ cannot satisfy `T <: ::Id` + | ^^^^ types differ + | + = note: expected reference `&::Id` + found reference `&T` -error[E0284]: type annotations needed: cannot satisfy `::Id == Option` - --> $DIR/specialization-transmute.rs:27:13 +error[E0271]: type mismatch resolving `::Id == Option` + --> $DIR/specialization-transmute.rs:27:48 | LL | let s = transmute::>(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `::Id == Option` + | ---------------------------------- ^ type mismatch resolving `::Id == Option` + | | + | 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 | @@ -28,4 +40,5 @@ LL | fn transmute, 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`. diff --git a/tests/ui/traits/new-solver/specialization-unconstrained.rs b/tests/ui/traits/new-solver/specialization/specialization-unconstrained.rs similarity index 85% rename from tests/ui/traits/new-solver/specialization-unconstrained.rs rename to tests/ui/traits/new-solver/specialization/specialization-unconstrained.rs index 02150689ee5c3..0062ef982afb1 100644 --- a/tests/ui/traits/new-solver/specialization-unconstrained.rs +++ b/tests/ui/traits/new-solver/specialization/specialization-unconstrained.rs @@ -18,5 +18,5 @@ fn test, U>() {} fn main() { test::(); - //~^ ERROR cannot satisfy `::Id == ()` + //~^ ERROR type mismatch resolving `::Id == ()` } diff --git a/tests/ui/traits/new-solver/specialization-unconstrained.stderr b/tests/ui/traits/new-solver/specialization/specialization-unconstrained.stderr similarity index 66% rename from tests/ui/traits/new-solver/specialization-unconstrained.stderr rename to tests/ui/traits/new-solver/specialization/specialization-unconstrained.stderr index 910925cbaeb0d..1c161b0dc6dd5 100644 --- a/tests/ui/traits/new-solver/specialization-unconstrained.stderr +++ b/tests/ui/traits/new-solver/specialization/specialization-unconstrained.stderr @@ -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 `::Id == ()` - --> $DIR/specialization-unconstrained.rs:20:5 +error[E0271]: type mismatch resolving `::Id == ()` + --> $DIR/specialization-unconstrained.rs:20:12 | LL | test::(); - | ^^^^^^^^^^^^^^^ cannot satisfy `::Id == ()` + | ^^^ type mismatch resolving `::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 | @@ -22,4 +27,4 @@ LL | fn test, 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`.