Implement and use BikeshedGuaranteedNoDrop for union/unsafe field validity
This commit is contained in:
parent
c72d443cdd
commit
516afd557c
21 changed files with 365 additions and 66 deletions
|
@ -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 {}
|
||||||
|
|
|
@ -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 {}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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,89 +88,76 @@ 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 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");
|
|
||||||
}
|
}
|
||||||
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.
|
/// 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 {
|
for field in &def.non_enum_variant().fields {
|
||||||
tcx.dcx().span_delayed_bug(span, "could not normalize field type");
|
if !allowed_union_or_unsafe_field(tcx, field.ty(tcx, args), typing_env, span) {
|
||||||
continue;
|
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 {
|
||||||
if !allowed_union_or_unsafe_field(tcx, field_ty, typing_env, span) {
|
field_span,
|
||||||
let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) {
|
sugg: errors::InvalidUnionFieldSuggestion {
|
||||||
// We are currently checking the type this field came from, so it must be local.
|
lo: ty_span.shrink_to_lo(),
|
||||||
Some(Node::Field(field)) => (field.span, field.ty.span),
|
hi: ty_span.shrink_to_hi(),
|
||||||
_ => unreachable!("mir field has to correspond to hir field"),
|
},
|
||||||
};
|
note: (),
|
||||||
tcx.dcx().emit_err(errors::InvalidUnionField {
|
});
|
||||||
field_span,
|
return false;
|
||||||
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
|
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")
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -690,6 +690,7 @@ bidirectional_lang_item_map! {
|
||||||
AsyncFnOnce,
|
AsyncFnOnce,
|
||||||
AsyncFnOnceOutput,
|
AsyncFnOnceOutput,
|
||||||
AsyncIterator,
|
AsyncIterator,
|
||||||
|
BikeshedGuaranteedNoDrop,
|
||||||
CallOnceFuture,
|
CallOnceFuture,
|
||||||
CallRefFuture,
|
CallRefFuture,
|
||||||
Clone,
|
Clone,
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>>;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ pub enum TraitSolverLangItem {
|
||||||
AsyncFnOnce,
|
AsyncFnOnce,
|
||||||
AsyncFnOnceOutput,
|
AsyncFnOnceOutput,
|
||||||
AsyncIterator,
|
AsyncIterator,
|
||||||
|
BikeshedGuaranteedNoDrop,
|
||||||
CallOnceFuture,
|
CallOnceFuture,
|
||||||
CallRefFuture,
|
CallRefFuture,
|
||||||
Clone,
|
Clone,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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!();
|
||||||
|
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue