From 0cbc3aef48465aee0b8ddb0b4e34d4b18fc37987 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 30 Oct 2024 00:08:52 +0000 Subject: [PATCH] Derived causes for ~const bounds --- compiler/rustc_middle/src/traits/mod.rs | 67 +++++++++++++++---- compiler/rustc_middle/src/ty/predicate.rs | 22 ++++++ .../src/error_reporting/traits/suggestions.rs | 53 +++++++++++++++ .../src/solve/fulfill.rs | 63 +++++++++++++++-- compiler/rustc_type_ir/src/binder.rs | 1 + .../const-generics/issues/issue-88119.stderr | 18 +++++ .../item-bound-entailment-fails.stderr | 5 ++ 7 files changed, 211 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 40e5ec4595982..af279a4fd20bc 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -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(), @@ -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>), + + /// 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 { @@ -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, - pub span: Span, -} - impl<'tcx> ObligationCauseCode<'tcx> { /// Returns the base obligation, ignoring derived obligations. pub fn peel_derives(&self) -> &Self { @@ -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, + 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. diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 3ecaa3e22d39c..32d6455e82557 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -634,6 +634,28 @@ impl<'tcx> UpcastFrom, PolyProjectionPredicate<'tcx>> for Clause<'t } } +impl<'tcx> UpcastFrom, 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, 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, NormalizesTo<'tcx>> for Predicate<'tcx> { fn upcast_from(from: NormalizesTo<'tcx>, tcx: TyCtxt<'tcx>) -> Self { PredicateKind::NormalizesTo(from).upcast(tcx) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index a7d1f1bd8d042..cb7921e4a4f49 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -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; diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index ca8f122d7dfaa..649938d180ffe 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -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())); @@ -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) => { @@ -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 @@ -615,3 +621,52 @@ fn derive_cause<'tcx>( }; cause } + +fn derive_host_cause<'tcx>( + tcx: TyCtxt<'tcx>, + candidate_kind: inspect::ProbeKind>, + 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 +} diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index c06a578d8ec70..484c9968d7565 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -91,6 +91,7 @@ impl_binder_encode_decode! { ty::ExistentialPredicate, ty::TraitRef, ty::ExistentialTraitRef, + ty::HostEffectPredicate, } impl Binder diff --git a/tests/ui/const-generics/issues/issue-88119.stderr b/tests/ui/const-generics/issues/issue-88119.stderr index a0ca33e38ef83..a99c01080e245 100644 --- a/tests/ui/const-generics/issues/issue-88119.stderr +++ b/tests/ui/const-generics/issues/issue-88119.stderr @@ -11,12 +11,30 @@ error[E0284]: type annotations needed: cannot normalize `<&T as ConstName>::{con | LL | impl 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 const ConstName for &T + | ^^^^^^^^^ ^^ +LL | where +LL | [(); name_len::()]:, + | --------------------- 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 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 const ConstName for &mut T + | ^^^^^^^^^ ^^^^^^ +LL | where +LL | [(); name_len::()]:, + | --------------------- unsatisfied trait bound introduced here error: aborting due to 3 previous errors diff --git a/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr b/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr index 054a8ac757794..8c4594fae5064 100644 --- a/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr +++ b/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr @@ -25,6 +25,11 @@ error[E0277]: the trait bound `T: ~const Bar` is not satisfied LL | type Assoc = C | ^^^^ | +note: required for `C` to implement `~const Bar` + --> $DIR/item-bound-entailment-fails.rs:15:15 + | +LL | impl const Bar for C 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 |