Skip to content
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/writeback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
} else {
let predicate = self.tcx().erase_and_anonymize_regions(predicate);
if cause.has_infer() || cause.has_placeholders() {
// We can't use the the obligation cause as it references
// We can't use the obligation cause as it references
// information local to this query.
cause = self.fcx.misc(cause.span);
}
Expand Down
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
101 changes: 57 additions & 44 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
let mut expected = source.descr_expected();
let path_str = Segment::names_to_string(path);
let item_str = path.last().unwrap().ident;

if let Some(res) = res {
BaseError {
msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
Expand Down Expand Up @@ -821,12 +822,18 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
args_snippet = snippet;
}

err.span_suggestion(
call_span,
format!("try calling `{ident}` as a method"),
format!("self.{path_str}({args_snippet})"),
Applicability::MachineApplicable,
);
if let Some(Res::Def(DefKind::Struct, def_id)) = res {
self.update_err_for_private_tuple_struct_fields(err, &source, def_id);
err.note("constructor is not visible here due to private fields");
} else {
err.span_suggestion(
call_span,
format!("try calling `{ident}` as a method"),
format!("self.{path_str}({args_snippet})"),
Applicability::MachineApplicable,
);
}

return (true, suggested_candidates, candidates);
}
}
Expand Down Expand Up @@ -1611,6 +1618,47 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
}
}

fn update_err_for_private_tuple_struct_fields(
&mut self,
err: &mut Diag<'_>,
source: &PathSource<'_, '_, '_>,
def_id: DefId,
) -> Option<Vec<Span>> {
match source {
// e.g. `if let Enum::TupleVariant(field1, field2) = _`
PathSource::TupleStruct(_, pattern_spans) => {
err.primary_message(
"cannot match against a tuple struct which contains private fields",
);

// Use spans of the tuple struct pattern.
Some(Vec::from(*pattern_spans))
}
// e.g. `let _ = Enum::TupleVariant(field1, field2);`
PathSource::Expr(Some(Expr {
kind: ExprKind::Call(path, args),
span: call_span,
..
})) => {
err.primary_message(
"cannot initialize a tuple struct which contains private fields",
);
self.suggest_alternative_construction_methods(
def_id,
err,
path.span,
*call_span,
&args[..],
);
// Use spans of the tuple struct definition.
self.r
.field_idents(def_id)
.map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>())
}
_ => None,
}
}

/// Provides context-dependent help for errors reported by the `smart_resolve_path_fragment`
/// function.
/// Returns `true` if able to provide context-dependent help.
Expand Down Expand Up @@ -1942,42 +1990,6 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
return true;
};

let update_message =
|this: &mut Self, err: &mut Diag<'_>, source: &PathSource<'_, '_, '_>| {
match source {
// e.g. `if let Enum::TupleVariant(field1, field2) = _`
PathSource::TupleStruct(_, pattern_spans) => {
err.primary_message(
"cannot match against a tuple struct which contains private fields",
);

// Use spans of the tuple struct pattern.
Some(Vec::from(*pattern_spans))
}
// e.g. `let _ = Enum::TupleVariant(field1, field2);`
PathSource::Expr(Some(Expr {
kind: ExprKind::Call(path, args),
span: call_span,
..
})) => {
err.primary_message(
"cannot initialize a tuple struct which contains private fields",
);
this.suggest_alternative_construction_methods(
def_id,
err,
path.span,
*call_span,
&args[..],
);
// Use spans of the tuple struct definition.
this.r
.field_idents(def_id)
.map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>())
}
_ => None,
}
};
let is_accessible = self.r.is_accessible_from(ctor_vis, self.parent_scope.module);
if let Some(use_span) = self.r.inaccessible_ctor_reexport.get(&span)
&& is_accessible
Expand Down Expand Up @@ -2006,13 +2018,14 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
Applicability::MachineApplicable,
);
}
update_message(self, err, &source);
self.update_err_for_private_tuple_struct_fields(err, &source, def_id);
}
if !is_expected(ctor_def) || is_accessible {
return true;
}

let field_spans = update_message(self, err, &source);
let field_spans =
self.update_err_for_private_tuple_struct_fields(err, &source, def_id);

if let Some(spans) =
field_spans.filter(|spans| spans.len() > 0 && fields.len() == spans.len())
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>(
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 compiler/rustc_type_ir/src/search_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -924,7 +924,7 @@ enum RebaseReason<X: Cx> {
///
/// This either happens in the first evaluation step for the cycle head.
/// In this case the used provisional result depends on the cycle `PathKind`.
/// We store this path kind to check whether the the provisional cache entry
/// We store this path kind to check whether the provisional cache entry
/// we're rebasing relied on the same cycles.
///
/// In later iterations cycles always return `stack_entry.provisional_result`
Expand Down
7 changes: 6 additions & 1 deletion library/core/src/hint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//!
//! Hints may be compile time or runtime.

use crate::marker::Destruct;
use crate::mem::MaybeUninit;
use crate::{intrinsics, ub_checks};

Expand Down Expand Up @@ -771,7 +772,11 @@ pub const fn cold_path() {
/// ```
#[inline(always)]
#[stable(feature = "select_unpredictable", since = "1.88.0")]
pub fn select_unpredictable<T>(condition: bool, true_val: T, false_val: T) -> T {
#[rustc_const_unstable(feature = "const_select_unpredictable", issue = "145938")]
pub const fn select_unpredictable<T>(condition: bool, true_val: T, false_val: T) -> T
where
T: [const] Destruct,
{
// FIXME(https://github.com/rust-lang/unsafe-code-guidelines/issues/245):
// Change this to use ManuallyDrop instead.
let mut true_val = MaybeUninit::new(true_val);
Expand Down
8 changes: 6 additions & 2 deletions library/core/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
#![allow(missing_docs)]

use crate::ffi::va_list::{VaArgSafe, VaListImpl};
use crate::marker::{ConstParamTy, DiscriminantKind, PointeeSized, Tuple};
use crate::marker::{ConstParamTy, Destruct, DiscriminantKind, PointeeSized, Tuple};
use crate::ptr;

mod bounds;
Expand Down Expand Up @@ -477,11 +477,15 @@ pub const fn unlikely(b: bool) -> bool {
/// However unlike the public form, the intrinsic will not drop the value that
/// is not selected.
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_const_unstable(feature = "const_select_unpredictable", issue = "145938")]
#[rustc_intrinsic]
#[rustc_nounwind]
#[miri::intrinsic_fallback_is_spec]
#[inline]
pub fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T {
pub const fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T
where
T: [const] Destruct,
{
if b { true_val } else { false_val }
}

Expand Down
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
#![feature(const_cmp)]
#![feature(const_destruct)]
#![feature(const_eval_select)]
#![feature(const_select_unpredictable)]
#![feature(core_intrinsics)]
#![feature(coverage_attribute)]
#![feature(disjoint_bitor)]
Expand Down
Loading
Loading