Add derived causes for host effect predicates

This commit is contained in:
Michael Goulet 2024-12-10 22:08:44 +00:00
parent 243d2ca4db
commit 2be9ffc1af
15 changed files with 278 additions and 39 deletions

View file

@ -125,6 +125,15 @@ impl<'tcx> ObligationCause<'tcx> {
self
}
pub fn derived_host_cause(
mut self,
parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
variant: impl FnOnce(DerivedHostCause<'tcx>) -> ObligationCauseCode<'tcx>,
) -> ObligationCause<'tcx> {
self.code = variant(DerivedHostCause { parent_host_pred, parent_code: self.code }).into();
self
}
pub fn to_constraint_category(&self) -> ConstraintCategory<'tcx> {
match self.code() {
ObligationCauseCode::MatchImpl(cause, _) => cause.to_constraint_category(),
@ -278,6 +287,14 @@ pub enum ObligationCauseCode<'tcx> {
/// Derived obligation for WF goals.
WellFormedDerived(DerivedCause<'tcx>),
/// Derived obligation (i.e. `where` clause) on an user-provided impl
/// or a trait alias.
ImplDerivedHost(Box<ImplDerivedHostCause<'tcx>>),
/// Derived obligation (i.e. `where` clause) on an user-provided impl
/// or a trait alias.
BuiltinDerivedHost(DerivedHostCause<'tcx>),
/// Derived obligation refined to point at a specific argument in
/// a call or method expression.
FunctionArg {
@ -437,36 +454,38 @@ pub enum WellFormedLoc {
},
}
#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
#[derive(TypeVisitable, TypeFoldable)]
pub struct ImplDerivedCause<'tcx> {
pub derived: DerivedCause<'tcx>,
/// The `DefId` of the `impl` that gave rise to the `derived` obligation.
/// If the `derived` obligation arose from a trait alias, which conceptually has a synthetic impl,
/// then this will be the `DefId` of that trait alias. Care should therefore be taken to handle
/// that exceptional case where appropriate.
pub impl_or_alias_def_id: DefId,
/// The index of the derived predicate in the parent impl's predicates.
pub impl_def_predicate_index: Option<usize>,
pub span: Span,
}
impl<'tcx> ObligationCauseCode<'tcx> {
/// Returns the base obligation, ignoring derived obligations.
pub fn peel_derives(&self) -> &Self {
let mut base_cause = self;
while let Some((parent_code, _)) = base_cause.parent() {
while let Some(parent_code) = base_cause.parent() {
base_cause = parent_code;
}
base_cause
}
pub fn parent(&self) -> Option<&Self> {
match self {
ObligationCauseCode::FunctionArg { parent_code, .. } => Some(parent_code),
ObligationCauseCode::BuiltinDerived(derived)
| ObligationCauseCode::WellFormedDerived(derived)
| ObligationCauseCode::ImplDerived(box ImplDerivedCause { derived, .. }) => {
Some(&derived.parent_code)
}
ObligationCauseCode::BuiltinDerivedHost(derived)
| ObligationCauseCode::ImplDerivedHost(box ImplDerivedHostCause { derived, .. }) => {
Some(&derived.parent_code)
}
_ => None,
}
}
/// Returns the base obligation and the base trait predicate, if any, ignoring
/// derived obligations.
pub fn peel_derives_with_predicate(&self) -> (&Self, Option<ty::PolyTraitPredicate<'tcx>>) {
let mut base_cause = self;
let mut base_trait_pred = None;
while let Some((parent_code, parent_pred)) = base_cause.parent() {
while let Some((parent_code, parent_pred)) = base_cause.parent_with_predicate() {
base_cause = parent_code;
if let Some(parent_pred) = parent_pred {
base_trait_pred = Some(parent_pred);
@ -476,7 +495,7 @@ impl<'tcx> ObligationCauseCode<'tcx> {
(base_cause, base_trait_pred)
}
pub fn parent(&self) -> Option<(&Self, Option<ty::PolyTraitPredicate<'tcx>>)> {
pub fn parent_with_predicate(&self) -> Option<(&Self, Option<ty::PolyTraitPredicate<'tcx>>)> {
match self {
ObligationCauseCode::FunctionArg { parent_code, .. } => Some((parent_code, None)),
ObligationCauseCode::BuiltinDerived(derived)
@ -573,6 +592,42 @@ pub struct DerivedCause<'tcx> {
pub parent_code: InternedObligationCauseCode<'tcx>,
}
#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
#[derive(TypeVisitable, TypeFoldable)]
pub struct ImplDerivedCause<'tcx> {
pub derived: DerivedCause<'tcx>,
/// The `DefId` of the `impl` that gave rise to the `derived` obligation.
/// If the `derived` obligation arose from a trait alias, which conceptually has a synthetic impl,
/// then this will be the `DefId` of that trait alias. Care should therefore be taken to handle
/// that exceptional case where appropriate.
pub impl_or_alias_def_id: DefId,
/// The index of the derived predicate in the parent impl's predicates.
pub impl_def_predicate_index: Option<usize>,
pub span: Span,
}
#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
#[derive(TypeVisitable, TypeFoldable)]
pub struct DerivedHostCause<'tcx> {
/// The trait predicate of the parent obligation that led to the
/// current obligation. Note that only trait obligations lead to
/// derived obligations, so we just store the trait predicate here
/// directly.
pub parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
/// The parent trait had this cause.
pub parent_code: InternedObligationCauseCode<'tcx>,
}
#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
#[derive(TypeVisitable, TypeFoldable)]
pub struct ImplDerivedHostCause<'tcx> {
pub derived: DerivedHostCause<'tcx>,
/// The `DefId` of the `impl` that gave rise to the `derived` obligation.
pub impl_def_id: DefId,
pub span: Span,
}
#[derive(Clone, Debug, PartialEq, Eq, TypeVisitable)]
pub enum SelectionError<'tcx> {
/// The trait is not implemented.

View file

@ -634,6 +634,28 @@ impl<'tcx> UpcastFrom<TyCtxt<'tcx>, PolyProjectionPredicate<'tcx>> for Clause<'t
}
}
impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>>
for Predicate<'tcx>
{
fn upcast_from(
from: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
tcx: TyCtxt<'tcx>,
) -> Self {
from.map_bound(ty::ClauseKind::HostEffect).upcast(tcx)
}
}
impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>>
for Clause<'tcx>
{
fn upcast_from(
from: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
tcx: TyCtxt<'tcx>,
) -> Self {
from.map_bound(ty::ClauseKind::HostEffect).upcast(tcx)
}
}
impl<'tcx> UpcastFrom<TyCtxt<'tcx>, NormalizesTo<'tcx>> for Predicate<'tcx> {
fn upcast_from(from: NormalizesTo<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
PredicateKind::NormalizesTo(from).upcast(tcx)

View file

@ -353,7 +353,7 @@ where
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
ecx.add_goals(
GoalSource::Misc,
GoalSource::ImplWhereBound,
const_conditions.into_iter().map(|trait_ref| {
goal.with(
cx,

View file

@ -753,7 +753,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
applied_do_not_recommend = true;
}
}
if let Some((parent_cause, _parent_pred)) = base_cause.parent() {
if let Some(parent_cause) = base_cause.parent() {
base_cause = parent_cause.clone();
} else {
break;
@ -778,7 +778,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
trait_ref.skip_binder().args.type_at(1).to_opt_closure_kind()
&& !found_kind.extends(expected_kind)
{
if let Some((_, Some(parent))) = obligation.cause.code().parent() {
if let Some((_, Some(parent))) = obligation.cause.code().parent_with_predicate() {
// If we have a derived obligation, then the parent will be a `AsyncFn*` goal.
trait_ref = parent.to_poly_trait_ref();
} else if let &ObligationCauseCode::FunctionArg { arg_hir_id, .. } =
@ -926,7 +926,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let Some(typeck) = &self.typeck_results else {
return false;
};
let Some((ObligationCauseCode::QuestionMark, Some(y))) = obligation.cause.code().parent()
let Some((ObligationCauseCode::QuestionMark, Some(y))) =
obligation.cause.code().parent_with_predicate()
else {
return false;
};
@ -1179,7 +1180,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let mut code = obligation.cause.code();
let mut pred = obligation.predicate.as_trait_clause();
while let Some((next_code, next_pred)) = code.parent() {
while let Some((next_code, next_pred)) = code.parent_with_predicate() {
if let Some(pred) = pred {
self.enter_forall(pred, |pred| {
diag.note(format!(
@ -2095,7 +2096,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let mut code = obligation.cause.code();
let mut trait_pred = trait_predicate;
let mut peeled = false;
while let Some((parent_code, parent_trait_pred)) = code.parent() {
while let Some((parent_code, parent_trait_pred)) = code.parent_with_predicate() {
code = parent_code;
if let Some(parent_trait_pred) = parent_trait_pred {
trait_pred = parent_trait_pred;

View file

@ -464,7 +464,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// Get the root obligation, since the leaf obligation we have may be unhelpful (#87437)
let mut real_trait_pred = trait_pred;
while let Some((parent_code, parent_trait_pred)) = code.parent() {
while let Some((parent_code, parent_trait_pred)) = code.parent_with_predicate() {
code = parent_code;
if let Some(parent_trait_pred) = parent_trait_pred {
real_trait_pred = parent_trait_pred;
@ -1447,7 +1447,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let mut span = obligation.cause.span;
let mut trait_pred = trait_pred;
let mut code = obligation.cause.code();
while let Some((c, Some(parent_trait_pred))) = code.parent() {
while let Some((c, Some(parent_trait_pred))) = code.parent_with_predicate() {
// We want the root obligation, in order to detect properly handle
// `for _ in &mut &mut vec![] {}`.
code = c;
@ -3470,6 +3470,59 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
)
});
}
ObligationCauseCode::ImplDerivedHost(ref data) => {
let self_ty =
self.resolve_vars_if_possible(data.derived.parent_host_pred.self_ty());
let msg = format!(
"required for `{self_ty}` to implement `{} {}`",
data.derived.parent_host_pred.skip_binder().constness,
data.derived
.parent_host_pred
.map_bound(|pred| pred.trait_ref)
.print_only_trait_path(),
);
match tcx.hir().get_if_local(data.impl_def_id) {
Some(Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }),
..
})) => {
let mut spans = vec![self_ty.span];
spans.extend(of_trait.as_ref().map(|t| t.path.span));
let mut spans: MultiSpan = spans.into();
spans.push_span_label(data.span, "unsatisfied trait bound introduced here");
err.span_note(spans, msg);
}
_ => {
err.note(msg);
}
}
ensure_sufficient_stack(|| {
self.note_obligation_cause_code(
body_id,
err,
data.derived.parent_host_pred,
param_env,
&data.derived.parent_code,
obligated_types,
seen_requirements,
long_ty_file,
)
});
}
ObligationCauseCode::BuiltinDerivedHost(ref data) => {
ensure_sufficient_stack(|| {
self.note_obligation_cause_code(
body_id,
err,
data.parent_host_pred,
param_env,
&data.parent_code,
obligated_types,
seen_requirements,
long_ty_file,
)
});
}
ObligationCauseCode::WellFormedDerived(ref data) => {
let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred);
let parent_predicate = parent_trait_ref;

View file

@ -474,8 +474,11 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
// for normalizes-to.
let pred_kind = goal.goal().predicate.kind();
let child_mode = match pred_kind.skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(parent_trait_pred)) => {
ChildMode::Trait(pred_kind.rebind(parent_trait_pred))
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
ChildMode::Trait(pred_kind.rebind(pred))
}
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => {
ChildMode::Host(pred_kind.rebind(pred))
}
ty::PredicateKind::NormalizesTo(normalizes_to)
if matches!(
@ -492,6 +495,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
};
let mut impl_where_bound_count = 0;
let mut impl_const_condition_bound_count = 0;
for nested_goal in candidate.instantiate_nested_goals(self.span()) {
trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
@ -504,7 +508,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
let obligation;
match (child_mode, nested_goal.source()) {
(ChildMode::Trait(_), GoalSource::Misc) => {
(ChildMode::Trait(_) | ChildMode::Host(_), GoalSource::Misc) => {
continue;
}
(ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
@ -517,6 +521,16 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
));
impl_where_bound_count += 1;
}
(ChildMode::Host(parent_host_pred), GoalSource::ImplWhereBound) => {
obligation = make_obligation(derive_host_cause(
tcx,
candidate.kind(),
self.obligation.cause.clone(),
impl_const_condition_bound_count,
parent_host_pred,
));
impl_const_condition_bound_count += 1;
}
// Skip over a higher-ranked predicate.
(_, GoalSource::InstantiateHigherRanked) => {
obligation = self.obligation.clone();
@ -575,6 +589,10 @@ enum ChildMode<'tcx> {
// and skip all `GoalSource::Misc`, which represent useless obligations
// such as alias-eq which may not hold.
Trait(ty::PolyTraitPredicate<'tcx>),
// Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
// and skip all `GoalSource::Misc`, which represent useless obligations
// such as alias-eq which may not hold.
Host(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>),
// Skip trying to derive an `ObligationCause` from this obligation, and
// report *all* sub-obligations as if they came directly from the parent
// obligation.
@ -616,3 +634,52 @@ fn derive_cause<'tcx>(
};
cause
}
fn derive_host_cause<'tcx>(
tcx: TyCtxt<'tcx>,
candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>,
mut cause: ObligationCause<'tcx>,
idx: usize,
parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
) -> ObligationCause<'tcx> {
match candidate_kind {
inspect::ProbeKind::TraitCandidate {
source: CandidateSource::Impl(impl_def_id),
result: _,
} => {
if let Some((_, span)) = tcx
.predicates_of(impl_def_id)
.instantiate_identity(tcx)
.into_iter()
.chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map(
|(trait_ref, span)| {
(
trait_ref.to_host_effect_clause(
tcx,
parent_host_pred.skip_binder().constness,
),
span,
)
},
))
.nth(idx)
{
cause =
cause.derived_host_cause(parent_host_pred, |derived| {
ObligationCauseCode::ImplDerivedHost(Box::new(
traits::ImplDerivedHostCause { derived, impl_def_id, span },
))
})
}
}
inspect::ProbeKind::TraitCandidate {
source: CandidateSource::BuiltinImpl(..),
result: _,
} => {
cause =
cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost);
}
_ => {}
};
cause
}

View file

@ -1,6 +1,8 @@
use rustc_hir as hir;
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes};
use rustc_infer::traits::{ImplSource, Obligation, PredicateObligation};
use rustc_infer::traits::{
ImplDerivedHostCause, ImplSource, Obligation, ObligationCauseCode, PredicateObligation,
};
use rustc_middle::span_bug;
use rustc_middle::ty::fast_reject::DeepRejectCtxt;
use rustc_middle::ty::{self, TypingMode};
@ -248,9 +250,22 @@ fn evaluate_host_effect_from_selection_candiate<'tcx>(
tcx.const_conditions(impl_.impl_def_id)
.instantiate(tcx, impl_.args)
.into_iter()
.map(|(trait_ref, _)| {
obligation.with(
.map(|(trait_ref, span)| {
Obligation::new(
tcx,
obligation.cause.clone().derived_host_cause(
ty::Binder::dummy(obligation.predicate),
|derived| {
ObligationCauseCode::ImplDerivedHost(Box::new(
ImplDerivedHostCause {
derived,
impl_def_id: impl_.impl_def_id,
span,
},
))
},
),
obligation.param_env,
trait_ref
.to_host_effect_clause(tcx, obligation.predicate.constness),
)

View file

@ -89,6 +89,7 @@ impl_binder_encode_decode! {
ty::ExistentialPredicate<I>,
ty::TraitRef<I>,
ty::ExistentialTraitRef<I>,
ty::HostEffectPredicate<I>,
}
impl<I: Interner, T> Binder<I, T>

View file

@ -11,12 +11,30 @@ error[E0284]: type annotations needed: cannot normalize `<&T as ConstName>::{con
|
LL | impl<T: ?Sized + ConstName> const ConstName for &T
| ^^ cannot normalize `<&T as ConstName>::{constant#0}`
|
note: required for `&T` to implement `~const ConstName`
--> $DIR/issue-88119.rs:19:35
|
LL | impl<T: ?Sized + ConstName> const ConstName for &T
| ^^^^^^^^^ ^^
LL | where
LL | [(); name_len::<T>()]:,
| --------------------- unsatisfied trait bound introduced here
error[E0284]: type annotations needed: cannot normalize `<&mut T as ConstName>::{constant#0}`
--> $DIR/issue-88119.rs:26:49
|
LL | impl<T: ?Sized + ConstName> const ConstName for &mut T
| ^^^^^^ cannot normalize `<&mut T as ConstName>::{constant#0}`
|
note: required for `&mut T` to implement `~const ConstName`
--> $DIR/issue-88119.rs:26:35
|
LL | impl<T: ?Sized + ConstName> const ConstName for &mut T
| ^^^^^^^^^ ^^^^^^
LL | where
LL | [(); name_len::<T>()]:,
| --------------------- unsatisfied trait bound introduced here
error: aborting due to 3 previous errors

View file

@ -5,7 +5,7 @@ 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
--> $DIR/assoc-type-const-bound-usage-fail.rs:20:5
|
LL | <T as Trait>::Assoc::func();
| ^^^^^^^^^^^^^^^^^^^

View file

@ -1,11 +1,11 @@
error[E0277]: the trait bound `T: ~const Trait` is not satisfied
error[E0277]: the trait bound `<T as Trait>::Assoc: ~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
error[E0277]: the trait bound `<T as Trait>::Assoc: ~const Trait` is not satisfied
--> $DIR/assoc-type-const-bound-usage-fail.rs:20:5
|
LL | <T as Trait>::Assoc::func();
| ^^^^^^^^^^^^^^^^^^^

View file

@ -15,9 +15,11 @@ trait Trait {
const fn unqualified<T: Trait>() {
T::Assoc::func();
//~^ ERROR the trait bound `T: ~const Trait` is not satisfied
//[current]~^ ERROR the trait bound `T: ~const Trait` is not satisfied
//[next]~^^ ERROR the trait bound `<T as Trait>::Assoc: ~const Trait` is not satisfied
<T as Trait>::Assoc::func();
//~^ ERROR the trait bound `T: ~const Trait` is not satisfied
//[current]~^ ERROR the trait bound `T: ~const Trait` is not satisfied
//[next]~^^ ERROR the trait bound `<T as Trait>::Assoc: ~const Trait` is not satisfied
}
const fn works<T: ~const Trait>() {

View file

@ -12,7 +12,7 @@ note: required by a bound in `bar`
LL | const fn bar<T: ~const Foo>(t: T) -> impl ~const Foo {
| ^^^^^^ required by this bound in `bar`
error[E0277]: the trait bound `(): const Foo` is not satisfied
error[E0277]: the trait bound `impl Foo: const Foo` is not satisfied
--> $DIR/const-opaque.rs:33:12
|
LL | opaque.method();

View file

@ -31,7 +31,7 @@ const _: () = {
let opaque = bar(());
//[no]~^ ERROR the trait bound `(): const Foo` is not satisfied
opaque.method();
//[no]~^ ERROR the trait bound `(): const Foo` is not satisfied
//[no]~^ ERROR the trait bound `impl Foo: const Foo` is not satisfied
std::mem::forget(opaque);
};

View file

@ -16,6 +16,11 @@ error[E0277]: the trait bound `T: ~const Bar` is not satisfied
LL | type Assoc<T> = C<T>
| ^^^^
|
note: required for `C<T>` to implement `~const Bar`
--> $DIR/item-bound-entailment-fails.rs:14:15
|
LL | impl<T> const Bar for C<T> where T: ~const Bar {}
| ^^^ ^^^^ ------ unsatisfied trait bound introduced here
note: required by a bound in `Foo::Assoc`
--> $DIR/item-bound-entailment-fails.rs:5:20
|