diff options
21 files changed, 366 insertions, 67 deletions
diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index a0a381638c0..79820232496 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -57,6 +57,9 @@ impl<T: ?Sized> LegacyReceiver for Box<T> {} #[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 5a4ee0a198c..2ff1d757fd4 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -54,6 +54,9 @@ impl<T: ?Sized, A: Allocator> LegacyReceiver for Box<T, A> {} #[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 1852987b167..34c0837b25a 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 9ed56d7bde5..71a10ad3a0c 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 b7cd545d02d..811bd8fb458 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 e28fcc555dc..b529d17540a 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<TyCtxt<'tcx>> 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 a9b4593c13d..9c07981a027 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 d0b01b14d63..a3274fb4011 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<I, Self>, ) -> Result<Candidate<I>, NoSolution>; + fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal<I, Self>, + ) -> Result<Candidate<I>, 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 fb53e778e60..eb398e8724d 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<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("BikeshedGuaranteedNoDrop is not const"); + } + fn consider_structural_builtin_unsize_candidates( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal<I, Self>, 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 c8ea0b119ff..1d0ec7b45ca 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<Candidate<I>, NoSolution> { panic!("`TransmuteFrom` does not have an associated type: {:?}", goal) } + + fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( + _ecx: &mut EvalCtxt<'_, D>, + goal: Goal<I, Self>, + ) -> Result<Candidate<I>, NoSolution> { + unreachable!("`BikeshedGuaranteedNoDrop` does not have an associated type: {:?}", goal) + } } impl<D, I> 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 46fae5c6fa4..beb7f1bf30e 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: + /// + /// ``` + /// impl<T> BikeshedGuaranteedNoDrop for T where T: Copy {} + /// impl<T> BikeshedGuaranteedNoDrop for ManuallyDrop<T> {} + /// ``` + /// + /// 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<I, Self>, + ) -> Result<Candidate<I>, 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<T>` 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 bb59b4c40bd..e93a722ccc0 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 bfab009a7e3..5b362b2356e 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 eb4adde716a..32cbb97e314 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<T>` 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 6b1e1774f03..436ce3dddd9 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_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 4e6d645e6fa..6924216bd26 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -540,6 +540,8 @@ pub trait AdtDef<I: Interner>: 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<I, impl IntoIterator<Item = I::Ty>>; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index eeb80bc3ab4..65f7cdf8f92 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 029c8b356d0..042ee419d57 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<T: ?Sized> 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<T>` 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 a68552175c3..1c5f9eeba3c 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -39,6 +39,9 @@ impl<T: ?Sized> 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/layout/malformed-unsized-type-in-union.rs b/tests/ui/layout/malformed-unsized-type-in-union.rs index 5d8ec576cf0..e97024ce9d7 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<Item = Missing> } //~^ 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 ad4f0cda19e..bdfabc0b151 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<Item = Missing> } | ^^^^^^^ 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<Item = Missing> } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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<dyn Iterator<Item = Missing>> } + | +++++++++++++++++++++++ + + +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`. |
