Skip to content

Commit

Permalink
Derived causes for ~const bounds
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Oct 30, 2024
1 parent 86e215e commit 0cbc3ae
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 18 deletions.
67 changes: 53 additions & 14 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,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(),
Expand Down Expand Up @@ -270,6 +279,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 {
Expand Down Expand Up @@ -429,20 +446,6 @@ 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 {
Expand Down Expand Up @@ -546,6 +549,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, TypeVisitable)]
pub enum SelectionError<'tcx> {
/// The trait is not implemented.
Expand Down
22 changes: 22 additions & 0 deletions compiler/rustc_middle/src/ty/predicate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3469,6 +3469,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;
Expand Down
63 changes: 59 additions & 4 deletions compiler/rustc_trait_selection/src/solve/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,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()));

Expand All @@ -507,9 +508,15 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
));
impl_where_bound_count += 1;
}
(ChildMode::Host(_), GoalSource::ImplWhereBound) => {
// TODO:
obligation = make_obligation(self.obligation.cause.clone());
(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) => {
Expand Down Expand Up @@ -572,7 +579,6 @@ enum ChildMode<'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.
#[expect(dead_code)]
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
Expand Down Expand Up @@ -615,3 +621,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
}
1 change: 1 addition & 0 deletions compiler/rustc_type_ir/src/binder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ impl_binder_encode_decode! {
ty::ExistentialPredicate<I>,
ty::TraitRef<I>,
ty::ExistentialTraitRef<I>,
ty::HostEffectPredicate<I>,
}

impl<I: Interner, T> Binder<I, T>
Expand Down
18 changes: 18 additions & 0 deletions tests/ui/const-generics/issues/issue-88119.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,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:15: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:6:20
|
Expand Down

0 comments on commit 0cbc3ae

Please sign in to comment.