diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 697029e55f7cb..50b6fe1ad5ecb 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -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); } diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 48bd0963c8743..695a29e58a5e7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -211,10 +211,32 @@ where } fn consider_builtin_copy_clone_candidate( - _ecx: &mut EvalCtxt<'_, D>, - _goal: Goal, + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, ) -> Result, 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( diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 1589e3f4092c4..9e82237694e3c 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -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), @@ -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); } } @@ -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> { + 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::>()) + } + _ => None, + } + } + /// Provides context-dependent help for errors reported by the `smart_resolve_path_fragment` /// function. /// Returns `true` if able to provide context-dependent help. @@ -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::>()) - } - _ => 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 @@ -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()) diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 7f2a9999d6464..c29e3281f2248 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -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; @@ -303,6 +303,9 @@ fn evaluate_host_effect_from_builtin_impls<'tcx>( obligation: &HostEffectObligation<'tcx>, ) -> Result>, 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) @@ -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>, 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>, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 70a0896ae384c..ea903bac9d617 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -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)) diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index b2a871a21149e..4f3f140af67d1 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -924,7 +924,7 @@ enum RebaseReason { /// /// 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` diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 23cfdf5bfde2b..6efe95a9edce9 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -4,6 +4,7 @@ //! //! Hints may be compile time or runtime. +use crate::marker::Destruct; use crate::mem::MaybeUninit; use crate::{intrinsics, ub_checks}; @@ -771,7 +772,11 @@ pub const fn cold_path() { /// ``` #[inline(always)] #[stable(feature = "select_unpredictable", since = "1.88.0")] -pub fn select_unpredictable(condition: bool, true_val: T, false_val: T) -> T { +#[rustc_const_unstable(feature = "const_select_unpredictable", issue = "145938")] +pub const fn select_unpredictable(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); diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index c2da8a1a92096..4cee77fda4fba 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -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; @@ -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(b: bool, true_val: T, false_val: T) -> T { +pub const fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T +where + T: [const] Destruct, +{ if b { true_val } else { false_val } } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index bc428c37a88f9..e213e1d91a75d 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -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)] diff --git a/library/coretests/tests/hint.rs b/library/coretests/tests/hint.rs index 24de27b24b802..d15730823eb5b 100644 --- a/library/coretests/tests/hint.rs +++ b/library/coretests/tests/hint.rs @@ -1,25 +1,33 @@ #[test] fn select_unpredictable_drop() { use core::cell::Cell; + struct X<'a>(&'a Cell); - impl Drop for X<'_> { + impl const Drop for X<'_> { fn drop(&mut self) { self.0.set(true); } } - let a_dropped = Cell::new(false); - let b_dropped = Cell::new(false); - let a = X(&a_dropped); - let b = X(&b_dropped); - assert!(!a_dropped.get()); - assert!(!b_dropped.get()); - let selected = core::hint::select_unpredictable(core::hint::black_box(true), a, b); - assert!(!a_dropped.get()); - assert!(b_dropped.get()); - drop(selected); - assert!(a_dropped.get()); - assert!(b_dropped.get()); + const fn do_test() { + let a_dropped = Cell::new(false); + let b_dropped = Cell::new(false); + let a = X(&a_dropped); + let b = X(&b_dropped); + assert!(!a_dropped.get()); + assert!(!b_dropped.get()); + let selected = core::hint::select_unpredictable(core::hint::black_box(true), a, b); + assert!(!a_dropped.get()); + assert!(b_dropped.get()); + drop(selected); + assert!(a_dropped.get()); + assert!(b_dropped.get()); + } + + do_test(); + const { + do_test(); + } } #[test] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 5c2522acb1362..24e59d7cd73b6 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -27,6 +27,7 @@ #![feature(const_option_ops)] #![feature(const_ref_cell)] #![feature(const_result_trait_fn)] +#![feature(const_select_unpredictable)] #![feature(const_trait_impl)] #![feature(control_flow_ok)] #![feature(core_float_math)] diff --git a/tests/ui/resolve/private-constructor-self-issue-147343.rs b/tests/ui/resolve/private-constructor-self-issue-147343.rs new file mode 100644 index 0000000000000..3c5ac603933c8 --- /dev/null +++ b/tests/ui/resolve/private-constructor-self-issue-147343.rs @@ -0,0 +1,16 @@ +mod m { + pub struct S(crate::P); +} + +use m::S; + +struct P; + +impl P { + fn foo(self) { + S(self); + //~^ ERROR cannot initialize a tuple struct which contains private fields [E0423] + } +} + +fn main() {} diff --git a/tests/ui/resolve/private-constructor-self-issue-147343.stderr b/tests/ui/resolve/private-constructor-self-issue-147343.stderr new file mode 100644 index 0000000000000..f9290d7dcb356 --- /dev/null +++ b/tests/ui/resolve/private-constructor-self-issue-147343.stderr @@ -0,0 +1,11 @@ +error[E0423]: cannot initialize a tuple struct which contains private fields + --> $DIR/private-constructor-self-issue-147343.rs:11:9 + | +LL | S(self); + | ^ + | + = note: constructor is not visible here due to private fields + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0423`. diff --git a/tests/ui/traits/const-traits/const-traits-alloc.rs b/tests/ui/traits/const-traits/const-traits-alloc.rs index 07725ef02f185..4dfec2f77f1ba 100644 --- a/tests/ui/traits/const-traits/const-traits-alloc.rs +++ b/tests/ui/traits/const-traits/const-traits-alloc.rs @@ -1,4 +1,4 @@ -//@ run-pass +//@ check-pass #![feature(const_trait_impl, const_default)] #![allow(dead_code)] // alloc::string diff --git a/tests/ui/traits/const-traits/const-traits-core.rs b/tests/ui/traits/const-traits/const-traits-core.rs index 6df53daae137f..2cafde4f5bd0f 100644 --- a/tests/ui/traits/const-traits/const-traits-core.rs +++ b/tests/ui/traits/const-traits/const-traits-core.rs @@ -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 @@ -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() {}