From 2be9ffc1afd0623ab0697cbaca5693b80c6d362c Mon Sep 17 00:00:00 2001 From: Michael Goulet <michael@errs.io> Date: Tue, 10 Dec 2024 22:08:44 +0000 Subject: [PATCH 1/2] Add derived causes for host effect predicates --- compiler/rustc_middle/src/traits/mod.rs | 89 +++++++++++++++---- compiler/rustc_middle/src/ty/predicate.rs | 22 +++++ .../src/solve/effect_goals.rs | 2 +- .../traits/fulfillment_errors.rs | 11 +-- .../src/error_reporting/traits/suggestions.rs | 57 +++++++++++- .../src/solve/fulfill.rs | 73 ++++++++++++++- .../src/traits/effects.rs | 21 ++++- compiler/rustc_type_ir/src/binder.rs | 1 + .../const-generics/issues/issue-88119.stderr | 18 ++++ ...type-const-bound-usage-fail.current.stderr | 2 +- ...oc-type-const-bound-usage-fail.next.stderr | 6 +- .../assoc-type-const-bound-usage-fail.rs | 6 +- .../const-traits/const-opaque.no.stderr | 2 +- tests/ui/traits/const-traits/const-opaque.rs | 2 +- .../item-bound-entailment-fails.stderr | 5 ++ 15 files changed, 278 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 99211c1f92440..db2bb8a7248a6 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -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. 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<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) diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index ce7552e30f0fe..281796e84b169 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -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, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index f9a3040832601..1678aa8c657b4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -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; 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 007a220ae697f..9d85ca1dd4dd7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -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; diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 2b2623a050ec9..7aa82c52c9f26 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -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 +} diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 91484ef99dbb8..0ac24eb54e748 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -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), ) diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index 47447af22158d..cb59bc608c203 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -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> diff --git a/tests/ui/const-generics/issues/issue-88119.stderr b/tests/ui/const-generics/issues/issue-88119.stderr index 370244fe8c984..f219c90849a26 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<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 diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr index 9c29a894749e9..ccd4af99cf41b 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr @@ -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(); | ^^^^^^^^^^^^^^^^^^^ 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 index 9c29a894749e9..ee1b663b99981 100644 --- 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 @@ -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(); | ^^^^^^^^^^^^^^^^^^^ 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 3761fea19684d..224ebb2daf16e 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 @@ -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>() { diff --git a/tests/ui/traits/const-traits/const-opaque.no.stderr b/tests/ui/traits/const-traits/const-opaque.no.stderr index 1278e1257467b..33aa1065df356 100644 --- a/tests/ui/traits/const-traits/const-opaque.no.stderr +++ b/tests/ui/traits/const-traits/const-opaque.no.stderr @@ -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(); diff --git a/tests/ui/traits/const-traits/const-opaque.rs b/tests/ui/traits/const-traits/const-opaque.rs index 96cdd7d9f2619..bfcadd521a54e 100644 --- a/tests/ui/traits/const-traits/const-opaque.rs +++ b/tests/ui/traits/const-traits/const-opaque.rs @@ -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); }; 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 3fc6f58470947..0d53bc5897ef3 100644 --- a/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr +++ b/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr @@ -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 | From ebdf19a8bbd1efb533bab35b0a2b327e4a33282f Mon Sep 17 00:00:00 2001 From: Michael Goulet <michael@errs.io> Date: Mon, 23 Dec 2024 20:17:53 +0000 Subject: [PATCH 2/2] Recurse on GAT where clauses in fulfillment error proof tree visitor --- .../src/solve/effect_goals.rs | 4 ++-- .../rustc_trait_selection/src/solve/fulfill.rs | 14 +++++++++----- compiler/rustc_type_ir/src/solve/mod.rs | 4 ++++ ...oc-type-const-bound-usage-fail-2.current.stderr | 2 +- ...assoc-type-const-bound-usage-fail-2.next.stderr | 6 +++--- .../assoc-type-const-bound-usage-fail-2.rs | 6 ++---- ...ssoc-type-const-bound-usage-fail.current.stderr | 2 +- .../assoc-type-const-bound-usage-fail.next.stderr | 6 +++--- .../assoc-type-const-bound-usage-fail.rs | 6 ++---- .../ui/traits/const-traits/const-opaque.no.stderr | 2 +- tests/ui/traits/const-traits/const-opaque.rs | 2 +- 11 files changed, 29 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 281796e84b169..7669a305d58d1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -103,7 +103,7 @@ where |ecx| { // Const conditions must hold for the implied const bound to hold. ecx.add_goals( - GoalSource::Misc, + GoalSource::AliasBoundConstCondition, cx.const_conditions(alias_ty.def_id) .iter_instantiated(cx, alias_ty.args) .map(|trait_ref| { @@ -353,7 +353,7 @@ where ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { ecx.add_goals( - GoalSource::ImplWhereBound, + GoalSource::AliasBoundConstCondition, const_conditions.into_iter().map(|trait_ref| { goal.with( cx, diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 7aa82c52c9f26..986176aca770e 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -413,6 +413,7 @@ impl<'tcx> BestObligation<'tcx> { matches!( nested_goal.source(), GoalSource::ImplWhereBound + | GoalSource::AliasBoundConstCondition | GoalSource::InstantiateHigherRanked | GoalSource::AliasWellFormed ) && match self.consider_ambiguities { @@ -495,7 +496,6 @@ 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())); @@ -521,21 +521,25 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { )); impl_where_bound_count += 1; } - (ChildMode::Host(parent_host_pred), GoalSource::ImplWhereBound) => { + ( + ChildMode::Host(parent_host_pred), + GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition, + ) => { obligation = make_obligation(derive_host_cause( tcx, candidate.kind(), self.obligation.cause.clone(), - impl_const_condition_bound_count, + impl_where_bound_count, parent_host_pred, )); - impl_const_condition_bound_count += 1; + impl_where_bound_count += 1; } // Skip over a higher-ranked predicate. (_, GoalSource::InstantiateHigherRanked) => { obligation = self.obligation.clone(); } - (ChildMode::PassThrough, _) | (_, GoalSource::AliasWellFormed) => { + (ChildMode::PassThrough, _) + | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => { obligation = make_obligation(self.obligation.cause.clone()); } } diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 8fe512026e5d9..1ae904d50e066 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -68,6 +68,10 @@ pub enum GoalSource { /// FIXME(-Znext-solver=coinductive): Explain how and why this /// changes whether cycles are coinductive. ImplWhereBound, + /// Const conditions that need to hold for `~const` alias bounds to hold. + /// + /// FIXME(-Znext-solver=coinductive): Are these even coinductive? + AliasBoundConstCondition, /// Instantiating a higher-ranked goal and re-proving it. InstantiateHigherRanked, /// Predicate required for an alias projection to be well-formed. 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 index 03da9159bea0b..4cd87002e4910 100644 --- 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 @@ -5,7 +5,7 @@ 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 + --> $DIR/assoc-type-const-bound-usage-fail-2.rs:26:5 | LL | <T as Trait>::Assoc::<U>::func(); | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr index ce58b486a16ef..4cd87002e4910 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.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 `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied +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 `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied - --> $DIR/assoc-type-const-bound-usage-fail-2.rs:27:5 +error[E0277]: the trait bound `U: ~const Other` is not satisfied + --> $DIR/assoc-type-const-bound-usage-fail-2.rs:26:5 | LL | <T as Trait>::Assoc::<U>::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 bdd98eaf541f8..e1c30b5361124 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 @@ -22,11 +22,9 @@ trait Other {} const fn fails<T: ~const Trait, U: Other>() { T::Assoc::<U>::func(); - //[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 + //~^ ERROR the trait bound `U: ~const Other` is not satisfied <T as Trait>::Assoc::<U>::func(); - //[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 + //~^ ERROR the trait bound `U: ~const Other` is not satisfied } const fn works<T: ~const Trait, U: ~const Other>() { diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr index ccd4af99cf41b..9c29a894749e9 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr @@ -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:20:5 + --> $DIR/assoc-type-const-bound-usage-fail.rs:19:5 | LL | <T as Trait>::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 index ee1b663b99981..9c29a894749e9 100644 --- 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 @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `<T as Trait>::Assoc: ~const Trait` is not satisfied +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 as Trait>::Assoc: ~const Trait` is not satisfied - --> $DIR/assoc-type-const-bound-usage-fail.rs:20:5 +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(); | ^^^^^^^^^^^^^^^^^^^ 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 224ebb2daf16e..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 @@ -15,11 +15,9 @@ trait Trait { const fn unqualified<T: Trait>() { T::Assoc::func(); - //[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 + //~^ ERROR the trait bound `T: ~const Trait` is not satisfied <T as Trait>::Assoc::func(); - //[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 + //~^ ERROR the trait bound `T: ~const Trait` is not satisfied } const fn works<T: ~const Trait>() { diff --git a/tests/ui/traits/const-traits/const-opaque.no.stderr b/tests/ui/traits/const-traits/const-opaque.no.stderr index 33aa1065df356..1278e1257467b 100644 --- a/tests/ui/traits/const-traits/const-opaque.no.stderr +++ b/tests/ui/traits/const-traits/const-opaque.no.stderr @@ -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 `impl Foo: const Foo` is not satisfied +error[E0277]: the trait bound `(): const Foo` is not satisfied --> $DIR/const-opaque.rs:33:12 | LL | opaque.method(); diff --git a/tests/ui/traits/const-traits/const-opaque.rs b/tests/ui/traits/const-traits/const-opaque.rs index bfcadd521a54e..96cdd7d9f2619 100644 --- a/tests/ui/traits/const-traits/const-opaque.rs +++ b/tests/ui/traits/const-traits/const-opaque.rs @@ -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 `impl Foo: const Foo` is not satisfied + //[no]~^ ERROR the trait bound `(): const Foo` is not satisfied std::mem::forget(opaque); };