rust-lang / rfcs

RFCs for changes to Rust
https://rust-lang.github.io/rfcs/
Apache License 2.0
5.98k stars 1.57k forks source link

Meta issue: FFI attributes syntax #2637

Open gnzlbg opened 5 years ago

gnzlbg commented 5 years ago

There are some FFI (and C FFI) specific attributes that we might want to add to Rust (e.g. see https://github.com/rust-lang/rust/pull/58327 and https://github.com/rust-lang/rust/pull/58315, amongst others).

We probably want to expose FFI attributes in a clean way.

Prefix vs no prefix

Many FFI attributes don't have a prefix, e.g., #[link_name], etc. Some like #[returns_twice] are required for correctness, and some like pure and const are optimization hints that might be ignored by the compiler. These do however have overloaded names that we might want to put into context to avoid confusion with, e.g., const fn.

Also, some attributes like pure and const have names with overloaded semantics that might make sense to disambiguate, e.g., instead of #[const] which is very similar to const fn, maybe #[ffi_const].

FFI can be used to interface with any language, but some attributes might only make sense when interfacing with some particular language.

Option namespacing

Some FFI attributes might need to behave slightly different depending on the toolchain used to compile the code that's being interfaced via FFI.

For example, clang currently exposes both the [[const]] and [[gcc::const]] attributes. These currently are identical, but this separation is forward compatible with diverging semantics in the optimization hints that const has in the different toolchains. This is important both for interfacing clang binaries with gcc compiled libraries and vice-verse, and also, for not performing incorrect optimizations on programs that satisfy e.g. [[gcc::const]] requirements but not clang's [[const]].

Even if this is not RFC'ed right now, it might be worth it to have some plan for this in the pipe line in case it becomes necessary, and to make sure that merged RFCs are forward compatible with this. Otherwise we might end with #[c_ffi_const_clang] and #[gcc_ffi_c_const] or some similar zoo of attributes.

Centril commented 5 years ago

Some notes:

gnzlbg commented 5 years ago

not part of the standard C definition.

Restricting Rust foreign function interface to "standard C" was never a goal AFAIK. So I don't think that this arguments supports giving these different names.

I think that GCC has named these attributes poorly and misleadingly. I'm not inclined to continue in their footsteps

These attributes are very old. As a consequence, a huge amount of C code in the wild uses them, C programmers know them, and most C compilers support them.

The name is bad today, but "pure" wasn't really mainstream term back then.

This is an opportunity to rename const to something else both to avoid confusion with Rust and because it's not a good name.

For the user copy-pasting a C function declaration into Rust, giving it the same name has maximum discoverability. Giving them a different name, reduces this discoverability.

So it appears to me that giving these good names is at tension with making them discoverable.

The PR balances the trade-offs by using #[c_ffi_const] instead of #[const], but I'd like to hear different ways to balance these trade-offs (ideally concrete suggestions), and whether there is a way that could allow us to have our cake and eat it too (giving them good Rusty names, while making them very discoverable).

  • const is a keyword. Thus, #[ffi::const] and #[ffi(const)] are both illegal according to the attribute grammar. We could use a hack, but I'd strongly prefer not to.

Indeed :/ Underscores create new identifiers, so as a namespacing method, it doesn't have this problem. Alternatively, #[ffi("const")] wouldn't have this problem either.

  • and I don't want to recognize that LLVM or GCC exist in the language specification itself.

Maybe it would be better to handle this at the ABI level? For example, via: extern "C(clang)" { ... }.

petrochenkov commented 5 years ago

[ffi(const)] is ok.

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

With https://github.com/rust-lang/rust/pull/57367 the "no keywords" restriction is only active for built-in attributes due to using MetaItem as implementation detail and this can be overridden for individual attributes (e.g. cfg_attr already doesn't fit into MetaItem).

Centril commented 5 years ago

[ffi(const)] is ok.

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

Oh right; we changed this... I should know this since I reviewed the PR 😅. Still, I think ffi::thing seems cleaner in terms of namespacing instead of having a monolithic attribute.

Restricting Rust foreign function interface to "standard C" was never a goal AFAIK. So I don't think that this arguments supports giving these different names.

What notable stable FFI things do we expose that are not standard C? I think it's an important consideration because it matters wrt. universality and because if something is in standard C it is unlikely to ever change and you don't get the potential for dialectal problems between clang, gcc, etc. The examples to come to my mind are packed and specifying alignment (but that's useful for non-FFI purposes as well).

and most C compilers support them.

But notably not MSVC.

The name is bad today, but "pure" wasn't really mainstream term back then.

I think it was misleading when it was first introduced in 2000 (which is not that old) and I expect the term "pure function" has been used for some time...

For the user copy-pasting a C function declaration into Rust, giving it the same name has maximum discoverability. Giving them a different name, reduces this discoverability.

I think it's relatively easy to write a sentence about the correspondence to GCC/clang/etc. and it will quite likely be the first thing you find if you search for "Rust pure". If you search for "Rust const" you'd find something entirely different anyways (at least in the first search results) so that doesn't seem optimal in terms of discoverability.

The PR balances the trade-offs by using #[c_ffi_const] instead of #[const], but I'd like to hear different ways to balance these trade-offs (ideally concrete suggestions), and whether there is a way that could allow us to have our cake and eat it too (giving them good Rusty names, while making them very discoverable).

I'll think of something.

Maybe it would be better to handle this at the ABI level? For example, via: extern "C(clang)" { ... }.

What is that repainting intended to achieve? You still get the multiplicative complexity of all these different toolchains as well as the encouragement of toolchain dependence in code (which is the opposite of what I think we should aim for).

Centril commented 5 years ago

Relevant paper: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0078r0.pdf

gnzlbg commented 5 years ago

What notable stable FFI things do we expose that are not standard C?

That's offtopic, we can talk about this in Discord if you want, but let's stay on this issue.

But notably not MSVC.

That's mainly why I suggested using const(clang) or const(gcc), but MSVC is a bad example, and there is precedent in stable Rust of ignoring it (but that's offtopic too).

it will quite likely be the first thing you find if you search for

const and pure will be a pain to discover with online searches: as you mention const means something else in Rust and is widely used already, while pure used to mean something in Rust, and is a mainstream term.

But I have a solution.

This issue is about adding a consistent way of mapping C attributes in Rust. Let's assume the syntax is #[ffi(...)]. We would teach this as: "if you need to map a C attribute to Rust FFI, you use #[ffi(c_attr_name)] - that's all you need to know". If the name in Rust is the same as in C, that works and does the correct thing. If we changed the name, we reserve the C name, and if a user types it in, we error, with a suggestion "Maybe you meant the C FFI attribute rust_attr_name ?".

That way we don't have to use C names, and people writing code don't need to remember the mapping.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0078r0.pdf

(note: that's a C++ paper, and there hasn't been a new revision since 2015)

jeff-davis commented 5 years ago

This issue is blocking #2633 and it seems like @gnzlbg has a reasonable solution. What would help move this forward?

gnzlbg commented 5 years ago

So I've had more time to think about this, and while this issue is worth resolving, I think #2633 needs extra work for unrelated reasons (will post there in a sec).