| author | |
| committer | |
| log | 5fed721290cc7ecf396b7815f830e1fabe8b4711 |
| tree | 851e5f39cdfd916c395e405d29ddad056c29d5df |
| parent | 7d0bb0774e957e81fee7240d410944a0a56c303d |
| parent | b0995cb9f9540a6d463dcc2722cb3b8918a61e8e |
closes #61759 files changed, 500 insertions(+), 45 deletions(-)
src-self-hosted/Module.zig+12-1| ... | ... | @@ -2570,7 +2570,18 @@ pub fn analyzeIsNull( |
| 2570 | 2570 | operand: *Inst, |
| 2571 | 2571 | invert_logic: bool, |
| 2572 | 2572 | ) 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 | ||
| 2583 | pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst { | |
| 2584 | return self.fail(scope, src, "TODO implement analysis of iserr", .{}); | |
| 2574 | 2585 | } |
| 2575 | 2586 | |
| 2576 | 2587 | /// 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 |
| 273 | 273 | .AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)), |
| 274 | 274 | .ErrorSetDecl => return errorSetDecl(mod, scope, rl, node.castTag(.ErrorSetDecl).?), |
| 275 | 275 | .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).?), | |
| 276 | 279 | |
| 277 | 280 | .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", .{}), | |
| 279 | 281 | .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}), |
| 280 | 282 | .OrElse => return mod.failNode(scope, node, "TODO implement astgen.expr for .OrElse", .{}), |
| 281 | 283 | .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}), |
| 282 | 284 | .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}), |
| 283 | 285 | .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), |
| 284 | 286 | .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", .{}), | |
| 286 | 287 | .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}), |
| 287 | 288 | .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}), |
| 288 | 289 | .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}), |
| 289 | 290 | .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}), |
| 290 | 291 | .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", .{}), | |
| 292 | 292 | .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}), |
| 293 | 293 | .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}), |
| 294 | 294 | .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}), |
| ... | ... | @@ -497,7 +497,7 @@ fn varDecl( |
| 497 | 497 | const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTrailer("type_node")) |type_node| a: { |
| 498 | 498 | const type_inst = try typeExpr(mod, scope, type_node); |
| 499 | 499 | 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 } }; | |
| 501 | 501 | } else a: { |
| 502 | 502 | const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred); |
| 503 | 503 | 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, |
| 624 | 624 | .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type, |
| 625 | 625 | .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type, |
| 626 | 626 | .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, | |
| 628 | 628 | }, child_type); |
| 629 | 629 | } |
| 630 | 630 | |
| ... | ... | @@ -750,6 +750,93 @@ fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!* |
| 750 | 750 | }); |
| 751 | 751 | } |
| 752 | 752 | |
| 753 | fn 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 | ||
| 753 | 840 | /// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating. |
| 754 | 841 | /// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used. |
| 755 | 842 | fn 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 |
| 794 | 881 | const lhs = try expr(mod, scope, .ref, node.lhs); |
| 795 | 882 | const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?); |
| 796 | 883 | |
| 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 | ||
| 887 | fn 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 }, .{})); | |
| 800 | 895 | } |
| 801 | 896 | |
| 802 | 897 | fn 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 |
| 1080 | 1175 | } |
| 1081 | 1176 | } |
| 1082 | 1177 | |
| 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 | ||
| 1083 | 1184 | var expr_scope: Scope.GenZIR = .{ |
| 1084 | 1185 | .parent = scope, |
| 1085 | 1186 | .decl = scope.decl().?, |
| ... | ... | @@ -1198,6 +1299,181 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W |
| 1198 | 1299 | return &while_block.base; |
| 1199 | 1300 | } |
| 1200 | 1301 | |
| 1302 | fn 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 | ||
| 1201 | 1477 | fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { |
| 1202 | 1478 | const tree = scope.tree(); |
| 1203 | 1479 | 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 { |
| 829 | 829 | // No side effects, so if it's unreferenced, do nothing. |
| 830 | 830 | if (inst.base.isUnused()) |
| 831 | 831 | 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 | ||
| 832 | 842 | switch (arch) { |
| 833 | 843 | else => return self.fail(inst.base.src, "TODO implement intCast for {}", .{self.target.cpu.arch}), |
| 834 | 844 | } |
| ... | ... | @@ -2039,15 +2049,29 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2039 | 2049 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x); |
| 2040 | 2050 | }, |
| 2041 | 2051 | 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]); | |
| 2043 | 2070 | }, |
| 2044 | 2071 | else => { |
| 2045 | 2072 | return self.fail(src, "TODO implement set abi_size=large stack variable with immediate", .{}); |
| 2046 | 2073 | }, |
| 2047 | 2074 | } |
| 2048 | if (x_big <= math.maxInt(u32)) {} else { | |
| 2049 | return self.fail(src, "TODO implement set stack variable with large immediate", .{}); | |
| 2050 | } | |
| 2051 | 2075 | }, |
| 2052 | 2076 | .embedded_in_code => |code_offset| { |
| 2053 | 2077 | 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 { |
| 2675 | 2675 | }; |
| 2676 | 2676 | } |
| 2677 | 2677 | |
| 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 | ||
| 2678 | 2685 | /// This enum does not directly correspond to `std.builtin.TypeId` because |
| 2679 | 2686 | /// it has extra enum tags in it, as a way of using less memory. For example, |
| 2680 | 2687 | /// 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 { |
| 65 | 65 | |
| 66 | 66 | undef, |
| 67 | 67 | zero, |
| 68 | one, | |
| 68 | 69 | void_value, |
| 69 | 70 | unreachable_value, |
| 70 | 71 | empty_array, |
| ... | ... | @@ -174,6 +175,7 @@ pub const Value = extern union { |
| 174 | 175 | .anyframe_type, |
| 175 | 176 | .undef, |
| 176 | 177 | .zero, |
| 178 | .one, | |
| 177 | 179 | .void_value, |
| 178 | 180 | .unreachable_value, |
| 179 | 181 | .empty_array, |
| ... | ... | @@ -313,6 +315,7 @@ pub const Value = extern union { |
| 313 | 315 | .null_value => return out_stream.writeAll("null"), |
| 314 | 316 | .undef => return out_stream.writeAll("undefined"), |
| 315 | 317 | .zero => return out_stream.writeAll("0"), |
| 318 | .one => return out_stream.writeAll("1"), | |
| 316 | 319 | .void_value => return out_stream.writeAll("{}"), |
| 317 | 320 | .unreachable_value => return out_stream.writeAll("unreachable"), |
| 318 | 321 | .bool_true => return out_stream.writeAll("true"), |
| ... | ... | @@ -447,6 +450,7 @@ pub const Value = extern union { |
| 447 | 450 | |
| 448 | 451 | .undef, |
| 449 | 452 | .zero, |
| 453 | .one, | |
| 450 | 454 | .void_value, |
| 451 | 455 | .unreachable_value, |
| 452 | 456 | .empty_array, |
| ... | ... | @@ -546,7 +550,9 @@ pub const Value = extern union { |
| 546 | 550 | .bool_false, |
| 547 | 551 | => return BigIntMutable.init(&space.limbs, 0).toConst(), |
| 548 | 552 | |
| 549 | .bool_true => return BigIntMutable.init(&space.limbs, 1).toConst(), | |
| 553 | .one, | |
| 554 | .bool_true, | |
| 555 | => return BigIntMutable.init(&space.limbs, 1).toConst(), | |
| 550 | 556 | |
| 551 | 557 | .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(), |
| 552 | 558 | .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(), |
| ... | ... | @@ -627,7 +633,9 @@ pub const Value = extern union { |
| 627 | 633 | .bool_false, |
| 628 | 634 | => return 0, |
| 629 | 635 | |
| 630 | .bool_true => return 1, | |
| 636 | .one, | |
| 637 | .bool_true, | |
| 638 | => return 1, | |
| 631 | 639 | |
| 632 | 640 | .int_u64 => return self.cast(Payload.Int_u64).?.int, |
| 633 | 641 | .int_i64 => return @intCast(u64, self.cast(Payload.Int_i64).?.int), |
| ... | ... | @@ -708,7 +716,9 @@ pub const Value = extern union { |
| 708 | 716 | .bool_false, |
| 709 | 717 | => return 0, |
| 710 | 718 | |
| 711 | .bool_true => return 1, | |
| 719 | .one, | |
| 720 | .bool_true, | |
| 721 | => return 1, | |
| 712 | 722 | |
| 713 | 723 | .int_u64 => return @intCast(i64, self.cast(Payload.Int_u64).?.int), |
| 714 | 724 | .int_i64 => return self.cast(Payload.Int_i64).?.int, |
| ... | ... | @@ -734,6 +744,7 @@ pub const Value = extern union { |
| 734 | 744 | .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val), |
| 735 | 745 | |
| 736 | 746 | .zero => 0, |
| 747 | .one => 1, | |
| 737 | 748 | .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int), |
| 738 | 749 | .int_i64 => @intToFloat(T, self.cast(Payload.Int_i64).?.int), |
| 739 | 750 | |
| ... | ... | @@ -814,7 +825,9 @@ pub const Value = extern union { |
| 814 | 825 | .bool_false, |
| 815 | 826 | => return 0, |
| 816 | 827 | |
| 817 | .bool_true => return 1, | |
| 828 | .one, | |
| 829 | .bool_true, | |
| 830 | => return 1, | |
| 818 | 831 | |
| 819 | 832 | .int_u64 => { |
| 820 | 833 | const x = self.cast(Payload.Int_u64).?.int; |
| ... | ... | @@ -900,7 +913,9 @@ pub const Value = extern union { |
| 900 | 913 | .bool_false, |
| 901 | 914 | => return true, |
| 902 | 915 | |
| 903 | .bool_true => { | |
| 916 | .one, | |
| 917 | .bool_true, | |
| 918 | => { | |
| 904 | 919 | const info = ty.intInfo(target); |
| 905 | 920 | if (info.signed) { |
| 906 | 921 | return info.bits >= 2; |
| ... | ... | @@ -1064,7 +1079,9 @@ pub const Value = extern union { |
| 1064 | 1079 | .@"error", |
| 1065 | 1080 | => unreachable, |
| 1066 | 1081 | |
| 1067 | .zero => false, | |
| 1082 | .zero, | |
| 1083 | .one, | |
| 1084 | => false, | |
| 1068 | 1085 | |
| 1069 | 1086 | .float_16 => @rem(self.cast(Payload.Float_16).?.val, 1) != 0, |
| 1070 | 1087 | .float_32 => @rem(self.cast(Payload.Float_32).?.val, 1) != 0, |
| ... | ... | @@ -1140,7 +1157,9 @@ pub const Value = extern union { |
| 1140 | 1157 | .bool_false, |
| 1141 | 1158 | => .eq, |
| 1142 | 1159 | |
| 1143 | .bool_true => .gt, | |
| 1160 | .one, | |
| 1161 | .bool_true, | |
| 1162 | => .gt, | |
| 1144 | 1163 | |
| 1145 | 1164 | .int_u64 => std.math.order(lhs.cast(Payload.Int_u64).?.int, 0), |
| 1146 | 1165 | .int_i64 => std.math.order(lhs.cast(Payload.Int_i64).?.int, 0), |
| ... | ... | @@ -1257,6 +1276,7 @@ pub const Value = extern union { |
| 1257 | 1276 | .enum_literal_type, |
| 1258 | 1277 | .anyframe_type, |
| 1259 | 1278 | .zero, |
| 1279 | .one, | |
| 1260 | 1280 | .bool_true, |
| 1261 | 1281 | .bool_false, |
| 1262 | 1282 | .null_value, |
| ... | ... | @@ -1339,6 +1359,7 @@ pub const Value = extern union { |
| 1339 | 1359 | .enum_literal_type, |
| 1340 | 1360 | .anyframe_type, |
| 1341 | 1361 | .zero, |
| 1362 | .one, | |
| 1342 | 1363 | .bool_true, |
| 1343 | 1364 | .bool_false, |
| 1344 | 1365 | .null_value, |
| ... | ... | @@ -1438,6 +1459,7 @@ pub const Value = extern union { |
| 1438 | 1459 | .enum_literal_type, |
| 1439 | 1460 | .anyframe_type, |
| 1440 | 1461 | .zero, |
| 1462 | .one, | |
| 1441 | 1463 | .empty_array, |
| 1442 | 1464 | .bool_true, |
| 1443 | 1465 | .bool_false, |
src-self-hosted/zir.zig+8| ... | ... | @@ -137,6 +137,8 @@ pub const Inst = struct { |
| 137 | 137 | ensure_result_used, |
| 138 | 138 | /// Emits a compile error if an error is ignored. |
| 139 | 139 | ensure_result_non_error, |
| 140 | /// Emits a compile error if operand cannot be indexed. | |
| 141 | ensure_indexable, | |
| 140 | 142 | /// Create a `E!T` type. |
| 141 | 143 | error_union_type, |
| 142 | 144 | /// Create an error set. |
| ... | ... | @@ -251,6 +253,8 @@ pub const Inst = struct { |
| 251 | 253 | unwrap_err_safe, |
| 252 | 254 | /// Same as previous, but without safety checks. Used for orelse, if and while |
| 253 | 255 | unwrap_err_unsafe, |
| 256 | /// Gets the error code value of an error union | |
| 257 | unwrap_err_code, | |
| 254 | 258 | /// Takes a *E!T and raises a compiler error if T != void |
| 255 | 259 | ensure_err_payload_void, |
| 256 | 260 | /// Enum literal |
| ... | ... | @@ -278,6 +282,7 @@ pub const Inst = struct { |
| 278 | 282 | .alloc, |
| 279 | 283 | .ensure_result_used, |
| 280 | 284 | .ensure_result_non_error, |
| 285 | .ensure_indexable, | |
| 281 | 286 | .bitcast_result_ptr, |
| 282 | 287 | .ref, |
| 283 | 288 | .bitcast_ref, |
| ... | ... | @@ -295,6 +300,7 @@ pub const Inst = struct { |
| 295 | 300 | .unwrap_optional_unsafe, |
| 296 | 301 | .unwrap_err_safe, |
| 297 | 302 | .unwrap_err_unsafe, |
| 303 | .unwrap_err_code, | |
| 298 | 304 | .ensure_err_payload_void, |
| 299 | 305 | .anyframe_type, |
| 300 | 306 | .bitnot, |
| ... | ... | @@ -409,6 +415,7 @@ pub const Inst = struct { |
| 409 | 415 | .elemptr, |
| 410 | 416 | .ensure_result_used, |
| 411 | 417 | .ensure_result_non_error, |
| 418 | .ensure_indexable, | |
| 412 | 419 | .@"export", |
| 413 | 420 | .floatcast, |
| 414 | 421 | .fieldptr, |
| ... | ... | @@ -450,6 +457,7 @@ pub const Inst = struct { |
| 450 | 457 | .unwrap_optional_unsafe, |
| 451 | 458 | .unwrap_err_safe, |
| 452 | 459 | .unwrap_err_unsafe, |
| 460 | .unwrap_err_code, | |
| 453 | 461 | .ptr_type, |
| 454 | 462 | .ensure_err_payload_void, |
| 455 | 463 | .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! |
| 48 | 48 | .declval_in_module => return analyzeInstDeclValInModule(mod, scope, old_inst.castTag(.declval_in_module).?), |
| 49 | 49 | .ensure_result_used => return analyzeInstEnsureResultUsed(mod, scope, old_inst.castTag(.ensure_result_used).?), |
| 50 | 50 | .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).?), | |
| 51 | 52 | .ref => return analyzeInstRef(mod, scope, old_inst.castTag(.ref).?), |
| 52 | 53 | .ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?), |
| 53 | 54 | .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! |
| 111 | 112 | .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?), |
| 112 | 113 | .isnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnull).?, true), |
| 113 | 114 | .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).?), | |
| 115 | 116 | .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?), |
| 116 | 117 | .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?), |
| 117 | 118 | .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! |
| 119 | 120 | .unwrap_optional_unsafe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_unsafe).?, false), |
| 120 | 121 | .unwrap_err_safe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_safe).?, true), |
| 121 | 122 | .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).?), | |
| 122 | 124 | .ensure_err_payload_void => return analyzeInstEnsureErrPayloadVoid(mod, scope, old_inst.castTag(.ensure_err_payload_void).?), |
| 123 | 125 | .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?), |
| 124 | 126 | .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. |
| 382 | 384 | } |
| 383 | 385 | } |
| 384 | 386 | |
| 387 | fn 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 | ||
| 385 | 400 | fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { |
| 386 | 401 | const var_type = try resolveType(mod, scope, inst.positionals.operand); |
| 387 | 402 | // TODO this should happen only for var allocs |
| ... | ... | @@ -786,11 +801,12 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp |
| 786 | 801 | const operand = try resolveInst(mod, scope, unwrap.positionals.operand); |
| 787 | 802 | assert(operand.ty.zigTypeTag() == .Pointer); |
| 788 | 803 | |
| 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}); | |
| 791 | 807 | } |
| 792 | 808 | |
| 793 | const child_type = try operand.ty.elemType().optionalChildAlloc(scope.arena()); | |
| 809 | const child_type = try elem_type.optionalChildAlloc(scope.arena()); | |
| 794 | 810 | const child_pointer = try mod.simplePtrType(scope, unwrap.base.src, child_type, operand.ty.isConstPtr(), .One); |
| 795 | 811 | |
| 796 | 812 | if (operand.value()) |val| { |
| ... | ... | @@ -815,6 +831,10 @@ fn analyzeInstUnwrapErr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, saf |
| 815 | 831 | return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErr", .{}); |
| 816 | 832 | } |
| 817 | 833 | |
| 834 | fn analyzeInstUnwrapErrCode(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { | |
| 835 | return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErrCode", .{}); | |
| 836 | } | |
| 837 | ||
| 818 | 838 | fn analyzeInstEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { |
| 819 | 839 | return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstEnsureErrPayloadVoid", .{}); |
| 820 | 840 | } |
| ... | ... | @@ -950,7 +970,8 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr |
| 950 | 970 | const entry = if (val.cast(Value.Payload.ErrorSet)) |payload| |
| 951 | 971 | (payload.fields.getEntry(field_name) orelse |
| 952 | 972 | 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); | |
| 954 | 975 | |
| 955 | 976 | const error_payload = try scope.arena().create(Value.Payload.Error); |
| 956 | 977 | error_payload.* = .{ |
| ... | ... | @@ -1062,9 +1083,19 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne |
| 1062 | 1083 | const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr); |
| 1063 | 1084 | const uncasted_index = try resolveInst(mod, scope, inst.positionals.index); |
| 1064 | 1085 | 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 | } | |
| 1065 | 1094 | |
| 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| { | |
| 1068 | 1099 | if (elem_index.value()) |index_val| { |
| 1069 | 1100 | // Both array pointer and index are compile-time known. |
| 1070 | 1101 | const index_u64 = index_val.toUnsignedInt(); |
| ... | ... | @@ -1075,7 +1106,7 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne |
| 1075 | 1106 | const type_payload = try scope.arena().create(Type.Payload.PointerSimple); |
| 1076 | 1107 | type_payload.* = .{ |
| 1077 | 1108 | .base = .{ .tag = .single_const_pointer }, |
| 1078 | .pointee_type = array_ptr.ty.elemType().elemType(), | |
| 1109 | .pointee_type = elem_ty.elemType().elemType(), | |
| 1079 | 1110 | }; |
| 1080 | 1111 | |
| 1081 | 1112 | return mod.constInst(scope, inst.base.src, .{ |
| ... | ... | @@ -1274,17 +1305,7 @@ fn analyzeInstCmp( |
| 1274 | 1305 | { |
| 1275 | 1306 | // comparing null with optionals |
| 1276 | 1307 | 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); | |
| 1288 | 1309 | } else if (is_equality_cmp and |
| 1289 | 1310 | ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr()))) |
| 1290 | 1311 | { |
| ... | ... | @@ -1332,8 +1353,9 @@ fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, inver |
| 1332 | 1353 | return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic); |
| 1333 | 1354 | } |
| 1334 | 1355 | |
| 1335 | fn 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", .{}); | |
| 1356 | fn 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); | |
| 1337 | 1359 | } |
| 1338 | 1360 | |
| 1339 | 1361 | fn 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 { |
| 820 | 820 | , |
| 821 | 821 | "", |
| 822 | 822 | ); |
| 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 | ); | |
| 823 | 907 | } |
| 824 | 908 | |
| 825 | 909 | { |
test/stage2/zir.zig+5-4| ... | ... | @@ -43,10 +43,11 @@ pub fn addCases(ctx: *TestContext) !void { |
| 43 | 43 | \\ |
| 44 | 44 | \\@entry = fn(@fnty, { |
| 45 | 45 | \\ %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) | |
| 50 | 51 | \\ %v0 = deref(%eptr0) |
| 51 | 52 | \\ %v1 = deref(%eptr1) |
| 52 | 53 | \\ %v2 = deref(%eptr2) |