authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-03-15 12:12:04+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-17 01:51:55+01:00
log524345b6353c5aa4a848f4e579ab81f8ac126319
tree962c991f936cbafebc954932c013a0a781176387
parent2d237da50b38a242e31d95adee18b4e1dcfa67b7

Sema: handle containers as inline assembly output types correctly

Inline assembly now rejects output types that don't have a well-defined in-memory layout and correctly resolves the layout of the ones that do.

4 files changed, 139 insertions(+), 11 deletions(-)

src/Sema.zig+28-11
......@@ -15593,18 +15593,35 @@ fn zirAsm(
1559315593
1559415594 const name = sema.code.nullTerminatedString(output.data.name);
1559515595
15596 if (is_type) {
15597 // Indicate the output is the asm instruction return value.
15598 arg.* = .none;
15599 const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand);
15600 expr_ty = Air.internedToRef(out_ty.toIntern());
15601 } else {
15602 const inst = sema.resolveInst(output.data.operand);
15603 if (!sema.checkRuntimeValue(inst)) {
15604 const output_name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls);
15605 return sema.failWithContainsReferenceToComptimeVar(block, output_src, output_name, "assembly output", .fromInterned(inst.toInterned().?));
15596 const out_ty: Type = out_ty: {
15597 if (is_type) {
15598 // Indicate the output is the asm instruction return value.
15599 arg.* = .none;
15600
15601 const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand);
15602 try sema.ensureLayoutResolved(out_ty, ret_ty_src, .asm_out_type);
15603 expr_ty = .fromType(out_ty);
15604 break :out_ty out_ty;
15605 } else {
15606 const inst = sema.resolveInst(output.data.operand);
15607 arg.* = inst;
15608
15609 if (!sema.checkRuntimeValue(inst)) {
15610 const output_name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls);
15611 return sema.failWithContainsReferenceToComptimeVar(block, output_src, output_name, "assembly output", .fromInterned(inst.toInterned().?));
15612 }
15613 break :out_ty sema.typeOf(inst).childType(zcu);
1560615614 }
15607 arg.* = inst;
15615 };
15616 if (!out_ty.hasWellDefinedLayout(zcu)) {
15617 return sema.failWithOwnedErrorMsg(block, msg: {
15618 const msg = try sema.errMsg(output_src, "invalid inline assembly output type; '{f}' does not have a guaranteed in-memory layout", .{
15619 out_ty.fmt(pt),
15620 });
15621 errdefer msg.destroy(gpa);
15622 try sema.addDeclaredHereNote(msg, out_ty);
15623 break :msg msg;
15624 });
1560815625 }
1560915626
1561015627 const constraint = sema.code.nullTerminatedString(output.data.constraint);
src/Sema/type_resolution.zig+2
......@@ -34,6 +34,7 @@ pub const LayoutResolveReason = enum {
3434 bit_ptr_child,
3535 @"export",
3636 @"extern",
37 asm_out_type,
3738 builtin_type,
3839
3940 /// Written after string: "while resolving type 'T' "
......@@ -60,6 +61,7 @@ pub const LayoutResolveReason = enum {
6061 .bit_ptr_child => "for bit size check here",
6162 .@"export" => "for export here",
6263 .@"extern" => "for extern declaration here",
64 .asm_out_type => "for inline assembly output type declared here",
6365 .builtin_type => "from 'std.builtin'",
6466 // zig fmt: on
6567 };
test/behavior/asm.zig+71
......@@ -175,3 +175,74 @@ test "asm modifiers (AArch64)" {
175175 );
176176 try expectEqual(2 * x, double);
177177}
178
179test "packed output types (x86_64)" {
180 if (builtin.target.cpu.arch != .x86_64) return error.SkipZigTest;
181 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly
182
183 const S = packed struct(u32) { x: u32 };
184 {
185 const s: S = asm volatile ("mov $123, %[ret]"
186 : [ret] "=r" (-> S),
187 );
188 try expect(s.x == 123);
189 }
190 {
191 var s: S = undefined;
192 asm volatile ("mov $123, %[ret]"
193 : [ret] "=r" (s),
194 );
195 try expect(s.x == 123);
196 }
197
198 const U = packed union(u32) { x: u32 };
199 {
200 const u: U = asm volatile ("mov $123, %[ret]"
201 : [ret] "=r" (-> U),
202 );
203 try expect(u.x == 123);
204 }
205 {
206 var u: U = undefined;
207 asm volatile ("mov $123, %[ret]"
208 : [ret] "=r" (u),
209 );
210 try expect(u.x == 123);
211 }
212}
213
214test "extern output types (x86_64)" {
215 if (builtin.target.cpu.arch != .x86_64) return error.SkipZigTest;
216 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly
217 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/31531
218
219 const S = extern struct { x: u32 };
220 {
221 const s: S = asm volatile ("mov $123, %[ret]"
222 : [ret] "=r" (-> S),
223 );
224 try expect(s.x == 123);
225 }
226 {
227 var s: S = undefined;
228 asm volatile ("mov $123, %[ret]"
229 : [ret] "=r" (s),
230 );
231 try expect(s.x == 123);
232 }
233
234 const U = extern union { x: u32 };
235 {
236 const u: U = asm volatile ("mov $123, %[ret]"
237 : [ret] "=r" (-> U),
238 );
239 try expect(u.x == 123);
240 }
241 {
242 var u: U = undefined;
243 asm volatile ("mov $123, %[ret]"
244 : [ret] "=r" (u),
245 );
246 try expect(u.x == 123);
247 }
248}
test/cases/compile_errors/asm_output_type_no_guaranteed_in_memory_layout.zig created+38
......@@ -0,0 +1,38 @@
1const S = struct { x: u32 };
2export fn entry1() void {
3 const s = asm volatile (""
4 : [_] "=r" (-> S),
5 );
6 _ = s;
7}
8export fn entry2() void {
9 var s: S = undefined;
10 asm volatile (""
11 : [_] "=r" (s),
12 );
13}
14
15const U = union { x: u32 };
16export fn entry3() void {
17 const u = asm volatile (""
18 : [_] "=r" (-> U),
19 );
20 _ = u;
21}
22export fn entry4() void {
23 var u: U = undefined;
24 asm volatile (""
25 : [_] "=r" (u),
26 );
27}
28
29// error
30//
31// :4:24: error: invalid inline assembly output type; 'tmp.S' does not have a guaranteed in-memory layout
32// :1:11: note: struct declared here
33// :11:21: error: invalid inline assembly output type; 'tmp.S' does not have a guaranteed in-memory layout
34// :1:11: note: struct declared here
35// :18:24: error: invalid inline assembly output type; 'tmp.U' does not have a guaranteed in-memory layout
36// :15:11: note: union declared here
37// :25:21: error: invalid inline assembly output type; 'tmp.U' does not have a guaranteed in-memory layout
38// :15:11: note: union declared here