Remove legacy asm clobbers syntax handling

This commit is contained in:
Linus Groh 2025-10-15 17:30:06 +02:00
parent ee4df4ad3e
commit 688c151550
9 changed files with 11 additions and 431 deletions

View file

@ -501,10 +501,6 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
.@"asm",
=> return walkAsm(w, ast.fullAsm(node).?),
.asm_legacy => {
return walkAsmLegacy(w, ast.legacyAsm(node).?);
},
.enum_literal => {
return walkIdentifier(w, ast.nodeMainToken(node)); // name
},
@ -881,11 +877,6 @@ fn walkAsm(w: *Walk, asm_node: Ast.full.Asm) Error!void {
try walkExpressions(w, asm_node.ast.items);
}
fn walkAsmLegacy(w: *Walk, asm_node: Ast.full.AsmLegacy) Error!void {
try walkExpression(w, asm_node.ast.template);
try walkExpressions(w, asm_node.ast.items);
}
/// Check if it is already gutted (i.e. its body replaced with `@trap()`).
fn isFnBodyGutted(ast: *const Ast, body_node: Ast.Node.Index) bool {
// skip over discards

View file

@ -791,8 +791,6 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
try expr(w, scope, parent_decl, full.ast.template);
},
.asm_legacy => {},
.builtin_call_two,
.builtin_call_two_comma,
.builtin_call,

View file

