Commit 5b18af85cb

Andrew Kelley <andrew@ziglang.org>
2024-12-31 00:40:11
type checking for synthetic functions
1 parent a4895f3
Changed files (3)
src/link/Wasm/Flush.zig
@@ -682,7 +682,6 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
             .__wasm_call_ctors => @panic("TODO lower __wasm_call_ctors"),
             .__wasm_init_memory => @panic("TODO lower __wasm_init_memory "),
             .__wasm_init_tls => @panic("TODO lower __wasm_init_tls "),
-            .__zig_error_names => @panic("TODO lower __zig_error_names "),
             .object_function => |i| {
                 _ = i;
                 @panic("TODO lower object function code and apply relocations");
src/link/Wasm.zig
@@ -474,6 +474,10 @@ pub const SourceLocation = enum(u32) {
         err_msg.notes[err.note_slot - 1].source_location = .{ .wasm = sl };
     }
 
+    pub fn fail(sl: SourceLocation, diags: *link.Diags, comptime format: []const u8, args: anytype) error{LinkFailure} {
+        return diags.failSourceLocation(.{ .wasm = sl }, format, args);
+    }
+
     pub fn string(
         sl: SourceLocation,
         msg: []const u8,
@@ -881,12 +885,11 @@ pub const FunctionImport = extern struct {
         __wasm_call_ctors,
         __wasm_init_memory,
         __wasm_init_tls,
-        __zig_error_names,
         // Next, index into `object_functions`.
         // Next, index into `zcu_funcs`.
         _,
 
-        const first_object_function = @intFromEnum(Resolution.__zig_error_names) + 1;
+        const first_object_function = @intFromEnum(Resolution.__wasm_init_tls) + 1;
 
         pub const Unpacked = union(enum) {
             unresolved,
@@ -894,7 +897,6 @@ pub const FunctionImport = extern struct {
             __wasm_call_ctors,
             __wasm_init_memory,
             __wasm_init_tls,
-            __zig_error_names,
             object_function: ObjectFunctionIndex,
             zcu_func: ZcuFunc.Index,
         };
@@ -906,7 +908,6 @@ pub const FunctionImport = extern struct {
                 .__wasm_call_ctors => .__wasm_call_ctors,
                 .__wasm_init_memory => .__wasm_init_memory,
                 .__wasm_init_tls => .__wasm_init_tls,
-                .__zig_error_names => .__zig_error_names,
                 _ => {
                     const object_function_index = @intFromEnum(r) - first_object_function;
 
@@ -927,7 +928,6 @@ pub const FunctionImport = extern struct {
                 .__wasm_call_ctors => .__wasm_call_ctors,
                 .__wasm_init_memory => .__wasm_init_memory,
                 .__wasm_init_tls => .__wasm_init_tls,
-                .__zig_error_names => .__zig_error_names,
                 .object_function => |i| @enumFromInt(first_object_function + @intFromEnum(i)),
                 .zcu_func => |i| @enumFromInt(first_object_function + wasm.object_functions.items.len + @intFromEnum(i)),
             };
@@ -957,11 +957,11 @@ pub const FunctionImport = extern struct {
         pub fn typeIndex(r: Resolution, wasm: *Wasm) FunctionType.Index {
             return switch (unpack(r, wasm)) {
                 .unresolved => unreachable,
-                .__wasm_apply_global_tls_relocs => @panic("TODO"),
-                .__wasm_call_ctors => @panic("TODO"),
-                .__wasm_init_memory => @panic("TODO"),
-                .__wasm_init_tls => @panic("TODO"),
-                .__zig_error_names => @panic("TODO"),
+                .__wasm_apply_global_tls_relocs,
+                .__wasm_call_ctors,
+                .__wasm_init_memory,
+                => getExistingFuncType2(wasm, &.{}, &.{}),
+                .__wasm_init_tls => getExistingFuncType2(wasm, &.{.i32}, &.{}),
                 .object_function => |i| i.ptr(wasm).type_index,
                 .zcu_func => |i| i.typeIndex(wasm).?,
             };
@@ -974,7 +974,6 @@ pub const FunctionImport = extern struct {
                 .__wasm_call_ctors => @tagName(Unpacked.__wasm_call_ctors),
                 .__wasm_init_memory => @tagName(Unpacked.__wasm_init_memory),
                 .__wasm_init_tls => @tagName(Unpacked.__wasm_init_tls),
-                .__zig_error_names => @tagName(Unpacked.__zig_error_names),
                 .object_function => |i| i.ptr(wasm).name.slice(wasm),
                 .zcu_func => |i| i.name(wasm),
             };
@@ -2991,7 +2990,7 @@ fn markFunctionImport(
     name: String,
     import: *FunctionImport,
     func_index: FunctionImport.Index,
-) Allocator.Error!void {
+) link.File.FlushError!void {
     if (import.flags.alive) return;
     import.flags.alive = true;
 
@@ -3002,17 +3001,13 @@ fn markFunctionImport(
 
     if (import.resolution == .unresolved) {
         if (name == wasm.preloaded_strings.__wasm_init_memory) {
-            import.resolution = .__wasm_init_memory;
-            wasm.functions.putAssumeCapacity(.__wasm_init_memory, {});
+            try wasm.resolveFunctionSynthetic(import, .__wasm_init_memory, &.{}, &.{});
         } else if (name == wasm.preloaded_strings.__wasm_apply_global_tls_relocs) {
-            import.resolution = .__wasm_apply_global_tls_relocs;
-            wasm.functions.putAssumeCapacity(.__wasm_apply_global_tls_relocs, {});
+            try wasm.resolveFunctionSynthetic(import, .__wasm_apply_global_tls_relocs, &.{}, &.{});
         } else if (name == wasm.preloaded_strings.__wasm_call_ctors) {
-            import.resolution = .__wasm_call_ctors;
-            wasm.functions.putAssumeCapacity(.__wasm_call_ctors, {});
+            try wasm.resolveFunctionSynthetic(import, .__wasm_call_ctors, &.{}, &.{});
         } else if (name == wasm.preloaded_strings.__wasm_init_tls) {
-            import.resolution = .__wasm_init_tls;
-            wasm.functions.putAssumeCapacity(.__wasm_init_tls, {});
+            try wasm.resolveFunctionSynthetic(import, .__wasm_init_tls, &.{.i32}, &.{});
         } else {
             try wasm.function_imports.put(gpa, name, .fromObject(func_index, wasm));
         }
@@ -3022,7 +3017,7 @@ fn markFunctionImport(
 }
 
 /// Recursively mark alive everything referenced by the function.
-fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex) Allocator.Error!void {
+fn markFunction(wasm: *Wasm, i: ObjectFunctionIndex) link.File.FlushError!void {
     const comp = wasm.base.comp;
     const gpa = comp.gpa;
     const gop = try wasm.functions.getOrPut(gpa, .fromObjectFunction(wasm, i));
@@ -3046,7 +3041,7 @@ fn markGlobalImport(
     name: String,
     import: *GlobalImport,
     global_index: GlobalImport.Index,
-) !void {
+) link.File.FlushError!void {
     if (import.flags.alive) return;
     import.flags.alive = true;
 
@@ -3082,7 +3077,7 @@ fn markGlobalImport(
     }
 }
 
-fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex) Allocator.Error!void {
+fn markGlobal(wasm: *Wasm, i: ObjectGlobalIndex) link.File.FlushError!void {
     const comp = wasm.base.comp;
     const gpa = comp.gpa;
     const gop = try wasm.globals.getOrPut(gpa, .fromObjectGlobal(wasm, i));
@@ -3105,7 +3100,7 @@ fn markTableImport(
     name: String,
     import: *TableImport,
     table_index: TableImport.Index,
-) !void {
+) link.File.FlushError!void {
     if (import.flags.alive) return;
     import.flags.alive = true;
 
@@ -3127,7 +3122,7 @@ fn markTableImport(
     }
 }
 
-fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) Allocator.Error!void {
+fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) link.File.FlushError!void {
     const segment = segment_index.ptr(wasm);
     if (segment.flags.alive) return;
     segment.flags.alive = true;
@@ -3135,7 +3130,7 @@ fn markDataSegment(wasm: *Wasm, segment_index: ObjectDataSegment.Index) Allocato
     try wasm.markRelocations(segment.relocations(wasm));
 }
 
-fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) Allocator.Error!void {
+fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.File.FlushError!void {
     for (relocs.slice.tags(wasm), relocs.slice.pointees(wasm), relocs.slice.offsets(wasm)) |tag, *pointee, offset| {
         if (offset >= relocs.end) break;
         switch (tag) {
@@ -3751,7 +3746,7 @@ pub fn internValtypeList(wasm: *Wasm, valtype_list: []const std.wasm.Valtype) Al
     return .fromString(try internString(wasm, @ptrCast(valtype_list)));
 }
 
-pub fn getExistingValtypeList(wasm: *Wasm, valtype_list: []const std.wasm.Valtype) ?ValtypeList {
+pub fn getExistingValtypeList(wasm: *const Wasm, valtype_list: []const std.wasm.Valtype) ?ValtypeList {
     return .fromString(getExistingString(wasm, @ptrCast(valtype_list)) orelse return null);
 }
 
@@ -3766,6 +3761,13 @@ pub fn getExistingFuncType(wasm: *const Wasm, ft: FunctionType) ?FunctionType.In
     return @enumFromInt(index);
 }
 
+pub fn getExistingFuncType2(wasm: *const Wasm, params: []const std.wasm.Valtype, returns: []const std.wasm.Valtype) FunctionType.Index {
+    return getExistingFuncType(wasm, .{
+        .params = getExistingValtypeList(wasm, params).?,
+        .returns = getExistingValtypeList(wasm, returns).?,
+    }).?;
+}
+
 pub fn internFunctionType(
     wasm: *Wasm,
     cc: std.builtin.CallingConvention,
@@ -4080,3 +4082,26 @@ fn addZcuImportReserved(wasm: *Wasm, nav_index: InternPool.Nav.Index) ZcuImportI
     gop.value_ptr.* = {};
     return @enumFromInt(gop.index);
 }
+
+fn resolveFunctionSynthetic(
+    wasm: *Wasm,
+    import: *FunctionImport,
+    res: FunctionImport.Resolution,
+    params: []const std.wasm.Valtype,
+    returns: []const std.wasm.Valtype,
+) link.File.FlushError!void {
+    import.resolution = res;
+    wasm.functions.putAssumeCapacity(res, {});
+    // This is not only used for type-checking but also ensures the function
+    // type index is interned so that it is guaranteed to exist during `flush`.
+    const correct_func_type = try addFuncType(wasm, .{
+        .params = try internValtypeList(wasm, params),
+        .returns = try internValtypeList(wasm, returns),
+    });
+    if (import.type != correct_func_type) {
+        const diags = &wasm.base.comp.link_diags;
+        return import.source_location.fail(diags, "synthetic function {s} {} imported with incorrect signature {}", .{
+            @tagName(res), correct_func_type.fmt(wasm), import.type.fmt(wasm),
+        });
+    }
+}
src/link.zig
@@ -214,22 +214,35 @@ pub const Diags = struct {
         return error.LinkFailure;
     }
 
+    pub fn failSourceLocation(diags: *Diags, sl: SourceLocation, comptime format: []const u8, args: anytype) error{LinkFailure} {
+        @branchHint(.cold);
+        addErrorSourceLocation(diags, sl, format, args);
+        return error.LinkFailure;
+    }
+
     pub fn addError(diags: *Diags, comptime format: []const u8, args: anytype) void {
+        return addErrorSourceLocation(diags, .none, format, args);
+    }
+
+    pub fn addErrorSourceLocation(diags: *Diags, sl: SourceLocation, comptime format: []const u8, args: anytype) void {
         @branchHint(.cold);
         const gpa = diags.gpa;
         const eu_main_msg = std.fmt.allocPrint(gpa, format, args);
         diags.mutex.lock();
         defer diags.mutex.unlock();
-        addErrorLockedFallible(diags, eu_main_msg) catch |err| switch (err) {
+        addErrorLockedFallible(diags, sl, eu_main_msg) catch |err| switch (err) {
             error.OutOfMemory => diags.setAllocFailureLocked(),
         };
     }
 
-    fn addErrorLockedFallible(diags: *Diags, eu_main_msg: Allocator.Error![]u8) Allocator.Error!void {
+    fn addErrorLockedFallible(diags: *Diags, sl: SourceLocation, eu_main_msg: Allocator.Error![]u8) Allocator.Error!void {
         const gpa = diags.gpa;
         const main_msg = try eu_main_msg;
         errdefer gpa.free(main_msg);
-        try diags.msgs.append(gpa, .{ .msg = main_msg });
+        try diags.msgs.append(gpa, .{
+            .msg = main_msg,
+            .source_location = sl,
+        });
     }
 
     pub fn addErrorWithNotes(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes {