authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-26 19:53:49-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-26 19:53:49-07:00
log5fed721290cc7ecf396b7815f830e1fabe8b4711
tree851e5f39cdfd916c395e405d29ddad056c29d5df
parent7d0bb0774e957e81fee7240d410944a0a56c303d
parentb0995cb9f9540a6d463dcc2722cb3b8918a61e8e

Merge branch 'Vexu-stage2'

closes #6175

9 files changed, 500 insertions(+), 45 deletions(-)

src-self-hosted/Module.zig+12-1
......@@ -2570,7 +2570,18 @@ pub fn analyzeIsNull(
25702570 operand: *Inst,
25712571 invert_logic: bool,
25722572) InnerError!*Inst {
2573 return self.fail(scope, src, "TODO implement analysis of isnull and isnotnull", .{});
2573 if (operand.value()) |opt_val| {
2574 const is_null = opt_val.isNull();
2575 const bool_value = if (invert_logic) !is_null else is_null;
2576 return self.constBool(scope, src, bool_value);
2577 }
2578 const b = try self.requireRuntimeBlock(scope, src);
2579 const inst_tag: Inst.Tag = if (invert_logic) .isnonnull else .isnull;
2580 return self.addUnOp(b, src, Type.initTag(.bool), inst_tag, operand);
2581}
2582
2583pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst {
2584 return self.fail(scope, src, "TODO implement analysis of iserr", .{});
25742585}
25752586
25762587/// Asserts that lhs and rhs types are both numeric.
src-self-hosted/astgen.zig+284-8
......@@ -273,22 +273,22 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
273273 .AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)),
274274 .ErrorSetDecl => return errorSetDecl(mod, scope, rl, node.castTag(.ErrorSetDecl).?),
275275 .ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)),
276 .For => return forExpr(mod, scope, rl, node.castTag(.For).?),
277 .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),
278 .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),
276279
277280 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
278 .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}),
279281 .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),
280282 .OrElse => return mod.failNode(scope, node, "TODO implement astgen.expr for .OrElse", .{}),
281283 .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),
282284 .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
283285 .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
284286 .Slice => return mod.failNode(scope, node, "TODO implement astgen.expr for .Slice", .{}),
285 .ArrayAccess => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayAccess", .{}),
286287 .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}),
287288 .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),
288289 .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),
289290 .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}),
290291 .Switch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Switch", .{}),
291 .For => return mod.failNode(scope, node, "TODO implement astgen.expr for .For", .{}),
292292 .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),
293293 .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}),
294294 .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
......@@ -497,7 +497,7 @@ fn varDecl(
497497 const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTrailer("type_node")) |type_node| a: {
498498 const type_inst = try typeExpr(mod, scope, type_node);
499499 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
500 break :a .{ .alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst), .result_loc = .{ .ptr = alloc } };
500 break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
501501 } else a: {
502502 const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred);
503503 break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? } };
......@@ -624,7 +624,7 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo,
624624 .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type,
625625 .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type,
626626 .C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type,
627 .Slice => if (mutable) T.mut_slice_type else T.mut_slice_type,
627 .Slice => if (mutable) T.mut_slice_type else T.const_slice_type,
628628 }, child_type);
629629 }
630630
......@@ -750,6 +750,93 @@ fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*
750750 });
751751}
752752
753fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch) InnerError!*zir.Inst {
754 const tree = scope.tree();
755 const src = tree.token_locs[node.op_token].start;
756
757 const err_union_ptr = try expr(mod, scope, .ref, node.lhs);
758 // TODO we could avoid an unnecessary copy if .iserr took a pointer
759 const err_union = try addZIRUnOp(mod, scope, src, .deref, err_union_ptr);
760 const cond = try addZIRUnOp(mod, scope, src, .iserr, err_union);
761
762 var block_scope: Scope.GenZIR = .{
763 .parent = scope,
764 .decl = scope.decl().?,
765 .arena = scope.arena(),
766 .instructions = .{},
767 };
768 defer block_scope.instructions.deinit(mod.gpa);
769
770 const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
771 .condition = cond,
772 .then_body = undefined, // populated below
773 .else_body = undefined, // populated below
774 }, .{});
775
776 const block = try addZIRInstBlock(mod, scope, src, .{
777 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
778 });
779
780 // Most result location types can be forwarded directly; however
781 // if we need to write to a pointer which has an inferred type,
782 // proper type inference requires peer type resolution on the if's
783 // branches.
784 const branch_rl: ResultLoc = switch (rl) {
785 .discard, .none, .ty, .ptr, .ref => rl,
786 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
787 };
788
789 var err_scope: Scope.GenZIR = .{
790 .parent = scope,
791 .decl = block_scope.decl,
792 .arena = block_scope.arena,
793 .instructions = .{},
794 };
795 defer err_scope.instructions.deinit(mod.gpa);
796
797 var err_val_scope: Scope.LocalVal = undefined;
798 const err_sub_scope = blk: {
799 const payload = node.payload orelse
800 break :blk &err_scope.base;
801
802 const err_name = tree.tokenSlice(payload.castTag(.Payload).?.error_symbol.firstToken());
803 if (mem.eql(u8, err_name, "_"))
804 break :blk &err_scope.base;
805
806 const unwrapped_err_ptr = try addZIRUnOp(mod, &err_scope.base, src, .unwrap_err_code, err_union_ptr);
807 err_val_scope = .{
808 .parent = &err_scope.base,
809 .gen_zir = &err_scope,
810 .name = err_name,
811 .inst = try addZIRUnOp(mod, &err_scope.base, src, .deref, unwrapped_err_ptr),
812 };
813 break :blk &err_val_scope.base;
814 };
815
816 _ = try addZIRInst(mod, &err_scope.base, src, zir.Inst.Break, .{
817 .block = block,
818 .operand = try expr(mod, err_sub_scope, branch_rl, node.rhs),
819 }, .{});
820
821 var not_err_scope: Scope.GenZIR = .{
822 .parent = scope,
823 .decl = block_scope.decl,
824 .arena = block_scope.arena,
825 .instructions = .{},
826 };
827 defer not_err_scope.instructions.deinit(mod.gpa);
828
829 const unwrapped_payload = try addZIRUnOp(mod, &not_err_scope.base, src, .unwrap_err_unsafe, err_union_ptr);
830 _ = try addZIRInst(mod, &not_err_scope.base, src, zir.Inst.Break, .{
831 .block = block,
832 .operand = unwrapped_payload,
833 }, .{});
834
835 condbr.positionals.then_body = .{ .instructions = try err_scope.arena.dupe(*zir.Inst, err_scope.instructions.items) };
836 condbr.positionals.else_body = .{ .instructions = try not_err_scope.arena.dupe(*zir.Inst, not_err_scope.instructions.items) };
837 return rlWrap(mod, scope, rl, &block.base);
838}
839
753840/// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating.
754841/// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used.
755842fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
......@@ -794,9 +881,17 @@ fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfix
794881 const lhs = try expr(mod, scope, .ref, node.lhs);
795882 const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);
796883
797 const pointer = try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{});
798 if (rl == .ref) return pointer;
799 return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, pointer));
884 return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{}));
885}
886
887fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ArrayAccess) InnerError!*zir.Inst {
888 const tree = scope.tree();
889 const src = tree.token_locs[node.rtoken].start;
890
891 const array_ptr = try expr(mod, scope, .ref, node.lhs);
892 const index = try expr(mod, scope, .none, node.index_expr);
893
894 return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ElemPtr, .{ .array_ptr = array_ptr, .index = index }, .{}));
800895}
801896
802897fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
......@@ -1080,6 +1175,12 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
10801175 }
10811176 }
10821177
1178 if (while_node.label) |tok|
1179 return mod.failTok(scope, tok, "TODO labeled while", .{});
1180
1181 if (while_node.inline_token) |tok|
1182 return mod.failTok(scope, tok, "TODO inline while", .{});
1183
10831184 var expr_scope: Scope.GenZIR = .{
10841185 .parent = scope,
10851186 .decl = scope.decl().?,
......@@ -1198,6 +1299,181 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
11981299 return &while_block.base;
11991300}
12001301
1302fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) InnerError!*zir.Inst {
1303 if (for_node.label) |tok|
1304 return mod.failTok(scope, tok, "TODO labeled for", .{});
1305
1306 if (for_node.inline_token) |tok|
1307 return mod.failTok(scope, tok, "TODO inline for", .{});
1308
1309 var for_scope: Scope.GenZIR = .{
1310 .parent = scope,
1311 .decl = scope.decl().?,
1312 .arena = scope.arena(),
1313 .instructions = .{},
1314 };
1315 defer for_scope.instructions.deinit(mod.gpa);
1316
1317 // setup variables and constants
1318 const tree = scope.tree();
1319 const for_src = tree.token_locs[for_node.for_token].start;
1320 const index_ptr = blk: {
1321 const usize_type = try addZIRInstConst(mod, &for_scope.base, for_src, .{
1322 .ty = Type.initTag(.type),
1323 .val = Value.initTag(.usize_type),
1324 });
1325 const index_ptr = try addZIRUnOp(mod, &for_scope.base, for_src, .alloc, usize_type);
1326 // initialize to zero
1327 const zero = try addZIRInstConst(mod, &for_scope.base, for_src, .{
1328 .ty = Type.initTag(.usize),
1329 .val = Value.initTag(.zero),
1330 });
1331 _ = try addZIRBinOp(mod, &for_scope.base, for_src, .store, index_ptr, zero);
1332 break :blk index_ptr;
1333 };
1334 const array_ptr = try expr(mod, &for_scope.base, .ref, for_node.array_expr);
1335 _ = try addZIRUnOp(mod, &for_scope.base, for_node.array_expr.firstToken(), .ensure_indexable, array_ptr);
1336 const cond_src = tree.token_locs[for_node.array_expr.firstToken()].start;
1337 const len_ptr = try addZIRInst(mod, &for_scope.base, cond_src, zir.Inst.FieldPtr, .{
1338 .object_ptr = array_ptr,
1339 .field_name = try addZIRInst(mod, &for_scope.base, cond_src, zir.Inst.Str, .{ .bytes = "len" }, .{}),
1340 }, .{});
1341
1342 var loop_scope: Scope.GenZIR = .{
1343 .parent = &for_scope.base,
1344 .decl = for_scope.decl,
1345 .arena = for_scope.arena,
1346 .instructions = .{},
1347 };
1348 defer loop_scope.instructions.deinit(mod.gpa);
1349
1350 var cond_scope: Scope.GenZIR = .{
1351 .parent = &loop_scope.base,
1352 .decl = loop_scope.decl,
1353 .arena = loop_scope.arena,
1354 .instructions = .{},
1355 };
1356 defer cond_scope.instructions.deinit(mod.gpa);
1357
1358 // check condition i < array_expr.len
1359 const index = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, index_ptr);
1360 const len = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, len_ptr);
1361 const cond = try addZIRBinOp(mod, &cond_scope.base, cond_src, .cmp_lt, index, len);
1362
1363 const condbr = try addZIRInstSpecial(mod, &cond_scope.base, for_src, zir.Inst.CondBr, .{
1364 .condition = cond,
1365 .then_body = undefined, // populated below
1366 .else_body = undefined, // populated below
1367 }, .{});
1368 const cond_block = try addZIRInstBlock(mod, &loop_scope.base, for_src, .{
1369 .instructions = try loop_scope.arena.dupe(*zir.Inst, cond_scope.instructions.items),
1370 });
1371
1372 // increment index variable
1373 const one = try addZIRInstConst(mod, &loop_scope.base, for_src, .{
1374 .ty = Type.initTag(.usize),
1375 .val = Value.initTag(.one),
1376 });
1377 const index_2 = try addZIRUnOp(mod, &loop_scope.base, cond_src, .deref, index_ptr);
1378 const index_plus_one = try addZIRBinOp(mod, &loop_scope.base, for_src, .add, index_2, one);
1379 _ = try addZIRBinOp(mod, &loop_scope.base, for_src, .store, index_ptr, index_plus_one);
1380
1381 // looping stuff
1382 const loop = try addZIRInstLoop(mod, &for_scope.base, for_src, .{
1383 .instructions = try for_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
1384 });
1385 const for_block = try addZIRInstBlock(mod, scope, for_src, .{
1386 .instructions = try for_scope.arena.dupe(*zir.Inst, for_scope.instructions.items),
1387 });
1388
1389 // while body
1390 const then_src = tree.token_locs[for_node.body.lastToken()].start;
1391 var then_scope: Scope.GenZIR = .{
1392 .parent = &cond_scope.base,
1393 .decl = cond_scope.decl,
1394 .arena = cond_scope.arena,
1395 .instructions = .{},
1396 };
1397 defer then_scope.instructions.deinit(mod.gpa);
1398
1399 // Most result location types can be forwarded directly; however
1400 // if we need to write to a pointer which has an inferred type,
1401 // proper type inference requires peer type resolution on the while's
1402 // branches.
1403 const branch_rl: ResultLoc = switch (rl) {
1404 .discard, .none, .ty, .ptr, .ref => rl,
1405 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = for_block },
1406 };
1407
1408 var index_scope: Scope.LocalPtr = undefined;
1409 const then_sub_scope = blk: {
1410 const payload = for_node.payload.castTag(.PointerIndexPayload).?;
1411 const is_ptr = payload.ptr_token != null;
1412 const value_name = tree.tokenSlice(payload.value_symbol.firstToken());
1413 if (!mem.eql(u8, value_name, "_")) {
1414 return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement for value payload", .{});
1415 } else if (is_ptr) {
1416 return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
1417 }
1418
1419 const index_symbol_node = payload.index_symbol orelse
1420 break :blk &then_scope.base;
1421
1422 const index_name = tree.tokenSlice(index_symbol_node.firstToken());
1423 if (mem.eql(u8, index_name, "_")) {
1424 break :blk &then_scope.base;
1425 }
1426 // TODO make this const without an extra copy?
1427 index_scope = .{
1428 .parent = &then_scope.base,
1429 .gen_zir = &then_scope,
1430 .name = index_name,
1431 .ptr = index_ptr,
1432 };
1433 break :blk &index_scope.base;
1434 };
1435
1436 const then_result = try expr(mod, then_sub_scope, branch_rl, for_node.body);
1437 if (!then_result.tag.isNoReturn()) {
1438 _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
1439 .block = cond_block,
1440 .operand = then_result,
1441 }, .{});
1442 }
1443 condbr.positionals.then_body = .{
1444 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
1445 };
1446
1447 // else branch
1448 var else_scope: Scope.GenZIR = .{
1449 .parent = &cond_scope.base,
1450 .decl = cond_scope.decl,
1451 .arena = cond_scope.arena,
1452 .instructions = .{},
1453 };
1454 defer else_scope.instructions.deinit(mod.gpa);
1455
1456 if (for_node.@"else") |else_node| {
1457 const else_src = tree.token_locs[else_node.body.lastToken()].start;
1458 const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body);
1459 if (!else_result.tag.isNoReturn()) {
1460 _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.Break, .{
1461 .block = for_block,
1462 .operand = else_result,
1463 }, .{});
1464 }
1465 } else {
1466 const else_src = tree.token_locs[for_node.lastToken()].start;
1467 _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
1468 .block = for_block,
1469 }, .{});
1470 }
1471 condbr.positionals.else_body = .{
1472 .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
1473 };
1474 return &for_block.base;
1475}
1476
12011477fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
12021478 const tree = scope.tree();
12031479 const src = tree.token_locs[cfe.ltoken].start;
src-self-hosted/codegen.zig+28-4
......@@ -829,6 +829,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
829829 // No side effects, so if it's unreferenced, do nothing.
830830 if (inst.base.isUnused())
831831 return MCValue.dead;
832
833 const operand = try self.resolveInst(inst.operand);
834 const info_a = inst.operand.ty.intInfo(self.target.*);
835 const info_b = inst.base.ty.intInfo(self.target.*);
836 if (info_a.signed != info_b.signed)
837 return self.fail(inst.base.src, "TODO gen intcast sign safety in semantic analysis", .{});
838
839 if (info_a.bits == info_b.bits)
840 return operand;
841
832842 switch (arch) {
833843 else => return self.fail(inst.base.src, "TODO implement intCast for {}", .{self.target.cpu.arch}),
834844 }
......@@ -2039,15 +2049,29 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
20392049 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
20402050 },
20412051 8 => {
2042 return self.fail(src, "TODO implement set abi_size=8 stack variable with immediate", .{});
2052 // We have a positive stack offset value but we want a twos complement negative
2053 // offset from rbp, which is at the top of the stack frame.
2054 const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
2055 const twos_comp = @bitCast(u8, negative_offset);
2056
2057 // 64 bit write to memory would take two mov's anyways so we
2058 // insted just use two 32 bit writes to avoid register allocation
2059 try self.code.ensureCapacity(self.code.items.len + 14);
2060 var buf: [8]u8 = undefined;
2061 mem.writeIntLittle(u64, &buf, x_big);
2062
2063 // mov DWORD PTR [rbp+offset+4], immediate
2064 self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp + 4});
2065 self.code.appendSliceAssumeCapacity(buf[4..8]);
2066
2067 // mov DWORD PTR [rbp+offset], immediate
2068 self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp });
2069 self.code.appendSliceAssumeCapacity(buf[0..4]);
20432070 },
20442071 else => {
20452072 return self.fail(src, "TODO implement set abi_size=large stack variable with immediate", .{});
20462073 },
20472074 }
2048 if (x_big <= math.maxInt(u32)) {} else {
2049 return self.fail(src, "TODO implement set stack variable with large immediate", .{});
2050 }
20512075 },
20522076 .embedded_in_code => |code_offset| {
20532077 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});
src-self-hosted/type.zig+7
......@@ -2675,6 +2675,13 @@ pub const Type = extern union {
26752675 };
26762676 }
26772677
2678 pub fn isIndexable(self: Type) bool {
2679 const zig_tag = self.zigTypeTag();
2680 // TODO tuples are indexable
2681 return zig_tag == .Array or zig_tag == .Vector or self.isSlice() or
2682 (self.isSinglePointer() and self.elemType().zigTypeTag() == .Array);
2683 }
2684
26782685 /// This enum does not directly correspond to `std.builtin.TypeId` because
26792686 /// it has extra enum tags in it, as a way of using less memory. For example,
26802687 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
src-self-hosted/value.zig+29-7
......@@ -65,6 +65,7 @@ pub const Value = extern union {
6565
6666 undef,
6767 zero,
68 one,
6869 void_value,
6970 unreachable_value,
7071 empty_array,
......@@ -174,6 +175,7 @@ pub const Value = extern union {
174175 .anyframe_type,
175176 .undef,
176177 .zero,
178 .one,
177179 .void_value,
178180 .unreachable_value,
179181 .empty_array,
......@@ -313,6 +315,7 @@ pub const Value = extern union {
313315 .null_value => return out_stream.writeAll("null"),
314316 .undef => return out_stream.writeAll("undefined"),
315317 .zero => return out_stream.writeAll("0"),
318 .one => return out_stream.writeAll("1"),
316319 .void_value => return out_stream.writeAll("{}"),
317320 .unreachable_value => return out_stream.writeAll("unreachable"),
318321 .bool_true => return out_stream.writeAll("true"),
......@@ -447,6 +450,7 @@ pub const Value = extern union {
447450
448451 .undef,
449452 .zero,
453 .one,
450454 .void_value,
451455 .unreachable_value,
452456 .empty_array,
......@@ -546,7 +550,9 @@ pub const Value = extern union {
546550 .bool_false,
547551 => return BigIntMutable.init(&space.limbs, 0).toConst(),
548552
549 .bool_true => return BigIntMutable.init(&space.limbs, 1).toConst(),
553 .one,
554 .bool_true,
555 => return BigIntMutable.init(&space.limbs, 1).toConst(),
550556
551557 .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
552558 .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
......@@ -627,7 +633,9 @@ pub const Value = extern union {
627633 .bool_false,
628634 => return 0,
629635
630 .bool_true => return 1,
636 .one,
637 .bool_true,
638 => return 1,
631639
632640 .int_u64 => return self.cast(Payload.Int_u64).?.int,
633641 .int_i64 => return @intCast(u64, self.cast(Payload.Int_i64).?.int),
......@@ -708,7 +716,9 @@ pub const Value = extern union {
708716 .bool_false,
709717 => return 0,
710718
711 .bool_true => return 1,
719 .one,
720 .bool_true,
721 => return 1,
712722
713723 .int_u64 => return @intCast(i64, self.cast(Payload.Int_u64).?.int),
714724 .int_i64 => return self.cast(Payload.Int_i64).?.int,
......@@ -734,6 +744,7 @@ pub const Value = extern union {
734744 .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val),
735745
736746 .zero => 0,
747 .one => 1,
737748 .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int),
738749 .int_i64 => @intToFloat(T, self.cast(Payload.Int_i64).?.int),
739750
......@@ -814,7 +825,9 @@ pub const Value = extern union {
814825 .bool_false,
815826 => return 0,
816827
817 .bool_true => return 1,
828 .one,
829 .bool_true,
830 => return 1,
818831
819832 .int_u64 => {
820833 const x = self.cast(Payload.Int_u64).?.int;
......@@ -900,7 +913,9 @@ pub const Value = extern union {
900913 .bool_false,
901914 => return true,
902915
903 .bool_true => {
916 .one,
917 .bool_true,
918 => {
904919 const info = ty.intInfo(target);
905920 if (info.signed) {
906921 return info.bits >= 2;
......@@ -1064,7 +1079,9 @@ pub const Value = extern union {
10641079 .@"error",
10651080 => unreachable,
10661081
1067 .zero => false,
1082 .zero,
1083 .one,
1084 => false,
10681085
10691086 .float_16 => @rem(self.cast(Payload.Float_16).?.val, 1) != 0,
10701087 .float_32 => @rem(self.cast(Payload.Float_32).?.val, 1) != 0,
......@@ -1140,7 +1157,9 @@ pub const Value = extern union {
11401157 .bool_false,
11411158 => .eq,
11421159
1143 .bool_true => .gt,
1160 .one,
1161 .bool_true,
1162 => .gt,
11441163
11451164 .int_u64 => std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
11461165 .int_i64 => std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
......@@ -1257,6 +1276,7 @@ pub const Value = extern union {
12571276 .enum_literal_type,
12581277 .anyframe_type,
12591278 .zero,
1279 .one,
12601280 .bool_true,
12611281 .bool_false,
12621282 .null_value,
......@@ -1339,6 +1359,7 @@ pub const Value = extern union {
13391359 .enum_literal_type,
13401360 .anyframe_type,
13411361 .zero,
1362 .one,
13421363 .bool_true,
13431364 .bool_false,
13441365 .null_value,
......@@ -1438,6 +1459,7 @@ pub const Value = extern union {
14381459 .enum_literal_type,
14391460 .anyframe_type,
14401461 .zero,
1462 .one,
14411463 .empty_array,
14421464 .bool_true,
14431465 .bool_false,
src-self-hosted/zir.zig+8
......@@ -137,6 +137,8 @@ pub const Inst = struct {
137137 ensure_result_used,
138138 /// Emits a compile error if an error is ignored.
139139 ensure_result_non_error,
140 /// Emits a compile error if operand cannot be indexed.
141 ensure_indexable,
140142 /// Create a `E!T` type.
141143 error_union_type,
142144 /// Create an error set.
......@@ -251,6 +253,8 @@ pub const Inst = struct {
251253 unwrap_err_safe,
252254 /// Same as previous, but without safety checks. Used for orelse, if and while
253255 unwrap_err_unsafe,
256 /// Gets the error code value of an error union
257 unwrap_err_code,
254258 /// Takes a *E!T and raises a compiler error if T != void
255259 ensure_err_payload_void,
256260 /// Enum literal
......@@ -278,6 +282,7 @@ pub const Inst = struct {
278282 .alloc,
279283 .ensure_result_used,
280284 .ensure_result_non_error,
285 .ensure_indexable,
281286 .bitcast_result_ptr,
282287 .ref,
283288 .bitcast_ref,
......@@ -295,6 +300,7 @@ pub const Inst = struct {
295300 .unwrap_optional_unsafe,
296301 .unwrap_err_safe,
297302 .unwrap_err_unsafe,
303 .unwrap_err_code,
298304 .ensure_err_payload_void,
299305 .anyframe_type,
300306 .bitnot,
......@@ -409,6 +415,7 @@ pub const Inst = struct {
409415 .elemptr,
410416 .ensure_result_used,
411417 .ensure_result_non_error,
418 .ensure_indexable,
412419 .@"export",
413420 .floatcast,
414421 .fieldptr,
......@@ -450,6 +457,7 @@ pub const Inst = struct {
450457 .unwrap_optional_unsafe,
451458 .unwrap_err_safe,
452459 .unwrap_err_unsafe,
460 .unwrap_err_code,
453461 .ptr_type,
454462 .ensure_err_payload_void,
455463 .enum_literal,
src-self-hosted/zir_sema.zig+43-21
......@@ -48,6 +48,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
4848 .declval_in_module => return analyzeInstDeclValInModule(mod, scope, old_inst.castTag(.declval_in_module).?),
4949 .ensure_result_used => return analyzeInstEnsureResultUsed(mod, scope, old_inst.castTag(.ensure_result_used).?),
5050 .ensure_result_non_error => return analyzeInstEnsureResultNonError(mod, scope, old_inst.castTag(.ensure_result_non_error).?),
51 .ensure_indexable => return analyzeInstEnsureIndexable(mod, scope, old_inst.castTag(.ensure_indexable).?),
5152 .ref => return analyzeInstRef(mod, scope, old_inst.castTag(.ref).?),
5253 .ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?),
5354 .ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?),
......@@ -111,7 +112,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
111112 .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?),
112113 .isnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnull).?, true),
113114 .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false),
114 .iserr => return analyzeInstIsErr(mod, scope, old_inst.castTag(.iserr).?, true),
115 .iserr => return analyzeInstIsErr(mod, scope, old_inst.castTag(.iserr).?),
115116 .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?),
116117 .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?),
117118 .optional_type => return analyzeInstOptionalType(mod, scope, old_inst.castTag(.optional_type).?),
......@@ -119,6 +120,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
119120 .unwrap_optional_unsafe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_unsafe).?, false),
120121 .unwrap_err_safe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_safe).?, true),
121122 .unwrap_err_unsafe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_unsafe).?, false),
123 .unwrap_err_code => return analyzeInstUnwrapErrCode(mod, scope, old_inst.castTag(.unwrap_err_code).?),
122124 .ensure_err_payload_void => return analyzeInstEnsureErrPayloadVoid(mod, scope, old_inst.castTag(.ensure_err_payload_void).?),
123125 .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?),
124126 .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?),
......@@ -382,6 +384,19 @@ fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst.
382384 }
383385}
384386
387fn analyzeInstEnsureIndexable(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
388 const operand = try resolveInst(mod, scope, inst.positionals.operand);
389 const elem_ty = operand.ty.elemType();
390 if (elem_ty.isIndexable()) {
391 return mod.constVoid(scope, operand.src);
392 } else {
393 // TODO error notes
394 // error: type '{}' does not support indexing
395 // note: for loop operand must be an array, a slice or a tuple
396 return mod.fail(scope, operand.src, "for loop operand must be an array, a slice or a tuple", .{});
397 }
398}
399
385400fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
386401 const var_type = try resolveType(mod, scope, inst.positionals.operand);
387402 // TODO this should happen only for var allocs
......@@ -786,11 +801,12 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp
786801 const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
787802 assert(operand.ty.zigTypeTag() == .Pointer);
788803
789 if (operand.ty.elemType().zigTypeTag() != .Optional) {
790 return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{operand.ty.elemType()});
804 const elem_type = operand.ty.elemType();
805 if (elem_type.zigTypeTag() != .Optional) {
806 return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{elem_type});
791807 }
792808
793 const child_type = try operand.ty.elemType().optionalChildAlloc(scope.arena());
809 const child_type = try elem_type.optionalChildAlloc(scope.arena());
794810 const child_pointer = try mod.simplePtrType(scope, unwrap.base.src, child_type, operand.ty.isConstPtr(), .One);
795811
796812 if (operand.value()) |val| {
......@@ -815,6 +831,10 @@ fn analyzeInstUnwrapErr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, saf
815831 return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErr", .{});
816832}
817833
834fn analyzeInstUnwrapErrCode(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {
835 return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErrCode", .{});
836}
837
818838fn analyzeInstEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {
819839 return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstEnsureErrPayloadVoid", .{});
820840}
......@@ -950,7 +970,8 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr
950970 const entry = if (val.cast(Value.Payload.ErrorSet)) |payload|
951971 (payload.fields.getEntry(field_name) orelse
952972 return mod.fail(scope, fieldptr.base.src, "no error named '{}' in '{}'", .{ field_name, child_type })).*
953 else try mod.getErrorValue(field_name);
973 else
974 try mod.getErrorValue(field_name);
954975
955976 const error_payload = try scope.arena().create(Value.Payload.Error);
956977 error_payload.* = .{
......@@ -1062,9 +1083,19 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne
10621083 const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr);
10631084 const uncasted_index = try resolveInst(mod, scope, inst.positionals.index);
10641085 const elem_index = try mod.coerce(scope, Type.initTag(.usize), uncasted_index);
1086
1087 const elem_ty = switch (array_ptr.ty.zigTypeTag()) {
1088 .Pointer => array_ptr.ty.elemType(),
1089 else => return mod.fail(scope, inst.positionals.array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}),
1090 };
1091 if (!elem_ty.isIndexable()) {
1092 return mod.fail(scope, inst.base.src, "array access of non-array type '{}'", .{elem_ty});
1093 }
10651094
1066 if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) {
1067 if (array_ptr.value()) |array_ptr_val| {
1095 if (elem_ty.isSinglePointer() and elem_ty.elemType().zigTypeTag() == .Array) {
1096 // we have to deref the ptr operand to get the actual array pointer
1097 const array_ptr_deref = try mod.analyzeDeref(scope, inst.base.src, array_ptr, inst.positionals.array_ptr.src);
1098 if (array_ptr_deref.value()) |array_ptr_val| {
10681099 if (elem_index.value()) |index_val| {
10691100 // Both array pointer and index are compile-time known.
10701101 const index_u64 = index_val.toUnsignedInt();
......@@ -1075,7 +1106,7 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne
10751106 const type_payload = try scope.arena().create(Type.Payload.PointerSimple);
10761107 type_payload.* = .{
10771108 .base = .{ .tag = .single_const_pointer },
1078 .pointee_type = array_ptr.ty.elemType().elemType(),
1109 .pointee_type = elem_ty.elemType().elemType(),
10791110 };
10801111
10811112 return mod.constInst(scope, inst.base.src, .{
......@@ -1274,17 +1305,7 @@ fn analyzeInstCmp(
12741305 {
12751306 // comparing null with optionals
12761307 const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs;
1277 if (opt_operand.value()) |opt_val| {
1278 const is_null = opt_val.isNull();
1279 return mod.constBool(scope, inst.base.src, if (op == .eq) is_null else !is_null);
1280 }
1281 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
1282 const inst_tag: Inst.Tag = switch (op) {
1283 .eq => .isnull,
1284 .neq => .isnonnull,
1285 else => unreachable,
1286 };
1287 return mod.addUnOp(b, inst.base.src, Type.initTag(.bool), inst_tag, opt_operand);
1308 return mod.analyzeIsNull(scope, inst.base.src, opt_operand, op == .neq);
12881309 } else if (is_equality_cmp and
12891310 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
12901311 {
......@@ -1332,8 +1353,9 @@ fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, inver
13321353 return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic);
13331354}
13341355
1335fn analyzeInstIsErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst {
1336 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstIsErr", .{});
1356fn analyzeInstIsErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1357 const operand = try resolveInst(mod, scope, inst.positionals.operand);
1358 return mod.analyzeIsErr(scope, inst.base.src, operand);
13371359}
13381360
13391361fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst {
test/stage2/test.zig+84
......@@ -820,6 +820,90 @@ pub fn addCases(ctx: *TestContext) !void {
820820 ,
821821 "",
822822 );
823
824 // Array access.
825 case.addCompareOutput(
826 \\export fn _start() noreturn {
827 \\ assert("hello"[0] == 'h');
828 \\
829 \\ exit();
830 \\}
831 \\
832 \\pub fn assert(ok: bool) void {
833 \\ if (!ok) unreachable; // assertion failure
834 \\}
835 \\
836 \\fn exit() noreturn {
837 \\ asm volatile ("syscall"
838 \\ :
839 \\ : [number] "{rax}" (231),
840 \\ [arg1] "{rdi}" (0)
841 \\ : "rcx", "r11", "memory"
842 \\ );
843 \\ unreachable;
844 \\}
845 ,
846 "",
847 );
848
849 // 64bit set stack
850 case.addCompareOutput(
851 \\export fn _start() noreturn {
852 \\ var i: u64 = 0xFFEEDDCCBBAA9988;
853 \\ assert(i == 0xFFEEDDCCBBAA9988);
854 \\
855 \\ exit();
856 \\}
857 \\
858 \\pub fn assert(ok: bool) void {
859 \\ if (!ok) unreachable; // assertion failure
860 \\}
861 \\
862 \\fn exit() noreturn {
863 \\ asm volatile ("syscall"
864 \\ :
865 \\ : [number] "{rax}" (231),
866 \\ [arg1] "{rdi}" (0)
867 \\ : "rcx", "r11", "memory"
868 \\ );
869 \\ unreachable;
870 \\}
871 ,
872 "",
873 );
874
875 // Basic for loop
876 case.addCompareOutput(
877 \\export fn _start() noreturn {
878 \\ for ("hello") |_| print();
879 \\
880 \\ exit();
881 \\}
882 \\
883 \\fn print() void {
884 \\ asm volatile ("syscall"
885 \\ :
886 \\ : [number] "{rax}" (1),
887 \\ [arg1] "{rdi}" (1),
888 \\ [arg2] "{rsi}" (@ptrToInt("hello\n")),
889 \\ [arg3] "{rdx}" (6)
890 \\ : "rcx", "r11", "memory"
891 \\ );
892 \\ return;
893 \\}
894 \\
895 \\fn exit() noreturn {
896 \\ asm volatile ("syscall"
897 \\ :
898 \\ : [number] "{rax}" (231),
899 \\ [arg1] "{rdi}" (0)
900 \\ : "rcx", "r11", "memory"
901 \\ );
902 \\ unreachable;
903 \\}
904 ,
905 "hello\nhello\nhello\nhello\nhello\n",
906 );
823907 }
824908
825909 {
test/stage2/zir.zig+5-4
......@@ -43,10 +43,11 @@ pub fn addCases(ctx: *TestContext) !void {
4343 \\
4444 \\@entry = fn(@fnty, {
4545 \\ %a = str("\x32\x08\x01\x0a")
46 \\ %eptr0 = elemptr(%a, @0)
47 \\ %eptr1 = elemptr(%a, @1)
48 \\ %eptr2 = elemptr(%a, @2)
49 \\ %eptr3 = elemptr(%a, @3)
46 \\ %a_ref = ref(%a)
47 \\ %eptr0 = elemptr(%a_ref, @0)
48 \\ %eptr1 = elemptr(%a_ref, @1)
49 \\ %eptr2 = elemptr(%a_ref, @2)
50 \\ %eptr3 = elemptr(%a_ref, @3)
5051 \\ %v0 = deref(%eptr0)
5152 \\ %v1 = deref(%eptr1)
5253 \\ %v2 = deref(%eptr2)