@ -636,7 +636,6 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
.@"nosuspend",
.asm_simple,
.@"asm",
.asm_legacy,
.array_type,
.array_type_sentinel,
.error_value,
@ -1050,11 +1049,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
n = @enumFromInt(tree.extra_data[@intFromEnum(members.end) - 1]); // last parameter
}
},
.asm_legacy => {
_, const extra_index = tree.nodeData(n).node_and_extra;
const extra = tree.extraData(extra_index, Node.AsmLegacy);
return extra.rparen + end_offset;
},
.@"asm" => {
_, const extra_index = tree.nodeData(n).node_and_extra;
const extra = tree.extraData(extra_index, Node.Asm);
@ -1897,18 +1891,6 @@ pub fn asmSimple(tree: Ast, node: Node.Index) full.Asm {
});
}
pub fn asmLegacy(tree: Ast, node: Node.Index) full.AsmLegacy {
const template, const extra_index = tree.nodeData(node).node_and_extra;
const extra = tree.extraData(extra_index, Node.AsmLegacy);
const items = tree.extraDataSlice(.{ .start = extra.items_start, .end = extra.items_end }, Node.Index);
return tree.legacyAsmComponents(.{
.asm_token = tree.nodeMainToken(node),
.template = template,
.items = items,
.rparen = extra.rparen,
});
}
pub fn asmFull(tree: Ast, node: Node.Index) full.Asm {
const template, const extra_index = tree.nodeData(node).node_and_extra;
const extra = tree.extraData(extra_index, Node.Asm);
@ -2214,67 +2196,6 @@ fn fullSwitchCaseComponents(tree: Ast, info: full.SwitchCase.Components, node: N
return result;
}
fn legacyAsmComponents(tree: Ast, info: full.AsmLegacy.Components) full.AsmLegacy {
var result: full.AsmLegacy = .{
.ast = info,
.volatile_token = null,
.inputs = &.{},
.outputs = &.{},
.first_clobber = null,
};
if (tree.tokenTag(info.asm_token + 1) == .keyword_volatile) {
result.volatile_token = info.asm_token + 1;
}
const outputs_end: usize = for (info.items, 0..) |item, i| {
switch (tree.nodeTag(item)) {
.asm_output => continue,
else => break i,
}
} else info.items.len;
result.outputs = info.items[0..outputs_end];
result.inputs = info.items[outputs_end..];
if (info.items.len == 0) {
// asm ("foo" ::: "a", "b");
const template_token = tree.lastToken(info.template);
if (tree.tokenTag(template_token + 1) == .colon and
tree.tokenTag(template_token + 2) == .colon and
tree.tokenTag(template_token + 3) == .colon and
tree.tokenTag(template_token + 4) == .string_literal)
{
result.first_clobber = template_token + 4;
}
} else if (result.inputs.len != 0) {
// asm ("foo" :: [_] "" (y) : "a", "b");
const last_input = result.inputs[result.inputs.len - 1];
const rparen = tree.lastToken(last_input);
var i = rparen + 1;
// Allow a (useless) comma right after the closing parenthesis.
if (tree.tokenTag(i) == .comma) i = i + 1;
if (tree.tokenTag(i) == .colon and
tree.tokenTag(i + 1) == .string_literal)
{
result.first_clobber = i + 1;
}
} else {
// asm ("foo" : [_] "" (x) :: "a", "b");
const last_output = result.outputs[result.outputs.len - 1];
const rparen = tree.lastToken(last_output);
var i = rparen + 1;
// Allow a (useless) comma right after the closing parenthesis.
if (tree.tokenTag(i) == .comma) i = i + 1;
if (tree.tokenTag(i) == .colon and
tree.tokenTag(i + 1) == .colon and
tree.tokenTag(i + 2) == .string_literal)
{
result.first_clobber = i + 2;
}
}
return result;
}
fn fullAsmComponents(tree: Ast, info: full.Asm.Components) full.Asm {
var result: full.Asm = .{
.ast = info,
@ -2492,14 +2413,6 @@ pub fn fullAsm(tree: Ast, node: Node.Index) ?full.Asm {
};
}
/// To be deleted after 0.15.0 is tagged
pub fn legacyAsm(tree: Ast, node: Node.Index) ?full.AsmLegacy {
return switch (tree.nodeTag(node)) {
.asm_legacy => tree.asmLegacy(node),
else => null,
};
}
pub fn fullCall(tree: Ast, buffer: *[1]Ast.Node.Index, node: Node.Index) ?full.Call {
return switch (tree.nodeTag(node)) {
.call, .call_comma => tree.callFull(node),
@ -2894,21 +2807,6 @@ pub const full = struct {
};
};
pub const AsmLegacy = struct {
ast: Components,
volatile_token: ?TokenIndex,
first_clobber: ?TokenIndex,
outputs: []const Node.Index,
inputs: []const Node.Index,
pub const Components = struct {
asm_token: TokenIndex,
template: Node.Index,
items: []const Node.Index,
rparen: TokenIndex,
};
};
pub const Call = struct {
ast: Components,
@ -3905,14 +3803,6 @@ pub const Node = struct {
///
/// The `main_token` field is the `asm` token.
asm_simple,
/// `asm(lhs, a)`.
///
/// The `data` field is a `.node_and_extra`:
/// 1. a `Node.Index` to lhs.
/// 2. a `ExtraIndex` to `AsmLegacy`.
///
/// The `main_token` field is the `asm` token.
asm_legacy,
/// `asm(a, b)`.
///
/// The `data` field is a `.node_and_extra`:
@ -4089,14 +3979,6 @@ pub const Node = struct {
callconv_expr: OptionalIndex,
};
/// To be removed after 0.15.0 is tagged
pub const AsmLegacy = struct {
items_start: ExtraIndex,
items_end: ExtraIndex,
/// Needed to make lastToken() work.
rparen: TokenIndex,
};
pub const Asm = struct {
items_start: ExtraIndex,
items_end: ExtraIndex,

View file

@ -896,9 +896,6 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
.@"asm",
=> return renderAsm(r, tree.fullAsm(node).?, space),
// To be removed after 0.15.0 is tagged
.asm_legacy => return renderAsmLegacy(r, tree.legacyAsm(node).?, space),
.enum_literal => {
try renderToken(r, tree.nodeMainToken(node) - 1, .none); // .
return renderIdentifier(r, tree.nodeMainToken(node), space, .eagerly_unquote); // name
@ -2412,185 +2409,6 @@ fn renderContainerDecl(
return renderToken(r, rbrace, space); // rbrace
}
fn renderAsmLegacy(
r: *Render,
asm_node: Ast.full.AsmLegacy,
space: Space,
) Error!void {
const tree = r.tree;
const ais = r.ais;
try renderToken(r, asm_node.ast.asm_token, .space); // asm
if (asm_node.volatile_token) |volatile_token| {
try renderToken(r, volatile_token, .space); // volatile
try renderToken(r, volatile_token + 1, .none); // lparen
} else {
try renderToken(r, asm_node.ast.asm_token + 1, .none); // lparen
}
if (asm_node.ast.items.len == 0) {
try ais.forcePushIndent(.normal);
if (asm_node.first_clobber) |first_clobber| {
// asm ("foo" ::: "a", "b")
// asm ("foo" ::: "a", "b",)
try renderExpression(r, asm_node.ast.template, .space);
// Render the three colons.
try renderToken(r, first_clobber - 3, .none);
try renderToken(r, first_clobber - 2, .none);
try renderToken(r, first_clobber - 1, .space);
try ais.writeAll(".{ ");
var tok_i = first_clobber;
while (true) : (tok_i += 1) {
try ais.writeByte('.');
_ = try writeStringLiteralAsIdentifier(r, tok_i);
try ais.writeAll(" = true");
tok_i += 1;
switch (tree.tokenTag(tok_i)) {
.r_paren => {
try ais.writeAll(" }");
ais.popIndent();
return renderToken(r, tok_i, space);
},
.comma => {
if (tree.tokenTag(tok_i + 1) == .r_paren) {
try ais.writeAll(" }");
ais.popIndent();
return renderToken(r, tok_i + 1, space);
} else {
try renderToken(r, tok_i, .space);
}
},
else => unreachable,
}
}
} else {
unreachable;
}
}
try ais.forcePushIndent(.normal);
try renderExpression(r, asm_node.ast.template, .newline);
ais.setIndentDelta(asm_indent_delta);
const colon1 = tree.lastToken(asm_node.ast.template) + 1;
const colon2 = if (asm_node.outputs.len == 0) colon2: {
try renderToken(r, colon1, .newline); // :
break :colon2 colon1 + 1;
} else colon2: {
try renderToken(r, colon1, .space); // :
try ais.forcePushIndent(.normal);
for (asm_node.outputs, 0..) |asm_output, i| {
if (i + 1 < asm_node.outputs.len) {
const next_asm_output = asm_node.outputs[i + 1];
try renderAsmOutput(r, asm_output, .none);
const comma = tree.firstToken(next_asm_output) - 1;
try renderToken(r, comma, .newline); // ,
try renderExtraNewlineToken(r, tree.firstToken(next_asm_output));
} else if (asm_node.inputs.len == 0 and asm_node.first_clobber == null) {
try ais.pushSpace(.comma);
try renderAsmOutput(r, asm_output, .comma);
ais.popSpace();
ais.popIndent();
ais.setIndentDelta(indent_delta);
ais.popIndent();
return renderToken(r, asm_node.ast.rparen, space); // rparen
} else {
try ais.pushSpace(.comma);
try renderAsmOutput(r, asm_output, .comma);
ais.popSpace();
const comma_or_colon = tree.lastToken(asm_output) + 1;
ais.popIndent();
break :colon2 switch (tree.tokenTag(comma_or_colon)) {
.comma => comma_or_colon + 1,
else => comma_or_colon,
};
}
} else unreachable;
};
const colon3 = if (asm_node.inputs.len == 0) colon3: {
try renderToken(r, colon2, .newline); // :
break :colon3 colon2 + 1;
} else colon3: {
try renderToken(r, colon2, .space); // :
try ais.forcePushIndent(.normal);
for (asm_node.inputs, 0..) |asm_input, i| {
if (i + 1 < asm_node.inputs.len) {
const next_asm_input = asm_node.inputs[i + 1];
try renderAsmInput(r, asm_input, .none);
const first_token = tree.firstToken(next_asm_input);
try renderToken(r, first_token - 1, .newline); // ,
try renderExtraNewlineToken(r, first_token);
} else if (asm_node.first_clobber == null) {
try ais.pushSpace(.comma);
try renderAsmInput(r, asm_input, .comma);
ais.popSpace();
ais.popIndent();
ais.setIndentDelta(indent_delta);
ais.popIndent();
return renderToken(r, asm_node.ast.rparen, space); // rparen
} else {
try ais.pushSpace(.comma);
try renderAsmInput(r, asm_input, .comma);
ais.popSpace();
const comma_or_colon = tree.lastToken(asm_input) + 1;
ais.popIndent();
break :colon3 switch (tree.tokenTag(comma_or_colon)) {
.comma => comma_or_colon + 1,
else => comma_or_colon,
};
}
}
unreachable;
};
try renderToken(r, colon3, .space); // :
try ais.writeAll(".{ ");
const first_clobber = asm_node.first_clobber.?;
var tok_i = first_clobber;
while (true) {
switch (tree.tokenTag(tok_i + 1)) {
.r_paren => {
ais.setIndentDelta(indent_delta);
try ais.writeByte('.');
const lexeme_len = try writeStringLiteralAsIdentifier(r, tok_i);
try ais.writeAll(" = true }");
try renderSpace(r, tok_i, lexeme_len, .newline);
ais.popIndent();
return renderToken(r, tok_i + 1, space);
},
.comma => {
switch (tree.tokenTag(tok_i + 2)) {
.r_paren => {
ais.setIndentDelta(indent_delta);
try ais.writeByte('.');
const lexeme_len = try writeStringLiteralAsIdentifier(r, tok_i);
try ais.writeAll(" = true }");
try renderSpace(r, tok_i, lexeme_len, .newline);
ais.popIndent();
return renderToken(r, tok_i + 2, space);
},
else => {
try ais.writeByte('.');
_ = try writeStringLiteralAsIdentifier(r, tok_i);
try ais.writeAll(" = true");
try renderToken(r, tok_i + 1, .space);
tok_i += 2;
},
}
},
else => unreachable,
}
}
}
fn renderAsm(
r: *Render,
asm_node: Ast.full.Asm,

View file

@ -507,7 +507,6 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
.bool_or,
.@"asm",
.asm_simple,
.asm_legacy,
.string_literal,
.number_literal,
.call,
@ -814,12 +813,6 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
.@"asm",
=> return asmExpr(gz, scope, ri, node, tree.fullAsm(node).?),
.asm_legacy => {
return astgen.failNodeNotes(node, "legacy asm clobbers syntax", .{}, &[_]u32{
try astgen.errNoteNode(node, "use 'zig fmt' to auto-upgrade", .{}),
});
},
.string_literal => return stringLiteral(gz, ri, node),
.multiline_string_literal => return multilineStringLiteral(gz, ri, node),
@ -10398,7 +10391,6 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev
.@"asm",
.asm_simple,
.asm_legacy,
.identifier,
.field_access,
.deref,
@ -10642,7 +10634,6 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
.tagged_union_enum_tag_trailing,
.@"asm",
.asm_simple,
.asm_legacy,
.add,
.add_wrap,
.add_sat,
@ -10881,7 +10872,6 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
.tagged_union_enum_tag_trailing,
.@"asm",
.asm_simple,
.asm_legacy,
.add,
.add_wrap,
.add_sat,

View file

@ -310,7 +310,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
.unreachable_literal,
.asm_simple,
.@"asm",
.asm_legacy,
.enum_literal,
.error_value,
.anyframe_literal,

View file

@ -2857,32 +2857,6 @@ fn expectAsmExpr(p: *Parse) !Node.Index {
_ = p.eatToken(.colon) orelse break :clobbers .none;
// For automatic upgrades; delete after 0.15.0 released.
if (p.tokenTag(p.tok_i) == .string_literal) {
while (p.eatToken(.string_literal)) |_| {
switch (p.tokenTag(p.tok_i)) {
.comma => p.tok_i += 1,
.colon, .r_paren, .r_brace, .r_bracket => break,
// Likely just a missing comma; give error but continue parsing.
else => try p.warnExpected(.comma),
}
}
const rparen = try p.expectToken(.r_paren);
const span = try p.listToSpan(p.scratch.items[scratch_top..]);
return p.addNode(.{
.tag = .asm_legacy,
.main_token = asm_token,
.data = .{ .node_and_extra = .{
template,
try p.addExtra(Node.AsmLegacy{
.items_start = span.start,
.items_end = span.end,
.rparen = rparen,
}),
} },
});
}
break :clobbers (try p.expectExpr()).toOptional();
} else .none;

View file

@ -238,7 +238,7 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator
=> try zg.addErrorNode(node, "control flow is not allowed in ZON", .{}),
.@"comptime" => try zg.addErrorNode(node, "keyword 'comptime' is not allowed in ZON", .{}),
.asm_simple, .@"asm", .asm_legacy => try zg.addErrorNode(node, "inline asm is not allowed in ZON", .{}),
.asm_simple, .@"asm" => try zg.addErrorNode(node, "inline asm is not allowed in ZON", .{}),
.builtin_call_two,
.builtin_call_two_comma,

View file

@ -31,54 +31,16 @@ test "zig fmt: tuple struct" {
}
test "zig fmt: preserves clobbers in inline asm with stray comma" {
try testTransform(
try testCanonical(
\\fn foo() void {
\\ asm volatile (""
\\ : [_] "" (-> type),
\\ :
\\ : "clobber"
\\ );
\\ : .{ .clobber = true });
\\ asm volatile (""
\\ :
\\ : [_] "" (type),
\\ : "clobber"
\\ );
\\}
\\
,
\\fn foo() void {
\\ asm volatile (""
\\ : [_] "" (-> type),
\\ :
\\ : .{ .clobber = true }
\\ );
\\ asm volatile (""
\\ :
\\ : [_] "" (type),
\\ : .{ .clobber = true }
\\ );
\\}
\\
);
}
test "zig fmt: remove trailing comma at the end of assembly clobber" {
try testTransform(
\\fn foo() void {
\\ asm volatile (""
\\ : [_] "" (-> type),
\\ :
\\ : "clobber1", "clobber2",
\\ );
\\}
\\
,
\\fn foo() void {
\\ asm volatile (""
\\ : [_] "" (-> type),
\\ :
\\ : .{ .clobber1 = true, .clobber2 = true }
\\ );
\\ : .{ .clobber = true });
\\}
\\
);
@ -641,7 +603,7 @@ test "zig fmt: builtin call with trailing comma" {
}
test "zig fmt: asm expression with comptime content" {
try testTransform(
try testCanonical(
\\comptime {
\\ asm ("foo" ++ "bar");
\\}
@ -657,28 +619,7 @@ test "zig fmt: asm expression with comptime content" {
\\ asm volatile ("foo" ++ "bar"
\\ : [_] "" (x),
\\ : [_] "" (y),
\\ : "h", "e", "l", "l", "o"
\\ );
\\}
\\
,
\\comptime {
\\ asm ("foo" ++ "bar");
\\}
\\pub fn main() void {
\\ asm volatile ("foo" ++ "bar");
\\ asm volatile ("foo" ++ "bar"
\\ : [_] "" (x),
\\ );
\\ asm volatile ("foo" ++ "bar"
\\ : [_] "" (x),
\\ : [_] "" (y),
\\ );
\\ asm volatile ("foo" ++ "bar"
\\ : [_] "" (x),
\\ : [_] "" (y),
\\ : .{ .h = true, .e = true, .l = true, .l = true, .o = true }
\\ );
\\ : .{ .h = true, .e = true, .l = true, .l = true, .o = true });
\\}
\\
);
@ -2198,7 +2139,7 @@ test "zig fmt: simple asm" {
\\ asm ("not real assembly"
\\ :[a] "x" (->i32),:[a] "x" (1),);
\\ asm ("still not real assembly"
\\ :::"a","b",);
\\ :::.{.a=true,.b=true});
\\}
,
\\comptime {
@ -3940,24 +3881,13 @@ test "zig fmt: fn type" {
}
test "zig fmt: inline asm" {
try testTransform(
try testCanonical(
\\pub fn syscall1(number: usize, arg1: usize) usize {
\\ return asm volatile ("syscall"
\\ : [ret] "={rax}" (-> usize),
\\ : [number] "{rax}" (number),
\\ [arg1] "{rdi}" (arg1),
\\ : "rcx", "r11"
\\ );
\\}
\\
,
\\pub fn syscall1(number: usize, arg1: usize) usize {
\\ return asm volatile ("syscall"
\\ : [ret] "={rax}" (-> usize),
\\ : [number] "{rax}" (number),
\\ [arg1] "{rdi}" (arg1),
\\ : .{ .rcx = true, .r11 = true }
\\ );
\\ : .{ .rcx = true, .r11 = true });
\\}
\\
);
@ -5779,8 +5709,7 @@ test "zig fmt: canonicalize symbols (asm)" {
\\ [@"arg1"] "{rdi}" (arg),
\\ [arg2] "{rsi}" (arg),
\\ [arg3] "{rdx}" (arg),
\\ : "rcx", "fn"
\\ );
\\ : .{ .rcx = true, .@"fn" = true });
\\
\\ const @"false": usize = 10;
\\ const @"true" = "explode";
@ -5801,8 +5730,7 @@ test "zig fmt: canonicalize symbols (asm)" {
\\ [arg1] "{rdi}" (arg),
\\ [arg2] "{rsi}" (arg),
\\ [arg3] "{rdx}" (arg),
\\ : .{ .rcx = true, .@"fn" = true }
\\ );
\\ : .{ .rcx = true, .@"fn" = true });
\\
\\ const @"false": usize = 10;
\\ const @"true" = "explode";