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

Replace extern with extern(...) and layout(...) #4245

Closed
hryx opened this issue Jan 20, 2020 · 7 comments
Closed

Replace extern with extern(...) and layout(...) #4245

hryx opened this issue Jan 20, 2020 · 7 comments
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@hryx
Copy link
Contributor

hryx commented Jan 20, 2020

The title isn't meant as proposed syntax but summarizes the proposal's intent.

Background

I see two issues that make the extern keyword somewhat unintuitive:

  1. It is overloaded to mean different things in different contexts: symbol visibility, data type layout, and function calling convention.
  2. There is undocumented (even in the grammar) syntax to specify the linked library name of an external: a suffixed string literal. This is inconsistent with other qualifiers, like alignment, which have their own keywords.

Note: Use of extern for calling convention is superceded by callconv but has not yet been removed from the language. Perhaps it is already planned to do so.

Proposal

  1. Remove support for qualifying a function type with extern. To specify calling convention, use callconv().
  2. Replace extern with a new keyword for specifying non-Zig layout of structs/enums/unions (i.e. C or packed).
  3. Add options to extern (in the context of declaring an external) to specify linked library name, weak vs. strong, etc.

With this, extern would be consigned to variable/function declarations, such that it only means one thing: "There exists an external...".

😵 -> 😎

Details

Layout

Introduce a layout keyword which takes an enum value:

const S1 = struct layout(.C) {};
const S2 = struct layout(.Packed) {};

This would be consistent with how align and callconv are suffixed qualifiers.

Externals

Some examples to demonstrate various possibilities:

extern(.{.lib_name = "c", .linkage = .Weak}) var x: c_int;
extern(.{.lib_name = "c"}) threadlocal var errno: c_int;
extern const thing: ?*void; // no parens/options = defaults?
// After #1717
extern(.{.lib_name = "kernel32"}) const _DoAWindowsThing: fn() callconv(.Stdcall) void;
extern const f() c_int; // no parens/options = defaults?

A similar "options struct" for @extern() was already discussed: #3971 (comment)

I'm not too familiar with thread-local or the nuances of linkage; forgive me if any of the above is nonsensical.

One readability issue is that const and var get shoved to the right, burying information about mutability. This can already happen for externs, though to a lesser extent. To address this, we could consider reordering things so that extern qualification suffixes the identifier. (I am fond of this syntax, so if it sparks interest I would open a separate proposal.)

var x extern(...): c_int;
var y extern(...) = someComptimeExpression(); // inferred type
const z extern(...) fn() callconv(.C) void; // #1717

Thoughts and questions

With the ability to supply options for extern, would we even need @extern()? If not, could we follow a similar path and replace @export() with a configurable export?

Going farther outfield: extern is used for both declarations ("there exists an external") and definitions (give a default value to an external). Could we consign extern to the former, and only use export for the latter?


Related: #1522, #1917

@daurnimator daurnimator added the proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. label Jan 20, 2020
@Sobeston
Copy link
Contributor

I don't get these parts:

struct layout(.C, .Packed) {};

How can a struct be both packed, and conform to the C abi?
Can't a struct only conform to one layout?

@LemonBoy
Copy link
Contributor

Remove support for qualifying a function type with extern. To specify calling convention, use callconv().

In #3977 it was proposed (and accepted) by @andrewrk himself so I think that's planned.

Replace extern with a new keyword for specifying non-Zig layout of structs/enums/unions (i.e. C ABI, and maybe packing).

The layout(<enum>) looks nice.

Add options to extern (in the context of declaring an external) to specify linked library name, weak vs. strong, etc.

The extern "lib-name" format is also quite nice and readable, I'd be sad to see it go away.
Linkage of external variables is a nasty can of worms because, at the moment at least, we're assuming that every variable has a valid address and so it can be correctly typed as *T. If weak extern variables are allowed then we're forced to either return their address as ?*T (this kind of asymmetric behaviour is not that nice IMO) or accept this safety hole (oh no).

@hryx
Copy link
Contributor Author

hryx commented Jan 21, 2020

Can't a struct only conform to one layout?

Good catch @Sobeston! I remembered the rule incorrectly; for some reason I thought that packed didn't guarantee ordering 🤦‍♂ I'll revise that part of the proposal.

The extern "lib-name" format is also quite nice and readable, I'd be sad to see it go away.

I think that's fair, and a counter to this proposal might be just to document it better. Either way, it took me some digging to find out what extern "c" means (namely, not the same as extern "C" in C++).

@hryx
Copy link
Contributor Author

hryx commented Jan 21, 2020

I've updated the proposal to address packing. It's shorter now! 👍

@LemonBoy My knowledge is limited, but I assumed weak linkage for externs is on the table coz of #1917. If that's not necessarily true, I could definitely shrink this proposal way down to just cover layout(<enum>).

Otherwise, my line of thinking was that by pulling the thread a bit, it would make sense to consolidate extern/@extern()/@externWeak(), and by extension, export/@export().

@LemonBoy
Copy link
Contributor

My knowledge is limited, but I assumed weak linkage for externs is on the table coz of #1917. If that's not necessarily true, I could definitely shrink this proposal way down to just cover layout().

It's on the table (and on the pending PR queue) but I'm still not sure if @externWeak is the right way to go as it behaves like a global variable declaration (minus many safeguards like checks for symbol name conflicts), the same is also true for @export but I still can't get myself to say that's ok.

Otherwise, my line of thinking was that by pulling the thread a bit, it would make sense to consolidate extern/@extern()/@externWeak(), and by extension, export/@export()

It makes sense to export some fn/var after it's been defined (eg conditional exporting, symbol name trickery), but I can't think of a reason for setting the linkage status. To me those belong to the declaration site.

@andrewrk andrewrk added this to the 0.6.0 milestone Jan 22, 2020
@andrewrk
Copy link
Member

I can't think of a reason for setting the linkage status. To me those belong to the declaration site.

What about the OpenGL bindings use case? comptime code would use @extern to populate a struct of function pointers, for example.

@andrewrk
Copy link
Member

Will be solved by #8643.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

No branches or pull requests

5 participants