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

Meta issue: FFI attributes syntax #2637

Open
gnzlbg opened this issue Feb 12, 2019 · 8 comments
Open

Meta issue: FFI attributes syntax #2637

gnzlbg opened this issue Feb 12, 2019 · 8 comments
Labels
A-attributes Proposals relating to attributes A-ffi FFI related proposals. T-lang Relevant to the language team, which will review and decide on the RFC.

Comments

@gnzlbg
Copy link
Contributor

gnzlbg commented Feb 12, 2019

There are some FFI (and C FFI) specific attributes that we might want to add to Rust (e.g. see rust-lang/rust#58327 and rust-lang/rust#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].

  • Should we use prefixes at all?
  • Should we do so for all FFI attributes or only for some?
  • If we only use prexies for some attributes, do we decide for which on a 1 by 1 basis, or how do we decide this?

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

  • Should we take the language into account when prefixing attributes? (e.g. c_ffi or ffi::c)
  • Should we just use a general prefix, e.g., ffi? (independent of language)
  • Should we decide the prefix on a 1 by 1 basis? (should we define some rules for the prefixes? e.g. ffi and ffi_c vs ffi and c_ffi, etc.)

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]].

  • Should we already plan a syntax for platform toolchain disambiguation? (e.g. #[ffi::c::const(clang)] vs #[ffi::c::const(cranelift)] ?

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 Centril added A-ffi FFI related proposals. A-attributes Proposals relating to attributes T-lang Relevant to the language team, which will review and decide on the RFC. labels Feb 28, 2019
@Centril
Copy link
Contributor

Centril commented Feb 28, 2019

Some notes:

  • ffi:: as a prefix seems like a clean way to deal with complexity by namespacing. Disambiguating further with ffi::c:: also seems reasonable and clear. These attributes are probably not going to be used all that often and so clarity seems paramount.

  • 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.

    This is an opportunity to rename const to something else both to avoid confusion with Rust and because it's not a good name. As I noted on Experimentally add ffi_const and ffi_pure extern fn attributes rust#58327, I think that GCC has named these attributes poorly and misleadingly. I'm not inclined to continue in their footsteps, especially since it's, from what I can tell, not part of the standard C definition.

  • Re. #[ffi::c::const(clang)] vs #[ffi::c::const(cranelift)], I think that while there are few drawbacks in taking them into account syntactically, I'm already on the edge wrt. #[ffi::c::pure], and so I am opposed to the unbounded complexity this can occur. I also think this encourages too toolchain-dependent code and I don't want to recognize that LLVM or GCC exist in the language specification itself.

@gnzlbg
Copy link
Contributor Author

gnzlbg commented Feb 28, 2019

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
Copy link
Contributor

#[ffi(const)] is ok.
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=8bb34a21ec0ce26cf99bf2c61b30ef35

With rust-lang/rust#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
Copy link
Contributor

Centril commented Feb 28, 2019

#[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
Copy link
Contributor

Centril commented Feb 28, 2019

@gnzlbg
Copy link
Contributor Author

gnzlbg commented Feb 28, 2019

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
Copy link

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

@gnzlbg
Copy link
Contributor Author

gnzlbg commented May 23, 2019

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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-attributes Proposals relating to attributes A-ffi FFI related proposals. T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests

4 participants