Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,32 @@ where
}

fn consider_builtin_copy_clone_candidate(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
Err(NoSolution)
let cx = ecx.cx();

let self_ty = goal.predicate.self_ty();
let constituent_tys =
structural_traits::instantiate_constituent_tys_for_copy_clone_trait(ecx, self_ty)?;

ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
ecx.enter_forall(constituent_tys, |ecx, tys| {
ecx.add_goals(
GoalSource::ImplWhereBound,
tys.into_iter().map(|ty| {
goal.with(
cx,
ty::ClauseKind::HostEffect(
goal.predicate.with_replaced_self_ty(cx, ty),
),
)
}),
);
});

ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}

fn consider_builtin_fn_ptr_trait_candidate(
Expand Down
94 changes: 93 additions & 1 deletion compiler/rustc_trait_selection/src/traits/effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rustc_middle::span_bug;
use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::elaborate::elaborate;
use rustc_middle::ty::fast_reject::DeepRejectCtxt;
use rustc_middle::ty::{self, TypingMode};
use rustc_middle::ty::{self, Ty, TypingMode};
use thin_vec::{ThinVec, thin_vec};

use super::SelectionContext;
Expand Down Expand Up @@ -303,6 +303,9 @@ fn evaluate_host_effect_from_builtin_impls<'tcx>(
obligation: &HostEffectObligation<'tcx>,
) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
match selcx.tcx().as_lang_item(obligation.predicate.def_id()) {
Some(LangItem::Copy | LangItem::Clone) => {
evaluate_host_effect_for_copy_clone_goal(selcx, obligation)
}
Some(LangItem::Destruct) => evaluate_host_effect_for_destruct_goal(selcx, obligation),
Some(LangItem::Fn | LangItem::FnMut | LangItem::FnOnce) => {
evaluate_host_effect_for_fn_goal(selcx, obligation)
Expand All @@ -311,6 +314,95 @@ fn evaluate_host_effect_from_builtin_impls<'tcx>(
}
}

fn evaluate_host_effect_for_copy_clone_goal<'tcx>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use SelectionContext::copy_clone_conditions instead of duplicating the match here?

We do the same in the new solver, so I'd expect either duplicating this match in both solvers or in none of them.

I guess the issue is that copy_clone_conditions only expects self types for which assembly succeeded?

selcx: &mut SelectionContext<'_, 'tcx>,
obligation: &HostEffectObligation<'tcx>,
) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
let tcx = selcx.tcx();
let self_ty = obligation.predicate.self_ty();
let constituent_tys = match *self_ty.kind() {
// impl Copy/Clone for FnDef, FnPtr
ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Ok(ty::Binder::dummy(vec![])),

// Implementations are provided in core
ty::Uint(_)
| ty::Int(_)
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Bool
| ty::Float(_)
| ty::Char
| ty::RawPtr(..)
| ty::Never
| ty::Ref(_, _, ty::Mutability::Not)
| ty::Array(..) => Err(EvaluationFailure::NoSolution),

// Cannot implement in core, as we can't be generic over patterns yet,
// so we'd have to list all patterns and type combinations.
ty::Pat(ty, ..) => Ok(ty::Binder::dummy(vec![ty])),

ty::Dynamic(..)
| ty::Str
| ty::Slice(_)
| ty::Foreign(..)
| ty::Ref(_, _, ty::Mutability::Mut)
| ty::Adt(_, _)
| ty::Alias(_, _)
| ty::Param(_)
| ty::Placeholder(..) => Err(EvaluationFailure::NoSolution),

ty::Bound(..)
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
panic!("unexpected type `{self_ty:?}`")
}

// impl Copy/Clone for (T1, T2, .., Tn) where T1: Copy/Clone, T2: Copy/Clone, .. Tn: Copy/Clone
ty::Tuple(tys) => Ok(ty::Binder::dummy(tys.to_vec())),

// impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone
ty::Closure(_, args) => Ok(ty::Binder::dummy(vec![args.as_closure().tupled_upvars_ty()])),

// impl Copy/Clone for CoroutineClosure where Self::TupledUpvars: Copy/Clone
ty::CoroutineClosure(_, args) => {
Ok(ty::Binder::dummy(vec![args.as_coroutine_closure().tupled_upvars_ty()]))
}

// only when `coroutine_clone` is enabled and the coroutine is movable
// impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses)
ty::Coroutine(def_id, args) => match tcx.coroutine_movability(def_id) {
Copy link
Contributor

@lcnr lcnr Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to check should_stall_coroutine? 🤔

rn checking const Clone for a coroutine should cause a query cycle

ty::Movability::Static => Err(EvaluationFailure::NoSolution),
ty::Movability::Movable => {
if tcx.features().coroutine_clone() {
Ok(ty::Binder::dummy(vec![
args.as_coroutine().tupled_upvars_ty(),
Ty::new_coroutine_witness_for_coroutine(tcx, def_id, args),
]))
} else {
Err(EvaluationFailure::NoSolution)
}
}
},

ty::UnsafeBinder(_) => Err(EvaluationFailure::NoSolution),

// impl Copy/Clone for CoroutineWitness where T: Copy/Clone forall T in coroutine_hidden_types
ty::CoroutineWitness(def_id, args) => Ok(tcx
.coroutine_hidden_types(def_id)
.instantiate(tcx, args)
.map_bound(|bound| bound.types.to_vec())),
}?;

Ok(constituent_tys
.iter()
.map(|ty| {
obligation.with(
tcx,
ty.map_bound(|ty| ty::TraitRef::new(tcx, obligation.predicate.def_id(), [ty]))
.to_host_effect_clause(tcx, obligation.predicate.constness),
)
})
.collect())
}

// NOTE: Keep this in sync with `const_conditions_for_destruct` in the new solver.
fn evaluate_host_effect_for_destruct_goal<'tcx>(
selcx: &mut SelectionContext<'_, 'tcx>,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_type_ir/src/predicate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ impl<I: Interner> ty::Binder<I, TraitRef<I>> {
}

pub fn to_host_effect_clause(self, cx: I, constness: BoundConstness) -> I::Clause {
self.map_bound(|trait_ref| {
self.map_bound(|trait_ref: TraitRef<I>| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

ty::ClauseKind::HostEffect(HostEffectPredicate { trait_ref, constness })
})
.upcast(cx)
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/traits/const-traits/const-traits-alloc.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//@ run-pass
//@ check-pass
#![feature(const_trait_impl, const_default)]
#![allow(dead_code)]
// alloc::string
Expand Down
15 changes: 13 additions & 2 deletions tests/ui/traits/const-traits/const-traits-core.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
//@ run-pass
//@ check-pass
#![feature(
const_trait_impl, const_default, ptr_alignment_type, ascii_char, f16, f128, sync_unsafe_cell,
const_clone,
const_default,
const_trait_impl,
ptr_alignment_type,
ascii_char,
f16,
f128,
sync_unsafe_cell,
)]
#![allow(dead_code)]
// core::default
Expand Down Expand Up @@ -43,4 +50,8 @@ const REF_CELL: std::cell::RefCell<()> = Default::default();
const UNSAFE_CELL: std::cell::UnsafeCell<()> = Default::default();
const SYNC_UNSAFE_CELL: std::cell::SyncUnsafeCell<()> = Default::default();

// `Clone` for tuples
const BUILTIN_CLONE: () = ().clone();
const BUILTIN_CLONE_2: (u32, i32) = (42, 100).clone();

fn main() {}
Loading