about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDeadbeef <ent3rm4n@gmail.com>2024-06-14 12:16:15 +0000
committerDeadbeef <ent3rm4n@gmail.com>2024-06-28 10:57:35 +0000
commit72e8244e64b284f8f93a778e804b59c10305fc59 (patch)
tree753376943d4ce70ac6da8f56f7e24b3e4f000e97
parent99f77a2eda555b50b518f74823ab636a20efb87f (diff)
implement new effects desugaring
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs1
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs111
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs91
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs9
-rw-r--r--compiler/rustc_const_eval/src/const_eval/fn_queries.rs5
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs4
-rw-r--r--compiler/rustc_hir/src/hir.rs2
-rw-r--r--compiler/rustc_hir/src/intravisit.rs3
-rw-r--r--compiler/rustc_hir/src/lang_items.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/bounds.rs102
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs49
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs65
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs6
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs15
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs1
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs5
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs3
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs6
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs2
-rw-r--r--compiler/rustc_middle/src/query/mod.rs8
-rw-r--r--compiler/rustc_middle/src/ty/assoc.rs2
-rw-r--r--compiler/rustc_middle/src/ty/context.rs4
-rw-r--r--compiler/rustc_middle/src/ty/generics.rs3
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs12
-rw-r--r--compiler/rustc_smir/src/rustc_smir/context.rs6
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/ty.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs9
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs6
-rw-r--r--compiler/rustc_ty_utils/src/assoc.rs168
-rw-r--r--library/core/src/lib.rs1
-rw-r--r--library/core/src/marker.rs38
-rw-r--r--src/librustdoc/clean/mod.rs14
-rw-r--r--src/librustdoc/clean/types.rs4
-rw-r--r--src/librustdoc/json/conversions.rs16
-rw-r--r--tests/ui/const-generics/const_trait_fn-issue-88433.rs1
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/effects/group-traits.rs14
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs3
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr11
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs1
50 files changed, 636 insertions, 228 deletions
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 624bca525b9..218fa974022 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -103,7 +103,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         ParamMode::Optional,
                         ParenthesizedGenericArgs::Err,
                         ImplTraitContext::Disallowed(ImplTraitPosition::Path),
