-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Change untagged_unions to not allow union fields with drop #56440
Changes from 8 commits
acf82c7
bfac2f7
d17e54f
776d5d4
0950bdf
f4b2d71
87c66a8
acd71e9
0eda54f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1302,9 +1302,41 @@ fn check_union<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
def.destructor(tcx); // force the destructor to be evaluated | ||
check_representable(tcx, span, def_id); | ||
|
||
check_union_fields(tcx, span, def_id); | ||
check_packed(tcx, span, def_id); | ||
} | ||
|
||
fn check_union_fields<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, | ||
_sp: Span, | ||
item_def_id: DefId) | ||
-> bool { | ||
// Without the feature we check Copy types only later | ||
if !tcx.features().untagged_unions { | ||
return true; | ||
} | ||
let t = tcx.type_of(item_def_id); | ||
if let ty::Adt(def, substs) = t.sty { | ||
if def.is_union() { | ||
let fields = &def.non_enum_variant().fields; | ||
for field in fields { | ||
let field_ty = field.ty(tcx, substs); | ||
// We are currently checking the type this field came from, so it must be local | ||
let field_span = tcx.hir().span_if_local(field.did).unwrap(); | ||
let param_env = tcx.param_env(field.did); | ||
if field_ty.needs_drop(tcx, param_env) { | ||
struct_span_err!(tcx.sess, field_span, E0730, | ||
"unions may not contain fields that need dropping") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eventually, it would be nice if we could say with more detail which type needs to be dropped There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. but I guess it doesn't have to be part of this PR |
||
.span_note(field_span, | ||
"`std::mem::ManuallyDrop` can be used to wrap the type") | ||
.emit(); | ||
return false; | ||
} | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
fn check_opaque<'a, 'tcx>( | ||
tcx: TyCtxt<'a, 'tcx, 'tcx>, | ||
def_id: DefId, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4831,6 +4831,10 @@ fn make_recursive_type() -> impl Sized { | |
``` | ||
"##, | ||
|
||
E0730: r##" | ||
A `union` can not have fields with destructors. | ||
"##, | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have a system for assigning error numbers? Yes, the text is a bit lacking here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do not really have a system, no. You just pick one. |
||
} | ||
|
||
register_diagnostics! { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
#![feature(untagged_unions)] | ||
#![allow(dead_code)] | ||
// run-pass | ||
|
||
use std::mem::needs_drop; | ||
use std::mem::ManuallyDrop; | ||
|
||
struct NeedDrop; | ||
|
||
impl Drop for NeedDrop { | ||
fn drop(&mut self) {} | ||
} | ||
|
||
union UnionOk1<T> { | ||
empty: (), | ||
value: ManuallyDrop<T>, | ||
} | ||
|
||
union UnionOk2 { | ||
value: ManuallyDrop<NeedDrop>, | ||
} | ||
|
||
#[allow(dead_code)] | ||
union UnionOk3<T: Copy> { | ||
bluss marked this conversation as resolved.
Show resolved
Hide resolved
|
||
empty: (), | ||
value: T, | ||
} | ||
|
||
trait Foo { } | ||
|
||
trait ImpliesCopy : Copy { } | ||
|
||
#[allow(dead_code)] | ||
union UnionOk4<T: ImpliesCopy> { | ||
value: T, | ||
} | ||
|
||
fn main() { | ||
// NeedDrop should not make needs_drop true | ||
assert!(!needs_drop::<UnionOk1<NeedDrop>>()); | ||
assert!(!needs_drop::<UnionOk3<&Foo>>()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. In general, the
needs_drop
accessor is a "heuristic", basically used for generating more efficient MIR. I guess though that it is relevant to our semantics in terms of the NLL borrow check as well. I think I might prefer if we made a "language definition" version of this function at some point -- or maybe for now it suffices to amend the rustc documentation for this function to note that it is used for checking union fields and the like.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, updated docs. Yes, it seems like this method is sliding towards having to be more exact.