Commit f2d7096bb9

mlugg <mlugg@mlugg.co.uk>
2024-08-26 02:14:33
compiler: make `@export` take a pointer
Resolves: #14911
1 parent 492cc2e
Changed files (4)
lib/std/zig/AstGen.zig
@@ -2861,7 +2861,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
             .ensure_result_non_error,
             .ensure_err_union_payload_void,
             .@"export",
-            .export_value,
             .set_eval_branch_quota,
             .atomic_store,
             .store_node,
@@ -9249,87 +9248,11 @@ fn builtinCall(
         // zig fmt: on
 
         .@"export" => {
+            const exported = try expr(gz, scope, .{ .rl = .none }, params[0]);
             const export_options_ty = try gz.addBuiltinValue(node, .export_options);
-            const node_tags = tree.nodes.items(.tag);
-            const node_datas = tree.nodes.items(.data);
-            // This function causes a Decl to be exported. The first parameter is not an expression,
-            // but an identifier of the Decl to be exported.
-            var namespace: Zir.Inst.Ref = .none;
-            var decl_name: Zir.NullTerminatedString = .empty;
-            switch (node_tags[params[0]]) {
-                .identifier => {
-                    const ident_token = main_tokens[params[0]];
-                    if (isPrimitive(tree.tokenSlice(ident_token))) {
-                        return astgen.failTok(ident_token, "unable to export primitive value", .{});
-                    }
-                    decl_name = try astgen.identAsString(ident_token);
-
-                    var s = scope;
-                    var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
-                    while (true) switch (s.tag) {
-                        .local_val => {
-                            const local_val = s.cast(Scope.LocalVal).?;
-                            if (local_val.name == decl_name) {
-                                local_val.used = ident_token;
-                                _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
-                                    .operand = local_val.inst,
-                                    .options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = export_options_ty } }, params[1]),
-                                });
-                                return rvalue(gz, ri, .void_value, node);
-                            }
-                            s = local_val.parent;
-                        },
-                        .local_ptr => {
-                            const local_ptr = s.cast(Scope.LocalPtr).?;
-                            if (local_ptr.name == decl_name) {
-                                if (!local_ptr.maybe_comptime)
-                                    return astgen.failNode(params[0], "unable to export runtime-known value", .{});
-                                local_ptr.used = ident_token;
-                                const loaded = try gz.addUnNode(.load, local_ptr.ptr, node);
-                                _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
-                                    .operand = loaded,
-                                    .options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = export_options_ty } }, params[1]),
-                                });
-                                return rvalue(gz, ri, .void_value, node);
-                            }
-                            s = local_ptr.parent;
-                        },
-                        .gen_zir => s = s.cast(GenZir).?.parent,
-                        .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
-                        .namespace => {
-                            const ns = s.cast(Scope.Namespace).?;
-                            if (ns.decls.get(decl_name)) |i| {
-                                if (found_already) |f| {
-                                    return astgen.failNodeNotes(node, "ambiguous reference", .{}, &.{
-                                        try astgen.errNoteNode(f, "declared here", .{}),
-                                        try astgen.errNoteNode(i, "also declared here", .{}),
-                                    });
-                                }
-                                // We found a match but must continue looking for ambiguous references to decls.
-                                found_already = i;
-                            }
-                            s = ns.parent;
-                        },
-                        .top => break,
-                    };
-                    if (found_already == null) {
-                        const ident_name = try astgen.identifierTokenString(ident_token);
-                        return astgen.failNode(params[0], "use of undeclared identifier '{s}'", .{ident_name});
-                    }
-                },
-                .field_access => {
-                    const namespace_node = node_datas[params[0]].lhs;
-                    namespace = try typeExpr(gz, scope, namespace_node);
-                    const dot_token = main_tokens[params[0]];
-                    const field_ident = dot_token + 1;
-                    decl_name = try astgen.identAsString(field_ident);
-                },
-                else => return astgen.failNode(params[0], "symbol to export must identify a declaration", .{}),
-            }
             const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = export_options_ty } }, params[1]);
             _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
-                .namespace = namespace,
-                .decl_name = decl_name,
+                .exported = exported,
                 .options = options,
             });
             return rvalue(gz, ri, .void_value, node);
