authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-26 01:14:33+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-27 00:41:04+01:00
logf2d7096bb9d21f5668d22567d1c96b95970cb480
treefd41fd01840408d99ce07fcfd240a19c8db67ff8
parent492cc2ef8d1d21d96e25541c22ab885a31c62770
signaturelock-open Commit is signed but in an unrecognized format.

compiler: make `@export` take a pointer

Resolves: #14911

4 files changed, 64 insertions(+), 179 deletions(-)

lib/std/zig/AstGen.zig+2-79
......@@ -2861,7 +2861,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
28612861 .ensure_result_non_error,
28622862 .ensure_err_union_payload_void,
28632863 .@"export",
2864 .export_value,
28652864 .set_eval_branch_quota,
28662865 .atomic_store,
28672866 .store_node,
......@@ -9249,87 +9248,11 @@ fn builtinCall(
92499248 // zig fmt: on
92509249
92519250 .@"export" => {
9251 const exported = try expr(gz, scope, .{ .rl = .none }, params[0]);
92529252 const export_options_ty = try gz.addBuiltinValue(node, .export_options);
9253 const node_tags = tree.nodes.items(.tag);
9254 const node_datas = tree.nodes.items(.data);
9255 // This function causes a Decl to be exported. The first parameter is not an expression,
9256 // but an identifier of the Decl to be exported.
9257 var namespace: Zir.Inst.Ref = .none;
9258 var decl_name: Zir.NullTerminatedString = .empty;
9259 switch (node_tags[params[0]]) {
9260 .identifier => {
9261 const ident_token = main_tokens[params[0]];
9262 if (isPrimitive(tree.tokenSlice(ident_token))) {
9263 return astgen.failTok(ident_token, "unable to export primitive value", .{});
9264 }
9265 decl_name = try astgen.identAsString(ident_token);
9266
9267 var s = scope;
9268 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
9269 while (true) switch (s.tag) {
9270 .local_val => {
9271 const local_val = s.cast(Scope.LocalVal).?;
9272 if (local_val.name == decl_name) {
9273 local_val.used = ident_token;
9274 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
9275 .operand = local_val.inst,
9276 .options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = export_options_ty } }, params[1]),
9277 });
9278 return rvalue(gz, ri, .void_value, node);
9279 }
9280 s = local_val.parent;
9281 },
9282 .local_ptr => {
9283 const local_ptr = s.cast(Scope.LocalPtr).?;
9284 if (local_ptr.name == decl_name) {
9285 if (!local_ptr.maybe_comptime)
9286 return astgen.failNode(params[0], "unable to export runtime-known value", .{});
9287 local_ptr.used = ident_token;
9288 const loaded = try gz.addUnNode(.load, local_ptr.ptr, node);
9289 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
9290 .operand = loaded,
9291 .options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = export_options_ty } }, params[1]),
9292 });
9293 return rvalue(gz, ri, .void_value, node);
9294 }
9295 s = local_ptr.parent;
9296 },
9297 .gen_zir => s = s.cast(GenZir).?.parent,
9298 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
9299 .namespace => {
9300 const ns = s.cast(Scope.Namespace).?;
9301 if (ns.decls.get(decl_name)) |i| {
9302 if (found_already) |f| {
9303 return astgen.failNodeNotes(node, "ambiguous reference", .{}, &.{
9304 try astgen.errNoteNode(f, "declared here", .{}),
9305 try astgen.errNoteNode(i, "also declared here", .{}),
9306 });
9307 }
9308 // We found a match but must continue looking for ambiguous references to decls.
9309 found_already = i;
9310 }
9311 s = ns.parent;
9312 },
9313 .top => break,
9314 };
9315 if (found_already == null) {
9316 const ident_name = try astgen.identifierTokenString(ident_token);
9317 return astgen.failNode(params[0], "use of undeclared identifier '{s}'", .{ident_name});
9318 }
9319 },
9320 .field_access => {
9321 const namespace_node = node_datas[params[0]].lhs;
9322 namespace = try typeExpr(gz, scope, namespace_node);
9323 const dot_token = main_tokens[params[0]];
9324 const field_ident = dot_token + 1;
9325 decl_name = try astgen.identAsString(field_ident);
9326 },
9327 else => return astgen.failNode(params[0], "symbol to export must identify a declaration", .{}),
9328 }
93299253 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = export_options_ty } }, params[1]);
93309254 _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
9331 .namespace = namespace,
9332 .decl_name = decl_name,
9255 .exported = exported,
93339256 .options = options,
93349257 });
93359258 return rvalue(gz, ri, .void_value, node);
lib/std/zig/Zir.zig+2-21
......@@ -431,14 +431,9 @@ pub const Inst = struct {
431431 error_union_type,
432432 /// `error.Foo` syntax. Uses the `str_tok` field of the Data union.
433433 error_value,
434 /// Implements the `@export` builtin function, based on either an identifier to a Decl,
435 /// or field access of a Decl. The thing being exported is the Decl.
434 /// Implements the `@export` builtin function.
436435 /// Uses the `pl_node` union field. Payload is `Export`.
437436 @"export",
438 /// Implements the `@export` builtin function, based on a comptime-known value.
439 /// The thing being exported is the comptime-known value which is the operand.
440 /// Uses the `pl_node` union field. Payload is `ExportValue`.
441 export_value,
442437 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
443438 /// to the named field. The field name is stored in string_bytes. Used by a.b syntax.
444439 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
......@@ -1093,7 +1088,6 @@ pub const Inst = struct {
10931088 .ensure_result_non_error,
10941089 .ensure_err_union_payload_void,
10951090 .@"export",
1096 .export_value,
10971091 .field_ptr,
10981092 .field_val,
10991093 .field_ptr_named,
......@@ -1314,7 +1308,6 @@ pub const Inst = struct {
13141308 .validate_deref,
13151309 .validate_destructure,
13161310 .@"export",
1317 .export_value,
13181311 .set_runtime_safety,
13191312 .memcpy,
13201313 .memset,
......@@ -1637,7 +1630,6 @@ pub const Inst = struct {
16371630 .error_union_type = .pl_node,
16381631 .error_value = .str_tok,
16391632 .@"export" = .pl_node,
1640 .export_value = .pl_node,
16411633 .field_ptr = .pl_node,
16421634 .field_val = .pl_node,
16431635 .field_ptr_named = .pl_node,
......@@ -3425,17 +3417,7 @@ pub const Inst = struct {
34253417 };
34263418
34273419 pub const Export = struct {
3428 /// If present, this is referring to a Decl via field access, e.g. `a.b`.
3429 /// If omitted, this is referring to a Decl via identifier, e.g. `a`.
3430 namespace: Ref,
3431 /// Null-terminated string index.
3432 decl_name: NullTerminatedString,
3433 options: Ref,
3434 };
3435
3436 pub const ExportValue = struct {
3437 /// The comptime value to export.
3438 operand: Ref,
3420 exported: Ref,
34393421 options: Ref,
34403422 };
34413423
......@@ -3793,7 +3775,6 @@ fn findDeclsInner(
37933775 .error_union_type,
37943776 .error_value,
37953777 .@"export",
3796 .export_value,
37973778 .field_ptr,
37983779 .field_val,
37993780 .field_ptr_named,
src/Sema.zig+59-65
......@@ -1442,11 +1442,6 @@ fn analyzeBodyInner(
14421442 i += 1;
14431443 continue;
14441444 },
1445 .export_value => {
1446 try sema.zirExportValue(block, inst);
1447 i += 1;
1448 continue;
1449 },
14501445 .set_runtime_safety => {
14511446 try sema.zirSetRuntimeSafety(block, inst);
14521447 i += 1;
......@@ -6279,73 +6274,72 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
62796274 const ip = &zcu.intern_pool;
62806275 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
62816276 const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
6282 const src = block.nodeOffset(inst_data.src_node);
6283 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
6284 const options_src = block.builtinCallArgSrc(inst_data.src_node, 1);
6285 const decl_name = try ip.getOrPutString(
6286 zcu.gpa,
6287 pt.tid,
6288 sema.code.nullTerminatedString(extra.decl_name),
6289 .no_embedded_nulls,
6290 );
6291 const nav_index = if (extra.namespace != .none) index_blk: {
6292 const container_ty = try sema.resolveType(block, operand_src, extra.namespace);
6293 const container_namespace = container_ty.getNamespaceIndex(zcu);
6294
6295 const lookup = try sema.lookupInNamespace(block, operand_src, container_namespace, decl_name, false) orelse
6296 return sema.failWithBadMemberAccess(block, container_ty, operand_src, decl_name);
6297
6298 break :index_blk lookup.nav;
6299 } else try sema.lookupIdentifier(block, operand_src, decl_name);
6300 const options = try sema.resolveExportOptions(block, options_src, extra.options);
6301
6302 try sema.ensureNavResolved(src, nav_index);
63036277
6304 // Make sure to export the owner Nav if applicable.
6305 const exported_nav = switch (ip.indexToKey(ip.getNav(nav_index).status.resolved.val)) {
6306 .variable => |v| v.owner_nav,
6307 .@"extern" => |e| e.owner_nav,
6308 .func => |f| f.owner_nav,
6309 else => nav_index,
6310 };
6311 try sema.analyzeExport(block, src, options, exported_nav);
6312}
6313
6314fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
6315 const tracy = trace(@src());
6316 defer tracy.end();
6317
6318 const pt = sema.pt;
6319 const zcu = pt.zcu;
6320 const ip = &zcu.intern_pool;
6321 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
6322 const extra = sema.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data;
63236278 const src = block.nodeOffset(inst_data.src_node);
6324 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
6279 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0);
63256280 const options_src = block.builtinCallArgSrc(inst_data.src_node, 1);
6326 const operand = try sema.resolveInstConst(block, operand_src, extra.operand, .{
6281
6282 const ptr = try sema.resolveInst(extra.exported);
6283 const ptr_val = try sema.resolveConstDefinedValue(block, ptr_src, ptr, .{
63276284 .needed_comptime_reason = "export target must be comptime-known",
63286285 });
6286 const ptr_ty = ptr_val.typeOf(zcu);
6287
63296288 const options = try sema.resolveExportOptions(block, options_src, extra.options);
6330 if (options.linkage == .internal)
6331 return;
63326289
6333 // If the value has an owner Nav, export that instead.
6334 const maybe_owner_nav = switch (ip.indexToKey(operand.toIntern())) {
6335 .variable => |v| v.owner_nav,
6336 .@"extern" => |e| e.owner_nav,
6337 .func => |f| f.owner_nav,
6338 else => null,
6339 };
6340 if (maybe_owner_nav) |owner_nav| {
6341 return sema.analyzeExport(block, src, options, owner_nav);
6342 } else {
6343 try sema.exports.append(zcu.gpa, .{
6344 .opts = options,
6345 .src = src,
6346 .exported = .{ .uav = operand.toIntern() },
6347 .status = .in_progress,
6348 });
6290 {
6291 if (ptr_ty.zigTypeTag(zcu) != .Pointer) {
6292 return sema.fail(block, ptr_src, "expected pointer type, found '{}'", .{ptr_ty.fmt(pt)});
6293 }
6294 const ptr_ty_info = ptr_ty.ptrInfo(zcu);
6295 if (ptr_ty_info.flags.size == .Slice) {
6296 return sema.fail(block, ptr_src, "export target cannot be slice", .{});
6297 }
6298 if (ptr_ty_info.packed_offset.host_size != 0) {
6299 return sema.fail(block, ptr_src, "export target cannot be bit-pointer", .{});
6300 }
6301 }
6302
6303 const ptr_info = ip.indexToKey(ptr_val.toIntern()).ptr;
6304 switch (ptr_info.base_addr) {
6305 .comptime_alloc, .int, .comptime_field => return sema.fail(block, ptr_src, "export target must be a global variable or a comptime-known constant", .{}),
6306 .eu_payload, .opt_payload, .field, .arr_elem => return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}),
6307 .uav => |uav| {
6308 if (ptr_info.byte_offset != 0) {
6309 return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{});
6310 }
6311 if (options.linkage == .internal) return;
6312 const export_ty = Value.fromInterned(uav.val).typeOf(zcu);
6313 if (!try sema.validateExternType(export_ty, .other)) {
6314 return sema.failWithOwnedErrorMsg(block, msg: {
6315 const msg = try sema.errMsg(src, "unable to export type '{}'", .{export_ty.fmt(pt)});
6316 errdefer msg.destroy(sema.gpa);
6317 try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other);
6318 try sema.addDeclaredHereNote(msg, export_ty);
6319 break :msg msg;
6320 });
6321 }
6322 try sema.exports.append(zcu.gpa, .{
6323 .opts = options,
6324 .src = src,
6325 .exported = .{ .uav = uav.val },
6326 .status = .in_progress,
6327 });
6328 },
6329 .nav => |nav| {
6330 if (ptr_info.byte_offset != 0) {
6331 return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{});
6332 }
6333 try sema.ensureNavResolved(src, nav);
6334 // Make sure to export the owner Nav if applicable.
6335 const exported_nav = switch (ip.indexToKey(ip.getNav(nav).status.resolved.val)) {
6336 .variable => |v| v.owner_nav,
6337 .@"extern" => |e| e.owner_nav,
6338 .func => |f| f.owner_nav,
6339 else => nav,
6340 };
6341 try sema.analyzeExport(block, src, options, exported_nav);
6342 },
63496343 }
63506344}
63516345
src/print_zir.zig+1-14
......@@ -429,7 +429,6 @@ const Writer = struct {
429429 .elem_val_imm => try self.writeElemValImm(stream, inst),
430430
431431 .@"export" => try self.writePlNodeExport(stream, inst),
432 .export_value => try self.writePlNodeExportValue(stream, inst),
433432
434433 .call => try self.writeCall(stream, inst, .direct),
435434 .field_call => try self.writeCall(stream, inst, .field),
......@@ -1007,20 +1006,8 @@ const Writer = struct {
10071006 fn writePlNodeExport(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
10081007 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
10091008 const extra = self.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
1010 const decl_name = self.code.nullTerminatedString(extra.decl_name);
10111009
1012 try self.writeInstRef(stream, extra.namespace);
1013 try stream.print(", {p}, ", .{std.zig.fmtId(decl_name)});
1014 try self.writeInstRef(stream, extra.options);
1015 try stream.writeAll(") ");
1016 try self.writeSrcNode(stream, inst_data.src_node);
1017 }
1018
1019 fn writePlNodeExportValue(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1020 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
1021 const extra = self.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data;
1022
1023 try self.writeInstRef(stream, extra.operand);
1010 try self.writeInstRef(stream, extra.exported);
10241011 try stream.writeAll(", ");
10251012 try self.writeInstRef(stream, extra.options);
10261013 try stream.writeAll(") ");