You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
improper_ctypes fires for &mut T, &T, *const T and *mut T (when T: Sized) #66220
I'm not sure if this is intentional, but I haven't found any discussion regarding this. The improper_ctypes lint fires on pointer types when T is not FFI safe. However pointers have defined layout that is always FFI safe, independent of what the pointer is pointing to. It certainly seems valid to point out the pointer won't be able to be dereferenced by C. This should probably be a clippy lint though. Rust itself shouldn't fire false positives like this.
warning: `extern` fn uses type `Foo`, which is not FFI-safe
--> src/lib.rs:6:26
|
6 | pub extern "C" fn foo(_: *const Foo) {}
| ^^^^^^^^^^ not FFI-safe
|
= note: `#[warn(improper_ctypes)]` on by default
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
= note: this struct has unspecified layout
note: type defined here
--> src/lib.rs:1:1
|
1 | / pub struct Foo {
2 | | _x: i32,
3 | | }
| |_^
I am pretty sure this is intentional (or was, when this part of the lint was implemented), but that doesn't mean it can't change. This is a scenario where reasonable interpretations of the lint disagree with each other:
On the one hand, pointers to sized types are like C pointers, so they have defined memory layout and call ABI. If the intent of the code is that the other side of the FFI just passes the pointer around, then everything is fine and the warning is a false positive.
On the other hand, the pointee type is not FFI-safe and code behind the FFI cannot interact with that type correctly. If the intent is that the other side of the FFI dereference the pointer, then most likely the author of the Rust side made a mistake (e.g., missed a repr(C) annotation) and the warning is a true positive.
It is not obvious to me which side the improper_ctypes lint is supposed to serve. It's true that rustc lints ought to err on the side of fewer false positives, but on the other hand there are a lot of potential bugs (and very subtle ones, as things may appear to work correctly) that the current behavior catches, and false positives can be avoided by using *mut c_void or pointers to extern types for APIs where the pointee type is supposed to be opaque for the other side of the FFI.
I believe this could possibly be a separate lint (and with a different message too Pointer can't be dereferenced by C or so). Because otherwise improper_ctypes is likely to be disabled module or crate wide (as seen in quiche for example), which means actual calling convention mistakes won't show up anymore.
Reacted by Daniel Henry-Mantilla and Eduard-Mihai Burtescu
Splitting up into two different lints seems interesting (as long as both stay in rustc and enabled by default).
It comes with an interesting backwards compatibility question, though. We probably wouldn't want projects that used allow(improper_ctypes) to see warnings they opted out of again just because we shuffled around where they're emitted. So would we turn improper_ctypes into a lint group encompassing both new lints? Of course, that means that any old code (and any new code that copy-pastes or is written by someone with ingrained habits) will automatically disable both lints at once even if it would benefit from the "ABI is unspecified" subset.
The following does work, which surprised me because PhantomData alone is not FFI safe and this uses a tuple struct with two fields and claims to be repr(transparent) which I thought would hate structs with two fields, even if one of them was PhantomData...
I'm just looking for ways to describe the "FFI just passes the pointer around" intent in the type system. I realize the use of this might be noisy at scale given there's no automatic conversion between the types and would need manual wrapping and unwrapping.
Well idea would be to mark it with #[allow(opaque_c_ptr)] or so. Just passing *const String should be entirely fine (as long as you truly treat it as opaque).
Is there any way to mark a type (instead of the method) as "I know this is improper but don't warn me about it"?
I believe this doesn't work because the lint message is associated with the function rather than the type(s) which the lint finds objectionable. I don't know how difficult it would be to support this, I guess it would be something of a special case at best.
The following does work, which surprised me because PhantomData alone is not FFI safe and this uses a tuple struct with two fields and claims to be repr(transparent) which I thought would hate structs with two fields, even if one of them was PhantomData...
repr(transparent) explicitly allows extra fields that have size zero and alignment one, to enable the use of PhantomData and similar tricks for more type safety in FFI interfaces. PhantomData alone is not FFI-safe because it's a ZST which is not a standard C construct and it's not clear (or wasn't when the lint was written) to what degree we support the GNU extensions that introduce ZSTs to C. *const PhantomData<String> works, though it's a bit obscure, so your OpaquePtr<T> newtype seems best to me.
#65134 adds thousands of new warnings to Servo. The pervasive pattern is that we have many unsafe extern fn functions that take *const Foo parameters. The functions are extern so that they can be called from C++. The Foo Rust type is not FFI-safe, but in this case the pointer to it is FFI-safe because it is an opaque pointer as far as C++ code is concerned.
Changing the parameter type to *const c_void and adding an as cast near the top of the function body “fixes” the warning, but I feel it’s really not an improvement. Type-safety is diminished for no benefit that I can see.
Now I’m tempted to add #![allow(improper_ctypes)] to the parts of the crate that do the most FFI in order to unblock the compiler upgrade. But that feels very unproductive.
Please consider reverting #65134, or making raw pointers unconditionally FFI-safe.
CC @rust-lang/lang
Reacted by Mikail Bagishov, Christopher Serr and Brian Smith
FWIW, personally I have come to believe that we should definitely split up the lint (with or without the other option) since it's useful independently of this specific issue, but I don't think I can commit the time to explore this further or write up a more detailed proposal any time soon.
(If I get pinged to discuss that in Discord or Zulip, I probably won't be able to resist being nerd-sniped. Please use this knowledge responsibly :P)
I’m happy to perform any implementation required here, but I don’t have any particular opinions on the semantics - I just spotted #19834 as work needing done.
I’m happy to proceed with making the lint behave differently (for this case) depending on extern “C” fn or extern “C” {}; or with a separate lint if someone has opinions on what each lint would do.
Activity
sfackler commentedon Nov 8, 2019
Is
T: Sized?CryZe commentedon Nov 8, 2019
Yes, here's the playground example
hanna-kruppe commentedon Nov 8, 2019
I am pretty sure this is intentional (or was, when this part of the lint was implemented), but that doesn't mean it can't change. This is a scenario where reasonable interpretations of the lint disagree with each other:
repr(C)annotation) and the warning is a true positive.It is not obvious to me which side the
improper_ctypeslint is supposed to serve. It's true that rustc lints ought to err on the side of fewer false positives, but on the other hand there are a lot of potential bugs (and very subtle ones, as things may appear to work correctly) that the current behavior catches, and false positives can be avoided by using*mut c_voidor pointers toextern types for APIs where the pointee type is supposed to be opaque for the other side of the FFI.CryZe commentedon Nov 8, 2019
I believe this could possibly be a separate lint (and with a different message too
Pointer can't be dereferenced by Cor so). Because otherwise improper_ctypes is likely to be disabled module or crate wide (as seen in quiche for example), which means actual calling convention mistakes won't show up anymore.hanna-kruppe commentedon Nov 8, 2019
Splitting up into two different lints seems interesting (as long as both stay in rustc and enabled by default).
It comes with an interesting backwards compatibility question, though. We probably wouldn't want projects that used
allow(improper_ctypes)to see warnings they opted out of again just because we shuffled around where they're emitted. So would we turnimproper_ctypesinto a lint group encompassing both new lints? Of course, that means that any old code (and any new code that copy-pastes or is written by someone with ingrained habits) will automatically disable both lints at once even if it would benefit from the "ABI is unspecified" subset.Rantanen commentedon Nov 8, 2019
Is there any way to mark a type (instead of the method) as "I know this is improper but don't warn me about it"?
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=ccd4a6a4c205c17d84e17eb6d1ceac4f
The following does work, which surprised me because
PhantomDataalone is not FFI safe and this uses a tuple struct with two fields and claims to berepr(transparent)which I thought would hate structs with two fields, even if one of them wasPhantomData...I'm just looking for ways to describe the "FFI just passes the pointer around" intent in the type system. I realize the use of this might be noisy at scale given there's no automatic conversion between the types and would need manual wrapping and unwrapping.
CryZe commentedon Nov 8, 2019
Well idea would be to mark it with
#[allow(opaque_c_ptr)]or so. Just passing*const Stringshould be entirely fine (as long as you truly treat it as opaque).hanna-kruppe commentedon Nov 8, 2019
I believe this doesn't work because the lint message is associated with the function rather than the type(s) which the lint finds objectionable. I don't know how difficult it would be to support this, I guess it would be something of a special case at best.
repr(transparent)explicitly allows extra fields that have size zero and alignment one, to enable the use ofPhantomDataand similar tricks for more type safety in FFI interfaces.PhantomDataalone is not FFI-safe because it's a ZST which is not a standard C construct and it's not clear (or wasn't when the lint was written) to what degree we support the GNU extensions that introduce ZSTs to C.*const PhantomData<String>works, though it's a bit obscure, so yourOpaquePtr<T>newtype seems best to me.SimonSapin commentedon Nov 13, 2019
#65134 adds thousands of new warnings to Servo. The pervasive pattern is that we have many
unsafe extern fnfunctions that take*const Fooparameters. The functions areexternso that they can be called from C++. TheFooRust type is not FFI-safe, but in this case the pointer to it is FFI-safe because it is an opaque pointer as far as C++ code is concerned.Changing the parameter type to
*const c_voidand adding anascast near the top of the function body “fixes” the warning, but I feel it’s really not an improvement. Type-safety is diminished for no benefit that I can see.Now I’m tempted to add
#![allow(improper_ctypes)]to the parts of the crate that do the most FFI in order to unblock the compiler upgrade. But that feels very unproductive.Please consider reverting #65134, or making raw pointers unconditionally FFI-safe.
CC @rust-lang/lang
shepmaster commentedon Nov 13, 2019
Does it really? It’s nightly-only still, isn’t it? That level of churn (in the small timeframe present) seems acceptable in nightly.
23 remaining items
hanna-kruppe commentedon Nov 22, 2019
FWIW, personally I have come to believe that we should definitely split up the lint (with or without the other option) since it's useful independently of this specific issue, but I don't think I can commit the time to explore this further or write up a more detailed proposal any time soon.
(If I get pinged to discuss that in Discord or Zulip, I probably won't be able to resist being nerd-sniped. Please use this knowledge responsibly :P)
davidtwco commentedon Nov 22, 2019
I’m happy to perform any implementation required here, but I don’t have any particular opinions on the semantics - I just spotted #19834 as work needing done.
I’m happy to proceed with making the lint behave differently (for this case) depending on
extern “C” fnorextern “C” {}; or with a separate lint if someone has opinions on what each lint would do.pnkfelix commentedon Nov 28, 2019
triage: since PR #66378 has landed, I am reclassifying this as P-medium.
pnkfelix commentedon Dec 5, 2019
removing nomination label. T-compiler does not need to discuss this, at least not until the follow-up work is done.
Rollup merge of rust-lang#72700 - davidtwco:issue-66220-improper-ctyp…
Rollup merge of rust-lang#72700 - davidtwco:issue-66220-improper-ctyp…
Rollup merge of rust-lang#72700 - davidtwco:issue-66220-improper-ctyp…
davidtwco commentedon Jul 2, 2020
Closing, #72700 fixes this.