diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index b17a489a857db..91484ef99dbb8 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -1,13 +1,15 @@ use rustc_hir as hir; -use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt}; +use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes}; use rustc_infer::traits::{ImplSource, Obligation, PredicateObligation}; use rustc_middle::span_bug; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{self, TypingMode}; +use rustc_type_ir::elaborate::elaborate; use rustc_type_ir::solve::NoSolution; -use thin_vec::ThinVec; +use thin_vec::{ThinVec, thin_vec}; use super::SelectionContext; +use super::normalize::normalize_with_depth_to; pub type HostEffectObligation<'tcx> = Obligation<'tcx, ty::HostEffectPredicate<'tcx>>; @@ -38,6 +40,12 @@ pub fn evaluate_host_effect_obligation<'tcx>( Err(EvaluationFailure::NoSolution) => {} } + match evaluate_host_effect_from_item_bounds(selcx, obligation) { + Ok(result) => return Ok(result), + Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), + Err(EvaluationFailure::NoSolution) => {} + } + match evaluate_host_effect_from_selection_candiate(selcx, obligation) { Ok(result) => return Ok(result), Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), @@ -48,24 +56,45 @@ pub fn evaluate_host_effect_obligation<'tcx>( } fn match_candidate<'tcx>( - infcx: &InferCtxt<'tcx>, + selcx: &mut SelectionContext<'_, 'tcx>, obligation: &HostEffectObligation<'tcx>, candidate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, + candidate_is_unnormalized: bool, + more_nested: impl FnOnce(&mut SelectionContext<'_, 'tcx>, &mut ThinVec>), ) -> Result>, NoSolution> { if !candidate.skip_binder().constness.satisfies(obligation.predicate.constness) { return Err(NoSolution); } - let candidate = infcx.instantiate_binder_with_fresh_vars( + let mut candidate = selcx.infcx.instantiate_binder_with_fresh_vars( obligation.cause.span, BoundRegionConversionTime::HigherRankedType, candidate, ); - let mut nested = infcx - .at(&obligation.cause, obligation.param_env) - .eq(DefineOpaqueTypes::Yes, obligation.predicate.trait_ref, candidate.trait_ref)? - .into_obligations(); + let mut nested = thin_vec![]; + + // Unlike param-env bounds, item bounds may not be normalized. + if candidate_is_unnormalized { + candidate = normalize_with_depth_to( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth, + candidate, + &mut nested, + ); + } + + nested.extend( + selcx + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(DefineOpaqueTypes::Yes, obligation.predicate.trait_ref, candidate.trait_ref)? + .into_obligations(), + ); + + more_nested(selcx, &mut nested); for nested in &mut nested { nested.set_depth_from_parent(obligation.recursion_depth); @@ -82,36 +111,116 @@ fn evaluate_host_effect_from_bounds<'tcx>( let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx()); let mut candidate = None; - for predicate in obligation.param_env.caller_bounds() { - let bound_predicate = predicate.kind(); - if let ty::ClauseKind::HostEffect(data) = predicate.kind().skip_binder() { - let data = bound_predicate.rebind(data); - if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id { - continue; + for clause in obligation.param_env.caller_bounds() { + let bound_clause = clause.kind(); + let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else { + continue; + }; + let data = bound_clause.rebind(data); + if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id { + continue; + } + + if !drcx + .args_may_unify(obligation.predicate.trait_ref.args, data.skip_binder().trait_ref.args) + { + continue; + } + + let is_match = + infcx.probe(|_| match_candidate(selcx, obligation, data, false, |_, _| {}).is_ok()); + + if is_match { + if candidate.is_some() { + return Err(EvaluationFailure::Ambiguous); + } else { + candidate = Some(data); } + } + } - if !drcx.args_may_unify( - obligation.predicate.trait_ref.args, - data.skip_binder().trait_ref.args, + if let Some(data) = candidate { + Ok(match_candidate(selcx, obligation, data, false, |_, _| {}) + .expect("candidate matched before, so it should match again")) + } else { + Err(EvaluationFailure::NoSolution) + } +} + +fn evaluate_host_effect_from_item_bounds<'tcx>( + selcx: &mut SelectionContext<'_, 'tcx>, + obligation: &HostEffectObligation<'tcx>, +) -> Result>, EvaluationFailure> { + let infcx = selcx.infcx; + let tcx = infcx.tcx; + let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx()); + let mut candidate = None; + + let mut consider_ty = obligation.predicate.self_ty(); + while let ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) = *consider_ty.kind() { + if tcx.is_conditionally_const(alias_ty.def_id) { + for clause in elaborate( + tcx, + tcx.explicit_implied_const_bounds(alias_ty.def_id) + .iter_instantiated_copied(tcx, alias_ty.args) + .map(|(trait_ref, _)| { + trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness) + }), ) { - continue; - } + let bound_clause = clause.kind(); + let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else { + unreachable!("should not elaborate non-HostEffect from HostEffect") + }; + let data = bound_clause.rebind(data); + if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id { + continue; + } - let is_match = infcx.probe(|_| match_candidate(infcx, obligation, data).is_ok()); + if !drcx.args_may_unify( + obligation.predicate.trait_ref.args, + data.skip_binder().trait_ref.args, + ) { + continue; + } - if is_match { - if candidate.is_some() { - return Err(EvaluationFailure::Ambiguous); - } else { - candidate = Some(data); + let is_match = infcx + .probe(|_| match_candidate(selcx, obligation, data, true, |_, _| {}).is_ok()); + + if is_match { + if candidate.is_some() { + return Err(EvaluationFailure::Ambiguous); + } else { + candidate = Some((data, alias_ty)); + } } } } + + if kind != ty::Projection { + break; + } + + consider_ty = alias_ty.self_ty(); } - if let Some(data) = candidate { - Ok(match_candidate(infcx, obligation, data) - .expect("candidate matched before, so it should match again")) + if let Some((data, alias_ty)) = candidate { + Ok(match_candidate(selcx, obligation, data, true, |selcx, nested| { + // An alias bound only holds if we also check the const conditions + // of the alias, so we need to register those, too. + let const_conditions = normalize_with_depth_to( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth, + tcx.const_conditions(alias_ty.def_id).instantiate(tcx, alias_ty.args), + nested, + ); + nested.extend(const_conditions.into_iter().map(|(trait_ref, _)| { + obligation + .with(tcx, trait_ref.to_host_effect_clause(tcx, obligation.predicate.constness)) + })); + }) + .expect("candidate matched before, so it should match again")) } else { Err(EvaluationFailure::NoSolution) } diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-0.rs b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-0.rs index bac7ee023f479..9141d327aee87 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-0.rs +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-0.rs @@ -1,4 +1,5 @@ -//@ compile-flags: -Znext-solver +//@ revisions: current next +//@[next] compile-flags: -Znext-solver //@ check-pass #![feature(const_trait_impl)] diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-1.rs b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-1.rs index a0375cda079a7..19e86b50d3321 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-1.rs +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-1.rs @@ -1,5 +1,4 @@ -//@ compile-flags: -Znext-solver -//@ known-bug: unknown +//@ check-pass #![feature(const_trait_impl, generic_const_exprs)] #![allow(incomplete_features)] diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-1.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-1.stderr deleted file mode 100644 index 8d1c85c0c8aa0..0000000000000 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-1.stderr +++ /dev/null @@ -1,35 +0,0 @@ -error: `-Znext-solver=globally` and `generic_const_exprs` are incompatible, using them at the same time is not allowed - --> $DIR/assoc-type-const-bound-usage-1.rs:4:30 - | -LL | #![feature(const_trait_impl, generic_const_exprs)] - | ^^^^^^^^^^^^^^^^^^^ - | - = help: remove one of these features - -error[E0284]: type annotations needed: cannot normalize `unqualified::{constant#0}` - --> $DIR/assoc-type-const-bound-usage-1.rs:15:37 - | -LL | fn unqualified() -> Type<{ T::Assoc::func() }> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `unqualified::{constant#0}` - -error[E0284]: type annotations needed: cannot normalize `qualified::{constant#0}` - --> $DIR/assoc-type-const-bound-usage-1.rs:19:35 - | -LL | fn qualified() -> Type<{ ::Assoc::func() }> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `qualified::{constant#0}` - -error[E0284]: type annotations needed: cannot normalize `unqualified::{constant#0}` - --> $DIR/assoc-type-const-bound-usage-1.rs:16:5 - | -LL | Type - | ^^^^ cannot normalize `unqualified::{constant#0}` - -error[E0284]: type annotations needed: cannot normalize `qualified::{constant#0}` - --> $DIR/assoc-type-const-bound-usage-1.rs:20:5 - | -LL | Type - | ^^^^ cannot normalize `qualified::{constant#0}` - -error: aborting due to 5 previous errors - -For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.current.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.current.stderr new file mode 100644 index 0000000000000..03da9159bea0b --- /dev/null +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.current.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `U: ~const Other` is not satisfied + --> $DIR/assoc-type-const-bound-usage-fail-2.rs:24:5 + | +LL | T::Assoc::::func(); + | ^^^^^^^^^^^^^ + +error[E0277]: the trait bound `U: ~const Other` is not satisfied + --> $DIR/assoc-type-const-bound-usage-fail-2.rs:27:5 + | +LL | ::Assoc::::func(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr similarity index 79% rename from tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.stderr rename to tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr index c7af0a220ca31..ce58b486a16ef 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.stderr +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr @@ -1,11 +1,11 @@ error[E0277]: the trait bound `::Assoc: ~const Trait` is not satisfied - --> $DIR/assoc-type-const-bound-usage-fail-2.rs:23:5 + --> $DIR/assoc-type-const-bound-usage-fail-2.rs:24:5 | LL | T::Assoc::::func(); | ^^^^^^^^^^^^^ error[E0277]: the trait bound `::Assoc: ~const Trait` is not satisfied - --> $DIR/assoc-type-const-bound-usage-fail-2.rs:25:5 + --> $DIR/assoc-type-const-bound-usage-fail-2.rs:27:5 | LL | ::Assoc::::func(); | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs index b3a636b0f713d..bdd98eaf541f8 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs @@ -1,4 +1,5 @@ -//@ compile-flags: -Znext-solver +//@ revisions: current next +//@[next] compile-flags: -Znext-solver // Check that `~const` item bounds only hold if the where clauses on the // associated type are also const. @@ -21,9 +22,11 @@ trait Other {} const fn fails() { T::Assoc::::func(); - //~^ ERROR the trait bound `::Assoc: ~const Trait` is not satisfied + //[current]~^ ERROR the trait bound `U: ~const Other` is not satisfied + //[next]~^^ ERROR the trait bound `::Assoc: ~const Trait` is not satisfied ::Assoc::::func(); - //~^ ERROR the trait bound `::Assoc: ~const Trait` is not satisfied + //[current]~^ ERROR the trait bound `U: ~const Other` is not satisfied + //[next]~^^ ERROR the trait bound `::Assoc: ~const Trait` is not satisfied } const fn works() { diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr similarity index 77% rename from tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.stderr rename to tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr index 99fc924ad06b9..9c29a894749e9 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.stderr +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr @@ -1,11 +1,11 @@ error[E0277]: the trait bound `T: ~const Trait` is not satisfied - --> $DIR/assoc-type-const-bound-usage-fail.rs:16:5 + --> $DIR/assoc-type-const-bound-usage-fail.rs:17:5 | LL | T::Assoc::func(); | ^^^^^^^^ error[E0277]: the trait bound `T: ~const Trait` is not satisfied - --> $DIR/assoc-type-const-bound-usage-fail.rs:18:5 + --> $DIR/assoc-type-const-bound-usage-fail.rs:19:5 | LL | ::Assoc::func(); | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.next.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.next.stderr new file mode 100644 index 0000000000000..9c29a894749e9 --- /dev/null +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.next.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `T: ~const Trait` is not satisfied + --> $DIR/assoc-type-const-bound-usage-fail.rs:17:5 + | +LL | T::Assoc::func(); + | ^^^^^^^^ + +error[E0277]: the trait bound `T: ~const Trait` is not satisfied + --> $DIR/assoc-type-const-bound-usage-fail.rs:19:5 + | +LL | ::Assoc::func(); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.rs b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.rs index ce01086f0dc0b..3761fea19684d 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.rs +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.rs @@ -1,4 +1,5 @@ -//@ compile-flags: -Znext-solver +//@ revisions: current next +//@[next] compile-flags: -Znext-solver // Check that `~const` item bounds only hold if the parent trait is `~const`. // i.e. check that we validate the const conditions for the associated type diff --git a/tests/ui/traits/const-traits/assoc-type.stderr b/tests/ui/traits/const-traits/assoc-type.current.stderr similarity index 86% rename from tests/ui/traits/const-traits/assoc-type.stderr rename to tests/ui/traits/const-traits/assoc-type.current.stderr index b318675b6123f..4bf9acfbd6507 100644 --- a/tests/ui/traits/const-traits/assoc-type.stderr +++ b/tests/ui/traits/const-traits/assoc-type.current.stderr @@ -1,11 +1,11 @@ error[E0277]: the trait bound `NonConstAdd: ~const Add` is not satisfied - --> $DIR/assoc-type.rs:36:16 + --> $DIR/assoc-type.rs:37:16 | LL | type Bar = NonConstAdd; | ^^^^^^^^^^^ | note: required by a bound in `Foo::Bar` - --> $DIR/assoc-type.rs:32:15 + --> $DIR/assoc-type.rs:33:15 | LL | type Bar: ~const Add; | ^^^^^^ required by this bound in `Foo::Bar` diff --git a/tests/ui/traits/const-traits/assoc-type.next.stderr b/tests/ui/traits/const-traits/assoc-type.next.stderr new file mode 100644 index 0000000000000..4bf9acfbd6507 --- /dev/null +++ b/tests/ui/traits/const-traits/assoc-type.next.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `NonConstAdd: ~const Add` is not satisfied + --> $DIR/assoc-type.rs:37:16 + | +LL | type Bar = NonConstAdd; + | ^^^^^^^^^^^ + | +note: required by a bound in `Foo::Bar` + --> $DIR/assoc-type.rs:33:15 + | +LL | type Bar: ~const Add; + | ^^^^^^ required by this bound in `Foo::Bar` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/assoc-type.rs b/tests/ui/traits/const-traits/assoc-type.rs index 32c91fa51f135..a169b61994cbf 100644 --- a/tests/ui/traits/const-traits/assoc-type.rs +++ b/tests/ui/traits/const-traits/assoc-type.rs @@ -1,4 +1,5 @@ -//@ compile-flags: -Znext-solver +//@ revisions: current next +//@[next] compile-flags: -Znext-solver #![feature(const_trait_impl)]