-                        None,
                         // Method calls can't have bound modifiers
                         None,
                     ));
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 74e04eff4f3..d59ac576629 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -56,7 +56,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
         owner: NodeId,
         f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>,
     ) {
-        let mut lctx = LoweringContext::new(self.tcx, self.resolver);
+        let mut lctx = LoweringContext::new(self.tcx, self.resolver, self.ast_index);
         lctx.with_hir_id_owner(owner, |lctx| f(lctx));
 
         for (def_id, info) in lctx.children {
@@ -190,6 +190,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, (ty, body_id)) = self.lower_generics(
                     generics,
                     Const::No,
+                    false,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -221,7 +222,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
                     let itctx = ImplTraitContext::Universal;
                     let (generics, decl) =
-                        this.lower_generics(generics, header.constness, id, itctx, |this| {
+                        this.lower_generics(generics, header.constness, false, id, itctx, |this| {
                             this.lower_fn_decl(
                                 decl,
                                 id,
@@ -265,6 +266,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, ty) = self.lower_generics(
                     &generics,
                     Const::No,
+                    false,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| match ty {
@@ -293,6 +295,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, variants) = self.lower_generics(
                     generics,
                     Const::No,
+                    false,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -307,6 +310,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, struct_def) = self.lower_generics(
                     generics,
                     Const::No,
+                    false,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| this.lower_variant_data(hir_id, struct_def),
@@ -317,6 +321,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, vdata) = self.lower_generics(
                     generics,
                     Const::No,
+                    false,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| this.lower_variant_data(hir_id, vdata),
@@ -348,12 +353,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 // parent lifetime.
                 let itctx = ImplTraitContext::Universal;
                 let (generics, (trait_ref, lowered_ty)) =
-                    self.lower_generics(ast_generics, *constness, id, itctx, |this| {
+                    self.lower_generics(ast_generics, Const::No, false, id, itctx, |this| {
                         let modifiers = TraitBoundModifiers {
-                            constness: match *constness {
-                                Const::Yes(span) => BoundConstness::Maybe(span),
-                                Const::No => BoundConstness::Never,
-                            },
+                            constness: BoundConstness::Never,
                             asyncness: BoundAsyncness::Normal,
                             // we don't use this in bound lowering
                             polarity: BoundPolarity::Positive,
@@ -389,6 +391,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(*s)),
                 };
                 hir::ItemKind::Impl(self.arena.alloc(hir::Impl {
+                    constness: self.lower_constness(*constness),
                     safety: self.lower_safety(*safety, hir::Safety::Safe),
                     polarity,
                     defaultness,
@@ -400,15 +403,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 }))
             }
             ItemKind::Trait(box Trait { is_auto, safety, generics, bounds, items }) => {
-                // FIXME(const_trait_impl, effects, fee1-dead) this should be simplified if possible
-                let constness = attrs
-                    .unwrap_or(&[])
-                    .iter()
-                    .find(|x| x.has_name(sym::const_trait))
-                    .map_or(Const::No, |x| Const::Yes(x.span));
                 let (generics, (safety, items, bounds)) = self.lower_generics(
                     generics,
-                    constness,
+                    Const::No,
+                    false,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -429,6 +427,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, bounds) = self.lower_generics(
                     generics,
                     Const::No,
+                    false,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -609,30 +608,45 @@ impl<'hir> LoweringContext<'_, 'hir> {
         // This is used to track which lifetimes have already been defined,
         // and which need to be replicated when lowering an async fn.
 
-        let generics = match parent_hir.node().expect_item().kind {
+        let parent_item = parent_hir.node().expect_item();
+        let constness = match parent_item.kind {
             hir::ItemKind::Impl(impl_) => {
                 self.is_in_trait_impl = impl_.of_trait.is_some();
-                &impl_.generics
+                // N.B. the impl should always lower to methods that have `const host: bool` params if the trait
+                // is const. It doesn't matter whether the `impl` itself is const. Disallowing const fn from
+                // calling non-const impls are done through associated types.
+                if let Some(def_id) = impl_.of_trait.and_then(|tr| tr.trait_def_id()) {
+                    if let Some(local_def) = def_id.as_local() {
+                        match &self.ast_index[local_def] {
+                            AstOwner::Item(ast::Item { attrs, .. }) => attrs
+                                .iter()
+                                .find(|attr| attr.has_name(sym::const_trait))
+                                .map_or(Const::No, |attr| Const::Yes(attr.span)),
+                            _ => Const::No,
+                        }
+                    } else {
+                        self.tcx
+                            .get_attr(def_id, sym::const_trait)
+                            .map_or(Const::No, |attr| Const::Yes(attr.span))
+                    }
+                } else {
+                    Const::No
+                }
             }
-            hir::ItemKind::Trait(_, _, generics, _, _) => generics,
+            hir::ItemKind::Trait(_, _, _, _, _) => parent_hir
+                .attrs
+                .get(parent_item.hir_id().local_id)
+                .iter()
+                .find(|attr| attr.has_name(sym::const_trait))
+                .map_or(Const::No, |attr| Const::Yes(attr.span)),
             kind => {
                 span_bug!(item.span, "assoc item has unexpected kind of parent: {}", kind.descr())
             }
         };
 
-        if self.tcx.features().effects {
-            self.host_param_id = generics
-                .params
-                .iter()
-                .find(|param| {
-                    matches!(param.kind, hir::GenericParamKind::Const { is_host_effect: true, .. })
-                })
-                .map(|param| param.def_id);
-        }
-
         match ctxt {
-            AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item)),
-            AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item)),
+            AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item, constness)),
+            AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item, constness)),
         }
     }
 
@@ -648,7 +662,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     let fdec = &sig.decl;
                     let itctx = ImplTraitContext::Universal;
                     let (generics, (fn_dec, fn_args)) =
-                        self.lower_generics(generics, Const::No, i.id, itctx, |this| {
+                        self.lower_generics(generics, Const::No, false, i.id, itctx, |this| {
                             (
                                 // Disallow `impl Trait` in foreign items.
                                 this.lower_fn_decl(
@@ -765,7 +779,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
         }
     }
 
-    fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> {
+    fn lower_trait_item(
+        &mut self,
+        i: &AssocItem,
+        trait_constness: Const,
+    ) -> &'hir hir::TraitItem<'hir> {
         let hir_id = self.lower_node_id(i.id);
         self.lower_attrs(hir_id, &i.attrs);
         let trait_item_def_id = hir_id.expect_owner();
@@ -775,6 +793,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, kind) = self.lower_generics(
                     generics,
                     Const::No,
+                    false,
                     i.id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -795,6 +814,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     i.id,
                     FnDeclKind::Trait,
                     sig.header.coroutine_kind,
+                    trait_constness,
                 );
                 (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
             }
@@ -813,6 +833,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     i.id,
                     FnDeclKind::Trait,
                     sig.header.coroutine_kind,
+                    trait_constness,
                 );
                 (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
             }
@@ -822,6 +843,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, kind) = self.lower_generics(
                     &generics,
                     Const::No,
+                    false,
                     i.id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -894,7 +916,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
         self.expr(span, hir::ExprKind::Err(guar))
     }
 
-    fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> {
+    fn lower_impl_item(
+        &mut self,
+        i: &AssocItem,
+        constness_of_trait: Const,
+    ) -> &'hir hir::ImplItem<'hir> {
         // Since `default impl` is not yet implemented, this is always true in impls.
         let has_value = true;
         let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value);
@@ -905,6 +931,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics(
                 generics,
                 Const::No,
+                false,
                 i.id,
                 ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                 |this| {
@@ -930,6 +957,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     i.id,
                     if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
                     sig.header.coroutine_kind,
+                    constness_of_trait,
                 );
 
                 (generics, hir::ImplItemKind::Fn(sig, body_id))
@@ -940,6 +968,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 self.lower_generics(
                     &generics,
                     Const::No,
+                    false,
                     i.id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| match ty {
@@ -1352,15 +1381,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
         id: NodeId,
         kind: FnDeclKind,
         coroutine_kind: Option<CoroutineKind>,
+        parent_constness: Const,
     ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
         let header = self.lower_fn_header(sig.header);
         // Don't pass along the user-provided constness of trait associated functions; we don't want to
         // synthesize a host effect param for them. We reject `const` on them during AST validation.
-        let constness = if kind == FnDeclKind::Inherent { sig.header.constness } else { Const::No };
+        let constness =
+            if kind == FnDeclKind::Inherent { sig.header.constness } else { parent_constness };
         let itctx = ImplTraitContext::Universal;
-        let (generics, decl) = self.lower_generics(generics, constness, id, itctx, |this| {
-            this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
-        });
+        let (generics, decl) =
+            self.lower_generics(generics, constness, kind == FnDeclKind::Impl, id, itctx, |this| {
+                this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
+            });
         (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
     }
 
@@ -1436,6 +1468,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         &mut self,
         generics: &Generics,
         constness: Const,
+        force_append_constness: bool,
         parent_node_id: NodeId,
         itctx: ImplTraitContext,
         f: impl FnOnce(&mut Self) -> T,
@@ -1496,7 +1529,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
         // if the effects feature is enabled. This needs to be done before we lower where
         // clauses since where clauses need to bind to the DefId of the host param
         let host_param_parts = if let Const::Yes(span) = constness
-            && self.tcx.features().effects
+            // if this comes from implementing a `const` trait, we must force constness to be appended
+            // to the impl item, no matter whether effects is enabled.
+            && (self.tcx.features().effects || force_append_constness)
         {
             let span = self.lower_span(span);
             let param_node_id = self.next_node_id();
@@ -1609,6 +1644,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                             }),
                         )),
                     )),
+                    // FIXME(effects) we might not need a default.
                     default: Some(self.arena.alloc(hir::AnonConst {
                         def_id: anon_const,
                         hir_id: const_id,
@@ -1616,6 +1652,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         span,
                     })),
                     is_host_effect: true,
+                    synthetic: true,
                 },
                 colon_span: None,
                 pure_wrt_drop: false,
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 0a06304fcec..8fba46625ab 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -142,14 +142,19 @@ struct LoweringContext<'a, 'hir> {
     generics_def_id_map: Vec<LocalDefIdMap<LocalDefId>>,
 
     host_param_id: Option<LocalDefId>,
+    ast_index: &'a IndexSlice<LocalDefId, AstOwner<'a>>,
 }
 
 impl<'a, 'hir> LoweringContext<'a, 'hir> {
-    fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self {
+    fn new(
+        tcx: TyCtxt<'hir>,
+        resolver: &'a mut ResolverAstLowering,
+        ast_index: &'a IndexSlice<LocalDefId, AstOwner<'a>>,
+    ) -> Self {
         Self {
             // Pseudo-globals.
             tcx,
-            resolver: resolver,
+            resolver,
             arena: tcx.hir_arena,
 
             // HirId handling.
@@ -185,6 +190,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
             generics_def_id_map: Default::default(),
             host_param_id: None,
+            ast_index,
         }
     }
 
@@ -2135,7 +2141,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         param: &GenericParam,
         source: hir::GenericParamSource,
     ) -> hir::GenericParam<'hir> {
-        let (name, kind) = self.lower_generic_param_kind(param, source);
+        let (name, kind) = self.lower_generic_param_kind(
+            param,
+            source,
+            attr::contains_name(&param.attrs, sym::rustc_runtime),
+        );
 
         let hir_id = self.lower_node_id(param.id);
         self.lower_attrs(hir_id, &param.attrs);
@@ -2155,6 +2165,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         &mut self,
         param: &GenericParam,
         source: hir::GenericParamSource,
+        is_host_effect: bool,
     ) -> (hir::ParamName, hir::GenericParamKind<'hir>) {
         match &param.kind {
             GenericParamKind::Lifetime => {
@@ -2220,7 +2231,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
                 (
                     hir::ParamName::Plain(self.lower_ident(param.ident)),
-                    hir::GenericParamKind::Const { ty, default, is_host_effect: false },
+                    hir::GenericParamKind::Const { ty, default, is_host_effect, synthetic: false },
                 )
             }
         }
@@ -2607,78 +2618,6 @@ struct GenericArgsCtor<'hir> {
 }
 
 impl<'hir> GenericArgsCtor<'hir> {
-    fn push_constness(
-        &mut self,
-        lcx: &mut LoweringContext<'_, 'hir>,
-        constness: ast::BoundConstness,
-    ) {
-        if !lcx.tcx.features().effects {
-            return;
-        }
-
-        let (span, body) = match constness {
-            BoundConstness::Never => return,
-            BoundConstness::Always(span) => {
-                let span = lcx.lower_span(span);
-
-                let body = hir::ExprKind::Lit(
-                    lcx.arena.alloc(hir::Lit { node: LitKind::Bool(false), span }),
-                );
-
-                (span, body)
-            }
-            BoundConstness::Maybe(span) => {
-                let span = lcx.lower_span(span);
-
-                let Some(host_param_id) = lcx.host_param_id else {
-                    lcx.dcx().span_delayed_bug(
-                        span,
-                        "no host param id for call in const yet no errors reported",
-                    );
-                    return;
-                };
-
-                let hir_id = lcx.next_id();
-                let res = Res::Def(DefKind::ConstParam, host_param_id.to_def_id());
-                let body = hir::ExprKind::Path(hir::QPath::Resolved(
-                    None,
-                    lcx.arena.alloc(hir::Path {
-                        span,
-                        res,
-                        segments: arena_vec![
-                            lcx;
-                            hir::PathSegment::new(
-                                Ident { name: sym::host, span },
-                                hir_id,
-                                res
-                            )
-                        ],
-                    }),
-                ));
-
-                (span, body)
-            }
-        };
-        let body = lcx.lower_body(|lcx| (&[], lcx.expr(span, body)));
-
-        let id = lcx.next_node_id();
-        let hir_id = lcx.next_id();
-
-        let def_id = lcx.create_def(
-            lcx.current_hir_id_owner.def_id,
-            id,
-            kw::Empty,
-            DefKind::AnonConst,
-            span,
-        );
-
-        lcx.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id)));
-        self.args.push(hir::GenericArg::Const(hir::ConstArg {
-            value: lcx.arena.alloc(hir::AnonConst { def_id, hir_id, body, span }),
-            is_desugared_from_effects: true,
-        }))
-    }
-
     fn is_empty(&self) -> bool {
         self.args.is_empty()
             && self.constraints.is_empty()
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index 9d38e1e6784..9a1ca703699 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -107,8 +107,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         param_mode,
                         parenthesized_generic_args,
                         itctx,
-                        // if this is the last segment, add constness to the trait path
-                        if i == proj_start - 1 { modifiers.map(|m| m.constness) } else { None },
                         bound_modifier_allowed_features.clone(),
                     )
                 },
@@ -165,7 +163,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 ParenthesizedGenericArgs::Err,
                 itctx,
                 None,
-                None,
             ));
             let qpath = hir::QPath::TypeRelative(ty, hir_segment);
 
@@ -208,7 +205,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     ParenthesizedGenericArgs::Err,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Path),
                     None,
-                    None,
                 )
             })),
             span: self.lower_span(p.span),
@@ -222,7 +218,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         param_mode: ParamMode,
         parenthesized_generic_args: ParenthesizedGenericArgs,
         itctx: ImplTraitContext,
-        constness: Option<ast::BoundConstness>,
         // Additional features ungated with a bound modifier like `async`.
         // This is passed down to the implicit associated type binding in
         // parenthesized bounds.
@@ -289,10 +284,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             )
         };
 
-        if let Some(constness) = constness {
-            generic_args.push_constness(self, constness);
-        }
-
         let has_lifetimes =
             generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));
 
diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
index 8c66888d100..7acd08e0cce 100644
--- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
@@ -42,10 +42,7 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness {
         | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => {
             hir::Constness::Const
         }
-        hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }) => tcx
-            .generics_of(def_id)
-            .host_effect_index
-            .map_or(hir::Constness::NotConst, |_| hir::Constness::Const),
+        hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.constness,
         hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(..), .. }) => {
             // Intrinsics use `rustc_const_{un,}stable` attributes to indicate constness. All other
             // foreign items cannot be evaluated at compile-time.
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index f884f996927..a4245f0908e 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -833,6 +833,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         rustc_const_panic_str, Normal, template!(Word), WarnFollowing,
         EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
     ),
