Rollup merge of #134638 - compiler-errors:fx-item-bounds, r=lcnr

Fix effect predicates from item bounds in old solver

r? lcnr
This commit is contained in:
Stuart Cook 2024-12-24 14:05:21 +11:00 committed by GitHub
commit c2f44cd32c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 202 additions and 78 deletions

View file

@ -1,13 +1,15 @@
use rustc_hir as hir; 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_infer::traits::{ImplSource, Obligation, PredicateObligation};
use rustc_middle::span_bug; use rustc_middle::span_bug;
use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::fast_reject::DeepRejectCtxt;
use rustc_middle::ty::{self, TypingMode}; use rustc_middle::ty::{self, TypingMode};
use rustc_type_ir::elaborate::elaborate;
use rustc_type_ir::solve::NoSolution; use rustc_type_ir::solve::NoSolution;
use thin_vec::ThinVec; use thin_vec::{ThinVec, thin_vec};
use super::SelectionContext; use super::SelectionContext;
use super::normalize::normalize_with_depth_to;
pub type HostEffectObligation<'tcx> = Obligation<'tcx, ty::HostEffectPredicate<'tcx>>; pub type HostEffectObligation<'tcx> = Obligation<'tcx, ty::HostEffectPredicate<'tcx>>;
@ -38,6 +40,12 @@ pub fn evaluate_host_effect_obligation<'tcx>(
Err(EvaluationFailure::NoSolution) => {} 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) { match evaluate_host_effect_from_selection_candiate(selcx, obligation) {
Ok(result) => return Ok(result), Ok(result) => return Ok(result),
Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
@ -48,24 +56,45 @@ pub fn evaluate_host_effect_obligation<'tcx>(
} }
fn match_candidate<'tcx>( fn match_candidate<'tcx>(
infcx: &InferCtxt<'tcx>, selcx: &mut SelectionContext<'_, 'tcx>,
obligation: &HostEffectObligation<'tcx>, obligation: &HostEffectObligation<'tcx>,
candidate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, candidate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
candidate_is_unnormalized: bool,
more_nested: impl FnOnce(&mut SelectionContext<'_, 'tcx>, &mut ThinVec<PredicateObligation<'tcx>>),
) -> Result<ThinVec<PredicateObligation<'tcx>>, NoSolution> { ) -> Result<ThinVec<PredicateObligation<'tcx>>, NoSolution> {
if !candidate.skip_binder().constness.satisfies(obligation.predicate.constness) { if !candidate.skip_binder().constness.satisfies(obligation.predicate.constness) {
return Err(NoSolution); return Err(NoSolution);
} }
let candidate = infcx.instantiate_binder_with_fresh_vars( let mut candidate = selcx.infcx.instantiate_binder_with_fresh_vars(
obligation.cause.span, obligation.cause.span,
BoundRegionConversionTime::HigherRankedType, BoundRegionConversionTime::HigherRankedType,
candidate, candidate,
); );
let mut nested = infcx let mut nested = thin_vec![];
.at(&obligation.cause, obligation.param_env)
.eq(DefineOpaqueTypes::Yes, obligation.predicate.trait_ref, candidate.trait_ref)? // Unlike param-env bounds, item bounds may not be normalized.
.into_obligations(); 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 { for nested in &mut nested {
nested.set_depth_from_parent(obligation.recursion_depth); nested.set_depth_from_parent(obligation.recursion_depth);
@ -82,41 +111,121 @@ fn evaluate_host_effect_from_bounds<'tcx>(
let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx()); let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
let mut candidate = None; let mut candidate = None;
for predicate in obligation.param_env.caller_bounds() { for clause in obligation.param_env.caller_bounds() {
let bound_predicate = predicate.kind(); let bound_clause = clause.kind();
if let ty::ClauseKind::HostEffect(data) = predicate.kind().skip_binder() { let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else {
let data = bound_predicate.rebind(data); continue;
if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id { };
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( if !drcx
obligation.predicate.trait_ref.args, .args_may_unify(obligation.predicate.trait_ref.args, data.skip_binder().trait_ref.args)
data.skip_binder().trait_ref.args, {
) { continue;
continue; }
}
let is_match = infcx.probe(|_| match_candidate(infcx, obligation, data).is_ok()); let is_match =
infcx.probe(|_| match_candidate(selcx, obligation, data, false, |_, _| {}).is_ok());
if is_match { if is_match {
if candidate.is_some() { if candidate.is_some() {
return Err(EvaluationFailure::Ambiguous); return Err(EvaluationFailure::Ambiguous);
} else { } else {
candidate = Some(data); candidate = Some(data);
}
} }
} }
} }
if let Some(data) = candidate { if let Some(data) = candidate {
Ok(match_candidate(infcx, obligation, data) Ok(match_candidate(selcx, obligation, data, false, |_, _| {})
.expect("candidate matched before, so it should match again")) .expect("candidate matched before, so it should match again"))
} else { } else {
Err(EvaluationFailure::NoSolution) Err(EvaluationFailure::NoSolution)
} }
} }
fn evaluate_host_effect_from_item_bounds<'tcx>(
selcx: &mut SelectionContext<'_, 'tcx>,
obligation: &HostEffectObligation<'tcx>,
) -> Result<ThinVec<PredicateObligation<'tcx>>, 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)
}),
) {
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;
}
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, 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, 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)
}
}
fn evaluate_host_effect_from_selection_candiate<'tcx>( fn evaluate_host_effect_from_selection_candiate<'tcx>(
selcx: &mut SelectionContext<'_, 'tcx>, selcx: &mut SelectionContext<'_, 'tcx>,
obligation: &HostEffectObligation<'tcx>, obligation: &HostEffectObligation<'tcx>,

View file

@ -1,4 +1,5 @@
//@ compile-flags: -Znext-solver //@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@ check-pass //@ check-pass
#![feature(const_trait_impl)] #![feature(const_trait_impl)]

View file

@ -1,5 +1,4 @@
//@ compile-flags: -Znext-solver //@ check-pass
//@ known-bug: unknown
#![feature(const_trait_impl, generic_const_exprs)] #![feature(const_trait_impl, generic_const_exprs)]
#![allow(incomplete_features)] #![allow(incomplete_features)]

View file

@ -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<T>::{constant#0}`
--> $DIR/assoc-type-const-bound-usage-1.rs:15:37
|
LL | fn unqualified<T: const Trait>() -> Type<{ T::Assoc::func() }> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `unqualified<T>::{constant#0}`
error[E0284]: type annotations needed: cannot normalize `qualified<T>::{constant#0}`
--> $DIR/assoc-type-const-bound-usage-1.rs:19:35
|
LL | fn qualified<T: const Trait>() -> Type<{ <T as Trait>::Assoc::func() }> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `qualified<T>::{constant#0}`
error[E0284]: type annotations needed: cannot normalize `unqualified<T>::{constant#0}`
--> $DIR/assoc-type-const-bound-usage-1.rs:16:5
|
LL | Type
| ^^^^ cannot normalize `unqualified<T>::{constant#0}`
error[E0284]: type annotations needed: cannot normalize `qualified<T>::{constant#0}`
--> $DIR/assoc-type-const-bound-usage-1.rs:20:5
|
LL | Type
| ^^^^ cannot normalize `qualified<T>::{constant#0}`
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0284`.

View file

@ -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::<U>::func();
| ^^^^^^^^^^^^^
error[E0277]: the trait bound `U: ~const Other` is not satisfied
--> $DIR/assoc-type-const-bound-usage-fail-2.rs:27:5
|
LL | <T as Trait>::Assoc::<U>::func();
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -1,11 +1,11 @@
error[E0277]: the trait bound `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied error[E0277]: the trait bound `<T as Trait>::Assoc<U>: ~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::<U>::func(); LL | T::Assoc::<U>::func();
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
error[E0277]: the trait bound `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied error[E0277]: the trait bound `<T as Trait>::Assoc<U>: ~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 | <T as Trait>::Assoc::<U>::func(); LL | <T as Trait>::Assoc::<U>::func();
| ^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -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 // Check that `~const` item bounds only hold if the where clauses on the
// associated type are also const. // associated type are also const.
@ -21,9 +22,11 @@ trait Other {}
const fn fails<T: ~const Trait, U: Other>() { const fn fails<T: ~const Trait, U: Other>() {
T::Assoc::<U>::func(); T::Assoc::<U>::func();
//~^ ERROR the trait bound `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied //[current]~^ ERROR the trait bound `U: ~const Other` is not satisfied
//[next]~^^ ERROR the trait bound `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied
<T as Trait>::Assoc::<U>::func(); <T as Trait>::Assoc::<U>::func();
//~^ ERROR the trait bound `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied //[current]~^ ERROR the trait bound `U: ~const Other` is not satisfied
//[next]~^^ ERROR the trait bound `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied
} }
const fn works<T: ~const Trait, U: ~const Other>() { const fn works<T: ~const Trait, U: ~const Other>() {

View file

@ -1,11 +1,11 @@
error[E0277]: the trait bound `T: ~const Trait` is not satisfied 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(); LL | T::Assoc::func();
| ^^^^^^^^ | ^^^^^^^^
error[E0277]: the trait bound `T: ~const Trait` is not satisfied 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 | <T as Trait>::Assoc::func(); LL | <T as Trait>::Assoc::func();
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^

View file

@ -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 | <T as Trait>::Assoc::func();
| ^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -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`. // 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 // i.e. check that we validate the const conditions for the associated type

View file

@ -1,11 +1,11 @@
error[E0277]: the trait bound `NonConstAdd: ~const Add` is not satisfied 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; LL | type Bar = NonConstAdd;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
| |
note: required by a bound in `Foo::Bar` 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; LL | type Bar: ~const Add;
| ^^^^^^ required by this bound in `Foo::Bar` | ^^^^^^ required by this bound in `Foo::Bar`

View file

@ -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`.

View file

@ -1,4 +1,5 @@
//@ compile-flags: -Znext-solver //@ revisions: current next
//@[next] compile-flags: -Znext-solver
#![feature(const_trait_impl)] #![feature(const_trait_impl)]