aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-02-15 22:35:50 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-02-15 22:36:46 -0700
commit4006a3afb31f89be28721bdcd50fa64de63d6cbb (patch)
treec18eb51e9a24550554dbe39b0d66aea6e22afa27
parentbbf5a4d7c5726baf933e303e6c61c6bba38b694b (diff)
downloadzig-4006a3afb31f89be28721bdcd50fa64de63d6cbb.tar.gz
zig-4006a3afb31f89be28721bdcd50fa64de63d6cbb.zip
astgen: update more expression types to new mem layout
additionally introduce a new file to centralize all the data about builtin functions that we have, including: * enum tag identifying the builtin function * number of parameters. * whether the expression may need a memory location. * whether the expression allows an lvalue (currently only true for `@field`). Now there is only one ComptimeStringMap that has this data as the value, and we dispatch on the enum tag in order to asgen the builtin function. In particular this simplifies the logic for checking the number of parameters. This removes some untested code paths from if and while, which need to be restored with #7929 in mind. After this there are only a handful left of expression types to rework to the new memory layout, and then it will be only compile errors left to solve.
-rw-r--r--src/BuiltinFn.zig841
-rw-r--r--src/astgen.zig1075
2 files changed, 1357 insertions, 559 deletions
diff --git a/src/BuiltinFn.zig b/src/BuiltinFn.zig
new file mode 100644
index 0000000000..9776edfef3
--- /dev/null
+++ b/src/BuiltinFn.zig
@@ -0,0 +1,841 @@
+const std = @import("std");
+
+pub const Tag = enum {
+ add_with_overflow,
+ align_cast,
+ align_of,
+ as,
+ async_call,
+ atomic_load,
+ atomic_rmw,
+ atomic_store,
+ bit_cast,
+ bit_offset_of,
+ bool_to_int,
+ bit_size_of,
+ breakpoint,
+ mul_add,
+ byte_swap,
+ bit_reverse,
+ byte_offset_of,
+ call,
+ c_define,
+ c_import,
+ c_include,
+ clz,
+ cmpxchg_strong,
+ cmpxchg_weak,
+ compile_error,
+ compile_log,
+ ctz,
+ c_undef,
+ div_exact,
+ div_floor,
+ div_trunc,
+ embed_file,
+ enum_to_int,
+ error_name,
+ error_return_trace,
+ error_to_int,
+ err_set_cast,
+ @"export",
+ fence,
+ field,
+ field_parent_ptr,
+ float_cast,
+ float_to_int,
+ frame,
+ Frame,
+ frame_address,
+ frame_size,
+ has_decl,
+ has_field,
+ import,
+ int_cast,
+ int_to_enum,
+ int_to_error,
+ int_to_float,
+ int_to_ptr,
+ memcpy,
+ memset,
+ wasm_memory_size,
+ wasm_memory_grow,
+ mod,
+ mul_with_overflow,
+ panic,
+ pop_count,
+ ptr_cast,
+ ptr_to_int,
+ rem,
+ return_address,
+ set_align_stack,
+ set_cold,
+ set_eval_branch_quota,
+ set_float_mode,
+ set_runtime_safety,
+ shl_exact,
+ shl_with_overflow,
+ shr_exact,
+ shuffle,
+ size_of,
+ splat,
+ reduce,
+ src,
+ sqrt,
+ sin,
+ cos,
+ exp,
+ exp2,
+ log,
+ log2,
+ log10,
+ fabs,
+ floor,
+ ceil,
+ trunc,
+ round,
+ sub_with_overflow,
+ tag_name,
+ This,
+ truncate,
+ Type,
+ type_info,
+ type_name,
+ TypeOf,
+ union_init,
+};
+
+tag: Tag,
+
+/// `true` if the builtin call can take advantage of a result location pointer.
+needs_mem_loc: bool = false,
+/// `true` if the builtin call can be the left-hand side of an expression (assigned to).
+allows_lvalue: bool = false,
+/// The number of parameters to this builtin function. `null` means variable number
+/// of parameters.
+param_count: ?u8,
+
+pub const list = std.ComptimeStringMap(@This(), .{
+ .{
+ "@addWithOverflow",
+ .{
+ .tag = .add_with_overflow,
+ .param_count = 4,
+ },
+ },
+ .{
+ "@alignCast",
+ .{
+ .tag = align_cast,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@alignOf",
+ .{
+ .tag = .align_of,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@as",
+ .{
+ .tag = .as,
+ .needs_mem_loc = true,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@asyncCall",
+ .{
+ .tag = .async_call,
+ .param_count = null,
+ },
+ },
+ .{
+ "@atomicLoad",
+ .{
+ .tag = .atomic_load,
+ .param_count = 3,
+ },
+ },
+ .{
+ "@atomicRmw",
+ .{
+ .tag = .atomic_rmw,
+ .param_count = 5,
+ },
+ },
+ .{
+ "@atomicStore",
+ .{
+ .tag = .atomic_store,
+ .param_count = 4,
+ },
+ },
+ .{
+ "@bitCast",
+ .{
+ .tag = .bit_cast,
+ .needs_mem_loc = true,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@bitOffsetOf",
+ .{
+ .tag = .bit_offset_of,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@boolToInt",
+ .{
+ .tag = .bool_to_int,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@bitSizeOf",
+ .{
+ .tag = .bit_size_of,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@breakpoint",
+ .{
+ .tag = .breakpoint,
+ .param_count = 0,
+ },
+ },
+ .{
+ "@mulAdd",
+ .{
+ .tag = .mul_add,
+ .param_count = 4,
+ },
+ },
+ .{
+ "@byteSwap",
+ .{
+ .tag = .byte_swap,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@bitReverse",
+ .{
+ .tag = .bit_reverse,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@byteOffsetOf",
+ .{
+ .tag = .byte_offset_of,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@call",
+ .{
+ .tag = .call,
+ .needs_mem_loc = true,
+ .param_count = 3,
+ },
+ },
+ .{
+ "@cDefine",
+ .{
+ .tag = .c_define,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@cImport",
+ .{
+ .tag = .c_import,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@cInclude",
+ .{
+ .tag = .c_include,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@clz",
+ .{
+ .tag = .clz,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@cmpxchgStrong",
+ .{
+ .tag = .cmpxchg_strong,
+ .param_count = 6,
+ },
+ },
+ .{
+ "@cmpxchgWeak",
+ .{
+ .tag = .cmpxchg_weak,
+ .param_count = 6,
+ },
+ },
+ .{
+ "@compileError",
+ .{
+ .tag = .compile_error,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@compileLog",
+ .{
+ .tag = .compile_log,
+ .param_count = null,
+ },
+ },
+ .{
+ "@ctz",
+ .{
+ .tag = .ctz,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@cUndef",
+ .{
+ .tag = .c_undef,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@divExact",
+ .{
+ .tag = .div_exact,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@divFloor",
+ .{
+ .tag = .div_floor,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@divTrunc",
+ .{
+ .tag = .div_trunc,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@embedFile",
+ .{
+ .tag = .embed_file,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@enumToInt",
+ .{
+ .tag = .enum_to_int,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@errorName",
+ .{
+ .tag = .error_name,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@errorReturnTrace",
+ .{
+ .tag = .error_return_trace,
+ .param_count = 0,
+ },
+ },
+ .{
+ "@errorToInt",
+ .{
+ .tag = .error_to_int,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@errSetCast",
+ .{
+ .tag = .err_set_cast,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@export",
+ .{
+ .tag = .@"export",
+ .param_count = 2,
+ },
+ },
+ .{
+ "@fence",
+ .{
+ .tag = .fence,
+ .param_count = 0,
+ },
+ },
+ .{
+ "@field",
+ .{
+ .tag = .field,
+ .needs_mem_loc = true,
+ .param_count = 2,
+ .allows_lvalue = true,
+ },
+ },
+ .{
+ "@fieldParentPtr",
+ .{
+ .tag = .field_parent_ptr,
+ .param_count = 3,
+ },
+ },
+ .{
+ "@floatCast",
+ .{
+ .tag = .float_cast,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@floatToInt",
+ .{
+ .tag = .float_to_int,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@frame",
+ .{
+ .tag = .frame,
+ .param_count = 0,
+ },
+ },
+ .{
+ "@Frame",
+ .{
+ .tag = .Frame,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@frameAddress",
+ .{
+ .tag = .frame_address,
+ .param_count = 0,
+ },
+ },
+ .{
+ "@frameSize",
+ .{
+ .tag = .frame_size,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@hasDecl",
+ .{
+ .tag = .has_decl,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@hasField",
+ .{
+ .tag = .has_field,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@import",
+ .{
+ .tag = .import,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@intCast",
+ .{
+ .tag = .int_cast,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@intToEnum",
+ .{
+ .tag = .int_to_enum,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@intToError",
+ .{
+ .tag = .int_to_error,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@intToFloat",
+ .{
+ .tag = .int_to_float,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@intToPtr",
+ .{
+ .tag = .int_to_ptr,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@memcpy",
+ .{
+ .tag = .memcpy,
+ .param_count = 3,
+ },
+ },
+ .{
+ "@memset",
+ .{
+ .tag = .memset,
+ .param_count = 3,
+ },
+ },
+ .{
+ "@wasmMemorySize",
+ .{
+ .tag = .wasm_memory_size,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@wasmMemoryGrow",
+ .{
+ .tag = .wasm_memory_grow,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@mod",
+ .{
+ .tag = .mod,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@mulWithOverflow",
+ .{
+ .tag = .mul_with_overflow,
+ .param_count = 4,
+ },
+ },
+ .{
+ "@panic",
+ .{
+ .tag = .panic,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@popCount",
+ .{
+ .tag = .pop_count,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@ptrCast",
+ .{
+ .tag = .ptr_cast,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@ptrToInt",
+ .{
+ .tag = .ptr_to_int,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@rem",
+ .{
+ .tag = .rem,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@returnAddress",
+ .{
+ .tag = .return_address,
+ .param_count = 0,
+ },
+ },
+ .{
+ "@setAlignStack",
+ .{
+ .tag = .set_align_stack,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@setCold",
+ .{
+ .tag = .set_cold,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@setEvalBranchQuota",
+ .{
+ .tag = .set_eval_branch_quota,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@setFloatMode",
+ .{
+ .tag = .set_float_mode,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@setRuntimeSafety",
+ .{
+ .tag = .set_runtime_safety,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@shlExact",
+ .{
+ .tag = .shl_exact,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@shlWithOverflow",
+ .{
+ .tag = .shl_with_overflow,
+ .param_count = 4,
+ },
+ },
+ .{
+ "@shrExact",
+ .{
+ .tag = .shr_exact,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@shuffle",
+ .{
+ .tag = .shuffle,
+ .param_count = 4,
+ },
+ },
+ .{
+ "@sizeOf",
+ .{
+ .tag = .size_of,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@splat",
+ .{
+ .tag = .splat,
+ .needs_mem_loc = true,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@reduce",
+ .{
+ .tag = .reduce,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@src",
+ .{
+ .tag = .src,
+ .needs_mem_loc = true,
+ .param_count = 0,
+ },
+ },
+ .{
+ "@sqrt",
+ .{
+ .tag = .sqrt,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@sin",
+ .{
+ .tag = .sin,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@cos",
+ .{
+ .tag = .cos,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@exp",
+ .{
+ .tag = .exp,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@exp2",
+ .{
+ .tag = .exp2,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@log",
+ .{
+ .tag = .log,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@log2",
+ .{
+ .tag = .log2,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@log10",
+ .{
+ .tag = .log10,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@fabs",
+ .{
+ .tag = .fabs,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@floor",
+ .{
+ .tag = .floor,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@ceil",
+ .{
+ .tag = .ceil,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@trunc",
+ .{
+ .tag = .trunc,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@round",
+ .{
+ .tag = .round,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@subWithOverflow",
+ .{
+ .tag = .sub_with_overflow,
+ .param_count = 4,
+ },
+ },
+ .{
+ "@tagName",
+ .{
+ .tag = .tag_name,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@This",
+ .{
+ .tag = .This,
+ .param_count = 0,
+ },
+ },
+ .{
+ "@truncate",
+ .{
+ .tag = .truncate,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@Type",
+ .{
+ .tag = .Type,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@typeInfo",
+ .{
+ .tag = .type_info,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@typeName",
+ .{
+ .tag = .type_name,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@TypeOf",
+ .{
+ .tag = .TypeOf,
+ .param_count = null,
+ },
+ },
+ .{
+ "@unionInit",
+ .{
+ .tag = .union_init,
+ .needs_mem_loc = true,
+ .param_count = 3,
+ },
+ },
+});
diff --git a/src/astgen.zig b/src/astgen.zig
index da0c93bb8e..56d1497f63 100644
--- a/src/astgen.zig
+++ b/src/astgen.zig
@@ -1,16 +1,18 @@
const std = @import("std");
const mem = std.mem;
const Allocator = std.mem.Allocator;
+const assert = std.debug.assert;
+
const Value = @import("value.zig").Value;
const Type = @import("type.zig").Type;
const TypedValue = @import("TypedValue.zig");
-const assert = std.debug.assert;
const zir = @import("zir.zig");
const Module = @import("Module.zig");
const ast = std.zig.ast;
const trace = @import("tracy.zig").trace;
const Scope = Module.Scope;
const InnerError = Module.InnerError;
+const BuiltinFn = @import("BuiltinFn.zig");
pub const ResultLoc = union(enum) {
/// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
@@ -172,16 +174,21 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.I
.ContainerDecl,
.@"comptime",
.@"nosuspend",
- .builtin_call,
- .builtin_call_comma,
=> return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),
- // `@field` can be assigned to.
- .builtin_call_two, .builtin_call_two_comma => {
+ .builtin_call,
+ .builtin_call_comma,
+ .builtin_call_two,
+ .builtin_call_two_comma,
+ => {
const builtin_token = main_tokens[node];
const builtin_name = tree.tokenSlice(builtin_token);
- if (!mem.eql(u8, builtin_name, "@field")) {
- return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
+ // If the builtin is an invalid name, we don't cause an error here; instead
+ // let it pass, and the error will be "invalid builtin function" later.
+ if (BuiltinFn.list.get(builtin_name)) |info| {
+ if (!info.allows_lvalue) {
+ return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
+ }
}
},
@@ -276,22 +283,111 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In
.multiline_string_literal => return multilineStringLiteral(mod, scope, rl, node),
.integer_literal => return integerLiteral(mod, scope, rl, node),
+
.builtin_call => return builtinCall(mod, scope, rl, node),
- .call => return callExpr(mod, scope, rl, node),
- .@"unreachable" => return unreach(mod, scope, node),
+
+ .builtin_call_two, .builtin_call_two_comma => {
+ if (datas[node].lhs == 0) {
+ const params = [_]ast.Node.Index{};
+ return builtinCall(mod, scope, rl, node, &params);
+ } else if (datas[node].rhs == 0) {
+ const params = [_]ast.Node.Index{datas[node].lhs};
+ return builtinCall(mod, scope, rl, node, &params);
+ } else {
+ const params = [_]ast.Node.Index{ datas[node].lhs, datas[node].rhs };
+ return builtinCall(mod, scope, rl, node, &params);
+ }
+ },
+ .builtin_call, .builtin_call_comma => {
+ const params = tree.extra_data[datas[node].lhs..datas[node].rhs];
+ return builtinCall(mod, scope, rl, node, params);
+ },
+
+ .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => {
+ var params: [1]ast.Node.Index = undefined;
+ return callExpr(mod, scope, rl, tree.callOne(&params, node));
+ },
+ .call, .call_comma, .async_call, .async_call_comma => {
+ return callExpr(mod, scope, rl, tree.callFull(node));
+ },
+
+ .@"unreachable" => {
+ const main_token = main_tokens[node];
+ const src = token_starts[main_token];
+ return addZIRNoOp(mod, scope, src, .unreachable_safe);
+ },
.@"return" => return ret(mod, scope, node),
- .@"if" => return ifExpr(mod, scope, rl, node),
- .@"while" => return whileExpr(mod, scope, rl, node),
.period => return field(mod, scope, rl, node),
- .deref => return rvalue(mod, scope, rl, try deref(mod, scope, node)),
- .address_of => return rvalue(mod, scope, rl, try addressOf(mod, scope, node)),
- .float_literal => return rvalue(mod, scope, rl, try floatLiteral(mod, scope, node)),
- .undefined_literal => return rvalue(mod, scope, rl, try undefLiteral(mod, scope, node)),
- .bool_literal => return rvalue(mod, scope, rl, try boolLiteral(mod, scope, node)),
- .null_literal => return rvalue(mod, scope, rl, try nullLiteral(mod, scope, node)),
- .optional_type => return rvalue(mod, scope, rl, try optionalType(mod, scope, node)),
- .unwrap_optional => return unwrapOptional(mod, scope, rl, node),
+ .float_literal => return floatLiteral(mod, scope, rl, node),
+
+ .if_simple => return ifExpr(mod, scope, rl, tree.ifSimple(node)),
+ .@"if" => return ifExpr(mode, scope, rl, tree.ifFull(node)),
+
+ .while_simple => return whileExpr(mod, scope, rl, tree.whileSimple(node)),
+ .while_cont => return whileExpr(mod, scope, tree.whileCont(node)),
+ .@"while" => return whileExpr(mod, scope, rl, tree.whileFull(node)),
+ .deref => {
+ const lhs = try expr(mod, scope, .none, node_datas[node].lhs);
+ const src = token_starts[main_tokens[node]];
+ const result = try addZIRUnOp(mod, scope, src, .deref, lhs);
+ return rvalue(mod, scope, rl, result);
+ },
+ .address_of => {
+ const result = try expr(mod, scope, .ref, node_datas[node].lhs);
+ return rvalue(mod, scope, rl, result);
+ },
+ .undefined_literal => {
+ const main_token = main_tokens[node];
+ const src = token_starts[main_token];
+ const result = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.@"undefined"),
+ .val = Value.initTag(.undef),
+ });
+ return rvalue(mod, scope, rl, result);
+ },
+ .true_literal => {
+ const main_token = main_tokens[node];
+ const src = token_starts[main_token];
+ const result = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.bool),
+ .val = Value.initTag(.bool_true),
+ });
+ return rvalue(mod, scope, rl, result);
+ },
+ .false_literal => {
+ const main_token = main_tokens[node];
+ const src = token_starts[main_token];
+ const result = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.bool),
+ .val = Value.initTag(.bool_false),
+ });
+ return rvalue(mod, scope, rl, result);
+ },
+ .null_literal => {
+ const main_token = main_tokens[node];
+ const src = token_starts[main_token];
+ const result = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.@"null"),
+ .val = Value.initTag(.null_value),
+ });
+ return rvalue(mod, scope, rl, result);
+ },
+ .optional_type => {
+ const src = token_starts[main_tokens[node]];
+ const operand = try typeExpr(mod, scope, node_datas[node].lhs);
+ const result = try addZIRUnOp(mod, scope, src, .optional_type, operand);
+ return rvalue(mod, scope, rl, result);
+ },
+ .unwrap_optional => {
+ const operand = try expr(mod, scope, rl, node.lhs);
+ const op: zir.Inst.Tag = switch (rl) {
+ .ref => .optional_payload_safe_ptr,
+ else => .optional_payload_safe,
+ };
+ const src = token_starts[main_tokens[node]];
+ return addZIRUnOp(mod, scope, src, op, operand);
+ },
.block_two, .block_two_semicolon => {
const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
if (node_datas[node].lhs == 0) {
@@ -307,7 +403,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In
return blockExpr(mod, scope, rl, node, statements);
},
- .labeled_block => return labeledBlockExpr(mod, scope, rl, node, .block),
.@"break" => return rvalue(mod, scope, rl, try breakExpr(mod, scope, node)),
.@"continue" => return rvalue(mod, scope, rl, try continueExpr(mod, scope, node)),
.grouped_expression => return expr(mod, scope, rl, node.expr),
@@ -521,7 +616,12 @@ pub fn blockExpr(
const tracy = trace(@src());
defer tracy.end();
- try blockExprStmts(mod, scope, &block_node.base, statements);
+ const lbrace = main_tokens[node];
+ if (token_tags[lbrace - 1] == .colon) {
+ return labeledBlockExpr(mod, scope, rl, block_node, .block);
+ }
+
+ try blockExprStmts(mod, scope, block_node, statements);
return rvalueVoid(mod, scope, rl, block_node, {});
}
@@ -983,17 +1083,6 @@ fn negation(
return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
}
-fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
- return expr(mod, scope, .ref, node.rhs);
-}
-
-fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = token_starts[node.op_token];
- const operand = try typeExpr(mod, scope, node.rhs);
- return addZIRUnOp(mod, scope, src, .optional_type, operand);
-}
-
fn sliceType(mod: *Module, scope: *Scope, node: *ast.Node.slice_type) InnerError!*zir.Inst {
const tree = scope.tree();
const src = token_starts[node.op_token];
@@ -1123,18 +1212,6 @@ fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.enum_literal) !*zir.
return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{});
}
-fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = token_starts[node.rtoken];
-
- const operand = try expr(mod, scope, rl, node.lhs);
- const op: zir.Inst.Tag = switch (rl) {
- .ref => .optional_payload_safe_ptr,
- else => .optional_payload_safe,
- };
- return addZIRUnOp(mod, scope, src, op, operand);
-}
-
fn containerField(
mod: *Module,
scope: *Scope,
@@ -1583,51 +1660,25 @@ fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: as
return mem.eql(u8, ident_name_1, ident_name_2);
}
-pub fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
+pub fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst {
const tree = scope.tree();
- const src = token_starts[node.op_token];
- // TODO custom AST node for field access so that we don't have to go through a node cast here
- const field_name = try mod.identifierTokenString(scope, node.rhs.castTag(.identifier).?.token);
+ const token_starts = tree.tokens.items(.start);
+ const main_tokens = tree.nodes.items(.main_token);
+ const dot_token = main_tokens[node];
+ const src = token_starts[dot_token];
+ const field_ident = dot_token + 1;
+ const field_name = try mod.identifierTokenString(scope, field_ident);
if (rl == .ref) {
return addZirInstTag(mod, scope, src, .field_ptr, .{
.object = try expr(mod, scope, .ref, node.lhs),
.field_name = field_name,
});
+ } else {
+ return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val, .{
+ .object = try expr(mod, scope, .none, node.lhs),
+ .field_name = field_name,
+ }));
}
- return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val, .{
- .object = try expr(mod, scope, .none, node.lhs),
- .field_name = field_name,
- }));
-}
-
-fn namedField(
- mod: *Module,
- scope: *Scope,
- rl: ResultLoc,
- call: *ast.Node.builtin_call,
-) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 2);
-
- const tree = scope.tree();
- const src = token_starts[call.builtin_token];
- const params = call.params();
-
- const string_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.const_slice_u8_type),
- });
- const string_rl: ResultLoc = .{ .ty = string_type };
-
- if (rl == .ref) {
- return addZirInstTag(mod, scope, src, .field_ptr_named, .{
- .object = try expr(mod, scope, .ref, params[0]),
- .field_name = try comptimeExpr(mod, scope, string_rl, params[1]),
- });
- }
- return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val_named, .{
- .object = try expr(mod, scope, .none, params[0]),
- .field_name = try comptimeExpr(mod, scope, string_rl, params[1]),
- }));
}
fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.array_access) InnerError!*zir.Inst {
@@ -1681,13 +1732,6 @@ fn sliceExpr(mod: *Module, scope: *Scope, node: *ast.Node.slice) InnerError!*zir
);
}
-fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = token_starts[node.rtoken];
- const lhs = try expr(mod, scope, .none, node.lhs);
- return addZIRUnOp(mod, scope, src, .deref, lhs);
-}
-
fn simpleBinOp(
mod: *Module,
scope: *Scope,
@@ -1794,83 +1838,12 @@ fn boolBinOp(
return rvalue(mod, scope, rl, &block.base);
}
-const CondKind = union(enum) {
- bool,
- optional: ?*zir.Inst,
- err_union: ?*zir.Inst,
-
- fn cond(self: *CondKind, mod: *Module, block_scope: *Scope.GenZIR, src: usize, cond_node: *ast.Node) !*zir.Inst {
- switch (self.*) {
- .bool => {
- const bool_type = try addZIRInstConst(mod, &block_scope.base, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.bool_type),
- });
- return try expr(mod, &block_scope.base, .{ .ty = bool_type }, cond_node);
- },
- .optional => {
- const cond_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
- self.* = .{ .optional = cond_ptr };
- const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, cond_ptr);
- return try addZIRUnOp(mod, &block_scope.base, src, .is_non_null, result);
- },
- .err_union => {
- const err_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
- self.* = .{ .err_union = err_ptr };
- const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, err_ptr);
- return try addZIRUnOp(mod, &block_scope.base, src, .is_err, result);
- },
- }
- }
-
- fn thenSubScope(self: CondKind, mod: *Module, then_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
- if (self == .bool) return &then_scope.base;
-
- const payload = payload_node.?.castTag(.PointerPayload) orelse {
- // condition is error union and payload is not explicitly ignored
- _ = try addZIRUnOp(mod, &then_scope.base, src, .ensure_err_payload_void, self.err_union.?);
- return &then_scope.base;
- };
- const is_ptr = payload.ptr_token != null;
- const ident_node = payload.value_symbol.castTag(.identifier).?;
-
- // This intentionally does not support @"_" syntax.
- const ident_name = then_scope.base.tree().tokenSlice(ident_node.token);
- if (mem.eql(u8, ident_name, "_")) {
- if (is_ptr)
- return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
- return &then_scope.base;
- }
-
- return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement payload symbols", .{});
- }
-
- fn elseSubScope(self: CondKind, mod: *Module, else_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
- if (self != .err_union) return &else_scope.base;
-
- const payload_ptr = try addZIRUnOp(mod, &else_scope.base, src, .err_union_payload_unsafe_ptr, self.err_union.?);
-
- const payload = payload_node.?.castTag(.Payload).?;
- const ident_node = payload.error_symbol.castTag(.identifier).?;
-
- // This intentionally does not support @"_" syntax.
- const ident_name = else_scope.base.tree().tokenSlice(ident_node.token);
- if (mem.eql(u8, ident_name, "_")) {
- return &else_scope.base;
- }
-
- return mod.failNode(&else_scope.base, payload.error_symbol, "TODO implement payload symbols", .{});
- }
-};
-
-fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.@"if") InnerError!*zir.Inst {
- var cond_kind: CondKind = .bool;
- if (if_node.payload) |_| cond_kind = .{ .optional = null };
- if (if_node.@"else") |else_node| {
- if (else_node.payload) |payload| {
- cond_kind = .{ .err_union = null };
- }
- }
+fn ifExpr(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ if_full: ast.full.If,
+) InnerError!*zir.Inst {
var block_scope: Scope.GenZIR = .{
.parent = scope,
.decl = scope.ownerDecl().?,
@@ -1884,8 +1857,22 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.@"if")
const tree = scope.tree();
const node_datas = tree.nodes.items(.data);
const main_tokens = tree.nodes.items(.main_token);
- const if_src = token_starts[if_node.if_token];
- const cond = try cond_kind.cond(mod, &block_scope, if_src, if_node.condition);
+ const if_src = token_starts[if_full.ast.if_token];
+
+ const cond = c: {
+ // TODO https://github.com/ziglang/zig/issues/7929
+ if (if_full.ast.error_token) |error_token| {
+ return mod.failTok(scope, error_token, "TODO implement if error union", .{});
+ } else if (if_full.payload_token) |payload_token| {
+ return mod.failTok(scope, payload_token, "TODO implement if optional", .{});
+ } else {
+ const bool_type = try addZIRInstConst(mod, &block_scope.base, if_src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.bool_type),
+ });
+ break :c try expr(mod, &block_scope.base, .{ .ty = bool_type }, if_full.ast.cond_expr);
+ }
+ };
const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{
.condition = cond,
@@ -1897,7 +1884,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.@"if")
.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
});
- const then_src = token_starts[if_node.body.lastToken()];
+ const then_src = token_starts[tree.lastToken(if_full.ast.then_expr)];
var then_scope: Scope.GenZIR = .{
.parent = scope,
.decl = block_scope.decl,
@@ -1908,10 +1895,10 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.@"if")
defer then_scope.instructions.deinit(mod.gpa);
// declare payload to the then_scope
- const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, if_node.payload);
+ const then_sub_scope = &then_scope.base;
block_scope.break_count += 1;
- const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, if_node.body);
+ const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, if_full.ast.then_expr);
// We hold off on the break instructions as well as copying the then/else
// instructions into place until we know whether to keep store_to_block_ptr
// instructions or not.
@@ -1925,20 +1912,19 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.@"if")
};
defer else_scope.instructions.deinit(mod.gpa);
- var else_src: usize = undefined;
- var else_sub_scope: *Module.Scope = undefined;
- const else_result: ?*zir.Inst = if (if_node.@"else") |else_node| blk: {
- else_src = token_starts[else_node.body.lastToken()];
- // declare payload to the then_scope
- else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
-
+ const else_node = if_full.ast.else_expr;
+ const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: {
block_scope.break_count += 1;
- break :blk try expr(mod, else_sub_scope, block_scope.break_result_loc, else_node.body);
- } else blk: {
- else_src = token_starts[if_node.lastToken()];
- else_sub_scope = &else_scope.base;
- break :blk null;
- };
+ const sub_scope = &else_scope.base;
+ break :blk .{
+ .src = token_starts[tree.lastToken(else_node)],
+ .result = try expr(mod, sub_scope, block_scope.break_result_loc, else_node),
+ };
+ } else
+ .{
+ .src = token_starts[tree.lastToken(if_full.then_expr)],
+ .result = null,
+ };
return finishThenElseBlock(
mod,
@@ -1950,9 +1936,9 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.@"if")
&condbr.positionals.then_body,
&condbr.positionals.else_body,
then_src,
- else_src,
+ else_info.src,
then_result,
- else_result,
+ else_info.result,
block,
block,
);
@@ -1983,23 +1969,15 @@ fn whileExpr(
mod: *Module,
scope: *Scope,
rl: ResultLoc,
- while_node: *ast.Node.@"while",
+ while_full: ast.full.While,
) InnerError!*zir.Inst {
- var cond_kind: CondKind = .bool;
- if (while_node.payload) |_| cond_kind = .{ .optional = null };
- if (while_node.@"else") |else_node| {
- if (else_node.payload) |payload| {
- cond_kind = .{ .err_union = null };
- }
+ if (while_full.label_token) |label_token| {
+ try checkLabelRedefinition(mod, scope, label_token);
}
-
- if (while_node.label) |label| {
- try checkLabelRedefinition(mod, scope, label);
+ if (while_full.inline_token) |inline_token| {
+ return mod.failTok(scope, inline_token, "TODO inline while", .{});
}
- if (while_node.inline_token) |tok|
- return mod.failTok(scope, tok, "TODO inline while", .{});
-
var loop_scope: Scope.GenZIR = .{
.parent = scope,
.decl = scope.ownerDecl().?,
@@ -2022,12 +2000,25 @@ fn whileExpr(
const tree = scope.tree();
const node_datas = tree.nodes.items(.data);
const main_tokens = tree.nodes.items(.main_token);
- const while_src = token_starts[while_node.while_token];
+ const while_src = token_starts[while_full.ast.while_token];
const void_type = try addZIRInstConst(mod, scope, while_src, .{
.ty = Type.initTag(.type),
.val = Value.initTag(.void_type),
});
- const cond = try cond_kind.cond(mod, &continue_scope, while_src, while_node.condition);
+ const cond = c: {
+ // TODO https://github.com/ziglang/zig/issues/7929
+ if (while_full.ast.error_token) |error_token| {
+ return mod.failTok(scope, error_token, "TODO implement while error union", .{});
+ } else if (while_full.payload_token) |payload_token| {
+ return mod.failTok(scope, payload_token, "TODO implement while optional", .{});
+ } else {
+ const bool_type = try addZIRInstConst(mod, &block_scope.base, while_src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.bool_type),
+ });
+ break :c try expr(mod, &block_scope.base, .{ .ty = bool_type }, while_full.ast.cond_expr);
+ }
+ };
const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{
.condition = cond,
@@ -2041,8 +2032,8 @@ fn whileExpr(
// are no jumps to it. This happens when the last statement of a while body is noreturn
// and there are no `continue` statements.
// The "repeat" at the end of a loop body is implied.
- if (while_node.continue_expr) |cont_expr| {
- _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, cont_expr);
+ if (while_full.ast.cont_expr != 0) {
+ _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, while_full.ast.cont_expr);
}
const loop = try scope.arena().create(zir.Inst.Loop);
loop.* = .{
@@ -2062,14 +2053,14 @@ fn whileExpr(
});
loop_scope.break_block = while_block;
loop_scope.continue_block = cond_block;
- if (while_node.label) |some| {
+ if (while_full.label_token) |label_token| {
loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
- .token = some,
+ .token = label_token,
.block_inst = while_block,
});
}
- const then_src = token_starts[while_node.body.lastToken()];
+ const then_src = token_starts[tree.lastToken(while_full.ast.then_expr)];
var then_scope: Scope.GenZIR = .{
.parent = &continue_scope.base,
.decl = continue_scope.decl,
@@ -2080,10 +2071,10 @@ fn whileExpr(
defer then_scope.instructions.deinit(mod.gpa);
// declare payload to the then_scope
- const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, while_node.payload);
+ const then_sub_scope = &then_scope.base;
loop_scope.break_count += 1;
- const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, while_node.body);
+ const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr);
var else_scope: Scope.GenZIR = .{
.parent = &continue_scope.base,
@@ -2094,18 +2085,20 @@ fn whileExpr(
};
defer else_scope.instructions.deinit(mod.gpa);
- var else_src: usize = undefined;
- const else_result: ?*zir.Inst = if (while_node.@"else") |else_node| blk: {
- else_src = token_starts[else_node.body.lastToken()];
- // declare payload to the then_scope
- const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
-
+ const else_node = if_full.ast.else_expr;
+ const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: {
loop_scope.break_count += 1;
- break :blk try expr(mod, else_sub_scope, loop_scope.break_result_loc, else_node.body);
- } else blk: {
- else_src = token_starts[while_node.lastToken()];
- break :blk null;
- };
+ const sub_scope = &else_scope.base;
+ break :blk .{
+ .src = token_starts[tree.lastToken(else_node)],
+ .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node),
+ };
+ } else
+ .{
+ .src = token_starts[tree.lastToken(then_node)],
+ .result = null,
+ };
+
if (loop_scope.label) |some| {
if (!some.used) {
return mod.fail(scope, token_starts[some.token], "unused while label", .{});
@@ -2121,9 +2114,9 @@ fn whileExpr(
&condbr.positionals.then_body,
&condbr.positionals.else_body,
then_src,
- else_src,
+ else_info.src,
then_result,
- else_result,
+ else_info.result,
while_block,
cond_block,
);
@@ -2630,12 +2623,13 @@ fn switchCaseExpr(
}
}
-fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
+fn ret(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst {
const tree = scope.tree();
const node_datas = tree.nodes.items(.data);
const main_tokens = tree.nodes.items(.main_token);
- const src = token_starts[cfe.ltoken];
- if (cfe.getRHS()) |rhs_node| {
+ const src = token_starts[main_tokens[node]];
+ const rhs_node = node_datas[node].lhs;
+ if (rhs_node != 0) {
if (nodeMayNeedMemoryLocation(rhs_node, scope)) {
const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr);
const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node);
@@ -2885,64 +2879,29 @@ fn integerLiteral(
}
}
-fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
+fn floatLiteral(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ float_lit: ast.Node.Index,
+) InnerError!*zir.Inst {
const arena = scope.arena();
const tree = scope.tree();
- const node_datas = tree.nodes.items(.data);
const main_tokens = tree.nodes.items(.main_token);
- const bytes = tree.tokenSlice(float_lit.token);
+ const main_token = main_tokens[float_lit];
+ const bytes = tree.tokenSlice(main_token);
if (bytes.len > 2 and bytes[1] == 'x') {
- return mod.failTok(scope, float_lit.token, "TODO hex floats", .{});
+ return mod.failTok(scope, main_token, "TODO implement hex floats", .{});
}
-
const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
error.InvalidCharacter => unreachable, // validated by tokenizer
};
- const src = token_starts[float_lit.token];
- return addZIRInstConst(mod, scope, src, .{
+ const src = token_starts[main_token];
+ const result = try addZIRInstConst(mod, scope, src, .{
.ty = Type.initTag(.comptime_float),
.val = try Value.Tag.float_128.create(arena, float_number),
});
-}
-
-fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const arena = scope.arena();
- const tree = scope.tree();
- const node_datas = tree.nodes.items(.data);
- const main_tokens = tree.nodes.items(.main_token);
- const src = token_starts[node.token];
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.@"undefined"),
- .val = Value.initTag(.undef),
- });
-}
-
-fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const arena = scope.arena();
- const tree = scope.tree();
- const node_datas = tree.nodes.items(.data);
- const main_tokens = tree.nodes.items(.main_token);
- const src = token_starts[node.token];
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.bool),
- .val = switch (tree.token_ids[node.token]) {
- .keyword_true => Value.initTag(.bool_true),
- .keyword_false => Value.initTag(.bool_false),
- else => unreachable,
- },
- });
-}
-
-fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const arena = scope.arena();
- const tree = scope.tree();
- const node_datas = tree.nodes.items(.data);
- const main_tokens = tree.nodes.items(.main_token);
- const src = token_starts[node.token];
- return addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.@"null"),
- .val = Value.initTag(.null_value),
- });
+ return rvalue(mod, scope, rl, result);
}
fn assembly(mod: *Module, scope: *Scope, rl: ResultLoc, full: ast.full.Asm) InnerError!*zir.Inst {
@@ -2987,76 +2946,36 @@ fn assembly(mod: *Module, scope: *Scope, rl: ResultLoc, full: ast.full.Asm) Inne
return rvalue(mod, scope, rl, asm_inst);
}
-fn ensureBuiltinParamCount(mod: *Module, scope: *Scope, call: *ast.Node.builtin_call, count: u32) !void {
- if (call.params_len == count)
- return;
-
- const s = if (count == 1) "" else "s";
- return mod.failTok(scope, call.builtin_token, "expected {d} parameter{s}, found {d}", .{ count, s, call.params_len });
-}
-
-fn simpleCast(
- mod: *Module,
- scope: *Scope,
- rl: ResultLoc,
- call: *ast.Node.builtin_call,
- inst_tag: zir.Inst.Tag,
-) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 2);
- const tree = scope.tree();
- const node_datas = tree.nodes.items(.data);
- const main_tokens = tree.nodes.items(.main_token);
- const src = token_starts[call.builtin_token];
- const params = call.params();
- const dest_type = try typeExpr(mod, scope, params[0]);
- const rhs = try expr(mod, scope, .none, params[1]);
- const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs);
- return rvalue(mod, scope, rl, result);
-}
-
-fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.builtin_call) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 1);
- const operand = try expr(mod, scope, .none, call.params()[0]);
- const tree = scope.tree();
- const node_datas = tree.nodes.items(.data);
- const main_tokens = tree.nodes.items(.main_token);
- const src = token_starts[call.builtin_token];
- return addZIRUnOp(mod, scope, src, .ptrtoint, operand);
-}
-
fn as(
mod: *Module,
scope: *Scope,
rl: ResultLoc,
- call: *ast.Node.builtin_call,
+ builtin_token: ast.TokenIndex,
+ src: usize,
+ lhs: ast.Node.Index,
+ rhs: ast.Node.Index,
) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 2);
- const tree = scope.tree();
- const node_datas = tree.nodes.items(.data);
- const main_tokens = tree.nodes.items(.main_token);
- const src = token_starts[call.builtin_token];
- const params = call.params();
- const dest_type = try typeExpr(mod, scope, params[0]);
+ const dest_type = try typeExpr(mod, scope, lhs);
switch (rl) {
.none, .discard, .ref, .ty => {
- const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
+ const result = try expr(mod, scope, .{ .ty = dest_type }, rhs);
return rvalue(mod, scope, rl, result);
},
.ptr => |result_ptr| {
- return asRlPtr(mod, scope, rl, src, result_ptr, params[1], dest_type);
+ return asRlPtr(mod, scope, rl, src, result_ptr, rhs, dest_type);
},
.block_ptr => |block_scope| {
- return asRlPtr(mod, scope, rl, src, block_scope.rl_ptr.?, params[1], dest_type);
+ return asRlPtr(mod, scope, rl, src, block_scope.rl_ptr.?, rhs, dest_type);
},
.bitcasted_ptr => |bitcasted_ptr| {
// TODO here we should be able to resolve the inference; we now have a type for the result.
- return mod.failTok(scope, call.builtin_token, "TODO implement @as with result location @bitCast", .{});
+ return mod.failTok(scope, builtin_token, "TODO implement @as with result location @bitCast", .{});
},
.inferred_ptr => |result_alloc| {
// TODO here we should be able to resolve the inference; we now have a type for the result.
- return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
+ return mod.failTok(scope, builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
},
}
}
@@ -3105,170 +3024,290 @@ fn asRlPtr(
}
}
-fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.builtin_call) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 2);
- const tree = scope.tree();
- const node_datas = tree.nodes.items(.data);
- const main_tokens = tree.nodes.items(.main_token);
- const src = token_starts[call.builtin_token];
- const params = call.params();
- const dest_type = try typeExpr(mod, scope, params[0]);
+fn bitCast(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ builtin_token: ast.TokenIndex,
+ src: usize,
+ lhs: ast.Node.Index,
+ rhs: ast.Node.Index,
+) InnerError!*zir.Inst {
+ const dest_type = try typeExpr(mod, scope, lhs);
switch (rl) {
.none => {
- const operand = try expr(mod, scope, .none, params[1]);
+ const operand = try expr(mod, scope, .none, rhs);
return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
},
.discard => {
- const operand = try expr(mod, scope, .none, params[1]);
+ const operand = try expr(mod, scope, .none, rhs);
const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
_ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
return result;
},
.ref => {
- const operand = try expr(mod, scope, .ref, params[1]);
+ const operand = try expr(mod, scope, .ref, rhs);
const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand);
return result;
},
.ty => |result_ty| {
- const result = try expr(mod, scope, .none, params[1]);
+ const result = try expr(mod, scope, .none, rhs);
const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);
return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);
},
.ptr => |result_ptr| {
const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr);
- return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]);
+ return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, rhs);
},
.bitcasted_ptr => |bitcasted_ptr| {
- return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location another @bitCast", .{});
+ return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location another @bitCast", .{});
},
.block_ptr => |block_ptr| {
- return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location inferred peer types", .{});
+ return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location inferred peer types", .{});
},
.inferred_ptr => |result_alloc| {
// TODO here we should be able to resolve the inference; we now have a type for the result.
- return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{});
+ return mod.failTok(scope, builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{});
},
}
}
-fn import(mod: *Module, scope: *Scope, call: *ast.Node.builtin_call) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 1);
- const tree = scope.tree();
- const node_datas = tree.nodes.items(.data);
- const main_tokens = tree.nodes.items(.main_token);
- const src = token_starts[call.builtin_token];
- const params = call.params();
- const target = try expr(mod, scope, .none, params[0]);
- return addZIRUnOp(mod, scope, src, .import, target);
-}
-
-fn compileError(mod: *Module, scope: *Scope, call: *ast.Node.builtin_call) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 1);
- const tree = scope.tree();
- const node_datas = tree.nodes.items(.data);
- const main_tokens = tree.nodes.items(.main_token);
- const src = token_starts[call.builtin_token];
- const params = call.params();
- const target = try expr(mod, scope, .none, params[0]);
- return addZIRUnOp(mod, scope, src, .compile_error, target);
-}
-
-fn setEvalBranchQuota(mod: *Module, scope: *Scope, call: *ast.Node.builtin_call) InnerError!*zir.Inst {
- try ensureBuiltinParamCount(mod, scope, call, 1);
- const tree = scope.tree();
- const node_datas = tree.nodes.items(.data);
- const main_tokens = tree.nodes.items(.main_token);
- const src = token_starts[call.builtin_token];
- const params = call.params();
- const u32_type = try addZIRInstConst(mod, scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.u32_type),
- });
- const quota = try expr(mod, scope, .{ .ty = u32_type }, params[0]);
- return addZIRUnOp(mod, scope, src, .set_eval_branch_quota, quota);
-}
-
-fn typeOf(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.builtin_call) InnerError!*zir.Inst {
- const tree = scope.tree();
- const node_datas = tree.nodes.items(.data);
- const main_tokens = tree.nodes.items(.main_token);
- const arena = scope.arena();
- const src = token_starts[call.builtin_token];
- const params = call.params();
+fn typeOf(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ builtin_token: ast.TokenIndex,
+ src: usize,
+ params: []const ast.Node.Index,
+) InnerError!*zir.Inst {
if (params.len < 1) {
- return mod.failTok(scope, call.builtin_token, "expected at least 1 argument, found 0", .{});
+ return mod.failTok(scope, builtin_token, "expected at least 1 argument, found 0", .{});
}
if (params.len == 1) {
return rvalue(mod, scope, rl, try addZIRUnOp(mod, scope, src, .typeof, try expr(mod, scope, .none, params[0])));
}
+ const arena = scope.arena();
var items = try arena.alloc(*zir.Inst, params.len);
for (params) |param, param_i|
items[param_i] = try expr(mod, scope, .none, param);
return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.TypeOfPeer, .{ .items = items }, .{}));
}
-fn compileLog(mod: *Module, scope: *Scope, call: *ast.Node.builtin_call) InnerError!*zir.Inst {
- const tree = scope.tree();
- const node_datas = tree.nodes.items(.data);
- const main_tokens = tree.nodes.items(.main_token);
- const arena = scope.arena();
- const src = token_starts[call.builtin_token];
- const params = call.params();
- var targets = try arena.alloc(*zir.Inst, params.len);
- for (params) |param, param_i|
- targets[param_i] = try expr(mod, scope, .none, param);
- return addZIRInst(mod, scope, src, zir.Inst.CompileLog, .{ .to_log = targets }, .{});
-}
-fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.builtin_call) InnerError!*zir.Inst {
+fn builtinCall(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ call: ast.Node.Index,
+ params: []const ast.Node.Index,
+) InnerError!*zir.Inst {
const tree = scope.tree();
const node_datas = tree.nodes.items(.data);
const main_tokens = tree.nodes.items(.main_token);
- const builtin_name = tree.tokenSlice(call.builtin_token);
+ const builtin_token = main_tokens[call];
+ const builtin_name = tree.tokenSlice(builtin_token);
// We handle the different builtins manually because they have different semantics depending
// on the function. For example, `@as` and others participate in result location semantics,
// and `@cImport` creates a special scope that collects a .c source code text buffer.
// Also, some builtins have a variable number of parameters.
- if (mem.eql(u8, builtin_name, "@ptrToInt")) {
- return rvalue(mod, scope, rl, try ptrToInt(mod, scope, call));
- } else if (mem.eql(u8, builtin_name, "@as")) {
- return as(mod, scope, rl, call);
- } else if (mem.eql(u8, builtin_name, "@floatCast")) {
- return simpleCast(mod, scope, rl, call, .floatcast);
- } else if (mem.eql(u8, builtin_name, "@intCast")) {
- return simpleCast(mod, scope, rl, call, .intcast);
- } else if (mem.eql(u8, builtin_name, "@bitCast")) {
- return bitCast(mod, scope, rl, call);
- } else if (mem.eql(u8, builtin_name, "@TypeOf")) {
- return typeOf(mod, scope, rl, call);
- } else if (mem.eql(u8, builtin_name, "@breakpoint")) {
- const src = token_starts[call.builtin_token];
- return rvalue(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint));
- } else if (mem.eql(u8, builtin_name, "@import")) {
- return rvalue(mod, scope, rl, try import(mod, scope, call));
- } else if (mem.eql(u8, builtin_name, "@compileError")) {
- return compileError(mod, scope, call);
- } else if (mem.eql(u8, builtin_name, "@setEvalBranchQuota")) {
- return setEvalBranchQuota(mod, scope, call);
- } else if (mem.eql(u8, builtin_name, "@compileLog")) {
- return compileLog(mod, scope, call);
- } else if (mem.eql(u8, builtin_name, "@field")) {
- return namedField(mod, scope, rl, call);
- } else {
- return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{s}'", .{builtin_name});
+ const info = BuiltinFn.list.get(builtin_name) orelse {
+ return mod.failTok(scope, builtin_token, "invalid builtin function: '{s}'", .{
+ builtin_name,
+ });
+ };
+ if (info.param_count != params.len) {
+ const s = if (params.len == 1) "" else "s";
+ return mod.failTok(scope, builtin_token, "expected {d} parameter{s}, found {d}", .{
+ expected, s, found,
+ });
+ }
+ const src = token_starts[builtin_token];
+
+ switch (info.tag) {
+ .ptr_to_int => {
+ const operand = try expr(mod, scope, .none, params[0]);
+ const result = try addZIRUnOp(mod, scope, src, .ptrtoint, operand);
+ return rvalue(mod, scope, rl, result);
+ },
+ .float_cast => {
+ const dest_type = try typeExpr(mod, scope, params[0]);
+ const rhs = try expr(mod, scope, .none, params[1]);
+ const result = try addZIRBinOp(mod, scope, src, .floatcast, dest_type, rhs);
+ return rvalue(mod, scope, rl, result);
+ },
+ .int_cast => {
+ const dest_type = try typeExpr(mod, scope, params[0]);
+ const rhs = try expr(mod, scope, .none, params[1]);
+ const result = try addZIRBinOp(mod, scope, src, .intcast, dest_type, rhs);
+ return rvalue(mod, scope, rl, result);
+ },
+ .breakpoint => {
+ const result = try addZIRNoOp(mod, scope, src, .breakpoint);
+ return rvalue(mod, scope, rl, result);
+ },
+ .import => {
+ const target = try expr(mod, scope, .none, params[0]);
+ const result = try addZIRUnOp(mod, scope, src, .import, target);
+ return rvalue(mod, scope, rl, result);
+ },
+ .compile_error => {
+ const target = try expr(mod, scope, .none, params[0]);
+ const result = addZIRUnOp(mod, scope, src, .compile_error, target);
+ return rvalue(mod, scope, rl, result);
+ },
+ .set_eval_branch_quota => {
+ const u32_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.u32_type),
+ });
+ const quota = try expr(mod, scope, .{ .ty = u32_type }, params[0]);
+ const result = try addZIRUnOp(mod, scope, src, .set_eval_branch_quota, quota);
+ return rvalue(mod, scope, rl, result);
+ },
+ .compile_log => {
+ const arena = scope.arena();
+ var targets = try arena.alloc(*zir.Inst, params.len);
+ for (params) |param, param_i|
+ targets[param_i] = try expr(mod, scope, .none, param);
+ const result = try addZIRInst(mod, scope, src, zir.Inst.CompileLog, .{ .to_log = targets }, .{});
+ return rvalue(mod, scope, rl, result);
+ },
+ .field => {
+ const string_type = try addZIRInstConst(mod, scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.const_slice_u8_type),
+ });
+ const string_rl: ResultLoc = .{ .ty = string_type };
+
+ if (rl == .ref) {
+ return addZirInstTag(mod, scope, src, .field_ptr_named, .{
+ .object = try expr(mod, scope, .ref, params[0]),
+ .field_name = try comptimeExpr(mod, scope, string_rl, params[1]),
+ });
+ }
+ return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val_named, .{
+ .object = try expr(mod, scope, .none, params[0]),
+ .field_name = try comptimeExpr(mod, scope, string_rl, params[1]),
+ }));
+ },
+ .as => return as(mod, scope, rl, builtin_token, src, params[0], params[1]),
+ .bit_cast => return bitCast(mod, scope, rl, builtin_token, src, params[0], params[1]),
+ .TypeOf => return typeOf(mod, scope, rl, builtin_token, src, params),
+
+ .add_with_overflow,
+ .align_cast,
+ .align_of,
+ .async_call,
+ .atomic_load,
+ .atomic_rmw,
+ .atomic_store,
+ .bit_offset_of,
+ .bool_to_int,
+ .bit_size_of,
+ .mul_add,
+ .byte_swap,
+ .bit_reverse,
+ .byte_offset_of,
+ .call,
+ .c_define,
+ .c_import,
+ .c_include,
+ .clz,
+ .cmpxchg_strong,
+ .cmpxchg_weak,
+ .ctz,
+ .c_undef,
+ .div_exact,
+ .div_floor,
+ .div_trunc,
+ .embed_file,
+ .enum_to_int,
+ .error_name,
+ .error_return_trace,
+ .error_to_int,
+ .err_set_cast,
+ .@"export",
+ .fence,
+ .field_parent_ptr,
+ .float_to_int,
+ .frame,
+ .Frame,
+ .frame_address,
+ .frame_size,
+ .has_decl,
+ .has_field,
+ .int_to_enum,
+ .int_to_error,
+ .int_to_float,
+ .int_to_ptr,
+ .memcpy,
+ .memset,
+ .wasm_memory_size,
+ .wasm_memory_grow,
+ .mod,
+ .mul_with_overflow,
+ .panic,
+ .pop_count,
+ .ptr_cast,
+ .rem,
+ .return_address,
+ .set_align_stack,
+ .set_cold,
+ .set_float_mode,
+ .set_runtime_safety,
+ .shl_exact,
+ .shl_with_overflow,
+ .shr_exact,
+ .shuffle,
+ .size_of,
+ .splat,
+ .reduce,
+ .src,
+ .sqrt,
+ .sin,
+ .cos,
+ .exp,
+ .exp2,
+ .log,
+ .log2,
+ .log10,
+ .fabs,
+ .floor,
+ .ceil,
+ .trunc,
+ .round,
+ .sub_with_overflow,
+ .tag_name,
+ .This,
+ .truncate,
+ .Type,
+ .type_info,
+ .type_name,
+ .union_init,
+ => return mod.failTok(scope, builtin_token, "TODO: implement builtin function {s}", .{
+ builtin_name,
+ }),
}
}
-fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.call) InnerError!*zir.Inst {
+fn callExpr(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ call: ast.full.Call,
+) InnerError!*zir.Inst {
+ if (call.async_token) |async_token| {
+ return mod.failTok(scope, async_token, "TODO implement async fn call", .{});
+ }
+
const tree = scope.tree();
const node_datas = tree.nodes.items(.data);
const main_tokens = tree.nodes.items(.main_token);
- const lhs = try expr(mod, scope, .none, node.lhs);
+ const lhs = try expr(mod, scope, .none, call.ast.fn_expr);
- const param_nodes = node.params();
- const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);
- for (param_nodes) |param_node, i| {
+ const args = try scope.getGenZIR().arena.alloc(*zir.Inst, call.ast.params.len);
+ for (call.ast.params) |param_node, i| {
const param_src = token_starts[tree.firstToken(param_node)];
const param_type = try addZIRInst(mod, scope, param_src, zir.Inst.ParamType, .{
.func = lhs,
@@ -3277,7 +3316,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.call) In
args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node);
}
- const src = token_starts[node.lhs.firstToken()];
+ const src = token_starts[call.ast.lparen];
const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{
.func = lhs,
.args = args,
@@ -3286,14 +3325,6 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.call) In
return rvalue(mod, scope, rl, result);
}
-fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst {
- const tree = scope.tree();
- const node_datas = tree.nodes.items(.data);
- const main_tokens = tree.nodes.items(.main_token);
- const src = token_starts[unreach_node.token];
- return addZIRNoOp(mod, scope, src, .unreachable_safe);
-}
-
fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
const simple_types = std.ComptimeStringMap(Value.Tag, .{
.{ "u8", .u8_type },
@@ -3430,17 +3461,25 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool {
.deref,
.array_access,
.block,
+ .while_simple, // This variant cannot have an else expression.
+ .while_cont, // This variant cannot have an else expression.
+ .for_simple, // This variant cannot have an else expression.
+ .if_simple, // This variant cannot have an else expression.
=> return false,
- // Forward the question to a sub-expression.
- .grouped_expression => node = node.castTag(.grouped_expression).?.expr,
- .@"try" => node = node.castTag(.@"try").?.rhs,
- .@"await" => node = node.castTag(.@"await").?.rhs,
- .@"catch" => node = node.castTag(.@"catch").?.rhs,
- .@"orelse" => node = node.castTag(.@"orelse").?.rhs,
- .@"comptime" => node = node.castTag(.@"comptime").?.expr,
- .@"nosuspend" => node = node.castTag(.@"nosuspend").?.expr,
- .unwrap_optional => node = node.castTag(.unwrap_optional).?.lhs,
+ // Forward the question to the LHS sub-expression.
+ .grouped_expression,
+ .@"try",
+ .@"await",
+ .@"comptime",
+ .@"nosuspend",
+ .unwrap_optional,
+ => node = datas[node].lhs,
+
+ // Forward the question to the RHS sub-expression.
+ .@"catch",
+ .@"orelse",
+ => node = datas[node].rhs,
// True because these are exactly the expressions we need memory locations for.
.ArrayInitializer,
@@ -3451,125 +3490,43 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool {
// True because depending on comptime conditions, sub-expressions
// may be the kind that need memory locations.
- .@"while",
- .@"for",
+ .@"while", // This variant always has an else expression.
+ .@"if", // This variant always has an else expression.
+ .@"for", // This variant always has an else expression.
.@"switch",
+ .call_one,
+ .call_one_comma,
+ .async_call_one,
+ .async_call_one_comma,
.call,
- .labeled_block,
+ .call_comma,
+ .async_call,
+ .async_call_comma,
=> return true,
- .builtin_call => {
- @setEvalBranchQuota(5000);
- const builtin_needs_mem_loc = std.ComptimeStringMap(bool, .{
- .{ "@addWithOverflow", false },
- .{ "@alignCast", false },
- .{ "@alignOf", false },
- .{ "@as", true },
- .{ "@asyncCall", false },
- .{ "@atomicLoad", false },
- .{ "@atomicRmw", false },
- .{ "@atomicStore", false },
- .{ "@bitCast", true },
- .{ "@bitOffsetOf", false },
- .{ "@boolToInt", false },
- .{ "@bitSizeOf", false },
- .{ "@breakpoint", false },
- .{ "@mulAdd", false },
- .{ "@byteSwap", false },
- .{ "@bitReverse", false },
- .{ "@byteOffsetOf", false },
- .{ "@call", true },
- .{ "@cDefine", false },
- .{ "@cImport", false },
- .{ "@cInclude", false },
- .{ "@clz", false },
- .{ "@cmpxchgStrong", false },
- .{ "@cmpxchgWeak", false },
- .{ "@compileError", false },
- .{ "@compileLog", false },
- .{ "@ctz", false },
- .{ "@cUndef", false },
- .{ "@divExact", false },
- .{ "@divFloor", false },
- .{ "@divTrunc", false },
- .{ "@embedFile", false },
- .{ "@enumToInt", false },
- .{ "@errorName", false },
- .{ "@errorReturnTrace", false },
- .{ "@errorToInt", false },
- .{ "@errSetCast", false },
- .{ "@export", false },
- .{ "@fence", false },
- .{ "@field", true },
- .{ "@fieldParentPtr", false },
- .{ "@floatCast", false },
- .{ "@floatToInt", false },
- .{ "@frame", false },
- .{ "@Frame", false },
- .{ "@frameAddress", false },
- .{ "@frameSize", false },
- .{ "@hasDecl", false },
- .{ "@hasField", false },
- .{ "@import", false },
- .{ "@intCast", false },
- .{ "@intToEnum", false },
- .{ "@intToError", false },
- .{ "@intToFloat", false },
- .{ "@intToPtr", false },
- .{ "@memcpy", false },
- .{ "@memset", false },
- .{ "@wasmMemorySize", false },
- .{ "@wasmMemoryGrow", false },
- .{ "@mod", false },
- .{ "@mulWithOverflow", false },
- .{ "@panic", false },
- .{ "@popCount", false },
- .{ "@ptrCast", false },
- .{ "@ptrToInt", false },
- .{ "@rem", false },
- .{ "@returnAddress", false },
- .{ "@setAlignStack", false },
- .{ "@setCold", false },
- .{ "@setEvalBranchQuota", false },
- .{ "@setFloatMode", false },
- .{ "@setRuntimeSafety", false },
- .{ "@shlExact", false },
- .{ "@shlWithOverflow", false },
- .{ "@shrExact", false },
- .{ "@shuffle", false },
- .{ "@sizeOf", false },
- .{ "@splat", true },
- .{ "@reduce", false },
- .{ "@src", true },
- .{ "@sqrt", false },
- .{ "@sin", false },
- .{ "@cos", false },
- .{ "@exp", false },
- .{ "@exp2", false },
- .{ "@log", false },
- .{ "@log2", false },
- .{ "@log10", false },
- .{ "@fabs", false },
- .{ "@floor", false },
- .{ "@ceil", false },
- .{ "@trunc", false },
- .{ "@round", false },
- .{ "@subWithOverflow", false },
- .{ "@tagName", false },
- .{ "@This", false },
- .{ "@truncate", false },
- .{ "@Type", false },
- .{ "@typeInfo", false },
- .{ "@typeName", false },
- .{ "@TypeOf", false },
- .{ "@unionInit", true },
- });
- const name = scope.tree().tokenSlice(node.castTag(.builtin_call).?.builtin_token);
- return builtin_needs_mem_loc.get(name).?;
+ block_two,
+ block_two_semicolon,
+ block,
+ block_semicolon,
+ => {
+ const lbrace = main_tokens[node];
+ if (token_tags[lbrace - 1] == .colon) {
+ // Labeled blocks may need a memory location to forward
+ // to their break statements.
+ return true;
+ } else {
+ return false;
+ }
},
- // Depending on AST properties, they may need memory locations.
- .@"if" => return node.castTag(.@"if").?.@"else" != null,
+ .builtin_call => {
+ const builtin_token = main_tokens[node];
+ const builtin_name = tree.tokenSlice(builtin_token);
+ // If the builtin is an invalid name, we don't cause an error here; instead
+ // let it pass, and the error will be "invalid builtin function" later.
+ const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false;
+ return builtin_info.needs_mem_loc;
+ },
}
}
}