| author | |
| committer | |
| log | 515d6430c0298daf304e48b46e6b43802bbfdab4 |
| tree | 0b67deffdca555ce4deaa0b5fb01e3f1edcfbc9b |
| parent | 0f4173c5d834dca2710005ffc1e040a4b307df00 |
The Zig language specification will support identifiers and field access
in order to refer to which declaration to export with `@export`.
This commit implements the change in AstGen and updates the language
reference.4 files changed, 45 insertions(+), 13 deletions(-)
doc/langref.html.in+12-3| ... | ... | @@ -7525,13 +7525,22 @@ test "main" { |
| 7525 | 7525 | {#header_close#} |
| 7526 | 7526 | |
| 7527 | 7527 | {#header_open|@export#} |
| 7528 | <pre>{#syntax#}@export(identifier, comptime options: std.builtin.ExportOptions) void{#endsyntax#}</pre> | |
| 7528 | <pre>{#syntax#}@export(declaration, comptime options: std.builtin.ExportOptions) void{#endsyntax#}</pre> | |
| 7529 | 7529 | <p> |
| 7530 | 7530 | Creates a symbol in the output object file. |
| 7531 | 7531 | </p> |
| 7532 | 7532 | <p> |
| 7533 | This function can be called from a {#link|comptime#} block to conditionally export symbols. | |
| 7534 | When {#syntax#}identifier{#endsyntax#} is a function with the C calling convention and | |
| 7533 | <code>declaration</code> must be one of two things: | |
| 7534 | </p> | |
| 7535 | <ul> | |
| 7536 | <li>An identifier ({#syntax#}x{#endsyntax#}) identifying a {#link|function|Functions#} or | |
| 7537 | {#link|global variable|Global Variables#}.</li> | |
| 7538 | <li>Field access ({#syntax#}x.y{#endsyntax#}) looking up a {#link|function|Functions#} or | |
| 7539 | {#link|global variable|Global Variables#}.</li> | |
| 7540 | </ul> | |
| 7541 | <p> | |
| 7542 | This builtin can be called from a {#link|comptime#} block to conditionally export symbols. | |
| 7543 | When <code>declaration</code> is a function with the C calling convention and | |
| 7535 | 7544 | {#syntax#}options.linkage{#endsyntax#} is {#syntax#}Strong{#endsyntax#}, this is equivalent to |
| 7536 | 7545 | the {#syntax#}export{#endsyntax#} keyword used on a function: |
| 7537 | 7546 | </p> |
src/AstGen.zig+22-7| ... | ... | @@ -6706,18 +6706,33 @@ fn builtinCall( |
| 6706 | 6706 | |
| 6707 | 6707 | .@"export" => { |
| 6708 | 6708 | const node_tags = tree.nodes.items(.tag); |
| 6709 | const node_datas = tree.nodes.items(.data); | |
| 6709 | 6710 | // This function causes a Decl to be exported. The first parameter is not an expression, |
| 6710 | 6711 | // but an identifier of the Decl to be exported. |
| 6711 | if (node_tags[params[0]] != .identifier) { | |
| 6712 | return astgen.failNode(params[0], "the first @export parameter must be an identifier", .{}); | |
| 6712 | var namespace: Zir.Inst.Ref = .none; | |
| 6713 | var decl_name: u32 = 0; | |
| 6714 | switch (node_tags[params[0]]) { | |
| 6715 | .identifier => { | |
| 6716 | const ident_token = main_tokens[params[0]]; | |
| 6717 | decl_name = try astgen.identAsString(ident_token); | |
| 6718 | // TODO look for local variables in scope matching `decl_name` and emit a compile | |
| 6719 | // error. Only top-level declarations can be exported. Until this is done, the | |
| 6720 | // compile error will end up being "use of undeclared identifier" in Sema. | |
| 6721 | }, | |
| 6722 | .field_access => { | |
| 6723 | const namespace_node = node_datas[params[0]].lhs; | |
| 6724 | namespace = try typeExpr(gz, scope, namespace_node); | |
| 6725 | const dot_token = main_tokens[params[0]]; | |
| 6726 | const field_ident = dot_token + 1; | |
| 6727 | decl_name = try astgen.identAsString(field_ident); | |
| 6728 | }, | |
| 6729 | else => return astgen.failNode( | |
| 6730 | params[0], "the first @export parameter must be an identifier", .{}, | |
| 6731 | ), | |
| 6713 | 6732 | } |
| 6714 | const ident_token = main_tokens[params[0]]; | |
| 6715 | const decl_name = try astgen.identAsString(ident_token); | |
| 6716 | // TODO look for local variables in scope matching `decl_name` and emit a compile | |
| 6717 | // error. Only top-level declarations can be exported. Until this is done, the | |
| 6718 | // compile error will end up being "use of undeclared identifier" in Sema. | |
| 6719 | 6733 | const options = try comptimeExpr(gz, scope, .{ .ty = .export_options_type }, params[1]); |
| 6720 | 6734 | _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{ |
| 6735 | .namespace = namespace, | |
| 6721 | 6736 | .decl_name = decl_name, |
| 6722 | 6737 | .options = options, |
| 6723 | 6738 | }); |
src/Sema.zig+3| ... | ... | @@ -1985,6 +1985,9 @@ fn zirExport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError! |
| 1985 | 1985 | const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 1986 | 1986 | const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 1987 | 1987 | const decl_name = sema.code.nullTerminatedString(extra.decl_name); |
| 1988 | if (extra.namespace != .none) { | |
| 1989 | return sema.mod.fail(&block.base, src, "TODO: implement exporting with field access", .{}); | |
| 1990 | } | |
| 1988 | 1991 | const decl = try sema.lookupIdentifier(block, lhs_src, decl_name); |
| 1989 | 1992 | const options = try sema.resolveInstConst(block, rhs_src, extra.options); |
| 1990 | 1993 | const struct_obj = options.ty.castTag(.@"struct").?.data; |
src/Zir.zig+8-3| ... | ... | @@ -347,8 +347,9 @@ pub const Inst = struct { |
| 347 | 347 | error_union_type, |
| 348 | 348 | /// `error.Foo` syntax. Uses the `str_tok` field of the Data union. |
| 349 | 349 | error_value, |
| 350 | /// Implements the `@export` builtin function. | |
| 351 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 350 | /// Implements the `@export` builtin function, based on either an identifier to a Decl, | |
| 351 | /// or field access of a Decl. | |
| 352 | /// Uses the `pl_node` union field. Payload is `Export`. | |
| 352 | 353 | @"export", |
| 353 | 354 | /// Given a pointer to a struct or object that contains virtual fields, returns a pointer |
| 354 | 355 | /// to the named field. The field name is stored in string_bytes. Used by a.b syntax. |
| ... | ... | @@ -2738,6 +2739,9 @@ pub const Inst = struct { |
| 2738 | 2739 | }; |
| 2739 | 2740 | |
| 2740 | 2741 | pub const Export = struct { |
| 2742 | /// If present, this is referring to a Decl via field access, e.g. `a.b`. | |
| 2743 | /// If omitted, this is referring to a Decl via identifier, e.g. `a`. | |
| 2744 | namespace: Ref, | |
| 2741 | 2745 | /// Null-terminated string index. |
| 2742 | 2746 | decl_name: u32, |
| 2743 | 2747 | options: Ref, |
| ... | ... | @@ -3284,7 +3288,8 @@ const Writer = struct { |
| 3284 | 3288 | const extra = self.code.extraData(Inst.Export, inst_data.payload_index).data; |
| 3285 | 3289 | const decl_name = self.code.nullTerminatedString(extra.decl_name); |
| 3286 | 3290 | |
| 3287 | try stream.print("{}, ", .{std.zig.fmtId(decl_name)}); | |
| 3291 | try self.writeInstRef(stream, extra.namespace); | |
| 3292 | try stream.print(", {}, ", .{std.zig.fmtId(decl_name)}); | |
| 3288 | 3293 | try self.writeInstRef(stream, extra.options); |
| 3289 | 3294 | try stream.writeAll(") "); |
| 3290 | 3295 | try self.writeSrc(stream, inst_data.src()); |