lib/std/zig/Zir.zig
@@ -431,14 +431,9 @@ pub const Inst = struct {
         error_union_type,
         /// `error.Foo` syntax. Uses the `str_tok` field of the Data union.
         error_value,
-        /// Implements the `@export` builtin function, based on either an identifier to a Decl,
-        /// or field access of a Decl. The thing being exported is the Decl.
+        /// Implements the `@export` builtin function.
         /// Uses the `pl_node` union field. Payload is `Export`.
         @"export",
-        /// Implements the `@export` builtin function, based on a comptime-known value.
-        /// The thing being exported is the comptime-known value which is the operand.
-        /// Uses the `pl_node` union field. Payload is `ExportValue`.
-        export_value,
         /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
         /// to the named field. The field name is stored in string_bytes. Used by a.b syntax.
         /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
@@ -1093,7 +1088,6 @@ pub const Inst = struct {
                 .ensure_result_non_error,
                 .ensure_err_union_payload_void,
                 .@"export",
-                .export_value,
                 .field_ptr,
                 .field_val,
                 .field_ptr_named,
@@ -1314,7 +1308,6 @@ pub const Inst = struct {
                 .validate_deref,
                 .validate_destructure,
                 .@"export",
-                .export_value,
                 .set_runtime_safety,
                 .memcpy,
                 .memset,
@@ -1637,7 +1630,6 @@ pub const Inst = struct {
                 .error_union_type = .pl_node,
                 .error_value = .str_tok,
                 .@"export" = .pl_node,
-                .export_value = .pl_node,
                 .field_ptr = .pl_node,
                 .field_val = .pl_node,
                 .field_ptr_named = .pl_node,
@@ -3425,17 +3417,7 @@ pub const Inst = struct {
     };
 
     pub const Export = struct {
-        /// If present, this is referring to a Decl via field access, e.g. `a.b`.
-        /// If omitted, this is referring to a Decl via identifier, e.g. `a`.
-        namespace: Ref,
-        /// Null-terminated string index.
-        decl_name: NullTerminatedString,
-        options: Ref,
-    };
-
-    pub const ExportValue = struct {
-        /// The comptime value to export.
-        operand: Ref,
+        exported: Ref,
         options: Ref,
     };
 
@@ -3793,7 +3775,6 @@ fn findDeclsInner(
         .error_union_type,
         .error_value,
         .@"export",
-        .export_value,
         .field_ptr,
         .field_val,
         .field_ptr_named,
src/print_zir.zig
@@ -429,7 +429,6 @@ const Writer = struct {
             .elem_val_imm => try self.writeElemValImm(stream, inst),
 
             .@"export" => try self.writePlNodeExport(stream, inst),
-            .export_value => try self.writePlNodeExportValue(stream, inst),
 
             .call => try self.writeCall(stream, inst, .direct),
             .field_call => try self.writeCall(stream, inst, .field),
@@ -1007,20 +1006,8 @@ const Writer = struct {
     fn writePlNodeExport(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
         const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
-        const decl_name = self.code.nullTerminatedString(extra.decl_name);
 
-        try self.writeInstRef(stream, extra.namespace);
-        try stream.print(", {p}, ", .{std.zig.fmtId(decl_name)});
-        try self.writeInstRef(stream, extra.options);
-        try stream.writeAll(") ");
-        try self.writeSrcNode(stream, inst_data.src_node);
-    }
-
-    fn writePlNodeExportValue(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
-        const extra = self.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data;
-
-        try self.writeInstRef(stream, extra.operand);
+        try self.writeInstRef(stream, extra.exported);
         try stream.writeAll(", ");
         try self.writeInstRef(stream, extra.options);
         try stream.writeAll(") ");
src/Sema.zig
@@ -1442,11 +1442,6 @@ fn analyzeBodyInner(
                 i += 1;
                 continue;
             },
-            .export_value => {
-                try sema.zirExportValue(block, inst);
-                i += 1;
-                continue;
-            },
             .set_runtime_safety => {
                 try sema.zirSetRuntimeSafety(block, inst);
                 i += 1;
@@ -6279,73 +6274,72 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
     const ip = &zcu.intern_pool;
     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
-    const src = block.nodeOffset(inst_data.src_node);
-    const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
-    const options_src = block.builtinCallArgSrc(inst_data.src_node, 1);
-    const decl_name = try ip.getOrPutString(
-        zcu.gpa,
-        pt.tid,
-        sema.code.nullTerminatedString(extra.decl_name),
-        .no_embedded_nulls,
-    );
-    const nav_index = if (extra.namespace != .none) index_blk: {
-        const container_ty = try sema.resolveType(block, operand_src, extra.namespace);
-        const container_namespace = container_ty.getNamespaceIndex(zcu);
-
-        const lookup = try sema.lookupInNamespace(block, operand_src, container_namespace, decl_name, false) orelse
-            return sema.failWithBadMemberAccess(block, container_ty, operand_src, decl_name);
-
-        break :index_blk lookup.nav;
-    } else try sema.lookupIdentifier(block, operand_src, decl_name);
-    const options = try sema.resolveExportOptions(block, options_src, extra.options);
-
-    try sema.ensureNavResolved(src, nav_index);
 
-    // Make sure to export the owner Nav if applicable.
-    const exported_nav = switch (ip.indexToKey(ip.getNav(nav_index).status.resolved.val)) {
-        .variable => |v| v.owner_nav,
-        .@"extern" => |e| e.owner_nav,
-        .func => |f| f.owner_nav,
-        else => nav_index,
-    };
-    try sema.analyzeExport(block, src, options, exported_nav);
-}
-
-fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
-    const tracy = trace(@src());
-    defer tracy.end();
-
-    const pt = sema.pt;
-    const zcu = pt.zcu;
-    const ip = &zcu.intern_pool;
-    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
-    const extra = sema.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data;
     const src = block.nodeOffset(inst_data.src_node);
-    const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
+    const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0);
     const options_src = block.builtinCallArgSrc(inst_data.src_node, 1);
-    const operand = try sema.resolveInstConst(block, operand_src, extra.operand, .{
+
+    const ptr = try sema.resolveInst(extra.exported);
+    const ptr_val = try sema.resolveConstDefinedValue(block, ptr_src, ptr, .{
         .needed_comptime_reason = "export target must be comptime-known",
     });
+    const ptr_ty = ptr_val.typeOf(zcu);
+
     const options = try sema.resolveExportOptions(block, options_src, extra.options);
-    if (options.linkage == .internal)
-        return;
 
-    // If the value has an owner Nav, export that instead.
-    const maybe_owner_nav = switch (ip.indexToKey(operand.toIntern())) {
-        .variable => |v| v.owner_nav,
-        .@"extern" => |e| e.owner_nav,
-        .func => |f| f.owner_nav,
-        else => null,
-    };
-    if (maybe_owner_nav) |owner_nav| {
-        return sema.analyzeExport(block, src, options, owner_nav);
-    } else {
-        try sema.exports.append(zcu.gpa, .{
-            .opts = options,
-            .src = src,
-            .exported = .{ .uav = operand.toIntern() },
-            .status = .in_progress,
-        });
+    {
+        if (ptr_ty.zigTypeTag(zcu) != .Pointer) {
+            return sema.fail(block, ptr_src, "expected pointer type, found '{}'", .{ptr_ty.fmt(pt)});
+        }
+        const ptr_ty_info = ptr_ty.ptrInfo(zcu);
+        if (ptr_ty_info.flags.size == .Slice) {
+            return sema.fail(block, ptr_src, "export target cannot be slice", .{});
+        }
+        if (ptr_ty_info.packed_offset.host_size != 0) {
+            return sema.fail(block, ptr_src, "export target cannot be bit-pointer", .{});
+        }
+    }
+
+    const ptr_info = ip.indexToKey(ptr_val.toIntern()).ptr;
+    switch (ptr_info.base_addr) {
+        .comptime_alloc, .int, .comptime_field => return sema.fail(block, ptr_src, "export target must be a global variable or a comptime-known constant", .{}),
+        .eu_payload, .opt_payload, .field, .arr_elem => return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}),
+        .uav => |uav| {
+            if (ptr_info.byte_offset != 0) {
+                return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{});
+            }
+            if (options.linkage == .internal) return;
+            const export_ty = Value.fromInterned(uav.val).typeOf(zcu);
+            if (!try sema.validateExternType(export_ty, .other)) {
+                return sema.failWithOwnedErrorMsg(block, msg: {
+                    const msg = try sema.errMsg(src, "unable to export type '{}'", .{export_ty.fmt(pt)});
+                    errdefer msg.destroy(sema.gpa);
+                    try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other);
+                    try sema.addDeclaredHereNote(msg, export_ty);
+                    break :msg msg;
+                });
+            }
+            try sema.exports.append(zcu.gpa, .{
+                .opts = options,
+                .src = src,
+                .exported = .{ .uav = uav.val },
+                .status = .in_progress,
+            });
+        },
+        .nav => |nav| {
+            if (ptr_info.byte_offset != 0) {
+                return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{});
+            }
+            try sema.ensureNavResolved(src, nav);
+            // Make sure to export the owner Nav if applicable.
+            const exported_nav = switch (ip.indexToKey(ip.getNav(nav).status.resolved.val)) {
+                .variable => |v| v.owner_nav,
+                .@"extern" => |e| e.owner_nav,
+                .func => |f| f.owner_nav,
+                else => nav,
+            };
+            try sema.analyzeExport(block, src, options, exported_nav);
+        },
     }
 }