+    rustc_attr!(
+        rustc_runtime, Normal, template!(Word), WarnFollowing,
+        EncodeCrossCrate::No, INTERNAL_UNSTABLE
+    ),
 
     // ==========================================================================
     // Internal attributes, Layout related:
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 22a6c06bba3..6cccdec94c0 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -526,6 +526,7 @@ pub enum GenericParamKind<'hir> {
         /// Optional default value for the const generic param
         default: Option<&'hir AnonConst>,
         is_host_effect: bool,
+        synthetic: bool,
     },
 }
 
@@ -3364,6 +3365,7 @@ pub enum ItemKind<'hir> {
 /// Refer to [`ImplItem`] for an associated item within an impl block.
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct Impl<'hir> {
+    pub constness: Constness,
     pub safety: Safety,
     pub polarity: ImplPolarity,
     pub defaultness: Defaultness,
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 065ecc5d7b7..9bb3245ae05 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -543,6 +543,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::
             try_visit!(visitor.visit_enum_def(enum_definition, item.hir_id()));
         }
         ItemKind::Impl(Impl {
+            constness: _,
             safety: _,
             defaultness: _,
             polarity: _,
@@ -915,7 +916,7 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>(
     match param.kind {
         GenericParamKind::Lifetime { .. } => {}
         GenericParamKind::Type { ref default, .. } => visit_opt!(visitor, visit_ty, default),
-        GenericParamKind::Const { ref ty, ref default, is_host_effect: _ } => {
+        GenericParamKind::Const { ref ty, ref default, is_host_effect: _, synthetic: _ } => {
             try_visit!(visitor.visit_ty(ty));
             if let Some(ref default) = default {
                 try_visit!(visitor.visit_const_param_default(param.hir_id, default));
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 69461957f80..d49eb4ece99 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -393,6 +393,14 @@ language_item_table! {
 
     String,                  sym::String,              string,                     Target::Struct,         GenericRequirement::None;
     CStr,                    sym::CStr,                c_str,                      Target::Struct,         GenericRequirement::None;
+
+    EffectsRuntime,          sym::EffectsRuntime,      effects_runtime,            Target::Struct,         GenericRequirement::None;
+    EffectsNoRuntime,        sym::EffectsNoRuntime,    effects_no_runtime,         Target::Struct,         GenericRequirement::None;
+    EffectsMaybe,            sym::EffectsMaybe,        effects_maybe,              Target::Struct,         GenericRequirement::None;
+    EffectsMin,              sym::EffectsMin,          effects_min,                Target::Trait,          GenericRequirement::None;
+    EffectsMinOutput,        sym::EffectsMinOutput,    effects_min_output,         Target::AssocTy,        GenericRequirement::None;
+    EffectsCompat,           sym::EffectsCompat,       effects_compat,             Target::Trait,          GenericRequirement::Exact(1);
+    EffectsTyCompat,         sym::EffectsTyCompat,     effects_ty_compat,          Target::Trait,          GenericRequirement::Exact(1);
 }
 
 pub enum GenericRequirement {
diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs
index 7f0d72b3a8d..18a29c10b61 100644
--- a/compiler/rustc_hir_analysis/src/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/bounds.rs
@@ -1,9 +1,13 @@
 //! Bounds are restrictions applied to some types after they've been lowered from the HIR to the
 //! [`rustc_middle::ty`] form.
 
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_hir::def::DefKind;
 use rustc_hir::LangItem;
+use rustc_middle::ty::fold::FnMutDelegate;
 use rustc_middle::ty::{self, Ty, TyCtxt, Upcast};
-use rustc_span::Span;
+use rustc_span::def_id::DefId;
+use rustc_span::{sym, Span};
 
 /// Collects together a list of type bounds. These lists of bounds occur in many places
 /// in Rust's syntax:
@@ -24,6 +28,7 @@ use rustc_span::Span;
 #[derive(Default, PartialEq, Eq, Clone, Debug)]
 pub struct Bounds<'tcx> {
     clauses: Vec<(ty::Clause<'tcx>, Span)>,
+    effects_min_tys: FxIndexMap<Ty<'tcx>, Span>,
 }
 
 impl<'tcx> Bounds<'tcx> {
@@ -40,12 +45,14 @@ impl<'tcx> Bounds<'tcx> {
     pub fn push_trait_bound(
         &mut self,
         tcx: TyCtxt<'tcx>,
-        trait_ref: ty::PolyTraitRef<'tcx>,
+        defining_def_id: DefId,
+        bound_trait_ref: ty::PolyTraitRef<'tcx>,
         span: Span,
         polarity: ty::PredicatePolarity,
+        constness: ty::BoundConstness,
     ) {
         let clause = (
-            trait_ref
+            bound_trait_ref
                 .map_bound(|trait_ref| {
                     ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity })
                 })
@@ -53,11 +60,88 @@ impl<'tcx> Bounds<'tcx> {
             span,
         );
         // FIXME(-Znext-solver): We can likely remove this hack once the new trait solver lands.
-        if tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) {
+        if tcx.is_lang_item(bound_trait_ref.def_id(), LangItem::Sized) {
             self.clauses.insert(0, clause);
         } else {
             self.clauses.push(clause);
         }
+        // For `T: ~const Tr` or `T: const Tr`, we need to add an additional bound on the
+        // associated type of `<T as Tr>` and make sure that the effect is compatible.
+        if let Some(compat_val) = match (tcx.def_kind(defining_def_id), constness) {
+            // TODO: do we need `T: const Trait` anymore?
+            (_, ty::BoundConstness::Const) => Some(tcx.consts.false_),
+            // body owners that can have trait bounds
+            (DefKind::Const | DefKind::Fn | DefKind::AssocFn, ty::BoundConstness::ConstIfConst) => {
+                Some(tcx.expected_host_effect_param_for_body(defining_def_id))
+            }
+
+            (_, ty::BoundConstness::NotConst) => {
+                tcx.has_attr(bound_trait_ref.def_id(), sym::const_trait).then_some(tcx.consts.true_)
+            }
+
+            (
+                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.
+                // push `<T as Tr>::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");
+                    return;
+                };
+
+                let ty = bound_trait_ref
+                    .map_bound(|trait_ref| Ty::new_projection(tcx, assoc, trait_ref.args));
+
+                // Replace the binder with dummy types/lifetimes. This should work for any
+                // binder as long as they don't have any bounds e.g. `for<T: Trait>`.
+                let ty = tcx.replace_bound_vars_uncached(
+                    ty,
+                    FnMutDelegate {
+                        regions: &mut |_| tcx.lifetimes.re_static,
+                        types: &mut |_| tcx.types.unit,
+                        consts: &mut |_| unimplemented!("`~const` does not support const binders"),
+                    },
+                );
+
+                self.effects_min_tys.insert(ty, span);
+                return;
+            }
+            // for
+            // ```
+            // trait Foo { type Bar: ~const Trait }
+            // ```
+            // ensure that `<Self::Bar as Trait>::Effects: TyCompat<Self::Effects>`.
+            //
+            // FIXME(effects) this is equality for now, which wouldn't be helpful for a non-const implementor
+            // that uses a `Bar` that implements `Trait` with `Maybe` effects.
+            (DefKind::AssocTy, ty::BoundConstness::ConstIfConst) => {
+                // TODO write the actual impl
+                return;
+            }
+            // probably illegal in this position.
+            (_, ty::BoundConstness::ConstIfConst) => {
+                tcx.dcx().span_delayed_bug(span, "invalid `~const` encountered");
+                return;
+            }
+        } {
+            // create a new projection type `<T as Tr>::Effects`
+            let Some(assoc) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) else {
+                tcx.dcx().span_delayed_bug(
+                    span,
+                    "`~const` trait bound has no effect assoc yet no errors encountered?",
+                );
+                return;
+            };
+            let self_ty = Ty::new_projection(tcx, assoc, bound_trait_ref.skip_binder().args);
+            // make `<T as Tr>::Effects: Compat<runtime>`
+            let new_trait_ref = ty::TraitRef::new(
+                tcx,
+                tcx.require_lang_item(LangItem::EffectsCompat, Some(span)),
+                [ty::GenericArg::from(self_ty), compat_val.into()],
+            );
+            self.clauses.push((bound_trait_ref.rebind(new_trait_ref).upcast(tcx), span));
+        }
     }
 
     pub fn push_projection_bound(
@@ -79,7 +163,15 @@ impl<'tcx> Bounds<'tcx> {
         self.clauses.insert(0, (trait_ref.upcast(tcx), span));
     }
 
-    pub fn clauses(&self) -> impl Iterator<Item = (ty::Clause<'tcx>, Span)> + '_ {
+    pub fn clauses(
+        &self,
+        // TODO remove tcx
+        _tcx: TyCtxt<'tcx>,
+    ) -> impl Iterator<Item = (ty::Clause<'tcx>, Span)> + '_ {
         self.clauses.iter().cloned()
     }
+
+    pub fn effects_min_tys(&self) -> impl Iterator<Item = Ty<'tcx>> + '_ {
+        self.effects_min_tys.keys().copied()
+    }
 }
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index e13ea1a1935..e1813029778 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -879,7 +879,7 @@ pub(super) fn check_specialization_validity<'tcx>(
     let result = opt_result.unwrap_or(Ok(()));
 
     if let Err(parent_impl) = result {
-        if !tcx.is_impl_trait_in_trait(impl_item) {
+        if !tcx.is_impl_trait_in_trait(impl_item) && !tcx.is_effects_desugared_assoc_ty(impl_item) {
             report_forbidden_specialization(tcx, impl_item, parent_impl);
         } else {
             tcx.dcx().delayed_bug(format!("parent item: {parent_impl:?} not marked as default"));
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 7fa5c96bc9a..b5b68471b9d 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -1985,10 +1985,10 @@ pub(super) fn check_type_bounds<'tcx>(
     let infcx = tcx.infer_ctxt().build();
     let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
 
-    // A synthetic impl Trait for RPITIT desugaring has no HIR, which we currently use to get the
-    // span for an impl's associated type. Instead, for these, use the def_span for the synthesized
-    // associated type.
-    let impl_ty_span = if impl_ty.is_impl_trait_in_trait() {
+    // A synthetic impl Trait for RPITIT desugaring or assoc type for effects desugaring has no HIR,
+    // which we currently use to get the span for an impl's associated type. Instead, for these,
+    // use the def_span for the synthesized  associated type.
+    let impl_ty_span = if impl_ty.is_impl_trait_in_trait() || impl_ty.is_effects_desugaring {
         tcx.def_span(impl_ty_def_id)
     } else {
         match tcx.hir_node_by_def_id(impl_ty_def_id) {
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index a188c1b12ae..2230528a5ae 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -913,7 +913,12 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
         hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => Ok(()),
 
         // Const parameters are well formed if their type is structural match.
-        hir::GenericParamKind::Const { ty: hir_ty, default: _, is_host_effect: _ } => {
+        hir::GenericParamKind::Const {
+            ty: hir_ty,
+            default: _,
+            is_host_effect: _,
+            synthetic: _,
+        } => {
             let ty = tcx.type_of(param.def_id).instantiate_identity();
 
             if tcx.features().adt_const_params {
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index e7892f17660..f453dcfe90d 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -1638,44 +1638,19 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai
     let icx = ItemCtxt::new(tcx, def_id);
     let item = tcx.hir().expect_item(def_id);
     let impl_ = item.expect_impl();
-    impl_
-        .of_trait
-        .as_ref()
-        .map(|hir_trait_ref| {
-            let self_ty = tcx.type_of(def_id).instantiate_identity();
+    impl_.of_trait.as_ref().map(|ast_trait_ref| {
+        let selfty = tcx.type_of(def_id).instantiate_identity();
 
-            let trait_ref = if let Some(ErrorGuaranteed { .. }) = check_impl_constness(
-                tcx,
-                tcx.is_const_trait_impl_raw(def_id.to_def_id()),
-                hir_trait_ref,
-            ) {
-                // we have a const impl, but for a trait without `#[const_trait]`, so
-                // without the host param. If we continue with the HIR trait ref, we get
-                // ICEs for generic arg count mismatch. We do a little HIR editing to
-                // make HIR ty lowering happy.
-                let mut path_segments = hir_trait_ref.path.segments.to_vec();
-                let last_segment = path_segments.len() - 1;
-                let mut args = *path_segments[last_segment].args();
-                let last_arg = args.args.len() - 1;
-                assert!(matches!(args.args[last_arg], hir::GenericArg::Const(anon_const) if anon_const.is_desugared_from_effects));
-                args.args = &args.args[..args.args.len() - 1];
-                path_segments[last_segment].args = Some(tcx.hir_arena.alloc(args));
-                let path = hir::Path {
-                    span: hir_trait_ref.path.span,
-                    res: hir_trait_ref.path.res,
-                    segments: tcx.hir_arena.alloc_slice(&path_segments),
-                };
-                let trait_ref = tcx.hir_arena.alloc(hir::TraitRef { path: tcx.hir_arena.alloc(path), hir_ref_id: hir_trait_ref.hir_ref_id });
-                icx.lowerer().lower_impl_trait_ref(trait_ref, self_ty)
-            } else {
-                icx.lowerer().lower_impl_trait_ref(hir_trait_ref, self_ty)
-            };
-            ty::ImplTraitHeader {
-                trait_ref: ty::EarlyBinder::bind(trait_ref),
-                safety: impl_.safety,
-                polarity: polarity_of_impl(tcx, def_id, impl_, item.span)
-            }
-        })
+        check_impl_constness(tcx, tcx.is_const_trait_impl_raw(def_id.to_def_id()), ast_trait_ref);
+
+        let trait_ref = icx.lowerer().lower_impl_trait_ref(ast_trait_ref, selfty);
+
+        ty::ImplTraitHeader {
+            trait_ref: ty::EarlyBinder::bind(trait_ref),
+            safety: impl_.safety,
+            polarity: polarity_of_impl(tcx, def_id, impl_, item.span),
+        }
+    })
 }
 
 fn check_impl_constness(
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 303fa23dbc1..9b02c1a61fa 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -354,7 +354,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
                 kind,
             })
         }
-        GenericParamKind::Const { ty: _, default, is_host_effect } => {
+        GenericParamKind::Const { ty: _, default, is_host_effect, synthetic } => {
             if !matches!(allow_defaults, Defaults::Allowed)
                 && default.is_some()
                 // `host` effect params are allowed to have defaults.
@@ -388,6 +388,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
                 kind: ty::GenericParamDefKind::Const {
                     has_default: default.is_some(),
                     is_host_effect,
+                    synthetic,
                 },
             })
         }
@@ -541,7 +542,8 @@ struct AnonConstInParamTyDetector {
 
 impl<'v> Visitor<'v> for AnonConstInParamTyDetector {
     fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) {
-        if let GenericParamKind::Const { ty, default: _, is_host_effect: _ } = p.kind {
+        if let GenericParamKind::Const { ty, default: _, is_host_effect: _, synthetic: _ } = p.kind
+        {
             let prev = self.in_param_ty;
             self.in_param_ty = true;
             self.visit_ty(ty);
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index d084d3aefeb..57142414b9d 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -46,7 +46,7 @@ fn associated_type_bounds<'tcx>(
         }
     });
 
-    let all_bounds = tcx.arena.alloc_from_iter(bounds.clauses().chain(bounds_from_parent));
+    let all_bounds = tcx.arena.alloc_from_iter(bounds.clauses(tcx).chain(bounds_from_parent));
     debug!(
         "associated_type_bounds({}) = {:?}",
         tcx.def_path_str(assoc_item_def_id.to_def_id()),
@@ -75,7 +75,7 @@ fn opaque_type_bounds<'tcx>(
         icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
         debug!(?bounds);
 
-        tcx.arena.alloc_from_iter(bounds.clauses())
+        tcx.arena.alloc_from_iter(bounds.clauses(tcx))
     })
 }
 
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 3421c8da4e9..087e02a0305 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -57,6 +57,7 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic
 #[instrument(level = "trace", skip(tcx), ret)]
 fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::GenericPredicates<'_> {
     use rustc_hir::*;
+    use rustc_middle::ty::Ty; // to override hir::Ty
 
     match tcx.opt_rpitit_info(def_id.to_def_id()) {
         Some(ImplTraitInTraitData::Trait { fn_def_id, .. }) => {
@@ -84,6 +85,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
             return ty::GenericPredicates {
                 parent: Some(tcx.parent(def_id.to_def_id())),
                 predicates: tcx.arena.alloc_from_iter(predicates),
+                effects_min_tys: ty::List::empty(),
             };
         }
 
@@ -105,12 +107,43 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
             return ty::GenericPredicates {
                 parent: Some(impl_def_id),
                 predicates: tcx.arena.alloc_from_iter(impl_predicates),
+                effects_min_tys: ty::List::empty(),
             };
         }
 
         None => {}
     }
 
+    if tcx.is_effects_desugared_assoc_ty(def_id.to_def_id()) {
+        let mut predicates = Vec::new();
+
+        // Inherit predicates of parent (impl or trait)
+        let parent = tcx.local_parent(def_id);
+
+        let identity_args = ty::GenericArgs::identity_for_item(tcx, def_id);
+        let preds = tcx.explicit_predicates_of(parent);
+        predicates.extend(preds.instantiate_own(tcx, identity_args));
+        if let ty::AssocItemContainer::TraitContainer = tcx.associated_item(def_id).container {
+            // for traits, emit `type Effects: TyCompat<<(T1::Effects, ..) as Min>::Output>`
+            // TODO do the same for impls
+            let tup = Ty::new(tcx, ty::Tuple(preds.effects_min_tys));
+            // TODO span
+            let span = tcx.def_span(def_id);
+            let assoc = tcx.require_lang_item(LangItem::EffectsMinOutput, Some(span));
+            let proj = Ty::new_projection(tcx, assoc, [tup]);
+            // TODO this is bad
+            let self_proj = Ty::new_projection(tcx, def_id.to_def_id(), identity_args);
+            let trait_ = tcx.require_lang_item(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::GenericPredicates {
+            parent: Some(parent.to_def_id()),
+            predicates: tcx.arena.alloc_from_iter(predicates),
+            effects_min_tys: ty::List::empty(),
+        };
+    }
+
     let hir_id = tcx.local_def_id_to_hir_id(def_id);
     let node = tcx.hir_node(hir_id);
 
@@ -124,6 +157,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
     // We use an `IndexSet` to preserve order of insertion.
     // Preserving the order of insertion is important here so as not to break UI tests.
     let mut predicates: FxIndexSet<(ty::Clause<'_>, Span)> = FxIndexSet::default();
+    let mut effects_min_tys = Vec::new();
 
     let hir_generics = node.generics().unwrap_or(NO_GENERICS);
     if let Node::Item(item) = node {
@@ -150,11 +184,13 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
     // on a trait we must also consider the bounds that follow the trait's name,
     // like `trait Foo: A + B + C`.
     if let Some(self_bounds) = is_trait {
-        predicates.extend(
-            icx.lowerer()
-                .lower_mono_bounds(tcx.types.self_param, self_bounds, PredicateFilter::All)
-                .clauses(),
+        let bounds = icx.lowerer().lower_mono_bounds(
+            tcx.types.self_param,
+            self_bounds,
+            PredicateFilter::All,
         );
+        predicates.extend(bounds.clauses(tcx));
+        effects_min_tys.extend(bounds.effects_min_tys());
     }
 
     // In default impls, we can assume that the self type implements
@@ -187,7 +223,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
                     param.span,
                 );
                 trace!(?bounds);
-                predicates.extend(bounds.clauses());
+                predicates.extend(bounds.clauses(tcx));
                 trace!(?predicates);
             }
             hir::GenericParamKind::Const { .. } => {
@@ -238,7 +274,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
                     bound_vars,
                     OnlySelfBounds(false),
                 );
-                predicates.extend(bounds.clauses());
+                predicates.extend(bounds.clauses(tcx));
+                effects_min_tys.extend(bounds.effects_min_tys());
             }
 
             hir::WherePredicate::RegionPredicate(region_pred) => {
@@ -297,7 +334,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
     // and the duplicated parameter, to ensure that they do not get out of sync.
     if let Node::Item(&Item { kind: ItemKind::OpaqueTy(..), .. }) = node {
         let opaque_ty_node = tcx.parent_hir_node(hir_id);
-        let Node::Ty(&Ty { kind: TyKind::OpaqueDef(_, lifetimes, _), .. }) = opaque_ty_node else {
+        let Node::Ty(&hir::Ty { kind: TyKind::OpaqueDef(_, lifetimes, _), .. }) = opaque_ty_node
+        else {
             bug!("unexpected {opaque_ty_node:?}")
         };
         debug!(?lifetimes);
@@ -309,6 +347,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
     ty::GenericPredicates {
         parent: generics.parent,
         predicates: tcx.arena.alloc_from_iter(predicates),
+        effects_min_tys: tcx.mk_type_list(&effects_min_tys),
     }
 }
 
@@ -459,6 +498,7 @@ pub(super) fn explicit_predicates_of<'tcx>(
             ty::GenericPredicates {
                 parent: predicates_and_bounds.parent,
                 predicates: tcx.arena.alloc_slice(&predicates),
+                effects_min_tys: predicates_and_bounds.effects_min_tys,
             }
         }
     } else {
@@ -510,6 +550,7 @@ pub(super) fn explicit_predicates_of<'tcx>(
             return GenericPredicates {
                 parent: parent_preds.parent,
                 predicates: { tcx.arena.alloc_from_iter(filtered_predicates) },
+                effects_min_tys: parent_preds.effects_min_tys,
             };
         }
         gather_explicit_predicates_of(tcx, def_id)
@@ -587,7 +628,7 @@ pub(super) fn implied_predicates_with_filter(
 
     // Combine the two lists to form the complete set of superbounds:
     let implied_bounds =
-        &*tcx.arena.alloc_from_iter(superbounds.clauses().chain(where_bounds_that_match));
+        &*tcx.arena.alloc_from_iter(superbounds.clauses(tcx).chain(where_bounds_that_match));
     debug!(?implied_bounds);
 
     // Now require that immediate supertraits are lowered, which will, in
@@ -618,7 +659,11 @@ pub(super) fn implied_predicates_with_filter(
         _ => {}
     }
 
-    ty::GenericPredicates { parent: None, predicates: implied_bounds }
+    ty::GenericPredicates {
+        parent: None,
+        predicates: implied_bounds,
+        effects_min_tys: ty::List::empty(),
+    }
 }
 
 /// Returns the predicates defined on `item_def_id` of the form
@@ -744,7 +789,7 @@ impl<'tcx> ItemCtxt<'tcx> {
             );
         }
 
-        bounds.clauses().collect()
+        bounds.clauses(self.tcx).collect()
     }
 
     #[instrument(level = "trace", skip(self))]
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index abc3bb838db..5c54c9d8e53 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -951,7 +951,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     self.visit_ty(ty);
                 }
             }
-            GenericParamKind::Const { ty, default, is_host_effect: _ } => {
+            GenericParamKind::Const { ty, default, .. } => {
                 self.visit_ty(ty);
                 if let Some(default) = default {
                     self.visit_body(self.tcx.hir().body(default.body));
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
index 3f888c4e272..67046b5ac0e 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
@@ -256,6 +256,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>(
                             | GenericParamDefKind::Lifetime,
                             _,
                         ) => {
+                            // TODO: this should be removed
                             // SPECIAL CASE FOR DESUGARED EFFECT PARAMS
                             // This comes from the following example:
                             //
@@ -445,7 +446,7 @@ pub(crate) fn check_generic_arg_count(
         .own_params
         .iter()
         .filter(|param| {
-            matches!(param.kind, ty::GenericParamDefKind::Const { is_host_effect: true, .. })
+            matches!(param.kind, ty::GenericParamDefKind::Const { synthetic: true, .. })
         })
         .count();
     let named_const_param_count = param_counts.consts - synth_const_param_count;
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 24ea328889c..2a68d3915bb 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -698,7 +698,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         );
 
         debug!(?poly_trait_ref);
-        bounds.push_trait_bound(tcx, poly_trait_ref, span, polarity);
+        bounds.push_trait_bound(
+            tcx,
+            self.item_def_id().to_def_id(),
+            poly_trait_ref,
+            span,
+            polarity,
+            constness,
+        );
 
         let mut dup_constraints = FxIndexMap::default();
         for constraint in trait_segment.args().constraints {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
index 34924f09d09..df69c1938dd 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
@@ -59,7 +59,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
         let mut trait_bounds = vec![];
         let mut projection_bounds = vec![];
-        for (pred, span) in bounds.clauses() {
+        for (pred, span) in bounds.clauses(tcx) {
             let bound_pred = pred.kind();
             match bound_pred.skip_binder() {
                 ty::ClauseKind::Trait(trait_pred) => {
@@ -133,7 +133,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                             tcx.associated_items(pred.def_id())
                                 .in_definition_order()
                                 .filter(|item| item.kind == ty::AssocKind::Type)
-                                .filter(|item| !item.is_impl_trait_in_trait())
+                                .filter(|item| {
+                                    !item.is_impl_trait_in_trait() && !item.is_effects_desugaring
+                                })
                                 .map(|item| item.def_id),
                         );
                     }
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 25b0cbdc026..5105d60ae18 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -585,6 +585,7 @@ impl<'a> State<'a> {
                 self.print_struct(struct_def, generics, item.ident.name, item.span, true);
             }
             hir::ItemKind::Impl(&hir::Impl {
+                constness,
                 safety,
                 polarity,
                 defaultness,
@@ -599,6 +600,10 @@ impl<'a> State<'a> {
                 self.print_safety(safety);
                 self.word_nbsp("impl");
 
+                if let hir::Constness::Const = constness {
+                    self.word_nbsp("const");
+                }
+
                 if !generics.params.is_empty() {
                     self.print_generic_params(generics.params);
                     self.space();
@@ -2144,7 +2149,7 @@ impl<'a> State<'a> {
                     self.print_type(default);
                 }
             }
-            GenericParamKind::Const { ty, ref default, is_host_effect: _ } => {
+            GenericParamKind::Const { ty, ref default, is_host_effect: _, synthetic: _ } => {
                 self.word_space(":");
                 self.print_type(ty);
                 if let Some(default) = default {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 061afd03062..f6e21262b6a 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -1304,7 +1304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         self.fcx.ty_infer(Some(param), inf.span).into()
                     }
                     (
-                        &GenericParamDefKind::Const { has_default, is_host_effect },
+                        &GenericParamDefKind::Const { has_default, is_host_effect, .. },
                         GenericArg::Infer(inf),
                     ) => {
                         if has_default && is_host_effect {
@@ -1346,7 +1346,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             self.fcx.var_for_def(self.span, param)
                         }
                     }
-                    GenericParamDefKind::Const { has_default, is_host_effect } => {
+                    GenericParamDefKind::Const { has_default, is_host_effect, .. } => {
                         if has_default {
                             // N.B. this is a bit of a hack. `infer_args` is passed depending on
                             // whether the user has provided generic args. E.g. for `Vec::new`
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index c0d60477967..ed3cbb0663e 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -274,6 +274,7 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
                     }
                 }),
             ),
+            effects_min_tys: ty::List::empty(),
         }
     }
 
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index e1223307b53..dad909fc4e1 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -21,7 +21,7 @@ use rustc_middle::ty::{self, GenericParamDefKind, Ty, TypeVisitableExt};
 use rustc_middle::ty::{GenericArgs, GenericArgsRef};
 use rustc_middle::{bug, span_bug};
 use rustc_span::symbol::Ident;
-use rustc_span::Span;
+use rustc_span::{sym, Span};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::{self, NormalizeExt};
 
@@ -356,6 +356,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
         let (obligation, args) =
             self.obligation_for_method(cause, trait_def_id, self_ty, opt_input_types);
+        // FIXME(effects) find a better way to do this
+        // Operators don't have generic methods, but making them `#[const_trait]` gives them
+        // `const host: bool`.
+        let args = if self.tcx.has_attr(trait_def_id, sym::const_trait) {
+            self.tcx.mk_args_from_iter(
+                args.iter()
+                    .chain([self.tcx.expected_host_effect_param_for_body(self.body_id).into()]),
+            )
+        } else {
+            args
+        };
         self.construct_obligation_for_trait(m_name, trait_def_id, obligation, args)
     }
 
@@ -393,6 +404,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
         let mut obligations = vec![];
 
+        // TODO there is something wrong here because now methods for binops may get `const host: bool`
+
         // Instantiate late-bound regions and instantiate the trait
         // parameters into the method type to get the actual method type.
         //
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 9747a91ccbf..63b30d79aba 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -1279,6 +1279,7 @@ impl<'tcx> Pick<'tcx> {
                     trait_item_def_id: _,
                     fn_has_self_parameter: _,
                     opt_rpitit_info: _,
+                    is_effects_desugaring: _,
                 },
             kind: _,
             import_ids: _,
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index ea7037740f1..83b41e0540e 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -1348,7 +1348,9 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
     }
 
     fn get_associated_item(self, id: DefIndex, sess: &'a Session) -> ty::AssocItem {
-        let name = if self.root.tables.opt_rpitit_info.get(self, id).is_some() {
+        let name = if self.root.tables.opt_rpitit_info.get(self, id).is_some()
+            || self.root.tables.is_effects_desugaring.get(self, id)
+        {
             kw::Empty
         } else {
             self.item_name(id)
@@ -1371,6 +1373,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
             container,
             fn_has_self_parameter: has_self,
             opt_rpitit_info,
+            is_effects_desugaring: self.root.tables.is_effects_desugaring.get(self, id),
         }
     }
 
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index c9450142cd3..f696be08cb2 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -279,6 +279,9 @@ provide! { tcx, def_id, other, cdata,
             .process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys")))
     }
 
+    associated_type_for_effects => {
+        table
+    }
     associated_types_for_impl_traits_in_associated_fn => { table_defaulted_array }
 
     visibility => { cdata.get_visibility(def_id.index) }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 4bd2ec09a6e..f985c55eeec 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1454,6 +1454,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 for &def_id in associated_item_def_ids {
                     self.encode_info_for_assoc_item(def_id);
                 }
+                if let Some(assoc_def_id) = self.tcx.associated_type_for_effects(def_id) {
+                    record!(self.tables.associated_type_for_effects[def_id] <- assoc_def_id);
+                }
             }
             if def_kind == DefKind::Closure
                 && let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id)
@@ -1634,6 +1637,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 );
             }
         }
+        if item.is_effects_desugaring {
+            self.tables.is_effects_desugaring.set(def_id.index, true);
+        }
     }
 
     fn encode_mir(&mut self) {
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 87900c23d8d..cb5f3d20d99 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -391,7 +391,9 @@ define_tables! {
     inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
     inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
     associated_types_for_impl_traits_in_associated_fn: Table<DefIndex, LazyArray<DefId>>,
+    associated_type_for_effects: Table<DefIndex, Option<LazyValue<DefId>>>,
     opt_rpitit_info: Table<DefIndex, Option<LazyValue<ty::ImplTraitInTraitData>>>,
+    is_effects_desugaring: Table<DefIndex, bool>,
     unused_generic_params: Table<DefIndex, UnusedGenericParams>,
     // Reexported names are not associated with individual `DefId`s,
     // e.g. a glob import can introduce a lot of names, all with the same `DefId`.
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 230a44bcf24..d94f12af448 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -368,6 +368,7 @@ rustc_queries! {
         desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
+        feedable
     }
 
     /// The set of item bounds (see [`TyCtxt::explicit_item_bounds`]) that
@@ -378,6 +379,7 @@ rustc_queries! {
         desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
+        feedable
     }
 
     /// Elaborated version of the predicates from `explicit_item_bounds`.
@@ -847,6 +849,12 @@ rustc_queries! {
         separate_provide_extern
     }
 
+    query associated_type_for_effects(def_id: DefId) -> Option<DefId> {
+        desc { |tcx| "creating associated items for effects in `{}`", tcx.def_path_str(def_id) }
+        cache_on_disk_if { def_id.is_local() }
+        separate_provide_extern
+    }
+
     /// Given an impl trait in trait `opaque_ty_def_id`, create and return the corresponding
     /// associated item.
     query associated_type_for_impl_trait_in_trait(opaque_ty_def_id: LocalDefId) -> LocalDefId {
diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs
index 1cdde3f057c..820f5e950a9 100644
--- a/compiler/rustc_middle/src/ty/assoc.rs
+++ b/compiler/rustc_middle/src/ty/assoc.rs
@@ -34,6 +34,8 @@ pub struct AssocItem {
     /// return-position `impl Trait` in trait desugaring. The `ImplTraitInTraitData`
     /// provides additional information about its source.
     pub opt_rpitit_info: Option<ty::ImplTraitInTraitData>,
+
+    pub is_effects_desugaring: bool,
 }
 
 impl AssocItem {
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 4bac9396e59..656aba67112 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -3095,9 +3095,9 @@ impl<'tcx> TyCtxt<'tcx> {
         matches!(
             node,
             hir::Node::Item(hir::Item {
-                kind: hir::ItemKind::Impl(hir::Impl { generics, .. }),
+                kind: hir::ItemKind::Impl(hir::Impl { constness, .. }),
                 ..
-            }) if generics.params.iter().any(|p| matches!(p.kind, hir::GenericParamKind::Const { is_host_effect: true, .. }))
+            }) if matches!(constness, hir::Constness::Const)
         )
     }
 
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs
index 6467689a8aa..844023df1e3 100644
--- a/compiler/rustc_middle/src/ty/generics.rs
+++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -14,7 +14,7 @@ use super::{Clause, InstantiatedPredicates, ParamConst, ParamTy, Ty, TyCtxt};
 pub enum GenericParamDefKind {
     Lifetime,
     Type { has_default: bool, synthetic: bool },
-    Const { has_default: bool, is_host_effect: bool },
+    Const { has_default: bool, is_host_effect: bool, synthetic: bool },
 }
 
 impl GenericParamDefKind {
@@ -371,6 +371,7 @@ impl<'tcx> Generics {
 pub struct GenericPredicates<'tcx> {
     pub parent: Option<DefId>,
     pub predicates: &'tcx [(Clause<'tcx>, Span)],
+    pub effects_min_tys: &'tcx ty::List<Ty<'tcx>>,
 }
 
 impl<'tcx> GenericPredicates<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 9c2bfc12a18..7d57d88f40f 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1608,7 +1608,7 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
-    /// If the def-id is an associated type that was desugared from a
+    /// If the `def_id` is an associated type that was desugared from a
     /// return-position `impl Trait` from a trait, then provide the source info
     /// about where that RPITIT came from.
     pub fn opt_rpitit_info(self, def_id: DefId) -> Option<ImplTraitInTraitData> {
@@ -1619,6 +1619,16 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
+    /// Whether the `def_id` is an associated type that was desugared from a
+    /// `#[const_trait]` or `impl_const`.
+    pub fn is_effects_desugared_assoc_ty(self, def_id: DefId) -> bool {
+        if let DefKind::AssocTy = self.def_kind(def_id) {
+            self.associated_item(def_id).is_effects_desugaring
+        } else {
+            false
+        }
+    }
+
     pub fn find_field_index(self, ident: Ident, variant: &VariantDef) -> Option<FieldIdx> {
         variant.fields.iter_enumerated().find_map(|(i, field)| {
             self.hygienic_eq(ident, field.ident(self), variant.def_id).then_some(i)
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index e23f4289e98..4eefd0eb17c 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -160,7 +160,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
     fn predicates_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::GenericPredicates {
         let mut tables = self.0.borrow_mut();
         let def_id = tables[def_id];
-        let GenericPredicates { parent, predicates } = tables.tcx.predicates_of(def_id);
+        let GenericPredicates { parent, predicates, effects_min_tys: _ } =
+            tables.tcx.predicates_of(def_id);
         stable_mir::ty::GenericPredicates {
             parent: parent.map(|did| tables.trait_def(did)),
             predicates: predicates
@@ -181,7 +182,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
     ) -> stable_mir::ty::GenericPredicates {
         let mut tables = self.0.borrow_mut();
         let def_id = tables[def_id];
-        let GenericPredicates { parent, predicates } = tables.tcx.explicit_predicates_of(def_id);
+        let GenericPredicates { parent, predicates, effects_min_tys: _ } =
+            tables.tcx.explicit_predicates_of(def_id);
         stable_mir::ty::GenericPredicates {
             parent: parent.map(|did| tables.trait_def(did)),
             predicates: predicates
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index 74c3fcc9b9e..9382460d6d4 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -600,7 +600,7 @@ impl<'tcx> Stable<'tcx> for rustc_middle::ty::GenericParamDefKind {
             ty::GenericParamDefKind::Type { has_default, synthetic } => {
                 GenericParamDefKind::Type { has_default: *has_default, synthetic: *synthetic }
             }
-            ty::GenericParamDefKind::Const { has_default, is_host_effect: _ } => {
+            ty::GenericParamDefKind::Const { has_default, is_host_effect: _, synthetic: _ } => {
                 GenericParamDefKind::Const { has_default: *has_default }
             }
         }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 3b6147c4c0f..4a26e8ae097 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -194,6 +194,14 @@ symbols! {
         Display,
         DoubleEndedIterator,
         Duration,
+        EffectsCompat,
+        EffectsMaybe,
+        EffectsMin,
+        EffectsMinOutput,
+        EffectsNoRuntime,
+        EffectsRuntime,
+        EffectsTyCompat,
+        Effects__,
         Encodable,
         Encoder,
         Enumerate,
@@ -1658,6 +1666,7 @@ symbols! {
         rustc_reallocator,
         rustc_regions,
         rustc_reservation_impl,
+        rustc_runtime,
         rustc_safe_intrinsic,
         rustc_serialize,
         rustc_skip_during_method_dispatch,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index f4a026c0367..599a95f2300 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -4995,6 +4995,12 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>(
     let ty::ClauseKind::Projection(proj) = clause else {
         return;
     };
+    // avoid ICEing since effects desugared associated types don't have names.
+    // this path should only be hit for `~const` on invalid places, so they
+    // will have an informative error already.
+    if tcx.is_effects_desugared_assoc_ty(proj.projection_term.def_id) {
+        return;
+    }
     let name = tcx.item_name(proj.projection_term.def_id);
     let mut predicates = generics.predicates.iter().peekable();
     let mut prev: Option<&hir::WhereBoundPredicate<'_>> = None;
diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs
index 4395eb57cd6..df6c54ab289 100644
--- a/compiler/rustc_ty_utils/src/assoc.rs
+++ b/compiler/rustc_ty_utils/src/assoc.rs
@@ -4,8 +4,9 @@ use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_middle::query::Providers;
-use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt};
+use rustc_middle::ty::{self, ImplTraitInTraitData, Ty, TyCtxt};
 use rustc_middle::{bug, span_bug};
+use rustc_span::sym;
 use rustc_span::symbol::kw;
 
 pub(crate) fn provide(providers: &mut Providers) {
@@ -14,6 +15,7 @@ pub(crate) fn provide(providers: &mut Providers) {
         associated_item_def_ids,
         associated_items,
         associated_types_for_impl_traits_in_associated_fn,
+        associated_type_for_effects,
         associated_type_for_impl_trait_in_trait,
         impl_item_implementor_ids,
         ..*providers
@@ -44,7 +46,8 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] {
                                 )
                             })
                             .copied(),
-                    ),
+                    )
+                    .chain(tcx.associated_type_for_effects(def_id)),
             )
         }
         hir::ItemKind::Impl(impl_) => {
@@ -70,7 +73,8 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] {
                                 )
                             })
                             .copied()
-                    })),
+                    }))
+                    .chain(tcx.associated_type_for_effects(def_id)),
             )
         }
         _ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"),
@@ -143,6 +147,7 @@ fn associated_item_from_trait_item_ref(trait_item_ref: &hir::TraitItemRef) -> ty
         container: ty::TraitContainer,
         fn_has_self_parameter: has_self,
         opt_rpitit_info: None,
+        is_effects_desugaring: false,
     }
 }
 
@@ -162,6 +167,161 @@ fn associated_item_from_impl_item_ref(impl_item_ref: &hir::ImplItemRef) -> ty::A
         container: ty::ImplContainer,
         fn_has_self_parameter: has_self,
         opt_rpitit_info: None,
+        is_effects_desugaring: false,
+    }
+}
+
+/// Given an `def_id` of a trait or a trait impl:
+///
+/// If `def_id` is a trait that has `#[const_trait]`, then it synthesizes
+/// a new def id corresponding to a new associated type for the effects.
+///
+/// If `def_id` is an impl, then synthesize the associated type according
+/// to the constness of the impl.
+fn associated_type_for_effects(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> {
+    match tcx.def_kind(def_id) {
+        DefKind::Trait => {
+            let trait_def_id = def_id;
+            let Some(attr) = tcx.get_attr(def_id, sym::const_trait) else {
+                return None;
+            };
+
+            let span = attr.span;
+            let trait_assoc_ty = tcx.at(span).create_def(trait_def_id, kw::Empty, DefKind::AssocTy);
+
+            let local_def_id = trait_assoc_ty.def_id();
+            let def_id = local_def_id.to_def_id();
+
+            trait_assoc_ty.feed_hir();
+
+            // Copy span of the attribute.
+            trait_assoc_ty.def_ident_span(Some(span));
+
+            trait_assoc_ty.associated_item(ty::AssocItem {
+                name: kw::Empty,
+                kind: ty::AssocKind::Type,
+                def_id,
+                trait_item_def_id: None,
+                container: ty::TraitContainer,
+                fn_has_self_parameter: false,
+                opt_rpitit_info: None,
+                is_effects_desugaring: true,
+            });
+
+            // visibility is public.
+            trait_assoc_ty.visibility(ty::Visibility::Public);
+
+            // No default type
+            trait_assoc_ty.defaultness(hir::Defaultness::Default { has_value: false });
+
+            trait_assoc_ty.is_type_alias_impl_trait(false);
+
+            // Copy generics_of of the trait, making the trait as parent.
+            trait_assoc_ty.generics_of({
+                let parent_generics = tcx.generics_of(trait_def_id);
+                let parent_count = parent_generics.parent_count + parent_generics.own_params.len();
+
+                ty::Generics {
+                    parent: Some(trait_def_id.to_def_id()),
+                    parent_count,
+                    own_params: vec![],
+                    param_def_id_to_index: parent_generics.param_def_id_to_index.clone(),
+                    has_self: false,
+                    has_late_bound_regions: None,
+                    host_effect_index: parent_generics.host_effect_index,
+                }
+            });
+
+            trait_assoc_ty.explicit_item_bounds(ty::EarlyBinder::bind(&[]));
+            trait_assoc_ty.explicit_item_super_predicates(ty::EarlyBinder::bind(&[]));
+
+            // There are no inferred outlives for the synthesized associated type.
+            trait_assoc_ty.inferred_outlives_of(&[]);
+
+            Some(def_id)
+        }
+        DefKind::Impl { .. } => {
+            let impl_def_id = def_id;
+            let Some(trait_id) = tcx.trait_id_of_impl(def_id.to_def_id()) else { return None };
+
+            // first get the DefId of the assoc type on the trait, if there is not,
+            // then we don't need to generate it on the impl.
+            let Some(trait_assoc_id) = tcx.associated_type_for_effects(trait_id) else {
+                return None;
+            };
+
+            // TODO span is bad
+            let span = tcx.def_ident_span(def_id).unwrap();
+
+            let impl_assoc_ty = tcx.at(span).create_def(def_id, kw::Empty, DefKind::AssocTy);
+
+            let local_def_id = impl_assoc_ty.def_id();
+            let def_id = local_def_id.to_def_id();
+
+            impl_assoc_ty.feed_hir();
+
+            impl_assoc_ty.def_ident_span(Some(span));
+
+            impl_assoc_ty.associated_item(ty::AssocItem {
+                name: kw::Empty,
+                kind: ty::AssocKind::Type,
+                def_id,
+                trait_item_def_id: Some(trait_assoc_id),
+                container: ty::ImplContainer,
+                fn_has_self_parameter: false,
+                opt_rpitit_info: None,
+                is_effects_desugaring: true,
+            });
+
+            // visibility is public.
+            impl_assoc_ty.visibility(ty::Visibility::Public);
+
+            // no default value.
+            impl_assoc_ty.defaultness(hir::Defaultness::Final);
+
+            // set the type of the associated type! If this is a const impl,
+            // we set to Maybe, otherwise we set to `Runtime`.
+            let type_def_id = if tcx.is_const_trait_impl_raw(impl_def_id.to_def_id()) {
+                tcx.require_lang_item(hir::LangItem::EffectsMaybe, Some(span))
+            } else {
+                tcx.require_lang_item(hir::LangItem::EffectsRuntime, Some(span))
+            };
+            // TODO this is wrong
+            impl_assoc_ty.type_of(ty::EarlyBinder::bind(Ty::new_adt(
+                tcx,
+                tcx.adt_def(type_def_id),
+                ty::GenericArgs::empty(),
+            )));
+
+            // Copy generics_of of the impl, making the impl as parent.
+            impl_assoc_ty.generics_of({
+                let parent_generics = tcx.generics_of(impl_def_id);
+                let parent_count = parent_generics.parent_count + parent_generics.own_params.len();
+
+                ty::Generics {
+                    parent: Some(impl_def_id.to_def_id()),
+                    parent_count,
+                    own_params: vec![],
+                    param_def_id_to_index: parent_generics.param_def_id_to_index.clone(),
+                    has_self: false,
+                    has_late_bound_regions: None,
+                    host_effect_index: parent_generics.host_effect_index,
+                }
+            });
+
+            impl_assoc_ty.explicit_item_bounds(ty::EarlyBinder::bind(&[]));
+            impl_assoc_ty.explicit_item_super_predicates(ty::EarlyBinder::bind(&[]));
+
+            // There are no inferred outlives for the synthesized associated type.
+            impl_assoc_ty.inferred_outlives_of(&[]);
+
+            Some(def_id)
+        }
+        def_kind => bug!(
+            "associated_type_for_effects: {:?} should be Trait or Impl but is {:?}",
+            def_id,
+            def_kind
+        ),
     }
 }
 
@@ -275,6 +435,7 @@ fn associated_type_for_impl_trait_in_trait(
             fn_def_id: fn_def_id.to_def_id(),
             opaque_def_id: opaque_ty_def_id.to_def_id(),
         }),
+        is_effects_desugaring: false,
     });
 
     // Copy visility of the containing function.
@@ -326,6 +487,7 @@ fn associated_type_for_impl_trait_in_impl(
         container: ty::ImplContainer,
         fn_has_self_parameter: false,
         opt_rpitit_info: Some(ImplTraitInTraitData::Impl { fn_def_id: impl_fn_def_id.to_def_id() }),
+        is_effects_desugaring: false,
     });
 
     // Copy visility of the containing function.
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index d1692729a31..cfadfb0233e 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -231,6 +231,7 @@
 #![feature(let_chains)]
 #![feature(link_llvm_intrinsics)]
 #![feature(macro_metavar_expr)]
+#![feature(marker_trait_attr)]
 #![feature(min_exhaustive_patterns)]
 #![feature(min_specialization)]
 #![feature(multiple_supertrait_upcastable)]
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index 0fedb8835d1..b3f3cc02126 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -1027,3 +1027,41 @@ pub trait FnPtr: Copy + Clone {
 pub macro SmartPointer($item:item) {
     /* compiler built-in */
 }
+
+#[doc(hidden)]
+#[unstable(
+    feature = "effect_types",
+    issue = "none",
+    reason = "internal module for implementing effects"
+)]
+#[allow(missing_debug_implementations)] // these unit structs don't need `Debug` impls.
+#[cfg(not(bootstrap))]
+// TODO docs
+pub mod effects {
+    #[lang = "EffectsNoRuntime"]
+    pub struct NoRuntime;
+    #[lang = "EffectsMaybe"]
+    pub struct Maybe;
+    #[lang = "EffectsRuntime"]
+    pub struct Runtime;
+
+    #[lang = "EffectsCompat"]
+    pub trait Compat<#[rustc_runtime] const RUNTIME: bool> {}
+
+    impl Compat<false> for NoRuntime {}
+    impl Compat<true> for Runtime {}
+    impl<#[rustc_runtime] const RUNTIME: bool> Compat<RUNTIME> for Maybe {}
+
+    #[lang = "EffectsTyCompat"]
+    #[marker]
+    pub trait TyCompat<T> {}
+
+    impl<T> TyCompat<T> for T {}
+    impl<T> TyCompat<T> for Maybe {}
+
+    #[lang = "EffectsMin"]
+    pub trait Min {
+        #[lang = "EffectsMinOutput"]
+        type Output;
+    }
+}
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 22565ea4028..b5660cd8492 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -549,7 +549,7 @@ fn clean_generic_param_def<'tcx>(
                 },
             )
         }
