Skip to content
Merged
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
99 changes: 98 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,100 @@ 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?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, the built in Copy/Clone for non-const stuff is following the "assemble if looks okay, check nested predicates during confirmation", but the const stuff is not, so there is some divide here that prevents us from deduplicating without doing some refactor on how BuiltinCandidate works...

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) => {
if selcx.should_stall_coroutine(def_id) {
return Err(EvaluationFailure::Ambiguous);
}
match tcx.coroutine_movability(def_id) {
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_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2877,7 +2877,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
obligations
}

fn should_stall_coroutine(&self, def_id: DefId) -> bool {
pub(super) fn should_stall_coroutine(&self, def_id: DefId) -> bool {
match self.infcx.typing_mode() {
TypingMode::Analysis { defining_opaque_types_and_generators: stalled_generators } => {
def_id.as_local().is_some_and(|def_id| stalled_generators.contains(&def_id))
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