Skip to content
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

Types equal to/wrapping () result in "not FFI-safe" warning #66202

Closed
Rantanen opened this issue Nov 8, 2019 · 10 comments · Fixed by #72890
Closed

Types equal to/wrapping () result in "not FFI-safe" warning #66202

Rantanen opened this issue Nov 8, 2019 · 10 comments · Fixed by #72890
Assignees
Labels
A-ffi Area: Foreign Function Interface (FFI) A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@Rantanen
Copy link
Contributor

Rantanen commented Nov 8, 2019

// OK
pub extern "system" fn raw() -> () {}

// warning: `extern` fn uses type `()`, which is not FFI-safe
pub extern "system" fn assoc() -> <() as ToOwned>::Owned {}
//                                ^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe

#[repr(transparent)]
pub struct W<T>(T);

// warning: `extern` fn uses type `()`, which is not FFI-safe
pub extern "system" fn wrap() -> W<()> { W(()) }
//                              ^^^^^^ not FFI-safe
// note: composed only of `PhantomData`

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=62da3bf343e1358e16a6692a72ca7f33

All of these cases work in Beta. At least the associated type warning is recent regression in Nightly. I suspect #65134

CC @davidtwco

@davidtwco davidtwco added A-ffi Area: Foreign Function Interface (FFI) A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Nov 8, 2019
@davidtwco
Copy link
Member

@Rantanen This is related to #65134 in so far as that PR made the improper-ctypes lint apply to extern functions that are outside extern {} blocks. As you can see from this playground, when on stable (which doesn't have #65134 yet) and inside an extern {} block, the lint fires on those functions - so this is a pre-existing issue with the lint.

@rkruppe @eddyb do you know if this is a bug or is expected?

@hanna-kruppe
Copy link
Contributor

My knee-jerk reaction is that both are bugs that can and should be fixed.

  • <() as ToOwned>::Owned should have been normalized to (), why wasn't it?
  • The error message for W<()> is clearly bogus, and if plain () doesn't warn then clearly a transparent newtype around it shouldn't either. But note that #[repr(C)] struct Foo(()); also gets flagged for containing () because "tuple layout is unspecified", so unless we want to open that whole can of worms too, the fix can't be just making () FFI-safe.

@Rantanen
Copy link
Contributor Author

Rantanen commented Nov 8, 2019

This is related to #65134 in so far as that PR made the improper-ctypes lint apply to extern functions that are outside extern {} blocks.

Good to know! I was worried this was a change in functionality that wasn't expected.

I've since worked around this issue by normalizing the <() as Trait>::Foo case into () manually in my proc macro code. There are still some corner cases where this might be an issue for me, but I'm not sure if those will ever occur in practice.

@rodrimati1992
Copy link
Contributor

rodrimati1992 commented Nov 8, 2019

How can () not be ffi-safe,given that it's a zero sized type with an alignment of 1?

I find this lint to be useless in my own code,it warns for things whose layout won't change.

@hanna-kruppe
Copy link
Contributor

hanna-kruppe commented Nov 8, 2019

Passing ZSTs by value is on thin ice since zero-sized types don't exist in standard C and ABIs aren't always consistent about how they handle types that use GNU extensions to be zero-sized. The lint also fires on repr(C) structs without fields or with only PhantomData fields, accepting #[repr(C)] struct Foo(()); would deviate from that. While I don't want to say it's the wrong call, we should be consistent and that turns it into a larger question that's better handled independent from the bug originally reported here.

The special case of returning just () is defined to match a return type of void in C, so it's fine regardless independently of how we handle () when it occurs nested in structs.

I guess this also ties into the "linting likely mistakes in FFI code" versus "linting use of types with unspecified layout" tension that has come up in #66220 at the same time.

@rodrimati1992
Copy link
Contributor

That won't be a problem for me so long as Rust itself stays consistent in how it passes zero-sized types through extern "C" functions across compiler versions.

@hanna-kruppe
Copy link
Contributor

That isn't really the case, though. For example, #64259 changes how ZSTs are passed in the C ABI on (some target triples of) PowerPC. That is arguably a bugfix, but it's happening because ZSTs in C are such a non-standard mess.

@eddyb eddyb changed the title Indirect () results in "not FFI-safe" warning Associated type that should resolve to () results in "not FFI-safe" warning Nov 8, 2019
@eddyb
Copy link
Member

eddyb commented Nov 8, 2019

I thought we fixed the normalization problem for this years ago, ugh.
It's very weird that the message prints the normalized form, since there's nothing in the printing logic that would do that normalization for you.

@hanna-kruppe
Copy link
Contributor

@eddyb FWIW the title change is not fully accurate, only one of the two warnings in the OP involve associated types, the other is about transparent newtypes. Arguably these are two separate issues but at present both are discussed here so both should be covered by the title.

@eddyb eddyb changed the title Associated type that should resolve to () results in "not FFI-safe" warning Types equal to/wrapping () result in "not FFI-safe" warning Nov 8, 2019
@eddyb
Copy link
Member

eddyb commented Nov 8, 2019

I had to check this but apparently we always treat returning a ZST like void in C:

rust/src/librustc/ty/layout.rs

Lines 2711 to 2719 in 76ade3e

if arg.layout.is_zst() {
// For some forsaken reason, x86_64-pc-windows-gnu
// doesn't ignore zero-sized struct arguments.
// The same is true for s390x-unknown-linux-gnu
// and sparc64-unknown-linux-gnu.
if is_return || rust_abi || (!win_x64_gnu && !linux_s390x && !linux_sparc64) {
arg.mode = PassMode::Ignore;
}
}

(even if on some architectures passing in a ZST to C code can have strange requirements)

I wonder if this logic is wrong, compared to returning struct {} in GCC.

If it's not, we should allow any ZSTs, or at least transparent ones (note that the code snippet above doesn't look at anything other than the size being 0, not repr(transparent) etc.).

@davidtwco davidtwco self-assigned this Jun 1, 2020
davidtwco added a commit to davidtwco/rust that referenced this issue Jun 9, 2020
This commit adds a test of the improper ctypes lint, checking that
return type are normalized bethat return types are normalized before
being checked for FFI-safety, and that transparent newtype wrappers
are FFI-safe if the type being wrapped is FFI-safe.

Signed-off-by: David Wood <david@davidtw.co>
@bors bors closed this as completed in fda594e Jun 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ffi Area: Foreign Function Interface (FFI) A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants