authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-06-15 14:08:57-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-06-15 14:08:57-07:00
log515d6430c0298daf304e48b46e6b43802bbfdab4
tree0b67deffdca555ce4deaa0b5fb01e3f1edcfbc9b
parent0f4173c5d834dca2710005ffc1e040a4b307df00

AstGen: support `@export` with field access

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,13 +7525,22 @@ test "main" {
7525 {#header_close#}7525 {#header_close#}
75267526
7527 {#header_open|@export#}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 <p>7529 <p>
7530 Creates a symbol in the output object file.7530 Creates a symbol in the output object file.
7531 </p>7531 </p>
7532 <p>7532 <p>
7533 This function can be called from a {#link|comptime#} block to conditionally export symbols.7533 <code>declaration</code> must be one of two things:
7534 When {#syntax#}identifier{#endsyntax#} is a function with the C calling convention and7534 </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 {#syntax#}options.linkage{#endsyntax#} is {#syntax#}Strong{#endsyntax#}, this is equivalent to7544 {#syntax#}options.linkage{#endsyntax#} is {#syntax#}Strong{#endsyntax#}, this is equivalent to
7536 the {#syntax#}export{#endsyntax#} keyword used on a function:7545 the {#syntax#}export{#endsyntax#} keyword used on a function:
7537 </p>7546 </p>
src/AstGen.zig+22-7
...@@ -6706,18 +6706,33 @@ fn builtinCall(...@@ -6706,18 +6706,33 @@ fn builtinCall(
67066706
6707 .@"export" => {6707 .@"export" => {
6708 const node_tags = tree.nodes.items(.tag);6708 const node_tags = tree.nodes.items(.tag);
6709 const node_datas = tree.nodes.items(.data);
6709 // This function causes a Decl to be exported. The first parameter is not an expression,6710 // This function causes a Decl to be exported. The first parameter is not an expression,
6710 // but an identifier of the Decl to be exported.6711 // but an identifier of the Decl to be exported.
6711 if (node_tags[params[0]] != .identifier) {6712 var namespace: Zir.Inst.Ref = .none;
6712 return astgen.failNode(params[0], "the first @export parameter must be an identifier", .{});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 const options = try comptimeExpr(gz, scope, .{ .ty = .export_options_type }, params[1]);6733 const options = try comptimeExpr(gz, scope, .{ .ty = .export_options_type }, params[1]);
6720 _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{6734 _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
6735 .namespace = namespace,
6721 .decl_name = decl_name,6736 .decl_name = decl_name,
6722 .options = options,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,6 +1985,9 @@ fn zirExport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
1985 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };1985 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
1986 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };1986 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
1987 const decl_name = sema.code.nullTerminatedString(extra.decl_name);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 const decl = try sema.lookupIdentifier(block, lhs_src, decl_name);1991 const decl = try sema.lookupIdentifier(block, lhs_src, decl_name);
1989 const options = try sema.resolveInstConst(block, rhs_src, extra.options);1992 const options = try sema.resolveInstConst(block, rhs_src, extra.options);
1990 const struct_obj = options.ty.castTag(.@"struct").?.data;1993 const struct_obj = options.ty.castTag(.@"struct").?.data;
src/Zir.zig+8-3
...@@ -347,8 +347,9 @@ pub const Inst = struct {...@@ -347,8 +347,9 @@ pub const Inst = struct {
347 error_union_type,347 error_union_type,
348 /// `error.Foo` syntax. Uses the `str_tok` field of the Data union.348 /// `error.Foo` syntax. Uses the `str_tok` field of the Data union.
349 error_value,349 error_value,
350 /// Implements the `@export` builtin function.350 /// Implements the `@export` builtin function, based on either an identifier to a Decl,
351 /// Uses the `pl_node` union field. Payload is `Bin`.351 /// or field access of a Decl.
352 /// Uses the `pl_node` union field. Payload is `Export`.
352 @"export",353 @"export",
353 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer354 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
354 /// to the named field. The field name is stored in string_bytes. Used by a.b syntax.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,6 +2739,9 @@ pub const Inst = struct {
2738 };2739 };
27392740
2740 pub const Export = struct {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 /// Null-terminated string index.2745 /// Null-terminated string index.
2742 decl_name: u32,2746 decl_name: u32,
2743 options: Ref,2747 options: Ref,
...@@ -3284,7 +3288,8 @@ const Writer = struct {...@@ -3284,7 +3288,8 @@ const Writer = struct {
3284 const extra = self.code.extraData(Inst.Export, inst_data.payload_index).data;3288 const extra = self.code.extraData(Inst.Export, inst_data.payload_index).data;
3285 const decl_name = self.code.nullTerminatedString(extra.decl_name);3289 const decl_name = self.code.nullTerminatedString(extra.decl_name);
32863290
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 try self.writeInstRef(stream, extra.options);3293 try self.writeInstRef(stream, extra.options);
3289 try stream.writeAll(") ");3294 try stream.writeAll(") ");
3290 try self.writeSrc(stream, inst_data.src());3295 try self.writeSrc(stream, inst_data.src());