1
Fork 0

Implement and use BikeshedGuaranteedNoDrop for union/unsafe field validity

This commit is contained in:
Michael Goulet 2025-02-06 20:08:29 +00:00
parent c72d443cdd
commit 516afd557c
21 changed files with 365 additions and 66 deletions

View file

@ -57,6 +57,9 @@ impl<T: ?Sized> LegacyReceiver for Box<T> {}
#[lang = "copy"] #[lang = "copy"]
pub trait Copy {} pub trait Copy {}
#[lang = "bikeshed_guaranteed_no_drop"]
pub trait BikeshedGuaranteedNoDrop {}
impl Copy for bool {} impl Copy for bool {}
impl Copy for u8 {} impl Copy for u8 {}
impl Copy for u16 {} impl Copy for u16 {}

View file

@ -54,6 +54,9 @@ impl<T: ?Sized, A: Allocator> LegacyReceiver for Box<T, A> {}
#[lang = "copy"] #[lang = "copy"]
pub trait Copy {} pub trait Copy {}
#[lang = "bikeshed_guaranteed_no_drop"]
pub trait BikeshedGuaranteedNoDrop {}
impl Copy for bool {} impl Copy for bool {}
impl Copy for u8 {} impl Copy for u8 {}
impl Copy for u16 {} impl Copy for u16 {}

View file

@ -351,6 +351,7 @@ language_item_table! {
PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1); PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1);
ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::None; 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; MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;

View file

