-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Description
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:
- It is overloaded to mean different things in different contexts: symbol visibility, data type layout, and function calling convention.
- 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
- Remove support for qualifying a function type with
extern. To specify calling convention, usecallconv(). - Replace
externwith a new keyword for specifying non-Zig layout of structs/enums/unions (i.e. C or packed). - 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; // #1717Thoughts 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?