Skip to content

Use a trait to enforce field validity for union fields + unsafe fields + unsafe<> binder types #136660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 13, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_cranelift/example/mini_core.rs
Original file line number Diff line number Diff line change
@@ -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 {}
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_gcc/example/mini_core.rs
Original file line number Diff line number Diff line change
@@ -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 {}
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
@@ -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;

116 changes: 52 additions & 64 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
@@ -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")
};
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/traits/select.rs
Original file line number Diff line number Diff line change
@@ -168,6 +168,8 @@ pub enum SelectionCandidate<'tcx> {
BuiltinObjectCandidate,

BuiltinUnsizeCandidate,

BikeshedGuaranteedNoDropCandidate,
}

/// The result of trait evaluation. The order is important
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/adt.rs
Original file line number Diff line number Diff line change
@@ -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>,
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
@@ -690,6 +690,7 @@ bidirectional_lang_item_map! {
AsyncFnOnce,
AsyncFnOnceOutput,
AsyncIterator,
BikeshedGuaranteedNoDrop,
CallOnceFuture,
CallRefFuture,
Clone,
8 changes: 8 additions & 0 deletions compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
Original file line number Diff line number Diff line change
@@ -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),
}
};
7 changes: 7 additions & 0 deletions compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
Original file line number Diff line number Diff line change
@@ -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>,
Original file line number Diff line number Diff line change
@@ -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>
95 changes: 95 additions & 0 deletions compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
Original file line number Diff line number Diff line change
@@ -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<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);
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -515,6 +515,7 @@ symbols! {
bang,
begin_panic,
bench,
bikeshed_guaranteed_no_drop,
bin,
binaryheap_iter,
bind_by_move_pattern_guards,
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
92 changes: 92 additions & 0 deletions compiler/rustc_trait_selection/src/traits/select/confirmation.rs
Original file line number Diff line number Diff line change
@@ -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)
3 changes: 2 additions & 1 deletion compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
5 changes: 4 additions & 1 deletion compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
@@ -881,7 +881,10 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> 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],
)
}),
2 changes: 2 additions & 0 deletions compiler/rustc_type_ir/src/inherent.rs
Original file line number Diff line number Diff line change
@@ -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>>;

1 change: 1 addition & 0 deletions compiler/rustc_type_ir/src/lang_items.rs
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ pub enum TraitSolverLangItem {
AsyncFnOnce,
AsyncFnOnceOutput,
AsyncIterator,
BikeshedGuaranteedNoDrop,
CallOnceFuture,
CallRefFuture,
Clone,
17 changes: 17 additions & 0 deletions library/core/src/marker.rs
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions tests/auxiliary/minicore.rs
Original file line number Diff line number Diff line change
@@ -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,
10 changes: 5 additions & 5 deletions tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs
Original file line number Diff line number Diff line change
@@ -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);
11 changes: 5 additions & 6 deletions tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs
Original file line number Diff line number Diff line change
@@ -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);
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions tests/ui/layout/malformed-unsized-type-in-union.rs
Original file line number Diff line number Diff line change
@@ -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!();

17 changes: 15 additions & 2 deletions tests/ui/layout/malformed-unsized-type-in-union.stderr
Original file line number Diff line number Diff line change
@@ -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`.
24 changes: 12 additions & 12 deletions tests/ui/unsafe-binders/moves.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
//@ known-bug: unknown
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot of random fixes in this file to make it compile lol


#![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<NotCopyInner>;

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`
}
}

93 changes: 26 additions & 67 deletions tests/ui/unsafe-binders/moves.stderr
Original file line number Diff line number Diff line change
@@ -1,85 +1,44 @@
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)]
| ^^^^^^^^^^^^^^
|
= note: see issue #130516 <https://github.com/rust-lang/rust/issues/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<NotCopyInner>`, 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<NotCopyInner>` 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<NotCopyInner>` 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<NotCopyInner>`, 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<NotCopyInner>` to implement `Copy`
= note: required because it appears within the type `(ManuallyDrop<NotCopyInner>, ManuallyDrop<NotCopyInner>)`
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<NotCopyInner>`, 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`.