-
Notifications
You must be signed in to change notification settings - Fork 13.9k
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
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
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
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::ExprPathhir::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
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 lets.
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
| 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.