| 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,7 +2570,18 @@ pub fn analyzeIsNull( |
| 2570 | operand: *Inst, | 2570 | operand: *Inst, |
| 2571 | invert_logic: bool, | 2571 | invert_logic: bool, |
| 2572 | ) InnerError!*Inst { | 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 | /// Asserts that lhs and rhs types are both numeric. | 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,22 +273,22 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 273 | .AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)), | 273 | .AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)), |
| 274 | .ErrorSetDecl => return errorSetDecl(mod, scope, rl, node.castTag(.ErrorSetDecl).?), | 274 | .ErrorSetDecl => return errorSetDecl(mod, scope, rl, node.castTag(.ErrorSetDecl).?), |
| 275 | .ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)), | 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 | .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), | 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 | .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}), | 281 | .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}), |
| 280 | .OrElse => return mod.failNode(scope, node, "TODO implement astgen.expr for .OrElse", .{}), | 282 | .OrElse => return mod.failNode(scope, node, "TODO implement astgen.expr for .OrElse", .{}), |
| 281 | .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}), | 283 | .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}), |
| 282 | .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}), | 284 | .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}), |
| 283 | .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), | 285 | .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), |
| 284 | .Slice => return mod.failNode(scope, node, "TODO implement astgen.expr for .Slice", .{}), | 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 | .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}), | 287 | .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}), |
| 287 | .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}), | 288 | .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}), |
| 288 | .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}), | 289 | .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}), |
| 289 | .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}), | 290 | .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}), |
| 290 | .Switch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Switch", .{}), | 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 | .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}), | 292 | .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}), |
| 293 | .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}), | 293 | .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}), |
| 294 | .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}), | 294 | .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}), |
| ... | @@ -497,7 +497,7 @@ fn varDecl( | ... | @@ -497,7 +497,7 @@ fn varDecl( |
| 497 | const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTrailer("type_node")) |type_node| a: { | 497 | const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTrailer("type_node")) |type_node| a: { |
| 498 | const type_inst = try typeExpr(mod, scope, type_node); | 498 | const type_inst = try typeExpr(mod, scope, type_node); |
| 499 | const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst); | 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 | } else a: { | 501 | } else a: { |
| 502 | const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred); | 502 | const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred); |
| 503 | break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? } }; | 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,7 +624,7 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, |
| 624 | .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type, | 624 | .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type, |
| 625 | .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type, | 625 | .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type, |
| 626 | .C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type, | 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 | }, child_type); | 628 | }, child_type); |
| 629 | } | 629 | } |
| 630 | 630 | ||
| ... | @@ -750,6 +750,93 @@ fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!* | ... | @@ -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 | /// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating. | 840 | /// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating. |
| 754 | /// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used. | 841 | /// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used. |
| 755 | fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool { | 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,9 +881,17 @@ fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfix |
| 794 | const lhs = try expr(mod, scope, .ref, node.lhs); | 881 | const lhs = try expr(mod, scope, .ref, node.lhs); |
| 795 | const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?); | 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 }, .{}); | 884 | return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{})); |
| 798 | if (rl == .ref) return pointer; | 885 | } |
| 799 | return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, pointer)); | 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 | fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst { | 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,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 | var expr_scope: Scope.GenZIR = .{ | 1184 | var expr_scope: Scope.GenZIR = .{ |
| 1084 | .parent = scope, | 1185 | .parent = scope, |
| 1085 | .decl = scope.decl().?, | 1186 | .decl = scope.decl().?, |
| ... | @@ -1198,6 +1299,181 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W | ... | @@ -1198,6 +1299,181 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W |
| 1198 | return &while_block.base; | 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 | fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { | 1477 | fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { |
| 1202 | const tree = scope.tree(); | 1478 | const tree = scope.tree(); |
| 1203 | const src = tree.token_locs[cfe.ltoken].start; | 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,6 +829,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 829 | // No side effects, so if it's unreferenced, do nothing. | 829 | // No side effects, so if it's unreferenced, do nothing. |
| 830 | if (inst.base.isUnused()) | 830 | if (inst.base.isUnused()) |
| 831 | return MCValue.dead; | 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 | switch (arch) { | 842 | switch (arch) { |
| 833 | else => return self.fail(inst.base.src, "TODO implement intCast for {}", .{self.target.cpu.arch}), | 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,15 +2049,29 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2039 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x); | 2049 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x); |
| 2040 | }, | 2050 | }, |
| 2041 | 8 => { | 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 | else => { | 2071 | else => { |
| 2045 | return self.fail(src, "TODO implement set abi_size=large stack variable with immediate", .{}); | 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 | .embedded_in_code => |code_offset| { | 2076 | .embedded_in_code => |code_offset| { |
| 2053 | return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{}); | 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,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 | /// This enum does not directly correspond to `std.builtin.TypeId` because | 2685 | /// This enum does not directly correspond to `std.builtin.TypeId` because |
| 2679 | /// it has extra enum tags in it, as a way of using less memory. For example, | 2686 | /// it has extra enum tags in it, as a way of using less memory. For example, |
| 2680 | /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types | 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,6 +65,7 @@ pub const Value = extern union { |
| 65 | 65 | ||
| 66 | undef, | 66 | undef, |
| 67 | zero, | 67 | zero, |
| 68 | one, | ||
| 68 | void_value, | 69 | void_value, |
| 69 | unreachable_value, | 70 | unreachable_value, |
| 70 | empty_array, | 71 | empty_array, |
| ... | @@ -174,6 +175,7 @@ pub const Value = extern union { | ... | @@ -174,6 +175,7 @@ pub const Value = extern union { |
| 174 | .anyframe_type, | 175 | .anyframe_type, |
| 175 | .undef, | 176 | .undef, |
| 176 | .zero, | 177 | .zero, |
| 178 | .one, | ||
| 177 | .void_value, | 179 | .void_value, |
| 178 | .unreachable_value, | 180 | .unreachable_value, |
| 179 | .empty_array, | 181 | .empty_array, |
| ... | @@ -313,6 +315,7 @@ pub const Value = extern union { | ... | @@ -313,6 +315,7 @@ pub const Value = extern union { |
| 313 | .null_value => return out_stream.writeAll("null"), | 315 | .null_value => return out_stream.writeAll("null"), |
| 314 | .undef => return out_stream.writeAll("undefined"), | 316 | .undef => return out_stream.writeAll("undefined"), |
| 315 | .zero => return out_stream.writeAll("0"), | 317 | .zero => return out_stream.writeAll("0"), |
| 318 | .one => return out_stream.writeAll("1"), | ||
| 316 | .void_value => return out_stream.writeAll("{}"), | 319 | .void_value => return out_stream.writeAll("{}"), |
| 317 | .unreachable_value => return out_stream.writeAll("unreachable"), | 320 | .unreachable_value => return out_stream.writeAll("unreachable"), |
| 318 | .bool_true => return out_stream.writeAll("true"), | 321 | .bool_true => return out_stream.writeAll("true"), |
| ... | @@ -447,6 +450,7 @@ pub const Value = extern union { | ... | @@ -447,6 +450,7 @@ pub const Value = extern union { |
| 447 | 450 | ||
| 448 | .undef, | 451 | .undef, |
| 449 | .zero, | 452 | .zero, |
| 453 | .one, | ||
| 450 | .void_value, | 454 | .void_value, |
| 451 | .unreachable_value, | 455 | .unreachable_value, |
| 452 | .empty_array, | 456 | .empty_array, |
| ... | @@ -546,7 +550,9 @@ pub const Value = extern union { | ... | @@ -546,7 +550,9 @@ pub const Value = extern union { |
| 546 | .bool_false, | 550 | .bool_false, |
| 547 | => return BigIntMutable.init(&space.limbs, 0).toConst(), | 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 | .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(), | 557 | .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(), |
| 552 | .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(), | 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,7 +633,9 @@ pub const Value = extern union { |
| 627 | .bool_false, | 633 | .bool_false, |
| 628 | => return 0, | 634 | => return 0, |
| 629 | 635 | ||
| 630 | .bool_true => return 1, | 636 | .one, |
| 637 | .bool_true, | ||
| 638 | => return 1, | ||
| 631 | 639 | ||
| 632 | .int_u64 => return self.cast(Payload.Int_u64).?.int, | 640 | .int_u64 => return self.cast(Payload.Int_u64).?.int, |
| 633 | .int_i64 => return @intCast(u64, self.cast(Payload.Int_i64).?.int), | 641 | .int_i64 => return @intCast(u64, self.cast(Payload.Int_i64).?.int), |
| ... | @@ -708,7 +716,9 @@ pub const Value = extern union { | ... | @@ -708,7 +716,9 @@ pub const Value = extern union { |
| 708 | .bool_false, | 716 | .bool_false, |
| 709 | => return 0, | 717 | => return 0, |
| 710 | 718 | ||
| 711 | .bool_true => return 1, | 719 | .one, |
| 720 | .bool_true, | ||
| 721 | => return 1, | ||
| 712 | 722 | ||
| 713 | .int_u64 => return @intCast(i64, self.cast(Payload.Int_u64).?.int), | 723 | .int_u64 => return @intCast(i64, self.cast(Payload.Int_u64).?.int), |
| 714 | .int_i64 => return self.cast(Payload.Int_i64).?.int, | 724 | .int_i64 => return self.cast(Payload.Int_i64).?.int, |
| ... | @@ -734,6 +744,7 @@ pub const Value = extern union { | ... | @@ -734,6 +744,7 @@ pub const Value = extern union { |
| 734 | .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val), | 744 | .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val), |
| 735 | 745 | ||
| 736 | .zero => 0, | 746 | .zero => 0, |
| 747 | .one => 1, | ||
| 737 | .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int), | 748 | .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int), |
| 738 | .int_i64 => @intToFloat(T, self.cast(Payload.Int_i64).?.int), | 749 | .int_i64 => @intToFloat(T, self.cast(Payload.Int_i64).?.int), |
| 739 | 750 | ||
| ... | @@ -814,7 +825,9 @@ pub const Value = extern union { | ... | @@ -814,7 +825,9 @@ pub const Value = extern union { |
| 814 | .bool_false, | 825 | .bool_false, |
| 815 | => return 0, | 826 | => return 0, |
| 816 | 827 | ||
| 817 | .bool_true => return 1, | 828 | .one, |
| 829 | .bool_true, | ||
| 830 | => return 1, | ||
| 818 | 831 | ||
| 819 | .int_u64 => { | 832 | .int_u64 => { |
| 820 | const x = self.cast(Payload.Int_u64).?.int; | 833 | const x = self.cast(Payload.Int_u64).?.int; |
| ... | @@ -900,7 +913,9 @@ pub const Value = extern union { | ... | @@ -900,7 +913,9 @@ pub const Value = extern union { |
| 900 | .bool_false, | 913 | .bool_false, |
| 901 | => return true, | 914 | => return true, |
| 902 | 915 | ||
| 903 | .bool_true => { | 916 | .one, |
| 917 | .bool_true, | ||
| 918 | => { | ||
| 904 | const info = ty.intInfo(target); | 919 | const info = ty.intInfo(target); |
| 905 | if (info.signed) { | 920 | if (info.signed) { |
| 906 | return info.bits >= 2; | 921 | return info.bits >= 2; |
| ... | @@ -1064,7 +1079,9 @@ pub const Value = extern union { | ... | @@ -1064,7 +1079,9 @@ pub const Value = extern union { |
| 1064 | .@"error", | 1079 | .@"error", |
| 1065 | => unreachable, | 1080 | => unreachable, |
| 1066 | 1081 | ||
| 1067 | .zero => false, | 1082 | .zero, |
| 1083 | .one, | ||
| 1084 | => false, | ||
| 1068 | 1085 | ||
| 1069 | .float_16 => @rem(self.cast(Payload.Float_16).?.val, 1) != 0, | 1086 | .float_16 => @rem(self.cast(Payload.Float_16).?.val, 1) != 0, |
| 1070 | .float_32 => @rem(self.cast(Payload.Float_32).?.val, 1) != 0, | 1087 | .float_32 => @rem(self.cast(Payload.Float_32).?.val, 1) != 0, |
| ... | @@ -1140,7 +1157,9 @@ pub const Value = extern union { | ... | @@ -1140,7 +1157,9 @@ pub const Value = extern union { |
| 1140 | .bool_false, | 1157 | .bool_false, |
| 1141 | => .eq, | 1158 | => .eq, |
| 1142 | 1159 | ||
| 1143 | .bool_true => .gt, | 1160 | .one, |
| 1161 | .bool_true, | ||
| 1162 | => .gt, | ||
| 1144 | 1163 | ||
| 1145 | .int_u64 => std.math.order(lhs.cast(Payload.Int_u64).?.int, 0), | 1164 | .int_u64 => std.math.order(lhs.cast(Payload.Int_u64).?.int, 0), |
| 1146 | .int_i64 => std.math.order(lhs.cast(Payload.Int_i64).?.int, 0), | 1165 | .int_i64 => std.math.order(lhs.cast(Payload.Int_i64).?.int, 0), |
| ... | @@ -1257,6 +1276,7 @@ pub const Value = extern union { | ... | @@ -1257,6 +1276,7 @@ pub const Value = extern union { |
| 1257 | .enum_literal_type, | 1276 | .enum_literal_type, |
| 1258 | .anyframe_type, | 1277 | .anyframe_type, |
| 1259 | .zero, | 1278 | .zero, |
| 1279 | .one, | ||
| 1260 | .bool_true, | 1280 | .bool_true, |
| 1261 | .bool_false, | 1281 | .bool_false, |
| 1262 | .null_value, | 1282 | .null_value, |
| ... | @@ -1339,6 +1359,7 @@ pub const Value = extern union { | ... | @@ -1339,6 +1359,7 @@ pub const Value = extern union { |
| 1339 | .enum_literal_type, | 1359 | .enum_literal_type, |
| 1340 | .anyframe_type, | 1360 | .anyframe_type, |
| 1341 | .zero, | 1361 | .zero, |
| 1362 | .one, | ||
| 1342 | .bool_true, | 1363 | .bool_true, |
| 1343 | .bool_false, | 1364 | .bool_false, |
| 1344 | .null_value, | 1365 | .null_value, |
| ... | @@ -1438,6 +1459,7 @@ pub const Value = extern union { | ... | @@ -1438,6 +1459,7 @@ pub const Value = extern union { |
| 1438 | .enum_literal_type, | 1459 | .enum_literal_type, |
| 1439 | .anyframe_type, | 1460 | .anyframe_type, |
| 1440 | .zero, | 1461 | .zero, |
| 1462 | .one, | ||
| 1441 | .empty_array, | 1463 | .empty_array, |
| 1442 | .bool_true, | 1464 | .bool_true, |
| 1443 | .bool_false, | 1465 | .bool_false, |
src-self-hosted/zir.zig+8| ... | @@ -137,6 +137,8 @@ pub const Inst = struct { | ... | @@ -137,6 +137,8 @@ pub const Inst = struct { |
| 137 | ensure_result_used, | 137 | ensure_result_used, |
| 138 | /// Emits a compile error if an error is ignored. | 138 | /// Emits a compile error if an error is ignored. |
| 139 | ensure_result_non_error, | 139 | ensure_result_non_error, |
| 140 | /// Emits a compile error if operand cannot be indexed. | ||
| 141 | ensure_indexable, | ||
| 140 | /// Create a `E!T` type. | 142 | /// Create a `E!T` type. |
| 141 | error_union_type, | 143 | error_union_type, |
| 142 | /// Create an error set. | 144 | /// Create an error set. |
| ... | @@ -251,6 +253,8 @@ pub const Inst = struct { | ... | @@ -251,6 +253,8 @@ pub const Inst = struct { |
| 251 | unwrap_err_safe, | 253 | unwrap_err_safe, |
| 252 | /// Same as previous, but without safety checks. Used for orelse, if and while | 254 | /// Same as previous, but without safety checks. Used for orelse, if and while |
| 253 | unwrap_err_unsafe, | 255 | unwrap_err_unsafe, |
| 256 | /// Gets the error code value of an error union | ||
| 257 | unwrap_err_code, | ||
| 254 | /// Takes a *E!T and raises a compiler error if T != void | 258 | /// Takes a *E!T and raises a compiler error if T != void |
| 255 | ensure_err_payload_void, | 259 | ensure_err_payload_void, |
| 256 | /// Enum literal | 260 | /// Enum literal |
| ... | @@ -278,6 +282,7 @@ pub const Inst = struct { | ... | @@ -278,6 +282,7 @@ pub const Inst = struct { |
| 278 | .alloc, | 282 | .alloc, |
| 279 | .ensure_result_used, | 283 | .ensure_result_used, |
| 280 | .ensure_result_non_error, | 284 | .ensure_result_non_error, |
| 285 | .ensure_indexable, | ||
| 281 | .bitcast_result_ptr, | 286 | .bitcast_result_ptr, |
| 282 | .ref, | 287 | .ref, |
| 283 | .bitcast_ref, | 288 | .bitcast_ref, |
| ... | @@ -295,6 +300,7 @@ pub const Inst = struct { | ... | @@ -295,6 +300,7 @@ pub const Inst = struct { |
| 295 | .unwrap_optional_unsafe, | 300 | .unwrap_optional_unsafe, |
| 296 | .unwrap_err_safe, | 301 | .unwrap_err_safe, |
| 297 | .unwrap_err_unsafe, | 302 | .unwrap_err_unsafe, |
| 303 | .unwrap_err_code, | ||
| 298 | .ensure_err_payload_void, | 304 | .ensure_err_payload_void, |
| 299 | .anyframe_type, | 305 | .anyframe_type, |
| 300 | .bitnot, | 306 | .bitnot, |
| ... | @@ -409,6 +415,7 @@ pub const Inst = struct { | ... | @@ -409,6 +415,7 @@ pub const Inst = struct { |
| 409 | .elemptr, | 415 | .elemptr, |
| 410 | .ensure_result_used, | 416 | .ensure_result_used, |
| 411 | .ensure_result_non_error, | 417 | .ensure_result_non_error, |
| 418 | .ensure_indexable, | ||
| 412 | .@"export", | 419 | .@"export", |
| 413 | .floatcast, | 420 | .floatcast, |
| 414 | .fieldptr, | 421 | .fieldptr, |
| ... | @@ -450,6 +457,7 @@ pub const Inst = struct { | ... | @@ -450,6 +457,7 @@ pub const Inst = struct { |
| 450 | .unwrap_optional_unsafe, | 457 | .unwrap_optional_unsafe, |
| 451 | .unwrap_err_safe, | 458 | .unwrap_err_safe, |
| 452 | .unwrap_err_unsafe, | 459 | .unwrap_err_unsafe, |
| 460 | .unwrap_err_code, | ||
| 453 | .ptr_type, | 461 | .ptr_type, |
| 454 | .ensure_err_payload_void, | 462 | .ensure_err_payload_void, |
| 455 | .enum_literal, | 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,6 +48,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 48 | .declval_in_module => return analyzeInstDeclValInModule(mod, scope, old_inst.castTag(.declval_in_module).?), | 48 | .declval_in_module => return analyzeInstDeclValInModule(mod, scope, old_inst.castTag(.declval_in_module).?), |
| 49 | .ensure_result_used => return analyzeInstEnsureResultUsed(mod, scope, old_inst.castTag(.ensure_result_used).?), | 49 | .ensure_result_used => return analyzeInstEnsureResultUsed(mod, scope, old_inst.castTag(.ensure_result_used).?), |
| 50 | .ensure_result_non_error => return analyzeInstEnsureResultNonError(mod, scope, old_inst.castTag(.ensure_result_non_error).?), | 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 | .ref => return analyzeInstRef(mod, scope, old_inst.castTag(.ref).?), | 52 | .ref => return analyzeInstRef(mod, scope, old_inst.castTag(.ref).?), |
| 52 | .ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?), | 53 | .ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?), |
| 53 | .ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?), | 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,7 +112,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 111 | .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?), | 112 | .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?), |
| 112 | .isnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnull).?, true), | 113 | .isnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnull).?, true), |
| 113 | .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false), | 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 | .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?), | 116 | .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?), |
| 116 | .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?), | 117 | .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?), |
| 117 | .optional_type => return analyzeInstOptionalType(mod, scope, old_inst.castTag(.optional_type).?), | 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,6 +120,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 119 | .unwrap_optional_unsafe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_unsafe).?, false), | 120 | .unwrap_optional_unsafe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_unsafe).?, false), |
| 120 | .unwrap_err_safe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_safe).?, true), | 121 | .unwrap_err_safe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_safe).?, true), |
| 121 | .unwrap_err_unsafe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_unsafe).?, false), | 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 | .ensure_err_payload_void => return analyzeInstEnsureErrPayloadVoid(mod, scope, old_inst.castTag(.ensure_err_payload_void).?), | 124 | .ensure_err_payload_void => return analyzeInstEnsureErrPayloadVoid(mod, scope, old_inst.castTag(.ensure_err_payload_void).?), |
| 123 | .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?), | 125 | .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?), |
| 124 | .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?), | 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,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 | fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | 400 | fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { |
| 386 | const var_type = try resolveType(mod, scope, inst.positionals.operand); | 401 | const var_type = try resolveType(mod, scope, inst.positionals.operand); |
| 387 | // TODO this should happen only for var allocs | 402 | // TODO this should happen only for var allocs |
| ... | @@ -786,11 +801,12 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp | ... | @@ -786,11 +801,12 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp |
| 786 | const operand = try resolveInst(mod, scope, unwrap.positionals.operand); | 801 | const operand = try resolveInst(mod, scope, unwrap.positionals.operand); |
| 787 | assert(operand.ty.zigTypeTag() == .Pointer); | 802 | assert(operand.ty.zigTypeTag() == .Pointer); |
| 788 | 803 | ||
| 789 | if (operand.ty.elemType().zigTypeTag() != .Optional) { | 804 | const elem_type = operand.ty.elemType(); |
| 790 | return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{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 | const child_pointer = try mod.simplePtrType(scope, unwrap.base.src, child_type, operand.ty.isConstPtr(), .One); | 810 | const child_pointer = try mod.simplePtrType(scope, unwrap.base.src, child_type, operand.ty.isConstPtr(), .One); |
| 795 | 811 | ||
| 796 | if (operand.value()) |val| { | 812 | if (operand.value()) |val| { |
| ... | @@ -815,6 +831,10 @@ fn analyzeInstUnwrapErr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, saf | ... | @@ -815,6 +831,10 @@ fn analyzeInstUnwrapErr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, saf |
| 815 | return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErr", .{}); | 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 | fn analyzeInstEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { | 838 | fn analyzeInstEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { |
| 819 | return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstEnsureErrPayloadVoid", .{}); | 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,7 +970,8 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr |
| 950 | const entry = if (val.cast(Value.Payload.ErrorSet)) |payload| | 970 | const entry = if (val.cast(Value.Payload.ErrorSet)) |payload| |
| 951 | (payload.fields.getEntry(field_name) orelse | 971 | (payload.fields.getEntry(field_name) orelse |
| 952 | return mod.fail(scope, fieldptr.base.src, "no error named '{}' in '{}'", .{ field_name, child_type })).* | 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 | const error_payload = try scope.arena().create(Value.Payload.Error); | 976 | const error_payload = try scope.arena().create(Value.Payload.Error); |
| 956 | error_payload.* = .{ | 977 | error_payload.* = .{ |
| ... | @@ -1062,9 +1083,19 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne | ... | @@ -1062,9 +1083,19 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne |
| 1062 | const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr); | 1083 | const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr); |
| 1063 | const uncasted_index = try resolveInst(mod, scope, inst.positionals.index); | 1084 | const uncasted_index = try resolveInst(mod, scope, inst.positionals.index); |
| 1064 | const elem_index = try mod.coerce(scope, Type.initTag(.usize), uncasted_index); | 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) { | 1095 | if (elem_ty.isSinglePointer() and elem_ty.elemType().zigTypeTag() == .Array) { |
| 1067 | if (array_ptr.value()) |array_ptr_val| { | 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 | if (elem_index.value()) |index_val| { | 1099 | if (elem_index.value()) |index_val| { |
| 1069 | // Both array pointer and index are compile-time known. | 1100 | // Both array pointer and index are compile-time known. |
| 1070 | const index_u64 = index_val.toUnsignedInt(); | 1101 | const index_u64 = index_val.toUnsignedInt(); |
| ... | @@ -1075,7 +1106,7 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne | ... | @@ -1075,7 +1106,7 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne |
| 1075 | const type_payload = try scope.arena().create(Type.Payload.PointerSimple); | 1106 | const type_payload = try scope.arena().create(Type.Payload.PointerSimple); |
| 1076 | type_payload.* = .{ | 1107 | type_payload.* = .{ |
| 1077 | .base = .{ .tag = .single_const_pointer }, | 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 | return mod.constInst(scope, inst.base.src, .{ | 1112 | return mod.constInst(scope, inst.base.src, .{ |
| ... | @@ -1274,17 +1305,7 @@ fn analyzeInstCmp( | ... | @@ -1274,17 +1305,7 @@ fn analyzeInstCmp( |
| 1274 | { | 1305 | { |
| 1275 | // comparing null with optionals | 1306 | // comparing null with optionals |
| 1276 | const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs; | 1307 | const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs; |
| 1277 | if (opt_operand.value()) |opt_val| { | 1308 | return mod.analyzeIsNull(scope, inst.base.src, opt_operand, op == .neq); |
| 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); | ||
| 1288 | } else if (is_equality_cmp and | 1309 | } else if (is_equality_cmp and |
| 1289 | ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr()))) | 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,8 +1353,9 @@ fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, inver |
| 1332 | return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic); | 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 { | 1356 | fn analyzeInstIsErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { |
| 1336 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstIsErr", .{}); | 1357 | const operand = try resolveInst(mod, scope, inst.positionals.operand); |
| 1358 | return mod.analyzeIsErr(scope, inst.base.src, operand); | ||
| 1337 | } | 1359 | } |
| 1338 | 1360 | ||
| 1339 | fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst { | 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,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,10 +43,11 @@ pub fn addCases(ctx: *TestContext) !void { |
| 43 | \\ | 43 | \\ |
| 44 | \\@entry = fn(@fnty, { | 44 | \\@entry = fn(@fnty, { |
| 45 | \\ %a = str("\x32\x08\x01\x0a") | 45 | \\ %a = str("\x32\x08\x01\x0a") |
| 46 | \\ %eptr0 = elemptr(%a, @0) | 46 | \\ %a_ref = ref(%a) |
| 47 | \\ %eptr1 = elemptr(%a, @1) | 47 | \\ %eptr0 = elemptr(%a_ref, @0) |
| 48 | \\ %eptr2 = elemptr(%a, @2) | 48 | \\ %eptr1 = elemptr(%a_ref, @1) |
| 49 | \\ %eptr3 = elemptr(%a, @3) | 49 | \\ %eptr2 = elemptr(%a_ref, @2) |
| 50 | \\ %eptr3 = elemptr(%a_ref, @3) | ||
| 50 | \\ %v0 = deref(%eptr0) | 51 | \\ %v0 = deref(%eptr0) |
| 51 | \\ %v1 = deref(%eptr1) | 52 | \\ %v1 = deref(%eptr1) |
| 52 | \\ %v2 = deref(%eptr2) | 53 | \\ %v2 = deref(%eptr2) |