-
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
Add lint to deny transmuting &T to &mut T #24392
Conversation
r? @eddyb (rust_highfive has picked a reviewer for you, use r? to override) |
6a9a49e
to
5fc357e
Compare
if let ast::ExprPath(..) = expr.node { | ||
if let DefFn(did, _) = ty::resolve_expr(cx.tcx, expr) { | ||
if def_id_is_transmute(cx, did) { | ||
let typ = ty::node_id_to_type(cx.tcx, 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.
Just use expr_ty
?
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 actually stole most of this code from rustc::middle::instrinsicck...
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'm not sure why we need to go through resolve_expr
here, actually.
We have transmute(a)
, where transmute(a)
is an ExprCall
(where the first Expr
is an ExprPath
) and a
is some form of Expr
. Just running expr_ty
on both should be enough to get the types, without going through BareFnTy
and whatnot.
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.
It seems it's needed to get to the DefId
, so that we can determine if this method is actually std::mem::transmute
, even if it's been imported as a different name. The BareFnTy
is also needed to check the ABI.
I'll have another look later when I can access docs, but this looks good to me code-wise. There may be a way to avoid the csearch. I'm not certain if we want to add more lints though. @alexcrichton thoughts? |
I personally feel that we have far too many lints as-is, but I'm also quite biased! I don't believe we have a strict policy on what lints are included and which are not. |
So, this doesn't need to be a lint. It could be built in to the |
None | ||
} | ||
|
||
fn def_id_is_transmute(cx: &Context, def_id: DefId) -> bool { |
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 don't think we need the csearch here. Just check that the ABI is rust-intrinsic
and that the path matches transmute
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 don't know enough yet, so likely ignore me. However, it looks like csearch is needed if the fn def_id is not defined in the same crate (so most cases), right?
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.
Well, you don't need to lookup the def, the name of the function is already with you in the ExprPath
-- transmute(a)
is an ExprCall(ExprPath("transmute"), vec![whatever a is])
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.
Hm, good point!
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.
@Manishearth oh, what about use std::mem::transmute as trans
? I imagine that's why the original fn is being looked up?
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 right. Lookup is necessary in that case.
This would fix #13146 |
5fc357e
to
09047f7
Compare
@Manishearth okay, using Also added Also, nominate for beta, since breaking at 1.1 seems impossible? |
triage: beta-nominated wonder if that works |
Anything else required for this? |
Looks good to me. I'll let @alexcrichton r+ this though |
from nominated to (nominated, accepted) |
Maybe the documentation of |
I still personally feel that lints like this are too specific to be that beneficial. For example this does not provide an iron-clad guarantee that this kind of "undefined behavior" isn't actually happening. The biggest abuser of |
IMO if we crack down on UB now, even with semi effective solutions like this one; it will still be a net gain in the future when there isn't any UB abuse scattered all over the place. -----Original Message----- I still personally feel that lints like this are too specific to be that beneficial. For example this does not provide an iron-clad guarantee that this kind of "undefined behavior" isn't actually happening. The biggest abuser of &mut self in the standard library, channels, is not caught by this change and probably wouldn't with any form of lint. I know that lints are not meant to provide an iron-clad guarantee, but I'm just not sure how useful this will end up being (especially being deny-by-default). |
I happen to believe that any lint that's supposed to be so important to be deny-by-default should be a feature of the language proper rather than being left up to a lint. I dunno, maybe the perfect is the enemy of the good... |
I'm open to warn by default |
Since this is undefined behavior, I think it should be deny by default. If you somehow know better, you can add an I originally thought of adding this code to |
I do agree with @bstrie that it seems weird to implement a check for undefined behavior as a lint instead of some sort of other error. A lint is an admission that "sure, you may want to actually do this sometimes, just not all the time", but I'm not sure that we have a set of behavior where you would indeed want this kind of a transmute. |
I don't know, I'm wary of building in "no, you should never need this" into a language; there should always be a way to do stuff like this, just that there are more hoops to jump through. |
@alexcrichton I guess it's technically not undefined behavior to transmute if you already are using an unsafe {
let val = &*self.foo.get();
let val_mut: &mut Foo = transmute(val);
} Though, at that point, you should likely just do |
@alexcrichton I think that quote from nwin is regarding the repr + drop lint, not this one? As for motivation, it actually came from seeing it appear in crates on crates.io, such as the issue I linked regarding pool, which did this transmute in several places. I likewise started with a transmute in hyper's I genuinely think that more often than not, it's a mistake because people don't realize it is undefined behavior, and pointing it out and suggesting |
@seanmonstar yes it was not about this one, admittedly ambiguously formulated. |
tomaka:
Lints – and static code analysis in general – are regarded very positively in many language communities. E.g. many front-end devs won't allow code that doesn't pass jslint, and I (a seasoned Java dev with more than a decade of experience) have FindBugs check my code at all times. It's cheap and will sometimes catch oversights. Errors happen. Having an additional safety net cannot hurt. Rust is already very good in this regard. So while I certainly agree that not all lints belong in rustc, I don't feel that we have too many. But I'm just a rust newbie, so feel free to ignore my opinion. 😄 |
I'm with @seanmonstar on this one. The issue that I had (linked above) was something that a) seems to work fine, b) I had every reason to believe would work (exclusive ownership was guaranteed elsewhere), and c) should never be allowed by anyone serious about things. The suggestion to do the transmute was made by a seasoned (unnamed) Rust-y in IRC. Now that I've internalized some of the things on the UB link I know a bit better, but I could really use the help from lints written by smarter folks. |
⌛ Testing commit f224446 with merge f872a82... |
💔 Test failed - auto-mac-64-opt |
f224446
to
50d406b
Compare
@alexcrichton updated the failing run-pass tests |
@@ -22,5 +22,5 @@ fn bar<T>(_: &mut A, _: &T) {} | |||
|
|||
fn foo<T>(t: &T) { | |||
let b = B; | |||
bar(unsafe { mem::transmute(&b as &A) }, t) | |||
bar(&mut b as &mut A, t) |
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.
Did you run this test? I think that the binding to b
needs to be mut
for this to work? (I'd recommend being sure to run the tests for these cases).
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 pushed before running (the run caught what you noticed), since I needed to rebuild and it'd take a while. It's all done, and I'm pushing the corrections that make these 2 tests pass.
50d406b
to
5b0d828
Compare
⌛ Testing commit 5b0d828 with merge e609b8a... |
💔 Test failed - auto-mac-64-opt |
…d behavior [breaking-change] Technically breaking, since code that had been using these transmutes before will no longer compile. However, it was undefined behavior, so really, it's a good thing. Fixing your code would require some re-working to use an UnsafeCell instead. Closes rust-lang#13146
5b0d828
to
5624cfb
Compare
Sigh, had improved the lint message from review comments, but forgot to adjust the expected error in the compile-fail. Then the subsequent check-stage1-rpass for the previous failure hadn't caught it either. Now cfail passes as well. |
The [UnsafeCell documentation says it is undefined behavior](http://doc.rust-lang.org/nightly/std/cell/struct.UnsafeCell.html), so people shouldn't do it. This happened to catch one case in libstd that was doing this, and I switched that to use an UnsafeCell internally. Closes #13146
Thanks a ton for merging this; to add to the support, after this lint appeared I found the bug in |
The UnsafeCell documentation says it is undefined behavior, so people shouldn't do it.
This happened to catch one case in libstd that was doing this, and I switched that to use an UnsafeCell internally.
Closes #13146