@ -6,7 +6,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::MultiSpan; use rustc_errors::MultiSpan;
use rustc_errors::codes::*; use rustc_errors::codes::*;
use rustc_hir::def::{CtorKind, DefKind}; 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::infer::{RegionVariableOrigin, TyCtxtInferExt};
use rustc_infer::traits::{Obligation, ObligationCauseCode}; use rustc_infer::traits::{Obligation, ObligationCauseCode};
use rustc_lint_defs::builtin::{ 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::InferCtxtErrorExt;
use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective;
use rustc_trait_selection::traits; use rustc_trait_selection::traits;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::fold::TypeFoldable;
use tracing::{debug, instrument}; use tracing::{debug, instrument};
use ty::TypingMode; use ty::TypingMode;
@ -87,48 +88,42 @@ fn allowed_union_or_unsafe_field<'tcx>(
typing_env: ty::TypingEnv<'tcx>, typing_env: ty::TypingEnv<'tcx>,
span: Span, span: Span,
) -> bool { ) -> bool {
// We don't just accept all !needs_drop fields, due to semver concerns. // HACK (not that bad of a hack don't worry): Some codegen tests don't even define proper
let allowed = match ty.kind() { // impls for `Copy`. Let's short-circuit here for this validity check, since a lot of them
ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check) // use unions. We should eventually fix all the tests to define that lang item or use
ty::Tuple(tys) => { // minicore stubs.
// allow tuples of allowed types if ty.is_trivially_pure_clone_copy() {
tys.iter().all(|ty| allowed_union_or_unsafe_field(tcx, ty, typing_env, span)) return true;
}
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 `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;
}; };
if allowed && ty.needs_drop(tcx, typing_env) { let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
// This should never happen. But we can get here e.g. in case of name resolution errors. infcx.predicate_must_hold_modulo_regions(&Obligation::new(
tcx.dcx() tcx,
.span_delayed_bug(span, "we should never accept maybe-dropping union or unsafe fields"); ObligationCause::dummy_with_span(span),
} param_env,
allowed ty::TraitRef::new(tcx, def_id, [ty]),
))
} }
/// Check that the fields of the `union` do not need dropping. /// Check that the fields of the `union` do not need dropping.
fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool {
let item_type = tcx.type_of(item_def_id).instantiate_identity(); let def = tcx.adt_def(item_def_id);
if let ty::Adt(def, args) = item_type.kind() {
assert!(def.is_union()); assert!(def.is_union());
let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id); let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id);
for field in &def.non_enum_variant().fields { let args = ty::GenericArgs::identity_for_item(tcx, item_def_id);
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) { 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) { 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. // We are currently checking the type this field came from, so it must be local.
Some(Node::Field(field)) => (field.span, field.ty.span), Some(Node::Field(field)) => (field.span, field.ty.span),
@ -145,31 +140,24 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
return false; return false;
} }
} }
} else {
span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind());
}
true true
} }
/// Check that the unsafe fields do not need dropping. /// Check that the unsafe fields do not need dropping.
fn check_unsafe_fields(tcx: TyCtxt<'_>, item_def_id: LocalDefId) { fn check_unsafe_fields(tcx: TyCtxt<'_>, item_def_id: LocalDefId) {
let span = tcx.def_span(item_def_id); let span = tcx.def_span(item_def_id);
let item_type = tcx.type_of(item_def_id).instantiate_identity(); let def = tcx.adt_def(item_def_id);
let ty::Adt(def, args) = item_type.kind() else {
span_bug!(span, "structs/enums must be ty::Adt, but got {:?}", item_type.kind());
};
let typing_env = ty::TypingEnv::non_body_analysis(tcx, 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() { for field in def.all_fields() {
if !field.safety.is_unsafe() { if !field.safety.is_unsafe() {
continue; 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 { let hir::Node::Field(field) = tcx.hir_node_by_def_id(field.did.expect_local()) else {
unreachable!("field has to correspond to hir field") unreachable!("field has to correspond to hir field")
}; };

View file

@ -168,6 +168,8 @@ pub enum SelectionCandidate<'tcx> {
BuiltinObjectCandidate, BuiltinObjectCandidate,
BuiltinUnsizeCandidate, BuiltinUnsizeCandidate,
BikeshedGuaranteedNoDropCandidate,
} }
/// The result of trait evaluation. The order is important /// The result of trait evaluation. The order is important

View file

@ -217,6 +217,10 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
self.is_phantom_data() self.is_phantom_data()
} }
fn is_manually_drop(self) -> bool {
self.is_manually_drop()
}
fn all_field_tys( fn all_field_tys(
self, self,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,

View file

@ -690,6 +690,7 @@ bidirectional_lang_item_map! {
AsyncFnOnce, AsyncFnOnce,
AsyncFnOnceOutput, AsyncFnOnceOutput,
AsyncIterator, AsyncIterator,
BikeshedGuaranteedNoDrop,
CallOnceFuture, CallOnceFuture,
CallRefFuture, CallRefFuture,
Clone, Clone,

View file

@ -267,6 +267,11 @@ where
goal: Goal<I, Self>, goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution>; ) -> 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 /// Consider (possibly several) candidates to upcast or unsize a type to another
/// type, excluding the coercion of a sized type into a `dyn Trait`. /// type, excluding the coercion of a sized type into a `dyn Trait`.
/// ///
@ -478,6 +483,9 @@ where
Some(TraitSolverLangItem::TransmuteTrait) => { Some(TraitSolverLangItem::TransmuteTrait) => {
G::consider_builtin_transmute_candidate(self, goal) G::consider_builtin_transmute_candidate(self, goal)
} }
Some(TraitSolverLangItem::BikeshedGuaranteedNoDrop) => {
G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal)
}
_ => Err(NoSolution), _ => Err(NoSolution),
} }
}; };

View file

@ -374,6 +374,13 @@ where
unreachable!("TransmuteFrom is not const") 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( fn consider_structural_builtin_unsize_candidates(
_ecx: &mut EvalCtxt<'_, D>, _ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>, _goal: Goal<I, Self>,

View file

@ -942,6 +942,13 @@ where
) -> Result<Candidate<I>, NoSolution> { ) -> Result<Candidate<I>, NoSolution> {
panic!("`TransmuteFrom` does not have an associated type: {:?}", goal) 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> impl<D, I> EvalCtxt<'_, D>

View file

@ -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) /// ```ignore (builtin impl example)
/// trait Trait { /// trait Trait {
/// fn foo(&self); /// fn foo(&self);

View file

@ -515,6 +515,7 @@ symbols! {
bang, bang,
begin_panic, begin_panic,
bench, bench,
bikeshed_guaranteed_no_drop,
bin, bin,
binaryheap_iter, binaryheap_iter,
bind_by_move_pattern_guards, bind_by_move_pattern_guards,

View file

@ -102,6 +102,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.assemble_candidate_for_tuple(obligation, &mut candidates); self.assemble_candidate_for_tuple(obligation, &mut candidates);
} else if tcx.is_lang_item(def_id, LangItem::FnPtrTrait) { } else if tcx.is_lang_item(def_id, LangItem::FnPtrTrait) {
self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates); 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 { } else {
if tcx.is_lang_item(def_id, LangItem::Clone) { if tcx.is_lang_item(def_id, LangItem::Clone) {
// Same builtin conditions as `Copy`, i.e., every type which has builtin support // 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;
}
}
}
} }

