about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs34
-rw-r--r--compiler/rustc_hir_analysis/src/bounds.rs27
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs37
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs4
-rw-r--r--compiler/rustc_middle/src/ty/generics.rs4
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.stderr14
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs504
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.stderr32
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/effects/project.rs11
-rw-r--r--tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr34
10 files changed, 661 insertions, 40 deletions
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index a91d9231390..82cbed2639e 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -1088,7 +1088,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Const, assoc_name)
         });
 
-        let (bound, next_cand) = match (matching_candidates.next(), const_candidates.next()) {
+        let (mut bound, mut next_cand) = match (matching_candidates.next(), const_candidates.next())
+        {
             (Some(bound), _) => (bound, matching_candidates.next()),
             (None, Some(bound)) => (bound, const_candidates.next()),
             (None, None) => {
@@ -1103,6 +1104,37 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         };
         debug!(?bound);
 
+        // look for a candidate that is not the same as our first bound, disregarding
+        // whether the bound is const.
+        while let Some(mut bound2) = next_cand {
+            debug!(?bound2);
+            let tcx = self.tcx();
+            if bound2.bound_vars() != bound.bound_vars() {
+                break;
+            }
+
+            let generics = tcx.generics_of(bound.def_id());
+            let Some(host_index) = generics.host_effect_index else { break };
+
+            // always return the bound that contains the host param.
+            if let ty::ConstKind::Param(_) = bound2.skip_binder().args.const_at(host_index).kind() {
+                (bound, bound2) = (bound2, bound);
+            }
+
+            let unconsted_args = bound
+                .skip_binder()
+                .args
+                .iter()
+                .enumerate()
+                .map(|(n, arg)| if host_index == n { tcx.consts.true_.into() } else { arg });
+
+            if unconsted_args.eq(bound2.skip_binder().args.iter()) {
+                next_cand = matching_candidates.next().or_else(|| const_candidates.next());
+            } else {
+                break;
+            }
+        }
+
         if let Some(bound2) = next_cand {
             debug!(?bound2);
 
diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs
index 1d9ae2b9cb7..af1a4e5d99e 100644
--- a/compiler/rustc_hir_analysis/src/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/bounds.rs
@@ -44,6 +44,33 @@ impl<'tcx> Bounds<'tcx> {
         span: Span,
         polarity: ty::ImplPolarity,
     ) {
+        self.push_trait_bound_inner(tcx, trait_ref, span, polarity);
+
+        // if we have a host param, we push an unconst trait bound in addition
+        // to the const one.
+        // FIXME(effects) we should find a better way than name matching
+        if tcx.features().effects && trait_ref.skip_binder().args.host_effect_param().is_some() {
+            let generics = tcx.generics_of(trait_ref.def_id());
+            let Some(host_index) = generics.host_effect_index else { return };
+            let trait_ref = trait_ref.map_bound(|mut trait_ref| {
+                trait_ref.args =
+                    tcx.mk_args_from_iter(trait_ref.args.iter().enumerate().map(|(n, arg)| {
+                        if host_index == n { tcx.consts.true_.into() } else { arg }
+                    }));
+                trait_ref
+            });
+
+            self.push_trait_bound_inner(tcx, trait_ref, span, polarity);
+        }
+    }
+
+    fn push_trait_bound_inner(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+        span: Span,
+        polarity: ty::ImplPolarity,
+    ) {
         self.clauses.push((
             trait_ref
                 .map_bound(|trait_ref| {
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 1298c086087..06e18785a60 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -11,7 +11,7 @@ use rustc_hir::intravisit::{self, Visitor};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::ty::{GenericPredicates, ImplTraitInTraitData, ToPredicate};
 use rustc_span::symbol::Ident;
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::{sym, Span, DUMMY_SP};
 
 /// Returns a list of all type predicates (explicit and implicit) for the definition with
 /// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus
@@ -38,11 +38,38 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic
         // an obligation and instead be skipped. Otherwise we'd use
         // `tcx.def_span(def_id);`
         let span = rustc_span::DUMMY_SP;
-        result.predicates =
-            tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once((
-                ty::TraitRef::identity(tcx, def_id).to_predicate(tcx),
+        let non_const_bound = if tcx.features().effects && tcx.has_attr(def_id, sym::const_trait) {
+            // when `Self` is a const trait, also add `Self: Trait<.., true>` as implied bound,
+            // because only implementing `Self: Trait<.., false>` is currently not possible.
+            Some((
+                ty::TraitRef::new(
+                    tcx,
+                    def_id,
+                    ty::GenericArgs::for_item(tcx, def_id, |param, _| {
+                        if param.is_host_effect() {
+                            tcx.consts.true_.into()
+                        } else {
+                            tcx.mk_param_from_def(param)
+                        }
+                    }),
+                )
+                .to_predicate(tcx),
                 span,
-            ))));
+            ))
+        } else {
+            None
+        };
+        result.predicates = tcx.arena.alloc_from_iter(
+            result
+                .predicates
+                .iter()
+                .copied()
+                .chain(std::iter::once((
+                    ty::TraitRef::identity(tcx, def_id).to_predicate(tcx),
+                    span,
+                )))
+                .chain(non_const_bound),
+        );
     }
     debug!("predicates_of(def_id={:?}) = {:?}", def_id, result);
     result
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 4a245d30c8e..7677d6f953b 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -221,14 +221,14 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
         let item_def_id = tcx.hir().ty_param_owner(def_id);
         let generics = tcx.generics_of(item_def_id);
         let index = generics.param_def_id_to_index[&def_id.to_def_id()];
+        // HACK(eddyb) should get the original `Span`.
+        let span = tcx.def_span(def_id);
         ty::GenericPredicates {
             parent: None,
             predicates: tcx.arena.alloc_from_iter(
                 self.param_env.caller_bounds().iter().filter_map(|predicate| {
                     match predicate.kind().skip_binder() {
                         ty::ClauseKind::Trait(data) if data.self_ty().is_param(index) => {
-                            // HACK(eddyb) should get the original `Span`.
-                            let span = tcx.def_span(def_id);
                             Some((predicate, span))
                         }
                         _ => None,
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs
index 8e6c1cd4bbb..44e88d3e230 100644
--- a/compiler/rustc_middle/src/ty/generics.rs
+++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -79,6 +79,10 @@ impl GenericParamDef {
         }
     }
 
+    pub fn is_host_effect(&self) -> bool {
+        matches!(self.kind, GenericParamDefKind::Const { is_host_effect: true, .. })
+    }
+
     pub fn default_value<'tcx>(
         &self,
         tcx: TyCtxt<'tcx>,
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.stderr
index ad11c090f12..6d436018bf4 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.stderr
@@ -1,14 +1,12 @@
-error[E0277]: the trait bound `T: Foo` is not satisfied
-  --> $DIR/assoc-type-const-bound-usage.rs:12:6
+error[E0308]: mismatched types
+  --> $DIR/assoc-type-const-bound-usage.rs:12:5
    |
 LL |     <T as Foo>::Assoc::foo();
-   |      ^ the trait `Foo` is not implemented for `T`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^ expected `host`, found `true`
    |
-help: consider further restricting this bound
-   |
-LL | const fn foo<T: ~const Foo + Foo>() {
-   |                            +++++
+   = note: expected constant `host`
+              found constant `true`
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs
new file mode 100644
index 00000000000..c38b4b3f1a2
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs
@@ -0,0 +1,504 @@
+#![crate_type = "lib"]
+#![feature(no_core, lang_items, unboxed_closures, auto_traits, intrinsics, rustc_attrs)]
+#![feature(fundamental)]
+#![feature(const_trait_impl, effects, const_mut_refs)]
+#![allow(internal_features)]
+#![no_std]
+#![no_core]
+
+// known-bug: #110395
+
+#[lang = "sized"]
+trait Sized {}
+#[lang = "copy"]
+trait Copy {}
+
+#[lang = "add"]
+#[const_trait]
+trait Add<Rhs = Self> {
+    type Output;
+
+    fn add(self, rhs: Rhs) -> Self::Output;
+}
+
+// FIXME we shouldn't need to have to specify `Rhs`.
+impl const Add<i32> for i32 {
+    type Output = i32;
+    fn add(self, rhs: i32) -> i32 {
+        loop {}
+    }
+}
+
+fn foo() {
+    let x = 42_i32 + 43_i32;
+}
+
+const fn bar() {
+    let x = 42_i32 + 43_i32;
+}
+
+
+#[lang = "Try"]
+#[const_trait]
+trait Try: FromResidual {
+    type Output;
+    type Residual;
+
+    #[lang = "from_output"]
+    fn from_output(output: Self::Output) -> Self;
+
+    #[lang = "branch"]
+    fn branch(self) -> ControlFlow<Self::Residual, Self::Output>;
+}
+
+// FIXME
+// #[const_trait]
+trait FromResidual<R = <Self as Try>::Residual> {
+    #[lang = "from_residual"]
+    fn from_residual(residual: R) -> Self;
+}
+
+enum ControlFlow<B, C = ()> {
+    #[lang = "Continue"]
+    Continue(C),
+    #[lang = "Break"]
+    Break(B),
+}
+
+#[const_trait]
+#[lang = "fn"]
+#[rustc_paren_sugar]
+trait Fn<Args: Tuple>: ~const FnMut<Args> {
+    extern "rust-call" fn call(&self, args: Args) -> Self::Output;
+}
+
+#[const_trait]
+#[lang = "fn_mut"]
+#[rustc_paren_sugar]
+trait FnMut<Args: Tuple>: ~const FnOnce<Args> {
+    extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
+}
+
+#[const_trait]
+#[lang = "fn_once"]
+#[rustc_paren_sugar]
+trait FnOnce<Args: Tuple> {
+    type Output;
+
+    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+}
+
+struct ConstFnMutClosure<CapturedData, Function> {
+    data: CapturedData,
+    func: Function,
+}
+
+#[lang = "tuple_trait"]
+pub trait Tuple {}
+
+macro_rules! impl_fn_mut_tuple {
+    ($($var:ident)*) => {
+        impl<'a, $($var,)* ClosureArguments: Tuple, Function, ClosureReturnValue> const
+            FnOnce<ClosureArguments> for ConstFnMutClosure<($(&'a mut $var),*), Function>
+        where
+            Function: ~const Fn(($(&mut $var),*), ClosureArguments) -> ClosureReturnValue,
+            Function: ~const Destruct,
+        {
+            type Output = ClosureReturnValue;
+
+            extern "rust-call" fn call_once(mut self, args: ClosureArguments) -> Self::Output {
+            self.call_mut(args)
+            }
+        }
+        impl<'a, $($var,)* ClosureArguments: Tuple, Function, ClosureReturnValue> const
+            FnMut<ClosureArguments> for ConstFnMutClosure<($(&'a mut $var),*), Function>
+        where
+            Function: ~const Fn(($(&mut $var),*), ClosureArguments)-> ClosureReturnValue,
+            Function: ~const Destruct,
+        {
+            extern "rust-call" fn call_mut(&mut self, args: ClosureArguments) -> Self::Output {
+                #[allow(non_snake_case)]
+                let ($($var),*) = &mut self.data;
+                (self.func)(($($var),*), args)
+            }
+        }
+    };
+}
+//impl_fn_mut_tuple!(A);
+//impl_fn_mut_tuple!(A B);
+//impl_fn_mut_tuple!(A B C);
+//impl_fn_mut_tuple!(A B C D);
+//impl_fn_mut_tuple!(A B C D E);
+
+#[lang = "receiver"]
+trait Receiver {}
+
+impl<T: ?Sized> Receiver for &T {}
+
+impl<T: ?Sized> Receiver for &mut T {}
+
+#[lang = "destruct"]
+#[const_trait]
+trait Destruct {}
+
+#[lang = "freeze"]
+unsafe auto trait Freeze {}
+
+#[lang = "drop"]
+#[const_trait]
+trait Drop {
+    fn drop(&mut self);
+}
+
+/*
+#[const_trait]
+trait Residual<O> {
+    type TryType: ~const Try<Output = O, Residual = Self> + Try<Output = O, Residual = Self>;
+}
+*/
+
+const fn size_of<T>() -> usize {
+    42
+}
+
+impl Copy for u8 {}
+
+impl usize {
+    #[rustc_allow_incoherent_impl]
+    const fn repeat_u8(x: u8) -> usize {
+        usize::from_ne_bytes([x; size_of::<usize>()])
+    }
+    #[rustc_allow_incoherent_impl]
+    const fn from_ne_bytes(bytes: [u8; size_of::<Self>()]) -> Self {
+        loop {}
+    }
+}
+
+#[rustc_do_not_const_check] // hooked by const-eval
+const fn panic_display() {
+    panic_fmt();
+}
+
+fn panic_fmt() {}
+
+#[lang = "index"]
+#[const_trait]
+trait Index<Idx: ?Sized> {
+    type Output: ?Sized;
+
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+
+#[const_trait]
+unsafe trait SliceIndex<T: ?Sized> {
+    type Output: ?Sized;
+    fn index(self, slice: &T) -> &Self::Output;
+}
+
+impl<T, I> const Index<I> for [T]
+where
+    I: ~const SliceIndex<[T]>,
+{
+    type Output = I::Output;
+
+    #[inline]
+    fn index(&self, index: I) -> &I::Output {
+        index.index(self)
+    }
+}
+/* FIXME
+impl<T, I, const N: usize> const Index<I> for [T; N]
+where
+    [T]: ~const Index<I>,
+{
+    type Output = <[T] as Index<I>>::Output;
+
+    #[inline]
+    // FIXME: make `Self::Output` act like `<Self as ~const Index<I>>::Output`
+    fn index(&self, index: I) -> &<[T] as Index<I>>::Output {
+        Index::index(self as &[T], index)
+    }
+}
+*/
+
+#[lang = "unsize"]
+trait Unsize<T: ?Sized> {
+}
+
+#[lang = "coerce_unsized"]
+trait CoerceUnsized<T: ?Sized> {
+}
+
+impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
+
+
+#[lang = "deref"]
+// #[const_trait] FIXME
+trait Deref {
+    #[lang = "deref_target"]
+    type Target: ?Sized;
+
+    fn deref(&self) -> &Self::Target;
+}
+
+
+impl<T: ?Sized> /* const */ Deref for &T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        *self
+    }
+}
+
+impl<T: ?Sized> /* const */ Deref for &mut T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        *self
+    }
+}
+
+enum Option<T> {
+    #[lang = "None"]
+    None,
+    #[lang = "Some"]
+    Some(T),
+}
+
+impl<T> Option<T> {
+    const fn as_ref(&self) -> Option<&T> {
+        match *self {
+            Some(ref x) => Some(x),
+            None => None,
+        }
+    }
+
+    const fn as_mut(&mut self) -> Option<&mut T> {
+        match *self {
+            Some(ref mut x) => Some(x),
+            None => None,
+        }
+    }
+}
+
+use Option::*;
+
+/*
+const fn as_deref<T>(opt: &Option<T>) -> Option<&T::Target>
+where
+    T: ~const Deref,
+{
+    match opt {
+        Option::Some(t) => Option::Some(t.deref()),
+        Option::None => Option::None,
+    }
+}
+*/
+
+#[const_trait]
+trait Into<T>: Sized {
+    fn into(self) -> T;
+}
+
+#[const_trait]
+trait From<T>: Sized {
+    fn from(value: T) -> Self;
+}
+
+impl<T, U> const Into<U> for T
+where
+    U: ~const From<T>,
+{
+    fn into(self) -> U {
+        U::from(self)
+    }
+}
+
+impl<T> const From<T> for T {
+    fn from(t: T) -> T {
+        t
+    }
+}
+
+enum Result<T, E> {
+    Ok(T),
+    Err(E),
+}
+use Result::*;
+
+fn from_str(s: &str) -> Result<bool, ()> {
+    match s {
+        "true" => Ok(true),
+        "false" => Ok(false),
+        _ => Err(()),
+    }
+}
+
+#[lang = "eq"]
+#[const_trait]
+trait PartialEq<Rhs: ?Sized = Self> {
+    fn eq(&self, other: &Rhs) -> bool;
+    fn ne(&self, other: &Rhs) -> bool {
+        !self.eq(other)
+    }
+}
+
+// FIXME(effects): again, this should not error without Rhs specified
+impl PartialEq<str> for str {
+    fn eq(&self, other: &str) -> bool {
+        loop {}
+    }
+}
+
+
+#[lang = "not"]
+#[const_trait]
+trait Not {
+    type Output;
+    fn not(self) -> Self::Output;
+}
+
+impl const Not for bool {
+    type Output = bool;
+    fn not(self) -> bool {
+        !self
+    }
+}
+
+impl Copy for bool {}
+impl<'a> Copy for &'a str {}
+
+#[lang = "pin"]
+#[fundamental]
+#[repr(transparent)]
+struct Pin<P> {
+    pointer: P,
+}
+
+impl<P> Pin<P> {
+    #[lang = "new_unchecked"]
+    const unsafe fn new_unchecked(pointer: P) -> Pin<P> {
+        Pin { pointer }
+    }
+}
+
+impl<'a, T: ?Sized> Pin<&'a T> {
+    const fn get_ref(self) -> &'a T {
+        self.pointer
+    }
+}
+
+
+impl<P: Deref> Pin<P> {
+    /* const */ fn as_ref(&self) -> Pin<&P::Target>
+    where
+        P: /* ~const */ Deref,
+    {
+        unsafe { Pin::new_unchecked(&*self.pointer) }
+    }
+}
+
+
+impl<'a, T: ?Sized> Pin<&'a mut T> {
+    const unsafe fn get_unchecked_mut(self) -> &'a mut T {
+        self.pointer
+    }
+}
+/* FIXME lol
+impl<T> Option<T> {
+    const fn as_pin_ref(self: Pin<&Self>) -> Option<Pin<&T>> {
+        match Pin::get_ref(self).as_ref() {
+            Some(x) => unsafe { Some(Pin::new_unchecked(x)) },
+            None => None,
+        }
+    }
+
+    const fn as_pin_mut(self: Pin<&mut Self>) -> Option<Pin<&mut T>> {
+        unsafe {
+            match Pin::get_unchecked_mut(self).as_mut() {
+                Some(x) => Some(Pin::new_unchecked(x)),
+                None => None,
+            }
+        }
+    }
+}
+*/
+
+impl<P: /* ~const */ Deref> /* const */ Deref for Pin<P> {
+    type Target = P::Target;
+    fn deref(&self) -> &P::Target {
+        Pin::get_ref(Pin::as_ref(self))
+    }
+}
+
+impl<T> /* const */ Deref for Option<T> {
+    type Target = T;
+    fn deref(&self) -> &T {
+        loop {}
+    }
+}
+
+impl<P: Receiver> Receiver for Pin<P> {}
+
+impl<T: Clone> Clone for RefCell<T> {
+    fn clone(&self) -> RefCell<T> {
+        RefCell::new(self.borrow().clone())
+    }
+}
+
+struct RefCell<T: ?Sized> {
+    borrow: UnsafeCell<()>,
+    value: UnsafeCell<T>,
+}
+impl<T> RefCell<T> {
+    const fn new(value: T) -> RefCell<T> {
+        loop {}
+    }
+}
+impl<T: ?Sized> RefCell<T> {
+    fn borrow(&self) -> Ref<'_, T> {
+        loop {}
+    }
+}
+
+#[lang = "unsafe_cell"]
+#[repr(transparent)]
+struct UnsafeCell<T: ?Sized> {
+    value: T,
+}
+
+struct Ref<'b, T: ?Sized + 'b> {
+    value: *const T,
+    borrow: &'b UnsafeCell<()>,
+}
+
+impl<T: ?Sized> Deref for Ref<'_, T> {
+    type Target = T;
+
+    #[inline]
+    fn deref(&self) -> &T {
+        loop {}
+    }
+}
+
+#[lang = "clone"]
+#[rustc_trivial_field_reads]
+#[const_trait]
+trait Clone: Sized {
+    fn clone(&self) -> Self;
+    fn clone_from(&mut self, source: &Self)
+    where
+        Self: ~const Destruct,
+    {
+        *self = source.clone()
+    }
+}
+
+#[lang = "structural_peq"]
+trait StructuralPartialEq {}
+
+#[lang = "structural_teq"]
+trait StructuralEq {}
+
+const fn drop<T: ~const Destruct>(_: T) {}
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.stderr
new file mode 100644
index 00000000000..02429374218
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.stderr
@@ -0,0 +1,32 @@
+error[E0369]: cannot add `i32` to `i32`
+  --> $DIR/minicore.rs:33:20
+   |
+LL |     let x = 42_i32 + 43_i32;
+   |             ------ ^ ------ i32
+   |             |
+   |             i32
+
+error[E0369]: cannot add `i32` to `i32`
+  --> $DIR/minicore.rs:37:20
+   |
+LL |     let x = 42_i32 + 43_i32;
+   |             ------ ^ ------ i32
+   |             |
+   |             i32
+
+error[E0600]: cannot apply unary operator `!` to type `bool`
+  --> $DIR/minicore.rs:343:9
+   |
+LL |         !self.eq(other)
+   |         ^^^^^^^^^^^^^^^ cannot apply unary operator `!`
+
+error[E0600]: cannot apply unary operator `!` to type `bool`
+  --> $DIR/minicore.rs:365:9
+   |
+LL |         !self
+   |         ^^^^^ cannot apply unary operator `!`
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0369, E0600.
+For more information about an error, try `rustc --explain E0369`.
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/project.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/project.rs
new file mode 100644
index 00000000000..b30d7743edf
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/project.rs
@@ -0,0 +1,11 @@
+// check-pass
+#![feature(const_trait_impl, effects)]
+
+pub trait Owo<X = <Self as Uwu>::T> {}
+
+#[const_trait]
+pub trait Uwu: Owo {
+    type T;
+}
+
+fn main() {}
diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr
index c94563d3591..2a9647da782 100644
--- a/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr
+++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr
@@ -1,35 +1,21 @@
-error[E0277]: the trait bound `T: ~const Bar` is not satisfied
+error[E0308]: mismatched types
   --> $DIR/trait-where-clause-const.rs:21:5
    |
 LL |     T::b();
-   |     ^ the trait `Bar` is not implemented for `T`
+   |     ^^^^^^ expected `host`, found `true`
    |
-note: required by a bound in `Foo::b`
-  --> $DIR/trait-where-clause-const.rs:15:24
-   |
-LL |     fn b() where Self: ~const Bar;
-   |                        ^^^^^^^^^^ required by this bound in `Foo::b`
-help: consider further restricting this bound
-   |
-LL | const fn test1<T: ~const Foo + Bar + Bar>() {
-   |                                    +++++
+   = note: expected constant `host`
+              found constant `true`
 
-error[E0277]: the trait bound `T: ~const Bar` is not satisfied
-  --> $DIR/trait-where-clause-const.rs:23:12
+error[E0308]: mismatched types
+  --> $DIR/trait-where-clause-const.rs:23:5
    |
 LL |     T::c::<T>();
-   |            ^ the trait `Bar` is not implemented for `T`
-   |
-note: required by a bound in `Foo::c`
-  --> $DIR/trait-where-clause-const.rs:16:13
-   |
-LL |     fn c<T: ~const Bar>();
-   |             ^^^^^^^^^^ required by this bound in `Foo::c`
-help: consider further restricting this bound
+   |     ^^^^^^^^^^^ expected `host`, found `true`
    |
-LL | const fn test1<T: ~const Foo + Bar + Bar>() {
-   |                                    +++++
+   = note: expected constant `host`
+              found constant `true`
 
 error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0308`.