-        ty::GenericParamDefKind::Const { has_default, is_host_effect } => (
+        ty::GenericParamDefKind::Const { has_default, synthetic, is_host_effect: _ } => (
             def.name,
             GenericParamDefKind::Const {
                 ty: Box::new(clean_middle_ty(
@@ -572,7 +572,7 @@ fn clean_generic_param_def<'tcx>(
                 } else {
                     None
                 },
-                is_host_effect,
+                synthetic,
             },
         ),
     };
@@ -628,13 +628,13 @@ fn clean_generic_param<'tcx>(
                 },
             )
         }
-        hir::GenericParamKind::Const { ty, default, is_host_effect } => (
+        hir::GenericParamKind::Const { ty, default, synthetic, is_host_effect: _ } => (
             param.name.ident().name,
             GenericParamDefKind::Const {
                 ty: Box::new(clean_ty(ty, cx)),
                 default: default
                     .map(|ct| Box::new(ty::Const::from_anon_const(cx.tcx, ct.def_id).to_string())),
-                is_host_effect,
+                synthetic,
             },
         ),
     };
@@ -1411,7 +1411,11 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
             let mut generics = clean_ty_generics(
                 cx,
                 tcx.generics_of(assoc_item.def_id),
-                ty::GenericPredicates { parent: None, predicates },
+                ty::GenericPredicates {
+                    parent: None,
+                    predicates,
+                    effects_min_tys: ty::List::empty(),
+                },
             );
             simplify::move_bounds_to_generic_parameters(&mut generics);
 
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index c4020f2a450..9050a1c1207 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1321,7 +1321,7 @@ pub(crate) enum GenericParamDefKind {
     Lifetime { outlives: ThinVec<Lifetime> },
     Type { bounds: ThinVec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
     // Option<Box<String>> makes this type smaller than `Option<String>` would.
-    Const { ty: Box<Type>, default: Option<Box<String>>, is_host_effect: bool },
+    Const { ty: Box<Type>, default: Option<Box<String>>, synthetic: bool },
 }
 
 impl GenericParamDefKind {
@@ -1345,7 +1345,7 @@ impl GenericParamDef {
     pub(crate) fn is_synthetic_param(&self) -> bool {
         match self.kind {
             GenericParamDefKind::Lifetime { .. } => false,
-            GenericParamDefKind::Const { is_host_effect, .. } => is_host_effect,
+            GenericParamDefKind::Const { synthetic: is_host_effect, .. } => is_host_effect,
             GenericParamDefKind::Type { synthetic, .. } => synthetic,
         }
     }
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index d099a97f1cb..b965ab019cc 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -466,7 +466,7 @@ impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind {
                 default: default.map(|x| (*x).into_tcx(tcx)),
                 synthetic,
             },
-            Const { ty, default, is_host_effect: _ } => GenericParamDefKind::Const {
+            Const { ty, default, synthetic: _ } => GenericParamDefKind::Const {
                 type_: (*ty).into_tcx(tcx),
                 default: default.map(|x| *x),
             },
@@ -501,14 +501,12 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
                                     synthetic,
                                 }
                             }
-                            clean::GenericParamDefKind::Const {
-                                ty,
-                                default,
-                                is_host_effect: _,
-                            } => GenericParamDefKind::Const {
-                                type_: (*ty).into_tcx(tcx),
-                                default: default.map(|d| *d),
-                            },
+                            clean::GenericParamDefKind::Const { ty, default, synthetic: _ } => {
+                                GenericParamDefKind::Const {
+                                    type_: (*ty).into_tcx(tcx),
+                                    default: default.map(|d| *d),
+                                }
+                            }
                         };
                         GenericParamDef { name, kind }
                     })
