diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index a0a381638c061..79820232496a5 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -57,6 +57,9 @@ impl LegacyReceiver for Box {} #[lang = "copy"] pub trait Copy {} +#[lang = "bikeshed_guaranteed_no_drop"] +pub trait BikeshedGuaranteedNoDrop {} + impl Copy for bool {} impl Copy for u8 {} impl Copy for u16 {} diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index 5a4ee0a198cef..2ff1d757fd4e0 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -54,6 +54,9 @@ impl LegacyReceiver for Box {} #[lang = "copy"] pub trait Copy {} +#[lang = "bikeshed_guaranteed_no_drop"] +pub trait BikeshedGuaranteedNoDrop {} + impl Copy for bool {} impl Copy for u8 {} impl Copy for u16 {} diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 1852987b1677f..34c0837b25a52 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -351,6 +351,7 @@ language_item_table! { PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1); ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::None; + BikeshedGuaranteedNoDrop, sym::bikeshed_guaranteed_no_drop, bikeshed_guaranteed_no_drop, Target::Trait, GenericRequirement::Exact(0); MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 9ed56d7bde51e..71a10ad3a0c10 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -6,7 +6,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::MultiSpan; use rustc_errors::codes::*; use rustc_hir::def::{CtorKind, DefKind}; -use rustc_hir::{Node, intravisit}; +use rustc_hir::{LangItem, Node, intravisit}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, ObligationCauseCode}; use rustc_lint_defs::builtin::{ @@ -27,6 +27,7 @@ use rustc_session::lint::builtin::UNINHABITED_STATIC; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; use rustc_trait_selection::traits; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_type_ir::fold::TypeFoldable; use tracing::{debug, instrument}; use ty::TypingMode; @@ -87,89 +88,76 @@ fn allowed_union_or_unsafe_field<'tcx>( typing_env: ty::TypingEnv<'tcx>, span: Span, ) -> bool { - // We don't just accept all !needs_drop fields, due to semver concerns. - let allowed = match ty.kind() { - ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check) - ty::Tuple(tys) => { - // allow tuples of allowed types - tys.iter().all(|ty| allowed_union_or_unsafe_field(tcx, ty, typing_env, span)) - } - ty::Array(elem, _len) => { - // Like `Copy`, we do *not* special-case length 0. - allowed_union_or_unsafe_field(tcx, *elem, typing_env, span) - } - _ => { - // Fallback case: allow `ManuallyDrop` and things that are `Copy`, - // also no need to report an error if the type is unresolved. - ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop()) - || tcx.type_is_copy_modulo_regions(typing_env, ty) - || ty.references_error() - } - }; - if allowed && ty.needs_drop(tcx, typing_env) { - // This should never happen. But we can get here e.g. in case of name resolution errors. - tcx.dcx() - .span_delayed_bug(span, "we should never accept maybe-dropping union or unsafe fields"); + // HACK (not that bad of a hack don't worry): Some codegen tests don't even define proper + // impls for `Copy`. Let's short-circuit here for this validity check, since a lot of them + // use unions. We should eventually fix all the tests to define that lang item or use + // minicore stubs. + if ty.is_trivially_pure_clone_copy() { + return true; } - allowed + // If `BikeshedGuaranteedNoDrop` is not defined in a `#[no_core]` test, fall back to `Copy`. + // This is an underapproximation of `BikeshedGuaranteedNoDrop`, + let def_id = tcx + .lang_items() + .get(LangItem::BikeshedGuaranteedNoDrop) + .unwrap_or_else(|| tcx.require_lang_item(LangItem::Copy, Some(span))); + let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, ty) else { + tcx.dcx().span_delayed_bug(span, "could not normalize field type"); + return true; + }; + let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); + infcx.predicate_must_hold_modulo_regions(&Obligation::new( + tcx, + ObligationCause::dummy_with_span(span), + param_env, + ty::TraitRef::new(tcx, def_id, [ty]), + )) } /// Check that the fields of the `union` do not need dropping. fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { - let item_type = tcx.type_of(item_def_id).instantiate_identity(); - if let ty::Adt(def, args) = item_type.kind() { - assert!(def.is_union()); - - let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id); - for field in &def.non_enum_variant().fields { - let Ok(field_ty) = tcx.try_normalize_erasing_regions(typing_env, field.ty(tcx, args)) - else { - tcx.dcx().span_delayed_bug(span, "could not normalize field type"); - continue; - }; + let def = tcx.adt_def(item_def_id); + assert!(def.is_union()); - if !allowed_union_or_unsafe_field(tcx, field_ty, typing_env, span) { - let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) { - // We are currently checking the type this field came from, so it must be local. - Some(Node::Field(field)) => (field.span, field.ty.span), - _ => unreachable!("mir field has to correspond to hir field"), - }; - tcx.dcx().emit_err(errors::InvalidUnionField { - field_span, - sugg: errors::InvalidUnionFieldSuggestion { - lo: ty_span.shrink_to_lo(), - hi: ty_span.shrink_to_hi(), - }, - note: (), - }); - return false; - } + let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id); + let args = ty::GenericArgs::identity_for_item(tcx, item_def_id); + + for field in &def.non_enum_variant().fields { + if !allowed_union_or_unsafe_field(tcx, field.ty(tcx, args), typing_env, span) { + let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) { + // We are currently checking the type this field came from, so it must be local. + Some(Node::Field(field)) => (field.span, field.ty.span), + _ => unreachable!("mir field has to correspond to hir field"), + }; + tcx.dcx().emit_err(errors::InvalidUnionField { + field_span, + sugg: errors::InvalidUnionFieldSuggestion { + lo: ty_span.shrink_to_lo(), + hi: ty_span.shrink_to_hi(), + }, + note: (), + }); + return false; } - } else { - span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind()); } + true } /// Check that the unsafe fields do not need dropping. fn check_unsafe_fields(tcx: TyCtxt<'_>, item_def_id: LocalDefId) { let span = tcx.def_span(item_def_id); - let item_type = tcx.type_of(item_def_id).instantiate_identity(); - let ty::Adt(def, args) = item_type.kind() else { - span_bug!(span, "structs/enums must be ty::Adt, but got {:?}", item_type.kind()); - }; + let def = tcx.adt_def(item_def_id); + let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id); + let args = ty::GenericArgs::identity_for_item(tcx, item_def_id); + for field in def.all_fields() { if !field.safety.is_unsafe() { continue; } - let Ok(field_ty) = tcx.try_normalize_erasing_regions(typing_env, field.ty(tcx, args)) - else { - tcx.dcx().span_delayed_bug(span, "could not normalize field type"); - continue; - }; - if !allowed_union_or_unsafe_field(tcx, field_ty, typing_env, span) { + if !allowed_union_or_unsafe_field(tcx, field.ty(tcx, args), typing_env, span) { let hir::Node::Field(field) = tcx.hir_node_by_def_id(field.did.expect_local()) else { unreachable!("field has to correspond to hir field") }; diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index b7cd545d02db4..811bd8fb4588a 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -168,6 +168,8 @@ pub enum SelectionCandidate<'tcx> { BuiltinObjectCandidate, BuiltinUnsizeCandidate, + + BikeshedGuaranteedNoDropCandidate, } /// The result of trait evaluation. The order is important diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index e28fcc555dcd1..b529d17540a82 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -217,6 +217,10 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { self.is_phantom_data() } + fn is_manually_drop(self) -> bool { + self.is_manually_drop() + } + fn all_field_tys( self, tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index a9b4593c13da6..9c07981a0275e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -690,6 +690,7 @@ bidirectional_lang_item_map! { AsyncFnOnce, AsyncFnOnceOutput, AsyncIterator, + BikeshedGuaranteedNoDrop, CallOnceFuture, CallRefFuture, Clone, diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index d0b01b14d6358..a3274fb401156 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -267,6 +267,11 @@ where goal: Goal, ) -> Result, NoSolution>; + fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution>; + /// Consider (possibly several) candidates to upcast or unsize a type to another /// type, excluding the coercion of a sized type into a `dyn Trait`. /// @@ -478,6 +483,9 @@ where Some(TraitSolverLangItem::TransmuteTrait) => { G::consider_builtin_transmute_candidate(self, goal) } + Some(TraitSolverLangItem::BikeshedGuaranteedNoDrop) => { + G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal) + } _ => Err(NoSolution), } }; 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 fb53e778e6030..eb398e8724d44 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -374,6 +374,13 @@ where unreachable!("TransmuteFrom is not const") } + fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal, + ) -> Result, NoSolution> { + unreachable!("BikeshedGuaranteedNoDrop is not const"); + } + fn consider_structural_builtin_unsize_candidates( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index c8ea0b119ffbd..1d0ec7b45ca19 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -942,6 +942,13 @@ where ) -> Result, NoSolution> { panic!("`TransmuteFrom` does not have an associated type: {:?}", goal) } + + fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( + _ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + unreachable!("`BikeshedGuaranteedNoDrop` does not have an associated type: {:?}", goal) + } } impl EvalCtxt<'_, D> diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 46fae5c6fa465..dabfa5cc04c8d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -625,6 +625,101 @@ where }) } + /// NOTE: This is implemented as a built-in goal and not a set of impls like: + /// + /// ```rust,ignore (illustrative) + /// impl BikeshedGuaranteedNoDrop for T where T: Copy {} + /// impl BikeshedGuaranteedNoDrop for ManuallyDrop {} + /// ``` + /// + /// because these impls overlap, and I'd rather not build a coherence hack for + /// this harmless overlap. + fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { + return Err(NoSolution); + } + + let cx = ecx.cx(); + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + let ty = goal.predicate.self_ty(); + match ty.kind() { + // `&mut T` and `&T` always implement `BikeshedGuaranteedNoDrop`. + ty::Ref(..) => {} + // `ManuallyDrop` always implements `BikeshedGuaranteedNoDrop`. + ty::Adt(def, _) if def.is_manually_drop() => {} + // Arrays and tuples implement `BikeshedGuaranteedNoDrop` only if + // their constituent types implement `BikeshedGuaranteedNoDrop`. + ty::Tuple(tys) => { + ecx.add_goals( + GoalSource::ImplWhereBound, + tys.iter().map(|elem_ty| { + goal.with(cx, ty::TraitRef::new(cx, goal.predicate.def_id(), [elem_ty])) + }), + ); + } + ty::Array(elem_ty, _) => { + ecx.add_goal( + GoalSource::ImplWhereBound, + goal.with(cx, ty::TraitRef::new(cx, goal.predicate.def_id(), [elem_ty])), + ); + } + + // All other types implement `BikeshedGuaranteedNoDrop` only if + // they implement `Copy`. We could be smart here and short-circuit + // some trivially `Copy`/`!Copy` types, but there's no benefit. + ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Pat(..) + | ty::Dynamic(..) + | ty::Str + | ty::Slice(_) + | ty::Foreign(..) + | ty::Adt(..) + | ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::UnsafeBinder(_) + | ty::CoroutineWitness(..) => { + ecx.add_goal( + GoalSource::ImplWhereBound, + goal.with( + cx, + ty::TraitRef::new( + cx, + cx.require_lang_item(TraitSolverLangItem::Copy), + [ty], + ), + ), + ); + } + + ty::Bound(..) + | ty::Infer( + ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_), + ) => { + panic!("unexpected type `{ty:?}`") + } + } + + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + /// ```ignore (builtin impl example) /// trait Trait { /// fn foo(&self); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index bb59b4c40bd7f..e93a722ccc098 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -515,6 +515,7 @@ symbols! { bang, begin_panic, bench, + bikeshed_guaranteed_no_drop, bin, binaryheap_iter, bind_by_move_pattern_guards, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index bfab009a7e35c..5b362b2356e82 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -102,6 +102,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_candidate_for_tuple(obligation, &mut candidates); } else if tcx.is_lang_item(def_id, LangItem::FnPtrTrait) { self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates); + } else if tcx.is_lang_item(def_id, LangItem::BikeshedGuaranteedNoDrop) { + self.assemble_candidates_for_bikeshed_guaranteed_no_drop_trait( + obligation, + &mut candidates, + ); } else { if tcx.is_lang_item(def_id, LangItem::Clone) { // Same builtin conditions as `Copy`, i.e., every type which has builtin support @@ -1184,4 +1189,48 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } + + fn assemble_candidates_for_bikeshed_guaranteed_no_drop_trait( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + match obligation.predicate.self_ty().skip_binder().kind() { + ty::Ref(..) + | ty::Adt(..) + | ty::Tuple(_) + | ty::Array(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Pat(..) + | ty::Dynamic(..) + | ty::Str + | ty::Slice(_) + | ty::Foreign(..) + | ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::UnsafeBinder(_) + | ty::CoroutineWitness(..) + | ty::Bound(..) => { + candidates.vec.push(BikeshedGuaranteedNoDropCandidate); + } + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + candidates.ambiguous = true; + } + } + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index eb4adde716a08..32cbb97e314d6 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -20,6 +20,7 @@ use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, Upcast}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; use rustc_type_ir::elaborate; +use thin_vec::thin_vec; use tracing::{debug, instrument}; use super::SelectionCandidate::{self, *}; @@ -130,6 +131,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { TraitUpcastingUnsizeCandidate(idx) => { self.confirm_trait_upcasting_unsize_candidate(obligation, idx)? } + + BikeshedGuaranteedNoDropCandidate => { + self.confirm_bikeshed_guaranteed_no_drop_candidate(obligation) + } }; // The obligations returned by confirmation are recursively evaluated @@ -1346,6 +1351,93 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => bug!("source: {source}, target: {target}"), }) } + + fn confirm_bikeshed_guaranteed_no_drop_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> ImplSource<'tcx, PredicateObligation<'tcx>> { + let mut obligations = thin_vec![]; + + let tcx = self.tcx(); + let self_ty = obligation.predicate.self_ty(); + match *self_ty.skip_binder().kind() { + // `&mut T` and `&T` always implement `BikeshedGuaranteedNoDrop`. + ty::Ref(..) => {} + // `ManuallyDrop` always implements `BikeshedGuaranteedNoDrop`. + ty::Adt(def, _) if def.is_manually_drop() => {} + // Arrays and tuples implement `BikeshedGuaranteedNoDrop` only if + // their constituent types implement `BikeshedGuaranteedNoDrop`. + ty::Tuple(tys) => { + obligations.extend(tys.iter().map(|elem_ty| { + obligation.with( + tcx, + self_ty.rebind(ty::TraitRef::new( + tcx, + obligation.predicate.def_id(), + [elem_ty], + )), + ) + })); + } + ty::Array(elem_ty, _) => { + obligations.push(obligation.with( + tcx, + self_ty.rebind(ty::TraitRef::new( + tcx, + obligation.predicate.def_id(), + [elem_ty], + )), + )); + } + + // All other types implement `BikeshedGuaranteedNoDrop` only if + // they implement `Copy`. We could be smart here and short-circuit + // some trivially `Copy`/`!Copy` types, but there's no benefit. + ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Pat(..) + | ty::Dynamic(..) + | ty::Str + | ty::Slice(_) + | ty::Foreign(..) + | ty::Adt(..) + | ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::UnsafeBinder(_) + | ty::CoroutineWitness(..) + | ty::Bound(..) => { + obligations.push(obligation.with( + tcx, + self_ty.map_bound(|ty| { + ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Copy, Some(obligation.cause.span)), + [ty], + ) + }), + )); + } + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + panic!("unexpected type `{self_ty:?}`") + } + } + + ImplSource::Builtin(BuiltinImplSource::Misc, obligations) + } } /// Compute a goal that some RPITIT (right now, only RPITITs corresponding to Futures) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 6b1e1774f0350..436ce3dddd9f0 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1949,7 +1949,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | TraitAliasCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinObjectCandidate - | BuiltinUnsizeCandidate => false, + | BuiltinUnsizeCandidate + | BikeshedGuaranteedNoDropCandidate => false, // Non-global param candidates have already been handled, global // where-bounds get ignored. ParamCandidate(_) | ImplCandidate(_) => true, diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index c27bd269b0d9f..2f72b44f6b62f 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -881,7 +881,10 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { ty.map_bound(|ty| { ty::TraitRef::new( self.tcx(), - self.tcx().require_lang_item(LangItem::Copy, Some(self.span)), + self.tcx().require_lang_item( + LangItem::BikeshedGuaranteedNoDrop, + Some(self.span), + ), [ty], ) }), diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 4e6d645e6fae2..6924216bd26e6 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -540,6 +540,8 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { fn is_phantom_data(self) -> bool; + fn is_manually_drop(self) -> bool; + // FIXME: perhaps use `all_fields` and expose `FieldDef`. fn all_field_tys(self, interner: I) -> ty::EarlyBinder>; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index eeb80bc3ab420..65f7cdf8f922b 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -10,6 +10,7 @@ pub enum TraitSolverLangItem { AsyncFnOnce, AsyncFnOnceOutput, AsyncIterator, + BikeshedGuaranteedNoDrop, CallOnceFuture, CallRefFuture, Clone, diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 029c8b356d074..042ee419d57e4 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -453,6 +453,23 @@ impl Copy for ! {} #[stable(feature = "rust1", since = "1.0.0")] impl Copy for &T {} +/// Marker trait for the types that are allowed in union fields, unsafe fields, +/// and unsafe binder types. +/// +/// Implemented for: +/// * `&T`, `&mut T` for all `T`, +/// * `ManuallyDrop` for all `T`, +/// * tuples and arrays whose elements implement `BikeshedGuaranteedNoDrop`, +/// * or otherwise, all types that are `Copy`. +/// +/// Notably, this doesn't include all trivially-destructible types for semver +/// reasons. +/// +/// Bikeshed name for now. +#[unstable(feature = "bikeshed_guaranteed_no_drop", issue = "none")] +#[cfg_attr(not(bootstrap), lang = "bikeshed_guaranteed_no_drop")] +pub trait BikeshedGuaranteedNoDrop {} + /// Types for which it is safe to share references between threads. /// /// This trait is automatically implemented when the compiler determines diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index a68552175c318..1c5f9eeba3c49 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -39,6 +39,9 @@ impl LegacyReceiver for &mut T {} #[lang = "copy"] pub trait Copy: Sized {} +#[lang = "bikeshed_guaranteed_no_drop"] +pub trait BikeshedGuaranteedNoDrop {} + impl_marker_trait!( Copy => [ bool, char, diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs index e6af1d60e773f..b052aabb499e7 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs @@ -1,12 +1,12 @@ //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib //@ needs-llvm-components: arm +//@ add-core-stubs + #![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)] #![no_core] -#[lang = "sized"] -pub trait Sized {} -#[lang = "copy"] -pub trait Copy {} -impl Copy for u32 {} + +extern crate minicore; +use minicore::*; #[repr(C)] pub struct ReprCU64(u64); diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs index 5746d14f9b1a4..23d55526e5781 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs @@ -1,13 +1,12 @@ //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib //@ needs-llvm-components: arm +//@ add-core-stubs + #![feature(cmse_nonsecure_entry, no_core, lang_items)] #![no_core] -#[lang = "sized"] -pub trait Sized {} -#[lang = "copy"] -pub trait Copy {} -impl Copy for u32 {} -impl Copy for u8 {} + +extern crate minicore; +use minicore::*; #[repr(C)] pub struct ReprCU64(u64); diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr index 9c885d9531814..d37d9b5e8ff7d 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr @@ -1,5 +1,5 @@ error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:25:48 + --> $DIR/return-via-stack.rs:24:48 | LL | pub extern "C-cmse-nonsecure-entry" fn f1() -> ReprCU64 { | ^^^^^^^^ this type doesn't fit in the available registers @@ -8,7 +8,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn f1() -> ReprCU64 { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:30:48 + --> $DIR/return-via-stack.rs:29:48 | LL | pub extern "C-cmse-nonsecure-entry" fn f2() -> ReprCBytes { | ^^^^^^^^^^ this type doesn't fit in the available registers @@ -17,7 +17,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn f2() -> ReprCBytes { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:35:48 + --> $DIR/return-via-stack.rs:34:48 | LL | pub extern "C-cmse-nonsecure-entry" fn f3() -> U64Compound { | ^^^^^^^^^^^ this type doesn't fit in the available registers @@ -26,7 +26,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn f3() -> U64Compound { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:40:48 + --> $DIR/return-via-stack.rs:39:48 | LL | pub extern "C-cmse-nonsecure-entry" fn f4() -> ReprCAlign16 { | ^^^^^^^^^^^^ this type doesn't fit in the available registers @@ -35,7 +35,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn f4() -> ReprCAlign16 { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:47:48 + --> $DIR/return-via-stack.rs:46:48 | LL | pub extern "C-cmse-nonsecure-entry" fn f5() -> [u8; 5] { | ^^^^^^^ this type doesn't fit in the available registers @@ -44,7 +44,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn f5() -> [u8; 5] { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:53:50 + --> $DIR/return-via-stack.rs:52:50 | LL | pub extern "C-cmse-nonsecure-entry" fn u128() -> u128 { | ^^^^ this type doesn't fit in the available registers @@ -53,7 +53,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn u128() -> u128 { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:59:50 + --> $DIR/return-via-stack.rs:58:50 | LL | pub extern "C-cmse-nonsecure-entry" fn i128() -> i128 { | ^^^^ this type doesn't fit in the available registers @@ -62,7 +62,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn i128() -> i128 { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:76:56 + --> $DIR/return-via-stack.rs:75:56 | LL | pub extern "C-cmse-nonsecure-entry" fn union_rust() -> ReprRustUnionU64 { | ^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers @@ -71,7 +71,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn union_rust() -> ReprRustUnionU64 { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:81:53 + --> $DIR/return-via-stack.rs:80:53 | LL | pub extern "C-cmse-nonsecure-entry" fn union_c() -> ReprCUnionU64 { | ^^^^^^^^^^^^^ this type doesn't fit in the available registers diff --git a/tests/ui/layout/malformed-unsized-type-in-union.rs b/tests/ui/layout/malformed-unsized-type-in-union.rs index 5d8ec576cf01b..e97024ce9d704 100644 --- a/tests/ui/layout/malformed-unsized-type-in-union.rs +++ b/tests/ui/layout/malformed-unsized-type-in-union.rs @@ -2,6 +2,7 @@ union W { s: dyn Iterator } //~^ ERROR cannot find type `Missing` in this scope +//~| ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union static ONCE: W = todo!(); diff --git a/tests/ui/layout/malformed-unsized-type-in-union.stderr b/tests/ui/layout/malformed-unsized-type-in-union.stderr index ad4f0cda19e5c..bdfabc0b15190 100644 --- a/tests/ui/layout/malformed-unsized-type-in-union.stderr +++ b/tests/ui/layout/malformed-unsized-type-in-union.stderr @@ -4,6 +4,19 @@ error[E0412]: cannot find type `Missing` in this scope LL | union W { s: dyn Iterator } | ^^^^^^^ not found in this scope -error: aborting due to 1 previous error +error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union + --> $DIR/malformed-unsized-type-in-union.rs:3:11 + | +LL | union W { s: dyn Iterator } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` +help: wrap the field type in `ManuallyDrop<...>` + | +LL | union W { s: std::mem::ManuallyDrop> } + | +++++++++++++++++++++++ + + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0412`. +Some errors have detailed explanations: E0412, E0740. +For more information about an error, try `rustc --explain E0412`. diff --git a/tests/ui/unsafe-binders/moves.rs b/tests/ui/unsafe-binders/moves.rs index 5bfcee62402d6..9397c2bc20f97 100644 --- a/tests/ui/unsafe-binders/moves.rs +++ b/tests/ui/unsafe-binders/moves.rs @@ -1,40 +1,40 @@ -//@ known-bug: unknown - #![feature(unsafe_binders)] -// FIXME(unsafe_binders) ~^ WARN the feature `unsafe_binders` is incomplete +//~^ WARN the feature `unsafe_binders` is incomplete -use std::unsafe_binder::{wrap_binder, unwrap_binder}; -use std::mem::{drop, ManuallyDrop}; +use std::mem::{ManuallyDrop, drop}; +use std::unsafe_binder::{unwrap_binder, wrap_binder}; +#[derive(Default)] struct NotCopyInner; type NotCopy = ManuallyDrop; fn use_after_wrap() { unsafe { - let base = NotCopy; + let base = NotCopy::default(); let binder: unsafe<> NotCopy = wrap_binder!(base); drop(base); - // FIXME(unsafe_binders) ~^ ERROR use of moved value: `base` + //~^ ERROR use of moved value: `base` } } fn move_out_of_wrap() { unsafe { - let binder: unsafe<> NotCopy = wrap_binder!(NotCopy); + let binder: unsafe<> NotCopy = wrap_binder!(NotCopy::default()); drop(unwrap_binder!(binder)); drop(unwrap_binder!(binder)); - // FIXME(unsafe_binders) ~^ ERROR use of moved value: `binder` + //~^ ERROR use of moved value: `binder` } } fn not_conflicting() { unsafe { - let binder: unsafe<> (NotCopy, NotCopy) = wrap_binder!((NotCopy, NotCopy)); + let binder: unsafe<> (NotCopy, NotCopy) = + wrap_binder!((NotCopy::default(), NotCopy::default())); drop(unwrap_binder!(binder).0); drop(unwrap_binder!(binder).1); - // ^ NOT a problem. + // ^ NOT a problem, since the moves are disjoint. drop(unwrap_binder!(binder).0); - // FIXME(unsafe_binders) ~^ ERROR use of moved value: `binder.0` + //~^ ERROR use of moved value: `binder.0` } } diff --git a/tests/ui/unsafe-binders/moves.stderr b/tests/ui/unsafe-binders/moves.stderr index ca5079640087f..0f976d9e845a3 100644 --- a/tests/ui/unsafe-binders/moves.stderr +++ b/tests/ui/unsafe-binders/moves.stderr @@ -1,37 +1,5 @@ -error[E0423]: expected value, found type alias `NotCopy` - --> $DIR/moves.rs:14:20 - | -LL | let base = NotCopy; - | ^^^^^^^ - | - = note: can't use a type alias as a constructor - -error[E0423]: expected value, found type alias `NotCopy` - --> $DIR/moves.rs:23:53 - | -LL | let binder: unsafe<> NotCopy = wrap_binder!(NotCopy); - | ^^^^^^^ - | - = note: can't use a type alias as a constructor - -error[E0423]: expected value, found type alias `NotCopy` - --> $DIR/moves.rs:32:65 - | -LL | let binder: unsafe<> (NotCopy, NotCopy) = wrap_binder!((NotCopy, NotCopy)); - | ^^^^^^^ - | - = note: can't use a type alias as a constructor - -error[E0423]: expected value, found type alias `NotCopy` - --> $DIR/moves.rs:32:74 - | -LL | let binder: unsafe<> (NotCopy, NotCopy) = wrap_binder!((NotCopy, NotCopy)); - | ^^^^^^^ - | - = note: can't use a type alias as a constructor - warning: the feature `unsafe_binders` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/moves.rs:3:12 + --> $DIR/moves.rs:1:12 | LL | #![feature(unsafe_binders)] | ^^^^^^^^^^^^^^ @@ -39,47 +7,38 @@ LL | #![feature(unsafe_binders)] = note: see issue #130516 for more information = note: `#[warn(incomplete_features)]` on by default -error[E0277]: the trait bound `NotCopyInner: Copy` is not satisfied - --> $DIR/moves.rs:15:21 +error[E0382]: use of moved value: `base` + --> $DIR/moves.rs:15:14 | +LL | let base = NotCopy::default(); + | ---- move occurs because `base` has type `ManuallyDrop`, which does not implement the `Copy` trait LL | let binder: unsafe<> NotCopy = wrap_binder!(base); - | ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `NotCopyInner` - | - = note: required for `ManuallyDrop` to implement `Copy` -help: consider annotating `NotCopyInner` with `#[derive(Copy)]` - | -LL + #[derive(Copy)] -LL | struct NotCopyInner; - | + | ---- value moved here +LL | drop(base); + | ^^^^ value used here after move -error[E0277]: the trait bound `NotCopyInner: Copy` is not satisfied - --> $DIR/moves.rs:23:21 +error[E0382]: use of moved value: `binder` + --> $DIR/moves.rs:24:14 | -LL | let binder: unsafe<> NotCopy = wrap_binder!(NotCopy); - | ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `NotCopyInner` - | - = note: required for `ManuallyDrop` to implement `Copy` -help: consider annotating `NotCopyInner` with `#[derive(Copy)]` - | -LL + #[derive(Copy)] -LL | struct NotCopyInner; +LL | drop(unwrap_binder!(binder)); + | ---------------------- value moved here +LL | drop(unwrap_binder!(binder)); + | ^^^^^^^^^^^^^^^^^^^^^^ value used here after move | + = note: move occurs because `binder` has type `ManuallyDrop`, which does not implement the `Copy` trait + = note: this error originates in the macro `unwrap_binder` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `NotCopyInner: Copy` is not satisfied - --> $DIR/moves.rs:32:21 - | -LL | let binder: unsafe<> (NotCopy, NotCopy) = wrap_binder!((NotCopy, NotCopy)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `NotCopyInner` - | - = note: required for `ManuallyDrop` to implement `Copy` - = note: required because it appears within the type `(ManuallyDrop, ManuallyDrop)` -help: consider annotating `NotCopyInner` with `#[derive(Copy)]` +error[E0382]: use of moved value: `binder.0` + --> $DIR/moves.rs:36:14 | -LL + #[derive(Copy)] -LL | struct NotCopyInner; +LL | drop(unwrap_binder!(binder).0); + | ------------------------ value moved here +... +LL | drop(unwrap_binder!(binder).0); + | ^^^^^^^^^^^^^^^^^^^^^^^^ value used here after move | + = note: move occurs because `binder.0` has type `ManuallyDrop`, which does not implement the `Copy` trait -error: aborting due to 7 previous errors; 1 warning emitted +error: aborting due to 3 previous errors; 1 warning emitted -Some errors have detailed explanations: E0277, E0423. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0382`.