Open gnzlbg opened 5 years ago
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 https://github.com/rust-lang/rust/pull/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.
#[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.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)" { ... }
.
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
).
[ffi(const)] is ok.
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.
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).
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)
This issue is blocking #2633 and it seems like @gnzlbg has a reasonable solution. What would help move this forward?
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).
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 likepure
andconst
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
andconst
have names with overloaded semantics that might make sense to disambiguate, e.g., instead of#[const]
which is very similar toconst 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.
c_ffi
orffi::c
)ffi
? (independent of language)ffi
andffi_c
vsffi
andc_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 thatconst
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]]
.#[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.