diff --git a/tests/ui/const-generics/const_trait_fn-issue-88433.rs b/tests/ui/const-generics/const_trait_fn-issue-88433.rs
index cd008aa2c9f..f3271f833cc 100644
--- a/tests/ui/const-generics/const_trait_fn-issue-88433.rs
+++ b/tests/ui/const-generics/const_trait_fn-issue-88433.rs
@@ -1,4 +1,5 @@
 //@ build-pass
+//@ compile-flags: -Znext-solver
 
 #![feature(const_trait_impl, effects)] //~ WARN the feature `effects` is incomplete
 
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/group-traits.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/group-traits.rs
new file mode 100644
index 00000000000..2c5b6cc40e6
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/group-traits.rs
@@ -0,0 +1,14 @@
+//@ check-pass
+use std::ops::Add;
+
+pub trait GroupOpsOwned<Rhs = Self, Output = Self>: for<'r> Add<&'r Rhs, Output = Output> {}
+
+pub trait Curve: Sized + GroupOpsOwned<Self::AffineRepr> {
+    type AffineRepr;
+}
+
+pub trait CofactorCurve: Curve<AffineRepr = <Self as CofactorCurve>::Affine> {
+    type Affine;
+}
+
+fn main() {}
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs
index 825ddb6a5cd..a587de9f179 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs
@@ -1,8 +1,9 @@
 // Regression test for #92230.
 //
 //@ check-pass
