authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-15 15:42:02-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-15 15:42:02-07:00
loge70d6d19f5a937504ce0e2f79d03a6275b6ff357
tree9e24da46727df3f497cbec7821041eb23a00eded
parent6b103324d2d935260138584877dbe1593790b953

stage2: extract AST=>ZIR code to separate file


2 files changed, 490 insertions(+), 472 deletions(-)

src-self-hosted/Module.zig+14-472
......@@ -19,6 +19,7 @@ const Body = ir.Body;
1919const ast = std.zig.ast;
2020const trace = @import("tracy.zig").trace;
2121const liveness = @import("liveness.zig");
22const astgen = @import("astgen.zig");
2223
2324/// General-purpose allocator. Used for both temporary and long-term storage.
2425gpa: *Allocator,
......@@ -76,6 +77,8 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
7677
7778keep_source_files_loaded: bool,
7879
80pub const InnerError = error{ OutOfMemory, AnalysisFail };
81
7982const WorkItem = union(enum) {
8083 /// Write the machine code for a Decl to the output file.
8184 codegen_decl: *Decl,
......@@ -944,8 +947,6 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
944947 };
945948}
946949
947const InnerError = error{ OutOfMemory, AnalysisFail };
948
949950pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
950951 while (self.work_queue.readItem()) |work_item| switch (work_item) {
951952 .codegen_decl => |decl| switch (decl.analysis) {
......@@ -1140,7 +1141,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
11401141 .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}),
11411142 .type_expr => |node| node,
11421143 };
1143 param_types[i] = try self.astGenExpr(&fn_type_scope.base, param_type_node);
1144 param_types[i] = try astgen.expr(self, &fn_type_scope.base, param_type_node);
11441145 }
11451146 if (fn_proto.getTrailer("var_args_token")) |var_args_token| {
11461147 return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{});
......@@ -1168,7 +1169,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
11681169 .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}),
11691170 };
11701171
1171 const return_type_inst = try self.astGenExpr(&fn_type_scope.base, return_type_expr);
1172 const return_type_inst = try astgen.expr(self, &fn_type_scope.base, return_type_expr);
11721173 const fn_src = tree.token_locs[fn_proto.fn_token].start;
11731174 const fn_type_inst = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.FnType, .{
11741175 .return_type = return_type_inst,
......@@ -1209,7 +1210,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12091210
12101211 const body_block = body_node.cast(ast.Node.Block).?;
12111212
1212 try self.astGenBlock(&gen_scope.base, body_block);
1213 try astgen.blockExpr(self, &gen_scope.base, body_block);
12131214
12141215 if (!fn_type.fnReturnType().isNoReturn() and (gen_scope.instructions.items.len == 0 or
12151216 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()))
......@@ -1298,465 +1299,6 @@ fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Mo
12981299 unreachable;
12991300}
13001301
1301fn astGenExpr(self: *Module, scope: *Scope, ast_node: *ast.Node) InnerError!*zir.Inst {
1302 switch (ast_node.id) {
1303 .Identifier => return self.astGenIdent(scope, @fieldParentPtr(ast.Node.Identifier, "base", ast_node)),
1304 .Asm => return self.astGenAsm(scope, @fieldParentPtr(ast.Node.Asm, "base", ast_node)),
1305 .StringLiteral => return self.astGenStringLiteral(scope, @fieldParentPtr(ast.Node.StringLiteral, "base", ast_node)),
1306 .IntegerLiteral => return self.astGenIntegerLiteral(scope, @fieldParentPtr(ast.Node.IntegerLiteral, "base", ast_node)),
1307 .BuiltinCall => return self.astGenBuiltinCall(scope, @fieldParentPtr(ast.Node.BuiltinCall, "base", ast_node)),
1308 .Call => return self.astGenCall(scope, @fieldParentPtr(ast.Node.Call, "base", ast_node)),
1309 .Unreachable => return self.astGenUnreachable(scope, @fieldParentPtr(ast.Node.Unreachable, "base", ast_node)),
1310 .ControlFlowExpression => return self.astGenControlFlowExpression(scope, @fieldParentPtr(ast.Node.ControlFlowExpression, "base", ast_node)),
1311 .If => return self.astGenIf(scope, @fieldParentPtr(ast.Node.If, "base", ast_node)),
1312 .InfixOp => return self.astGenInfixOp(scope, @fieldParentPtr(ast.Node.InfixOp, "base", ast_node)),
1313 .BoolNot => return self.astGenBoolNot(scope, @fieldParentPtr(ast.Node.BoolNot, "base", ast_node)),
1314 else => return self.failNode(scope, ast_node, "TODO implement astGenExpr for {}", .{@tagName(ast_node.id)}),
1315 }
1316}
1317
1318fn astGenBoolNot(self: *Module, scope: *Scope, node: *ast.Node.BoolNot) InnerError!*zir.Inst {
1319 const operand = try self.astGenExpr(scope, node.rhs);
1320 const tree = scope.tree();
1321 const src = tree.token_locs[node.op_token].start;
1322 return self.addZIRInst(scope, src, zir.Inst.BoolNot, .{ .operand = operand }, .{});
1323}
1324
1325fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) InnerError!*zir.Inst {
1326 switch (infix_node.op) {
1327 .Assign => {
1328 if (infix_node.lhs.id == .Identifier) {
1329 const ident = @fieldParentPtr(ast.Node.Identifier, "base", infix_node.lhs);
1330 const tree = scope.tree();
1331 const ident_name = tree.tokenSlice(ident.token);
1332 if (std.mem.eql(u8, ident_name, "_")) {
1333 return self.astGenExpr(scope, infix_node.rhs);
1334 } else {
1335 return self.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
1336 }
1337 } else {
1338 return self.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
1339 }
1340 },
1341 .Add => {
1342 const lhs = try self.astGenExpr(scope, infix_node.lhs);
1343 const rhs = try self.astGenExpr(scope, infix_node.rhs);
1344
1345 const tree = scope.tree();
1346 const src = tree.token_locs[infix_node.op_token].start;
1347
1348 return self.addZIRInst(scope, src, zir.Inst.Add, .{ .lhs = lhs, .rhs = rhs }, .{});
1349 },
1350 .BangEqual,
1351 .EqualEqual,
1352 .GreaterThan,
1353 .GreaterOrEqual,
1354 .LessThan,
1355 .LessOrEqual,
1356 => {
1357 const lhs = try self.astGenExpr(scope, infix_node.lhs);
1358 const rhs = try self.astGenExpr(scope, infix_node.rhs);
1359
1360 const tree = scope.tree();
1361 const src = tree.token_locs[infix_node.op_token].start;
1362
1363 const op: std.math.CompareOperator = switch (infix_node.op) {
1364 .BangEqual => .neq,
1365 .EqualEqual => .eq,
1366 .GreaterThan => .gt,
1367 .GreaterOrEqual => .gte,
1368 .LessThan => .lt,
1369 .LessOrEqual => .lte,
1370 else => unreachable,
1371 };
1372
1373 return self.addZIRInst(scope, src, zir.Inst.Cmp, .{
1374 .lhs = lhs,
1375 .op = op,
1376 .rhs = rhs,
1377 }, .{});
1378 },
1379 else => |op| {
1380 return self.failNode(scope, &infix_node.base, "TODO implement infix operator {}", .{op});
1381 },
1382 }
1383}
1384
1385fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.Inst {
1386 if (if_node.payload) |payload| {
1387 return self.failNode(scope, payload, "TODO implement astGenIf for optionals", .{});
1388 }
1389 if (if_node.@"else") |else_node| {
1390 if (else_node.payload) |payload| {
1391 return self.failNode(scope, payload, "TODO implement astGenIf for error unions", .{});
1392 }
1393 }
1394 var block_scope: Scope.GenZIR = .{
1395 .decl = scope.decl().?,
1396 .arena = scope.arena(),
1397 .instructions = .{},
1398 };
1399 defer block_scope.instructions.deinit(self.gpa);
1400
1401 const cond = try self.astGenExpr(&block_scope.base, if_node.condition);
1402
1403 const tree = scope.tree();
1404 const if_src = tree.token_locs[if_node.if_token].start;
1405 const condbr = try self.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{
1406 .condition = cond,
1407 .true_body = undefined, // populated below
1408 .false_body = undefined, // populated below
1409 }, .{});
1410
1411 const block = try self.addZIRInstBlock(scope, if_src, .{
1412 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
1413 });
1414 var then_scope: Scope.GenZIR = .{
1415 .decl = block_scope.decl,
1416 .arena = block_scope.arena,
1417 .instructions = .{},
1418 };
1419 defer then_scope.instructions.deinit(self.gpa);
1420
1421 const then_result = try self.astGenExpr(&then_scope.base, if_node.body);
1422 if (!then_result.tag.isNoReturn()) {
1423 const then_src = tree.token_locs[if_node.body.lastToken()].start;
1424 _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{
1425 .block = block,
1426 .operand = then_result,
1427 }, .{});
1428 }
1429 condbr.positionals.true_body = .{
1430 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
1431 };
1432
1433 var else_scope: Scope.GenZIR = .{
1434 .decl = block_scope.decl,
1435 .arena = block_scope.arena,
1436 .instructions = .{},
1437 };
1438 defer else_scope.instructions.deinit(self.gpa);
1439
1440 if (if_node.@"else") |else_node| {
1441 const else_result = try self.astGenExpr(&else_scope.base, else_node.body);
1442 if (!else_result.tag.isNoReturn()) {
1443 const else_src = tree.token_locs[else_node.body.lastToken()].start;
1444 _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{
1445 .block = block,
1446 .operand = else_result,
1447 }, .{});
1448 }
1449 } else {
1450 // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
1451 // by directly allocating the body for this one instruction.
1452 const else_src = tree.token_locs[if_node.lastToken()].start;
1453 _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.BreakVoid, .{
1454 .block = block,
1455 }, .{});
1456 }
1457 condbr.positionals.false_body = .{
1458 .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
1459 };
1460
1461 return &block.base;
1462}
1463
1464fn astGenControlFlowExpression(
1465 self: *Module,
1466 scope: *Scope,
1467 cfe: *ast.Node.ControlFlowExpression,
1468) InnerError!*zir.Inst {
1469 switch (cfe.kind) {
1470 .Break => return self.failNode(scope, &cfe.base, "TODO implement astGenExpr for Break", .{}),
1471 .Continue => return self.failNode(scope, &cfe.base, "TODO implement astGenExpr for Continue", .{}),
1472 .Return => {},
1473 }
1474 const tree = scope.tree();
1475 const src = tree.token_locs[cfe.ltoken].start;
1476 if (cfe.rhs) |rhs_node| {
1477 const operand = try self.astGenExpr(scope, rhs_node);
1478 return self.addZIRInst(scope, src, zir.Inst.Return, .{ .operand = operand }, .{});
1479 } else {
1480 return self.addZIRInst(scope, src, zir.Inst.ReturnVoid, .{}, .{});
1481 }
1482}
1483
1484fn astGenIdent(self: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerError!*zir.Inst {
1485 const tree = scope.tree();
1486 const ident_name = tree.tokenSlice(ident.token);
1487 const src = tree.token_locs[ident.token].start;
1488 if (mem.eql(u8, ident_name, "_")) {
1489 return self.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});
1490 }
1491
1492 if (getSimplePrimitiveValue(ident_name)) |typed_value| {
1493 return self.addZIRInstConst(scope, src, typed_value);
1494 }
1495
1496 if (ident_name.len >= 2) integer: {
1497 const first_c = ident_name[0];
1498 if (first_c == 'i' or first_c == 'u') {
1499 const is_signed = first_c == 'i';
1500 const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
1501 error.Overflow => return self.failNode(
1502 scope,
1503 &ident.base,
1504 "primitive integer type '{}' exceeds maximum bit width of 65535",
1505 .{ident_name},
1506 ),
1507 error.InvalidCharacter => break :integer,
1508 };
1509 const val = switch (bit_count) {
1510 8 => if (is_signed) Value.initTag(.i8_type) else Value.initTag(.u8_type),
1511 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type),
1512 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type),
1513 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type),
1514 else => return self.failNode(scope, &ident.base, "TODO implement arbitrary integer bitwidth types", .{}),
1515 };
1516 return self.addZIRInstConst(scope, src, .{
1517 .ty = Type.initTag(.type),
1518 .val = val,
1519 });
1520 }
1521 }
1522
1523 if (self.lookupDeclName(scope, ident_name)) |decl| {
1524 return try self.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
1525 }
1526
1527 // Function parameter
1528 if (scope.decl()) |decl| {
1529 if (tree.root_node.decls()[decl.src_index].cast(ast.Node.FnProto)) |fn_proto| {
1530 for (fn_proto.params()) |param, i| {
1531 const param_name = tree.tokenSlice(param.name_token.?);
1532 if (mem.eql(u8, param_name, ident_name)) {
1533 return try self.addZIRInst(scope, src, zir.Inst.Arg, .{ .index = i }, .{});
1534 }
1535 }
1536 }
1537 }
1538
1539 return self.failNode(scope, &ident.base, "TODO implement local variable identifier lookup", .{});
1540}
1541
1542fn astGenStringLiteral(self: *Module, scope: *Scope, str_lit: *ast.Node.StringLiteral) InnerError!*zir.Inst {
1543 const tree = scope.tree();
1544 const unparsed_bytes = tree.tokenSlice(str_lit.token);
1545 const arena = scope.arena();
1546
1547 var bad_index: usize = undefined;
1548 const bytes = std.zig.parseStringLiteral(arena, unparsed_bytes, &bad_index) catch |err| switch (err) {
1549 error.InvalidCharacter => {
1550 const bad_byte = unparsed_bytes[bad_index];
1551 const src = tree.token_locs[str_lit.token].start;
1552 return self.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
1553 },
1554 else => |e| return e,
1555 };
1556
1557 const src = tree.token_locs[str_lit.token].start;
1558 return self.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
1559}
1560
1561fn astGenIntegerLiteral(self: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral) InnerError!*zir.Inst {
1562 const arena = scope.arena();
1563 const tree = scope.tree();
1564 const prefixed_bytes = tree.tokenSlice(int_lit.token);
1565 const base = if (mem.startsWith(u8, prefixed_bytes, "0x"))
1566 16
1567 else if (mem.startsWith(u8, prefixed_bytes, "0o"))
1568 8
1569 else if (mem.startsWith(u8, prefixed_bytes, "0b"))
1570 2
1571 else
1572 @as(u8, 10);
1573
1574 const bytes = if (base == 10)
1575 prefixed_bytes
1576 else
1577 prefixed_bytes[2..];
1578
1579 if (std.fmt.parseInt(u64, bytes, base)) |small_int| {
1580 const int_payload = try arena.create(Value.Payload.Int_u64);
1581 int_payload.* = .{ .int = small_int };
1582 const src = tree.token_locs[int_lit.token].start;
1583 return self.addZIRInstConst(scope, src, .{
1584 .ty = Type.initTag(.comptime_int),
1585 .val = Value.initPayload(&int_payload.base),
1586 });
1587 } else |err| {
1588 return self.failTok(scope, int_lit.token, "TODO implement int literals that don't fit in a u64", .{});
1589 }
1590}
1591
1592fn astGenBlock(self: *Module, scope: *Scope, block_node: *ast.Node.Block) !void {
1593 const tracy = trace(@src());
1594 defer tracy.end();
1595
1596 if (block_node.label) |label| {
1597 return self.failTok(scope, label, "TODO implement labeled blocks", .{});
1598 }
1599 for (block_node.statements()) |statement| {
1600 _ = try self.astGenExpr(scope, statement);
1601 }
1602}
1603
1604fn astGenAsm(self: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
1605 if (asm_node.outputs.len != 0) {
1606 return self.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{});
1607 }
1608 const arena = scope.arena();
1609 const tree = scope.tree();
1610
1611 const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len);
1612 const args = try arena.alloc(*zir.Inst, asm_node.inputs.len);
1613
1614 for (asm_node.inputs) |input, i| {
1615 // TODO semantically analyze constraints
1616 inputs[i] = try self.astGenExpr(scope, input.constraint);
1617 args[i] = try self.astGenExpr(scope, input.expr);
1618 }
1619
1620 const src = tree.token_locs[asm_node.asm_token].start;
1621 const return_type = try self.addZIRInstConst(scope, src, .{
1622 .ty = Type.initTag(.type),
1623 .val = Value.initTag(.void_type),
1624 });
1625 const asm_inst = try self.addZIRInst(scope, src, zir.Inst.Asm, .{
1626 .asm_source = try self.astGenExpr(scope, asm_node.template),
1627 .return_type = return_type,
1628 }, .{
1629 .@"volatile" = asm_node.volatile_token != null,
1630 //.clobbers = TODO handle clobbers
1631 .inputs = inputs,
1632 .args = args,
1633 });
1634 return asm_inst;
1635}
1636
1637fn astGenBuiltinCall(self: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
1638 const tree = scope.tree();
1639 const builtin_name = tree.tokenSlice(call.builtin_token);
1640 const src = tree.token_locs[call.builtin_token].start;
1641
1642 inline for (std.meta.declarations(zir.Inst)) |inst| {
1643 if (inst.data != .Type) continue;
1644 const T = inst.data.Type;
1645 if (!@hasDecl(T, "builtin_name")) continue;
1646 if (std.mem.eql(u8, builtin_name, T.builtin_name)) {
1647 var value: T = undefined;
1648 const positionals = @typeInfo(std.meta.fieldInfo(T, "positionals").field_type).Struct;
1649 if (positionals.fields.len == 0) {
1650 return self.addZIRInst(scope, src, T, value.positionals, value.kw_args);
1651 }
1652 const arg_count: ?usize = if (positionals.fields[0].field_type == []*zir.Inst) null else positionals.fields.len;
1653 if (arg_count) |some| {
1654 if (call.params_len != some) {
1655 return self.failTok(
1656 scope,
1657 call.builtin_token,
1658 "expected {} parameter{}, found {}",
1659 .{ some, if (some == 1) "" else "s", call.params_len },
1660 );
1661 }
1662 const params = call.params();
1663 inline for (positionals.fields) |p, i| {
1664 @field(value.positionals, p.name) = try self.astGenExpr(scope, params[i]);
1665 }
1666 } else {
1667 return self.failTok(scope, call.builtin_token, "TODO var args builtin '{}'", .{builtin_name});
1668 }
1669
1670 return self.addZIRInst(scope, src, T, value.positionals, .{});
1671 }
1672 }
1673 return self.failTok(scope, call.builtin_token, "TODO implement builtin call for '{}'", .{builtin_name});
1674}
1675
1676fn astGenCall(self: *Module, scope: *Scope, call: *ast.Node.Call) InnerError!*zir.Inst {
1677 const tree = scope.tree();
1678 const lhs = try self.astGenExpr(scope, call.lhs);
1679
1680 const param_nodes = call.params();
1681 const args = try scope.cast(Scope.GenZIR).?.arena.alloc(*zir.Inst, param_nodes.len);
1682 for (param_nodes) |param_node, i| {
1683 args[i] = try self.astGenExpr(scope, param_node);
1684 }
1685
1686 const src = tree.token_locs[call.lhs.firstToken()].start;
1687 return self.addZIRInst(scope, src, zir.Inst.Call, .{
1688 .func = lhs,
1689 .args = args,
1690 }, .{});
1691}
1692
1693fn astGenUnreachable(self: *Module, scope: *Scope, unreach_node: *ast.Node.Unreachable) InnerError!*zir.Inst {
1694 const tree = scope.tree();
1695 const src = tree.token_locs[unreach_node.token].start;
1696 return self.addZIRInst(scope, src, zir.Inst.Unreachable, .{}, .{});
1697}
1698
1699fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
1700 const simple_types = std.ComptimeStringMap(Value.Tag, .{
1701 .{ "u8", .u8_type },
1702 .{ "i8", .i8_type },
1703 .{ "isize", .isize_type },
1704 .{ "usize", .usize_type },
1705 .{ "c_short", .c_short_type },
1706 .{ "c_ushort", .c_ushort_type },
1707 .{ "c_int", .c_int_type },
1708 .{ "c_uint", .c_uint_type },
1709 .{ "c_long", .c_long_type },
1710 .{ "c_ulong", .c_ulong_type },
1711 .{ "c_longlong", .c_longlong_type },
1712 .{ "c_ulonglong", .c_ulonglong_type },
1713 .{ "c_longdouble", .c_longdouble_type },
1714 .{ "f16", .f16_type },
1715 .{ "f32", .f32_type },
1716 .{ "f64", .f64_type },
1717 .{ "f128", .f128_type },
1718 .{ "c_void", .c_void_type },
1719 .{ "bool", .bool_type },
1720 .{ "void", .void_type },
1721 .{ "type", .type_type },
1722 .{ "anyerror", .anyerror_type },
1723 .{ "comptime_int", .comptime_int_type },
1724 .{ "comptime_float", .comptime_float_type },
1725 .{ "noreturn", .noreturn_type },
1726 });
1727 if (simple_types.get(name)) |tag| {
1728 return TypedValue{
1729 .ty = Type.initTag(.type),
1730 .val = Value.initTag(tag),
1731 };
1732 }
1733 if (mem.eql(u8, name, "null")) {
1734 return TypedValue{
1735 .ty = Type.initTag(.@"null"),
1736 .val = Value.initTag(.null_value),
1737 };
1738 }
1739 if (mem.eql(u8, name, "undefined")) {
1740 return TypedValue{
1741 .ty = Type.initTag(.@"undefined"),
1742 .val = Value.initTag(.undef),
1743 };
1744 }
1745 if (mem.eql(u8, name, "true")) {
1746 return TypedValue{
1747 .ty = Type.initTag(.bool),
1748 .val = Value.initTag(.bool_true),
1749 };
1750 }
1751 if (mem.eql(u8, name, "false")) {
1752 return TypedValue{
1753 .ty = Type.initTag(.bool),
1754 .val = Value.initTag(.bool_false),
1755 };
1756 }
1757 return null;
1758}
1759
17601302fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
17611303 try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1);
17621304 try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1);
......@@ -2368,7 +1910,7 @@ fn newZIRInst(
23681910 return inst;
23691911}
23701912
2371fn addZIRInstSpecial(
1913pub fn addZIRInstSpecial(
23721914 self: *Module,
23731915 scope: *Scope,
23741916 src: usize,
......@@ -2383,7 +1925,7 @@ fn addZIRInstSpecial(
23831925 return inst;
23841926}
23851927
2386fn addZIRInst(
1928pub fn addZIRInst(
23871929 self: *Module,
23881930 scope: *Scope,
23891931 src: usize,
......@@ -2396,13 +1938,13 @@ fn addZIRInst(
23961938}
23971939
23981940/// TODO The existence of this function is a workaround for a bug in stage1.
2399fn addZIRInstConst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
1941pub fn addZIRInstConst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
24001942 const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type;
24011943 return self.addZIRInst(scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
24021944}
24031945
24041946/// TODO The existence of this function is a workaround for a bug in stage1.
2405fn addZIRInstBlock(self: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Block {
1947pub fn addZIRInstBlock(self: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Block {
24061948 const P = std.meta.fieldInfo(zir.Inst.Block, "positionals").field_type;
24071949 return self.addZIRInstSpecial(scope, src, zir.Inst.Block, P{ .body = body }, .{});
24081950}
......@@ -2637,7 +2179,7 @@ fn getNextAnonNameIndex(self: *Module) usize {
26372179 return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic);
26382180}
26392181
2640fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
2182pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
26412183 const namespace = scope.namespace();
26422184 const name_hash = namespace.fullyQualifiedNameHash(ident_name);
26432185 return self.decl_table.get(name_hash);
......@@ -3646,13 +3188,13 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I
36463188 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
36473189}
36483190
3649fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
3191pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
36503192 @setCold(true);
36513193 const err_msg = try ErrorMsg.create(self.gpa, src, format, args);
36523194 return self.failWithOwnedErrorMsg(scope, src, err_msg);
36533195}
36543196
3655fn failTok(
3197pub fn failTok(
36563198 self: *Module,
36573199 scope: *Scope,
36583200 token_index: ast.TokenIndex,
......@@ -3664,7 +3206,7 @@ fn failTok(
36643206 return self.fail(scope, src, format, args);
36653207}
36663208
3667fn failNode(
3209pub fn failNode(
36683210 self: *Module,
36693211 scope: *Scope,
36703212 ast_node: *ast.Node,
src-self-hosted/astgen.zig created+476
......@@ -0,0 +1,476 @@
1const std = @import("std");
2const mem = std.mem;
3const Value = @import("value.zig").Value;
4const Type = @import("type.zig").Type;
5const TypedValue = @import("TypedValue.zig");
6const assert = std.debug.assert;
7const zir = @import("zir.zig");
8const Module = @import("Module.zig");
9const ast = std.zig.ast;
10const trace = @import("tracy.zig").trace;
11const Scope = Module.Scope;
12const InnerError = Module.InnerError;
13
14pub fn expr(mod: *Module, scope: *Scope, ast_node: *ast.Node) InnerError!*zir.Inst {
15 switch (ast_node.id) {
16 .Identifier => return identifier(mod, scope, @fieldParentPtr(ast.Node.Identifier, "base", ast_node)),
17 .Asm => return assembly(mod, scope, @fieldParentPtr(ast.Node.Asm, "base", ast_node)),
18 .StringLiteral => return stringLiteral(mod, scope, @fieldParentPtr(ast.Node.StringLiteral, "base", ast_node)),
19 .IntegerLiteral => return integerLiteral(mod, scope, @fieldParentPtr(ast.Node.IntegerLiteral, "base", ast_node)),
20 .BuiltinCall => return builtinCall(mod, scope, @fieldParentPtr(ast.Node.BuiltinCall, "base", ast_node)),
21 .Call => return callExpr(mod, scope, @fieldParentPtr(ast.Node.Call, "base", ast_node)),
22 .Unreachable => return unreach(mod, scope, @fieldParentPtr(ast.Node.Unreachable, "base", ast_node)),
23 .ControlFlowExpression => return controlFlowExpr(mod, scope, @fieldParentPtr(ast.Node.ControlFlowExpression, "base", ast_node)),
24 .If => return ifExpr(mod, scope, @fieldParentPtr(ast.Node.If, "base", ast_node)),
25 .InfixOp => return infixOp(mod, scope, @fieldParentPtr(ast.Node.InfixOp, "base", ast_node)),
26 .BoolNot => return boolNot(mod, scope, @fieldParentPtr(ast.Node.BoolNot, "base", ast_node)),
27 .VarDecl => return varDecl(mod, scope, @fieldParentPtr(ast.Node.VarDecl, "base", ast_node)),
28 else => return mod.failNode(scope, ast_node, "TODO implement astgen.Expr for {}", .{@tagName(ast_node.id)}),
29 }
30}
31
32pub fn blockExpr(mod: *Module, scope: *Scope, block_node: *ast.Node.Block) !void {
33 const tracy = trace(@src());
34 defer tracy.end();
35
36 if (block_node.label) |label| {
37 return mod.failTok(scope, label, "TODO implement labeled blocks", .{});
38 }
39 for (block_node.statements()) |statement| {
40 _ = try expr(mod, scope, statement);
41 }
42}
43
44fn varDecl(mod: *Module, scope: *Scope, node: *ast.Node.VarDecl) InnerError!*zir.Inst {
45 return mod.failNode(scope, &node.base, "TODO implement var decls", .{});
46}
47
48fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.BoolNot) InnerError!*zir.Inst {
49 const operand = try expr(mod, scope, node.rhs);
50 const tree = scope.tree();
51 const src = tree.token_locs[node.op_token].start;
52 return mod.addZIRInst(scope, src, zir.Inst.BoolNot, .{ .operand = operand }, .{});
53}
54
55fn infixOp(mod: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) InnerError!*zir.Inst {
56 switch (infix_node.op) {
57 .Assign => {
58 if (infix_node.lhs.id == .Identifier) {
59 const ident = @fieldParentPtr(ast.Node.Identifier, "base", infix_node.lhs);
60 const tree = scope.tree();
61 const ident_name = tree.tokenSlice(ident.token);
62 if (std.mem.eql(u8, ident_name, "_")) {
63 return expr(mod, scope, infix_node.rhs);
64 } else {
65 return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
66 }
67 } else {
68 return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
69 }
70 },
71 .Add => {
72 const lhs = try expr(mod, scope, infix_node.lhs);
73 const rhs = try expr(mod, scope, infix_node.rhs);
74
75 const tree = scope.tree();
76 const src = tree.token_locs[infix_node.op_token].start;
77
78 return mod.addZIRInst(scope, src, zir.Inst.Add, .{ .lhs = lhs, .rhs = rhs }, .{});
79 },
80 .BangEqual,
81 .EqualEqual,
82 .GreaterThan,
83 .GreaterOrEqual,
84 .LessThan,
85 .LessOrEqual,
86 => {
87 const lhs = try expr(mod, scope, infix_node.lhs);
88 const rhs = try expr(mod, scope, infix_node.rhs);
89
90 const tree = scope.tree();
91 const src = tree.token_locs[infix_node.op_token].start;
92
93 const op: std.math.CompareOperator = switch (infix_node.op) {
94 .BangEqual => .neq,
95 .EqualEqual => .eq,
96 .GreaterThan => .gt,
97 .GreaterOrEqual => .gte,
98 .LessThan => .lt,
99 .LessOrEqual => .lte,
100 else => unreachable,
101 };
102
103 return mod.addZIRInst(scope, src, zir.Inst.Cmp, .{
104 .lhs = lhs,
105 .op = op,
106 .rhs = rhs,
107 }, .{});
108 },
109 else => |op| {
110 return mod.failNode(scope, &infix_node.base, "TODO implement infix operator {}", .{op});
111 },
112 }
113}
114
115fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.Inst {
116 if (if_node.payload) |payload| {
117 return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for optionals", .{});
118 }
119 if (if_node.@"else") |else_node| {
120 if (else_node.payload) |payload| {
121 return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for error unions", .{});
122 }
123 }
124 var block_scope: Scope.GenZIR = .{
125 .decl = scope.decl().?,
126 .arena = scope.arena(),
127 .instructions = .{},
128 };
129 defer block_scope.instructions.deinit(mod.gpa);
130
131 const cond = try expr(mod, &block_scope.base, if_node.condition);
132
133 const tree = scope.tree();
134 const if_src = tree.token_locs[if_node.if_token].start;
135 const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{
136 .condition = cond,
137 .true_body = undefined, // populated below
138 .false_body = undefined, // populated below
139 }, .{});
140
141 const block = try mod.addZIRInstBlock(scope, if_src, .{
142 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
143 });
144 var then_scope: Scope.GenZIR = .{
145 .decl = block_scope.decl,
146 .arena = block_scope.arena,
147 .instructions = .{},
148 };
149 defer then_scope.instructions.deinit(mod.gpa);
150
151 const then_result = try expr(mod, &then_scope.base, if_node.body);
152 if (!then_result.tag.isNoReturn()) {
153 const then_src = tree.token_locs[if_node.body.lastToken()].start;
154 _ = try mod.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{
155 .block = block,
156 .operand = then_result,
157 }, .{});
158 }
159 condbr.positionals.true_body = .{
160 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
161 };
162
163 var else_scope: Scope.GenZIR = .{
164 .decl = block_scope.decl,
165 .arena = block_scope.arena,
166 .instructions = .{},
167 };
168 defer else_scope.instructions.deinit(mod.gpa);
169
170 if (if_node.@"else") |else_node| {
171 const else_result = try expr(mod, &else_scope.base, else_node.body);
172 if (!else_result.tag.isNoReturn()) {
173 const else_src = tree.token_locs[else_node.body.lastToken()].start;
174 _ = try mod.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{
175 .block = block,
176 .operand = else_result,
177 }, .{});
178 }
179 } else {
180 // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
181 // by directly allocating the body for this one instruction.
182 const else_src = tree.token_locs[if_node.lastToken()].start;
183 _ = try mod.addZIRInst(&else_scope.base, else_src, zir.Inst.BreakVoid, .{
184 .block = block,
185 }, .{});
186 }
187 condbr.positionals.false_body = .{
188 .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
189 };
190
191 return &block.base;
192}
193
194fn controlFlowExpr(
195 mod: *Module,
196 scope: *Scope,
197 cfe: *ast.Node.ControlFlowExpression,
198) InnerError!*zir.Inst {
199 switch (cfe.kind) {
200 .Break => return mod.failNode(scope, &cfe.base, "TODO implement astgen.Expr for Break", .{}),
201 .Continue => return mod.failNode(scope, &cfe.base, "TODO implement astgen.Expr for Continue", .{}),
202 .Return => {},
203 }
204 const tree = scope.tree();
205 const src = tree.token_locs[cfe.ltoken].start;
206 if (cfe.rhs) |rhs_node| {
207 const operand = try expr(mod, scope, rhs_node);
208 return mod.addZIRInst(scope, src, zir.Inst.Return, .{ .operand = operand }, .{});
209 } else {
210 return mod.addZIRInst(scope, src, zir.Inst.ReturnVoid, .{}, .{});
211 }
212}
213
214fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerError!*zir.Inst {
215 const tree = scope.tree();
216 const ident_name = tree.tokenSlice(ident.token);
217 const src = tree.token_locs[ident.token].start;
218 if (mem.eql(u8, ident_name, "_")) {
219 return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});
220 }
221
222 if (getSimplePrimitiveValue(ident_name)) |typed_value| {
223 return mod.addZIRInstConst(scope, src, typed_value);
224 }
225
226 if (ident_name.len >= 2) integer: {
227 const first_c = ident_name[0];
228 if (first_c == 'i' or first_c == 'u') {
229 const is_signed = first_c == 'i';
230 const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
231 error.Overflow => return mod.failNode(
232 scope,
233 &ident.base,
234 "primitive integer type '{}' exceeds maximum bit width of 65535",
235 .{ident_name},
236 ),
237 error.InvalidCharacter => break :integer,
238 };
239 const val = switch (bit_count) {
240 8 => if (is_signed) Value.initTag(.i8_type) else Value.initTag(.u8_type),
241 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type),
242 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type),
243 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type),
244 else => return mod.failNode(scope, &ident.base, "TODO implement arbitrary integer bitwidth types", .{}),
245 };
246 return mod.addZIRInstConst(scope, src, .{
247 .ty = Type.initTag(.type),
248 .val = val,
249 });
250 }
251 }
252
253 if (mod.lookupDeclName(scope, ident_name)) |decl| {
254 return try mod.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
255 }
256
257 // Function parameter
258 if (scope.decl()) |decl| {
259 if (tree.root_node.decls()[decl.src_index].cast(ast.Node.FnProto)) |fn_proto| {
260 for (fn_proto.params()) |param, i| {
261 const param_name = tree.tokenSlice(param.name_token.?);
262 if (mem.eql(u8, param_name, ident_name)) {
263 return try mod.addZIRInst(scope, src, zir.Inst.Arg, .{ .index = i }, .{});
264 }
265 }
266 }
267 }
268
269 return mod.failNode(scope, &ident.base, "TODO implement local variable identifier lookup", .{});
270}
271
272fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.StringLiteral) InnerError!*zir.Inst {
273 const tree = scope.tree();
274 const unparsed_bytes = tree.tokenSlice(str_lit.token);
275 const arena = scope.arena();
276
277 var bad_index: usize = undefined;
278 const bytes = std.zig.parseStringLiteral(arena, unparsed_bytes, &bad_index) catch |err| switch (err) {
279 error.InvalidCharacter => {
280 const bad_byte = unparsed_bytes[bad_index];
281 const src = tree.token_locs[str_lit.token].start;
282 return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
283 },
284 else => |e| return e,
285 };
286
287 const src = tree.token_locs[str_lit.token].start;
288 return mod.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
289}
290
291fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral) InnerError!*zir.Inst {
292 const arena = scope.arena();
293 const tree = scope.tree();
294 const prefixed_bytes = tree.tokenSlice(int_lit.token);
295 const base = if (mem.startsWith(u8, prefixed_bytes, "0x"))
296 16
297 else if (mem.startsWith(u8, prefixed_bytes, "0o"))
298 8
299 else if (mem.startsWith(u8, prefixed_bytes, "0b"))
300 2
301 else
302 @as(u8, 10);
303
304 const bytes = if (base == 10)
305 prefixed_bytes
306 else
307 prefixed_bytes[2..];
308
309 if (std.fmt.parseInt(u64, bytes, base)) |small_int| {
310 const int_payload = try arena.create(Value.Payload.Int_u64);
311 int_payload.* = .{ .int = small_int };
312 const src = tree.token_locs[int_lit.token].start;
313 return mod.addZIRInstConst(scope, src, .{
314 .ty = Type.initTag(.comptime_int),
315 .val = Value.initPayload(&int_payload.base),
316 });
317 } else |err| {
318 return mod.failTok(scope, int_lit.token, "TODO implement int literals that don't fit in a u64", .{});
319 }
320}
321
322fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
323 if (asm_node.outputs.len != 0) {
324 return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{});
325 }
326 const arena = scope.arena();
327 const tree = scope.tree();
328
329 const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len);
330 const args = try arena.alloc(*zir.Inst, asm_node.inputs.len);
331
332 for (asm_node.inputs) |input, i| {
333 // TODO semantically analyze constraints
334 inputs[i] = try expr(mod, scope, input.constraint);
335 args[i] = try expr(mod, scope, input.expr);
336 }
337
338 const src = tree.token_locs[asm_node.asm_token].start;
339 const return_type = try mod.addZIRInstConst(scope, src, .{
340 .ty = Type.initTag(.type),
341 .val = Value.initTag(.void_type),
342 });
343 const asm_inst = try mod.addZIRInst(scope, src, zir.Inst.Asm, .{
344 .asm_source = try expr(mod, scope, asm_node.template),
345 .return_type = return_type,
346 }, .{
347 .@"volatile" = asm_node.volatile_token != null,
348 //.clobbers = TODO handle clobbers
349 .inputs = inputs,
350 .args = args,
351 });
352 return asm_inst;
353}
354
355fn builtinCall(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
356 const tree = scope.tree();
357 const builtin_name = tree.tokenSlice(call.builtin_token);
358 const src = tree.token_locs[call.builtin_token].start;
359
360 inline for (std.meta.declarations(zir.Inst)) |inst| {
361 if (inst.data != .Type) continue;
362 const T = inst.data.Type;
363 if (!@hasDecl(T, "builtin_name")) continue;
364 if (std.mem.eql(u8, builtin_name, T.builtin_name)) {
365 var value: T = undefined;
366 const positionals = @typeInfo(std.meta.fieldInfo(T, "positionals").field_type).Struct;
367 if (positionals.fields.len == 0) {
368 return mod.addZIRInst(scope, src, T, value.positionals, value.kw_args);
369 }
370 const arg_count: ?usize = if (positionals.fields[0].field_type == []*zir.Inst) null else positionals.fields.len;
371 if (arg_count) |some| {
372 if (call.params_len != some) {
373 return mod.failTok(
374 scope,
375 call.builtin_token,
376 "expected {} parameter{}, found {}",
377 .{ some, if (some == 1) "" else "s", call.params_len },
378 );
379 }
380 const params = call.params();
381 inline for (positionals.fields) |p, i| {
382 @field(value.positionals, p.name) = try expr(mod, scope, params[i]);
383 }
384 } else {
385 return mod.failTok(scope, call.builtin_token, "TODO var args builtin '{}'", .{builtin_name});
386 }
387
388 return mod.addZIRInst(scope, src, T, value.positionals, .{});
389 }
390 }
391 return mod.failTok(scope, call.builtin_token, "TODO implement builtin call for '{}'", .{builtin_name});
392}
393
394fn callExpr(mod: *Module, scope: *Scope, node: *ast.Node.Call) InnerError!*zir.Inst {
395 const tree = scope.tree();
396 const lhs = try expr(mod, scope, node.lhs);
397
398 const param_nodes = node.params();
399 const args = try scope.cast(Scope.GenZIR).?.arena.alloc(*zir.Inst, param_nodes.len);
400 for (param_nodes) |param_node, i| {
401 args[i] = try expr(mod, scope, param_node);
402 }
403
404 const src = tree.token_locs[node.lhs.firstToken()].start;
405 return mod.addZIRInst(scope, src, zir.Inst.Call, .{
406 .func = lhs,
407 .args = args,
408 }, .{});
409}
410
411fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.Unreachable) InnerError!*zir.Inst {
412 const tree = scope.tree();
413 const src = tree.token_locs[unreach_node.token].start;
414 return mod.addZIRInst(scope, src, zir.Inst.Unreachable, .{}, .{});
415}
416
417fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
418 const simple_types = std.ComptimeStringMap(Value.Tag, .{
419 .{ "u8", .u8_type },
420 .{ "i8", .i8_type },
421 .{ "isize", .isize_type },
422 .{ "usize", .usize_type },
423 .{ "c_short", .c_short_type },
424 .{ "c_ushort", .c_ushort_type },
425 .{ "c_int", .c_int_type },
426 .{ "c_uint", .c_uint_type },
427 .{ "c_long", .c_long_type },
428 .{ "c_ulong", .c_ulong_type },
429 .{ "c_longlong", .c_longlong_type },
430 .{ "c_ulonglong", .c_ulonglong_type },
431 .{ "c_longdouble", .c_longdouble_type },
432 .{ "f16", .f16_type },
433 .{ "f32", .f32_type },
434 .{ "f64", .f64_type },
435 .{ "f128", .f128_type },
436 .{ "c_void", .c_void_type },
437 .{ "bool", .bool_type },
438 .{ "void", .void_type },
439 .{ "type", .type_type },
440 .{ "anyerror", .anyerror_type },
441 .{ "comptime_int", .comptime_int_type },
442 .{ "comptime_float", .comptime_float_type },
443 .{ "noreturn", .noreturn_type },
444 });
445 if (simple_types.get(name)) |tag| {
446 return TypedValue{
447 .ty = Type.initTag(.type),
448 .val = Value.initTag(tag),
449 };
450 }
451 if (mem.eql(u8, name, "null")) {
452 return TypedValue{
453 .ty = Type.initTag(.@"null"),
454 .val = Value.initTag(.null_value),
455 };
456 }
457 if (mem.eql(u8, name, "undefined")) {
458 return TypedValue{
459 .ty = Type.initTag(.@"undefined"),
460 .val = Value.initTag(.undef),
461 };
462 }
463 if (mem.eql(u8, name, "true")) {
464 return TypedValue{
465 .ty = Type.initTag(.bool),
466 .val = Value.initTag(.bool_true),
467 };
468 }
469 if (mem.eql(u8, name, "false")) {
470 return TypedValue{
471 .ty = Type.initTag(.bool),
472 .val = Value.initTag(.bool_false),
473 };
474 }
475 return null;
476}