Rollup merge of #136660 - compiler-errors:BikeshedGuaranteedNoDrop, r=lcnr
Use a trait to enforce field validity for union fields + `unsafe` fields + `unsafe<>` binder types
This PR introduces a new, internal-only trait called `BikeshedGuaranteedNoDrop`[^1] to faithfully model the field check that used to be implemented manually by `allowed_union_or_unsafe_field`.
942db6782f/compiler/rustc_hir_analysis/src/check/check.rs (L84-L115)
Copying over the doc comment from the trait:
```rust
/// 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.
```
As far as I am aware, there's no new behavior being guaranteed by this trait, since it operates the same as the manually implemented check. We could easily rip out this trait and go back to using the manually implemented check for union fields, however using a trait means that this code can be shared by WF for `unsafe<>` binders too. See the last commit.
The only diagnostic changes are that this now fires false-negatives for fields that are ill-formed. I don't consider that to be much of a problem though.
r? oli-obk
[^1]: Please let's not bikeshed this name lol. There's no good name for `ValidForUnsafeFieldsUnsafeBindersAndUnionFields`.
This commit is contained in:
commit
4ea261018a
27 changed files with 426 additions and 166 deletions
|
@ -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 {}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 def = tcx.adt_def(item_def_id);
|
||||
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 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"),
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
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")
|
||||
};
|
||||
|
|
|
@ -168,6 +168,8 @@ pub enum SelectionCandidate<'tcx> {
|
|||
BuiltinObjectCandidate,
|
||||
|
||||
BuiltinUnsizeCandidate,
|
||||
|
||||
BikeshedGuaranteedNoDropCandidate,
|
||||
}
|
||||
|
||||
/// The result of trait evaluation. The order is important
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -690,6 +690,7 @@ bidirectional_lang_item_map! {
|
|||
AsyncFnOnce,
|
||||
AsyncFnOnceOutput,
|
||||
AsyncIterator,
|
||||
BikeshedGuaranteedNoDrop,
|
||||
CallOnceFuture,
|
||||
CallRefFuture,
|
||||
Clone,
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -516,6 +516,7 @@ symbols! {
|
|||
bang,
|
||||
begin_panic,
|
||||
bench,
|
||||
bikeshed_guaranteed_no_drop,
|
||||
bin,
|
||||
binaryheap_iter,
|
||||
bind_by_move_pattern_guards,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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],
|
||||
)
|
||||
}),
|
||||
|
|
|
@ -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>>;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ pub enum TraitSolverLangItem {
|
|||
AsyncFnOnce,
|
||||
AsyncFnOnceOutput,
|
||||
AsyncIterator,
|
||||
BikeshedGuaranteedNoDrop,
|
||||
CallOnceFuture,
|
||||
CallRefFuture,
|
||||
Clone,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!();
|
||||
|
||||
|
|
|
@ -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>> }
|
||||
| +++++++++++++++++++++++ +
|
||||
|
||||
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`.
|
||||
|
|
|
@ -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<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`
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <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
|
||||
error[E0382]: use of moved value: `binder.0`
|
||||
--> $DIR/moves.rs:36:14
|
||||
|
|
||||
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)]`
|
||||
|
|
||||
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`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue