From 7c2a24b50c48c9716a88e3053064a7d4470b011f Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 24 Aug 2024 14:01:00 +0800 Subject: [PATCH] properly elaborate effects implied bounds for super traits --- compiler/rustc_hir_analysis/src/bounds.rs | 44 +++++++++++-- .../src/collect/item_bounds.rs | 29 +-------- compiler/rustc_middle/src/ty/context.rs | 2 + compiler/rustc_type_ir/src/elaborate.rs | 65 +++++++++++++++++++ compiler/rustc_type_ir/src/lang_items.rs | 2 + .../super-traits-fail-3.rs | 2 +- .../super-traits-fail-3.yy.stderr | 22 ------- .../super-traits-fail.rs | 2 +- .../super-traits-fail.stderr | 23 +++++-- .../rfc-2632-const-trait-impl/super-traits.rs | 3 +- .../super-traits.stderr | 22 ------- 11 files changed, 132 insertions(+), 84 deletions(-) delete mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yy.stderr delete mode 100644 tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits.stderr diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index caf9960741d71..8947e7a22167b 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -91,12 +91,46 @@ impl<'tcx> Bounds<'tcx> { } tcx.consts.true_ } + (DefKind::Trait, ty::BoundConstness::ConstIfConst) => { + // we are in a trait, where `bound_trait_ref` could be: + // (1) a super trait `trait Foo: ~const Bar`. + // - This generates `::Effects: TyCompat<::Effects>` + // + // (2) a where clause `where for<..> Something: ~const Bar`. + // - This generates `for<..> ::Effects: TyCompat<::Effects>` + let Some(own_fx) = tcx.associated_type_for_effects(defining_def_id) else { + tcx.dcx().span_delayed_bug(span, "should not have allowed `~const` on a trait that doesn't have `#[const_trait]`"); + return; + }; + let own_fx_ty = Ty::new_projection( + tcx, + own_fx, + ty::GenericArgs::identity_for_item(tcx, own_fx), + ); + let Some(their_fx) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) + else { + tcx.dcx().span_delayed_bug(span, "`~const` on trait without Effects assoc"); + return; + }; + let their_fx_ty = + Ty::new_projection(tcx, their_fx, bound_trait_ref.skip_binder().args); + let compat = tcx.require_lang_item(LangItem::EffectsTyCompat, Some(span)); + let clause = bound_trait_ref + .map_bound(|_| { + let trait_ref = ty::TraitRef::new(tcx, compat, [own_fx_ty, their_fx_ty]); + ty::ClauseKind::Trait(ty::TraitPredicate { + trait_ref, + polarity: ty::PredicatePolarity::Positive, + }) + }) + .upcast(tcx); + + self.clauses.push((clause, span)); + return; + } - ( - DefKind::Trait | DefKind::Impl { of_trait: true }, - ty::BoundConstness::ConstIfConst, - ) => { - // this is either a where clause on an impl/trait header or on a trait. + (DefKind::Impl { of_trait: true }, ty::BoundConstness::ConstIfConst) => { + // this is a where clause on an impl header. // push `::Effects` into the set for the `Min` bound. let Some(assoc) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) else { tcx.dcx().span_delayed_bug(span, "`~const` on trait without Effects assoc"); diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 7557219aaa693..f44b4728ad533 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -360,33 +360,10 @@ pub(super) fn explicit_item_bounds_with_filter( None => {} } - if tcx.is_effects_desugared_assoc_ty(def_id.to_def_id()) { - let mut predicates = Vec::new(); - - let parent = tcx.local_parent(def_id); - - let preds = tcx.explicit_predicates_of(parent); - - if let ty::AssocItemContainer::TraitContainer = tcx.associated_item(def_id).container { - // for traits, emit `type Effects: TyCompat<<(T1::Effects, ..) as Min>::Output>` - let tup = Ty::new(tcx, ty::Tuple(preds.effects_min_tys)); - // FIXME(effects) span - let span = tcx.def_span(def_id); - let assoc = tcx.require_lang_item(hir::LangItem::EffectsIntersectionOutput, Some(span)); - let proj = Ty::new_projection(tcx, assoc, [tup]); - let self_proj = Ty::new_projection( - tcx, - def_id.to_def_id(), - ty::GenericArgs::identity_for_item(tcx, def_id), - ); - let trait_ = tcx.require_lang_item(hir::LangItem::EffectsTyCompat, Some(span)); - let trait_ref = ty::TraitRef::new(tcx, trait_, [self_proj, proj]); - predicates.push((ty::Binder::dummy(trait_ref).upcast(tcx), span)); - } - return ty::EarlyBinder::bind(tcx.arena.alloc_from_iter(predicates)); - } - let bounds = match tcx.hir_node_by_def_id(def_id) { + _ if tcx.is_effects_desugared_assoc_ty(def_id.to_def_id()) => { + associated_type_bounds(tcx, def_id, &[], tcx.def_span(def_id), filter) + } hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Type(bounds, _), span, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f017216489d0c..f1732e4859d52 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -622,11 +622,13 @@ bidirectional_lang_item_map! { Destruct, DiscriminantKind, DynMetadata, + EffectsCompat, EffectsIntersection, EffectsIntersectionOutput, EffectsMaybe, EffectsNoRuntime, EffectsRuntime, + EffectsTyCompat, Fn, FnMut, FnOnce, diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index 3db9b8b06610e..61736633cfa28 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -4,6 +4,7 @@ use smallvec::smallvec; use crate::data_structures::HashSet; use crate::inherent::*; +use crate::lang_items::TraitSolverLangItem; use crate::outlives::{Component, push_outlives_components}; use crate::{self as ty, Interner, Upcast as _}; @@ -89,6 +90,70 @@ impl> Elaborator { return; } + // HACK(effects): The following code is required to get implied bounds for effects associated + // types to work with super traits. + // + // Suppose `data` is a trait predicate with the form `::Fx: EffectsCompat` + // and we know that `trait Tr: ~const SuperTr`, we need to elaborate this predicate into + // `::Fx: EffectsCompat`. + // + // Since the semantics for elaborating bounds about effects is equivalent to elaborating + // bounds about super traits (elaborate `T: Tr` into `T: SuperTr`), we place effects elaboration + // next to super trait elaboration. + if cx.is_lang_item(data.def_id(), TraitSolverLangItem::EffectsCompat) + && matches!(self.mode, Filter::All) + { + // first, ensure that the predicate we've got looks like a `::Fx: EffectsCompat`. + if let ty::Alias(ty::AliasTyKind::Projection, alias_ty) = data.self_ty().kind() + { + // look for effects-level bounds that look like `::Fx: TyCompat<::Fx>` + // on the trait, which is proof to us that `Tr: ~const SuperTr`. We're looking for bounds on the + // associated trait, so we use `explicit_implied_predicates_of` since it gives us more than just + // `Self: SuperTr` bounds. + let bounds = cx.explicit_implied_predicates_of(cx.parent(alias_ty.def_id)); + + // instantiate the implied bounds, so we get `::Fx` and not `::Fx`. + let elaborated = bounds.iter_instantiated(cx, alias_ty.args).filter_map( + |(clause, _)| { + let ty::ClauseKind::Trait(tycompat_bound) = + clause.kind().skip_binder() + else { + return None; + }; + if !cx.is_lang_item( + tycompat_bound.def_id(), + TraitSolverLangItem::EffectsTyCompat, + ) { + return None; + } + + // extract `::Fx` from the `TyCompat` bound. + let supertrait_effects_ty = + tycompat_bound.trait_ref.args.type_at(1); + let ty::Alias(ty::AliasTyKind::Projection, supertrait_alias_ty) = + supertrait_effects_ty.kind() + else { + return None; + }; + + // The self types (`T`) must be equal for `::Fx` and `::Fx`. + if supertrait_alias_ty.self_ty() != alias_ty.self_ty() { + return None; + }; + + // replace the self type in the original bound `::Fx: EffectsCompat` + // to the effects type of the super trait. (`::Fx`) + let elaborated_bound = data.with_self_ty(cx, supertrait_effects_ty); + Some( + elaboratable + .child(bound_clause.rebind(elaborated_bound).upcast(cx)), + ) + }, + ); + self.extend_deduped(elaborated); + } + } + let map_to_child_clause = |(index, (clause, span)): (usize, (I::Clause, I::Span))| { elaboratable.child_with_derived_cause( diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index 265a411882735..c680c8447460d 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -20,11 +20,13 @@ pub enum TraitSolverLangItem { Destruct, DiscriminantKind, DynMetadata, + EffectsCompat, EffectsIntersection, EffectsIntersectionOutput, EffectsMaybe, EffectsNoRuntime, EffectsRuntime, + EffectsTyCompat, Fn, FnMut, FnOnce, diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs index 6694351265007..c7e224dcce02c 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs @@ -3,7 +3,7 @@ #![feature(const_trait_impl, effects)] //@ revisions: yy yn ny nn -//@[yy] known-bug: #110395 +//@[yy] check-pass #[cfg_attr(any(yy, yn), const_trait)] trait Foo { diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yy.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yy.stderr deleted file mode 100644 index ea0e6c690b72b..0000000000000 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yy.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error[E0277]: the trait bound `Foo::{synthetic#0}: ~const Compat` is not satisfied - --> $DIR/super-traits-fail-3.rs:22:7 - | -LL | x.a(); - | ^ the trait `~const Compat` is not implemented for `Foo::{synthetic#0}` - | -note: required by a bound in `Foo::a` - --> $DIR/super-traits-fail-3.rs:8:25 - | -LL | #[cfg_attr(any(yy, yn), const_trait)] - | ^^^^^^^^^^^ required by this bound in `Foo::a` -LL | trait Foo { -LL | fn a(&self); - | - required by a bound in this associated function -help: consider further restricting the associated type - | -LL | const fn foo(x: &T) where Foo::{synthetic#0}: ~const Compat { - | +++++++++++++++++++++++++++++++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.rs index 6c320c0462e16..da41d7fcc723a 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.rs @@ -17,6 +17,6 @@ impl Foo for S { } impl const Bar for S {} -// FIXME(effects) bad span +//~^ ERROR the trait bound fn main() {} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.stderr index 9a907bbee0a78..3870f0f722f99 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.stderr @@ -1,11 +1,24 @@ -error[E0277]: the trait bound `Maybe: TyCompat<<(Foo::{synthetic#0},) as std::marker::effects::Intersection>::Output>` is not satisfied +error[E0277]: the trait bound `Bar::{synthetic#0}: TyCompat` is not satisfied + --> $DIR/super-traits-fail.rs:19:12 + | +LL | impl const Bar for S {} + | ^^^ the trait `TyCompat` is not implemented for `Bar::{synthetic#0}`, which is required by `S: Bar` + | + = help: the trait `Bar` is implemented for `S` +note: required for `S` to implement `Bar` + --> $DIR/super-traits-fail.rs:12:7 + | +LL | trait Bar: ~const Foo {} + | ^^^ + +error[E0277]: the trait bound `Maybe: TyCompat` is not satisfied | note: required by a bound in `Bar::{synthetic#0}` - --> $DIR/super-traits-fail.rs:11:1 + --> $DIR/super-traits-fail.rs:12:12 | -LL | #[const_trait] - | ^^^^^^^^^^^^^^ required by this bound in `Bar::{synthetic#0}` +LL | trait Bar: ~const Foo {} + | ^^^^^^^^^^ required by this bound in `Bar::{synthetic#0}` -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits.rs index fbe89b00b974b..ff7349bba3cf2 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits.rs @@ -1,5 +1,4 @@ -// FIXME(effects) check-pass -//@ known-bug: #110395 +//@ check-pass //@ compile-flags: -Znext-solver #![allow(incomplete_features)] #![feature(const_trait_impl, effects)] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits.stderr deleted file mode 100644 index 5b6b39ee05ef6..0000000000000 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error[E0277]: the trait bound `Foo::{synthetic#0}: ~const Compat` is not satisfied - --> $DIR/super-traits.rs:23:7 - | -LL | t.a(); - | ^ the trait `~const Compat` is not implemented for `Foo::{synthetic#0}` - | -note: required by a bound in `Foo::a` - --> $DIR/super-traits.rs:7:1 - | -LL | #[const_trait] - | ^^^^^^^^^^^^^^ required by this bound in `Foo::a` -LL | trait Foo { -LL | fn a(&self); - | - required by a bound in this associated function -help: consider further restricting the associated type - | -LL | const fn foo(t: &T) where Foo::{synthetic#0}: ~const Compat { - | +++++++++++++++++++++++++++++++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`.