+//@ compile-flags: -Znext-solver
 
-#![feature(const_trait_impl)]
+#![feature(const_trait_impl, effects)]
 
 #[const_trait]
 pub trait Super {}
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr
index 8151b9aaa23..3b320f1c542 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr
@@ -282,12 +282,21 @@ help: wrap the field type in `ManuallyDrop<...>`
 LL | union Union<T: ~const Trait> { field: std::mem::ManuallyDrop<T> }
    |                                       +++++++++++++++++++++++ +
 
-error[E0275]: overflow evaluating the requirement `(): Trait`
+error[E0275]: overflow evaluating the requirement `Trait::{opaque#0} == _`
   --> $DIR/tilde-const-invalid-places.rs:34:34
    |
 LL |     type Type<T: ~const Trait> = ();
    |                                  ^^
    |
+note: required for `()` to implement `Trait`
+  --> $DIR/tilde-const-invalid-places.rs:56:23
+   |
+LL | impl<T: ~const Trait> Trait for T {}
+   |         ------------  ^^^^^     ^
+   |         |
+   |         unsatisfied trait bound introduced here
+   = note: 1 redundant requirement hidden
+   = note: required for `()` to implement `Trait`
 note: required by a bound in `NonConstTrait::Type`
   --> $DIR/tilde-const-invalid-places.rs:25:33
    |
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs
index 8f441410c17..3a8c6d5b1a4 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs
@@ -1,4 +1,5 @@
 // Ensure that we print unsatisfied always-const trait bounds as `const Trait` in diagnostics.
+//@ compile-flags: -Znext-solver
 
 #![feature(const_trait_impl, effects, generic_const_exprs)]
 #![allow(incomplete_features)]