-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Don't warn on unused field on union #43397
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
Conversation
r? @pnkfelix (rust_highfive has picked a reviewer for you, use r? to override) |
d425fa8
to
b24a625
Compare
src/librustc/middle/dead.rs
Outdated
if if let Some(node_id) = self.tcx.hir.as_local_node_id(did) { | ||
match self.tcx.hir.find(node_id) { | ||
Some(hir_map::NodeItem(item)) => match item.node { | ||
Item_::ItemUnion(_, _) => false, |
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.
This is throwing out the baby with the bathwater, union fields can be legitimately unused.
Ideally, use of a union field in a struct expression (let u = U { a: 10 }
) should mark it as "potentially used", and uses in field accesses/patterns (u.b
/let U { b } = u
) should mark the field as "used" and additionally mark all its "potentially used" siblings as "used".
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.
Just marking all union fields used in struct expressions as "used" in hope that at least one field of the same union will be read somewhere would be a good approximation as well.
The test case #![deny(dead_code)]
union Foo {
x: usize,
b: bool, //~ ERROR: field is never used
_unused: u16,
}
fn field_read(f: Foo) -> usize {
unsafe { f.x }
}
fn main() {
let _ = field_read(Foo { x: 2 });
} |
@kennytm: It seems normal to me? |
@GuillaumeGomez Well Travis is failing because of this. And as @petrochenkov said, the |
That's an union. If any of its fields is used, then shouldn't we consider all its fields used as well? |
Intuitively,
union U {
x: u32,
y: f32,
z: [u16; 2],
w: [u8; 4],
}
unsafe {
let mut u = U { x: 1 };
// x: written, y: -, z: -, w: -
let y = u.y;
// x: written, y: read, z: -, w: -
// -> x: used, y: used, z: -, w: -
// `y` is used because it is read.
// `x` is used because the write to `x` happens before read of `y`.
u.z = [5, 6];
// x: used, y: used, z: written, w: -
drop(u);
// w is entirely untouched, so unused.
// z is written, but the union is no longer read, so also unused.
// (current Rust treats `z` is used, but that's fine.)
} |
b24a625
to
1729c2e
Compare
I think the new code respects what you wanted @kennytm? |
Any news in here? |
I will review today or tomorrow. |
src/librustc/hir/intravisit.rs
Outdated
@@ -877,7 +877,7 @@ pub fn walk_impl_item_ref<'v, V: Visitor<'v>>(visitor: &mut V, impl_item_ref: &' | |||
|
|||
pub fn walk_struct_def<'v, V: Visitor<'v>>(visitor: &mut V, struct_definition: &'v VariantData) { | |||
visitor.visit_id(struct_definition.id()); | |||
walk_list!(visitor, visit_struct_field, struct_definition.fields()); | |||
walk_list!(visitor, visit_struct_field, struct_definition.fields().iter().rev()); |
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.
Could you avoid changing this infrastructure? It's used everywhere.
src/librustc/middle/dead.rs
Outdated
Some(hir_map::NodeItem(item)) => match item.node { | ||
// If this is an union's field, it means all previous fields | ||
// have been used as well so no need to check further. | ||
Item_::ItemUnion(_, _) => self.need_check_next_union_field = false, |
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.
The lint should not depend on the field definition order in any way, so this must be incorrect.
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.
I suggest to implement #43397 (comment), e.g. visit hir::ExprPath
hir::ExprStruct
in MarkSymbolVisitor::visit_expr
and mark all fields in it as used if they are union fields.
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.
The only way I see to mark a field as used is to add the "used" attr into its attr list. However, I can't do it in the visit_expr
method since the received parameter isn't mutable. Or maybe is there another way I didn't see?
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.
By "marking as used" I mean inserting the field's id into live_symbols
set in MarkSymbolVisitor
.
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.
One tiny tweak: "mark all fields in it as used if they are union fields" -> "mark all fields in it as used if they are union fields and the union has >1 fields". Test for this:
union NoDropLike { a: u8 } // should be reported as unused
let u = NoDropLike { a: 10 };
src/test/ui/union-fields.stderr
Outdated
--> $DIR/union-fields.rs:20:5 | ||
| | ||
20 | y: u32, | ||
| ^^^^^^ |
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.
Diagnostics for unions are not actually tested here.
Could you remove the struct V
(it's not especially relevant to the test) and test this instead:
union U1 {
a: u8, // should not be reported
b: u8, // should not be reported
c: u8, // should be reported
}
union U2 {
a: u8, // should be reported
b: u8, // should not be reported
c: u8, // should not be reported
}
let u = U1 { a: 0 };
let a = u.b;
let u = U2 { c: 0 };
let b = u.b;
1729c2e
to
90f54d0
Compare
src/librustc/middle/dead.rs
Outdated
fn mark_as_used_if_union(&mut self, did: DefId) { | ||
if let Some(node_id) = self.tcx.hir.as_local_node_id(did) { | ||
match self.tcx.hir.find(node_id) { | ||
Some(hir_map::NodeItem(item)) => match item.node { |
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.
@petrochenkov: For now it still doesn't work and I think it's because of here: it never enter in here. Any idea what I did wrong?
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.
Oh, sorry, I meant hir::ExprStruct
in #43397 (comment) of course, not hir::ExprPath
.
mark_as_used_if_union
is good except that it should add only fields used in ExprStruct
to the live_symbols
set, not all fields from the union.
Nit: mark_as_used_if_union
could also use some if let
s.
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.
Ah! That makes more sense!
@petrochenkov: Works way better with the correct check. ;) I think it's all good now. Last check? |
ICE when compiling rustc (librustc_errors) at stage1.
|
Strange. Checking what's wrong. |
src/librustc/middle/dead.rs
Outdated
@@ -231,6 +247,10 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { | |||
hir::ExprTupField(ref lhs, idx) => { | |||
self.handle_tup_field_access(&lhs, idx.node); | |||
} | |||
hir::ExprStruct(ref qpath, ref fields, _) => { | |||
let def = self.tables.qpath_def(qpath, expr.id); |
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.
The last thing is to make it work for type aliases (it'll also fix the ICE with Self
).
You can use self.tcx.expr_ty(expr)
to get the struct expression's type and then check that it's a union.
Test:
union U {
a: u8, // should not be reported
b: u8, // should not be reported
c: u8, // should be reported
}
type A = U;
let u = A { a: 0 };
let b = unsafe { u.b };
And done as well! |
@bors r+ |
📌 Commit f94157e has been approved by |
Don't warn on unused field on union Fixes #43393.
@GuillaumeGomez |
Sure. |
☀️ Test successful - status-appveyor, status-travis |
The original test case in #43393 caused the compiler to warn about an unused field for a field that got used in initialization, which definitely seems like a bug. With this change, does the compiler still warn about a field that nothing initializes or reads from or writes to, or does this disable the warning entirely? |
Do you have a code that could allow us to check if it's fixed? |
Yes, the warning is still issued for completely unused fields, see examples in the tests. |
Fixes #43393.