View file

@ -20,6 +20,7 @@ use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, Upcast};
use rustc_middle::{bug, span_bug}; use rustc_middle::{bug, span_bug};
use rustc_span::def_id::DefId; use rustc_span::def_id::DefId;
use rustc_type_ir::elaborate; use rustc_type_ir::elaborate;
use thin_vec::thin_vec;
use tracing::{debug, instrument}; use tracing::{debug, instrument};
use super::SelectionCandidate::{self, *}; use super::SelectionCandidate::{self, *};
@ -130,6 +131,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
TraitUpcastingUnsizeCandidate(idx) => { TraitUpcastingUnsizeCandidate(idx) => {
self.confirm_trait_upcasting_unsize_candidate(obligation, 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 // The obligations returned by confirmation are recursively evaluated
@ -1346,6 +1351,93 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
_ => bug!("source: {source}, target: {target}"), _ => 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) /// Compute a goal that some RPITIT (right now, only RPITITs corresponding to Futures)

View file

@ -1949,7 +1949,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| TraitAliasCandidate | TraitAliasCandidate
| TraitUpcastingUnsizeCandidate(_) | TraitUpcastingUnsizeCandidate(_)
| BuiltinObjectCandidate | BuiltinObjectCandidate
| BuiltinUnsizeCandidate => false, | BuiltinUnsizeCandidate
| BikeshedGuaranteedNoDropCandidate => false,
// Non-global param candidates have already been handled, global // Non-global param candidates have already been handled, global
// where-bounds get ignored. // where-bounds get ignored.
ParamCandidate(_) | ImplCandidate(_) => true, ParamCandidate(_) | ImplCandidate(_) => true,

View file

@ -540,6 +540,8 @@ pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq {
fn is_phantom_data(self) -> bool; fn is_phantom_data(self) -> bool;
fn is_manually_drop(self) -> bool;
// FIXME: perhaps use `all_fields` and expose `FieldDef`. // FIXME: perhaps use `all_fields` and expose `FieldDef`.
fn all_field_tys(self, interner: I) -> ty::EarlyBinder<I, impl IntoIterator<Item = I::Ty>>; fn all_field_tys(self, interner: I) -> ty::EarlyBinder<I, impl IntoIterator<Item = I::Ty>>;

View file

@ -10,6 +10,7 @@ pub enum TraitSolverLangItem {
AsyncFnOnce, AsyncFnOnce,
AsyncFnOnceOutput, AsyncFnOnceOutput,
AsyncIterator, AsyncIterator,
BikeshedGuaranteedNoDrop,
CallOnceFuture, CallOnceFuture,
CallRefFuture, CallRefFuture,
Clone, Clone,

View file

@ -453,6 +453,23 @@ impl Copy for ! {}
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Copy for &T {} 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. /// Types for which it is safe to share references between threads.
/// ///
/// This trait is automatically implemented when the compiler determines /// This trait is automatically implemented when the compiler determines

View file

@ -39,6 +39,9 @@ impl<T: ?Sized> LegacyReceiver for &mut T {}
#[lang = "copy"] #[lang = "copy"]
pub trait Copy: Sized {} pub trait Copy: Sized {}
#[lang = "bikeshed_guaranteed_no_drop"]
pub trait BikeshedGuaranteedNoDrop {}
impl_marker_trait!( impl_marker_trait!(
Copy => [ Copy => [
bool, char, bool, char,

View file

@ -2,6 +2,7 @@
union W { s: dyn Iterator<Item = Missing> } union W { s: dyn Iterator<Item = Missing> }
//~^ ERROR cannot find type `Missing` in this scope //~^ 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!(); static ONCE: W = todo!();

View file

@ -4,6 +4,19 @@ error[E0412]: cannot find type `Missing` in this scope
LL | union W { s: dyn Iterator<Item = Missing> } LL | union W { s: dyn Iterator<Item = Missing> }
| ^^^^^^^ not found in this scope | ^^^^^^^ 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>> }
| +++++++++++++++++++++++ +
For more information about this error, try `rustc --explain E0412`. error: aborting due to 2 previous errors
Some errors have detailed explanations: E0412, E0740.
For more information about an error, try `rustc --explain E0412`.