| author | |
| committer | |
| log | ff72b8a8194573bc1d7f95cbf3228b363194c775 |
| tree | d5608b46018c2cd5c38314134e4d4e10100f7c04 |
| parent | db82c1b9820449f2d1e6ef54dd32ec3ffd3c583f |
6 files changed, 138 insertions(+), 42 deletions(-)
src/AstGen.zig+41-15| ... | ... | @@ -2159,6 +2159,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner |
| 2159 | 2159 | .negate, |
| 2160 | 2160 | .negate_wrap, |
| 2161 | 2161 | .typeof, |
| 2162 | .typeof_builtin, | |
| 2162 | 2163 | .xor, |
| 2163 | 2164 | .optional_type, |
| 2164 | 2165 | .optional_payload_safe, |
| ... | ... | @@ -6875,29 +6876,54 @@ fn typeOf( |
| 6875 | 6876 | scope: *Scope, |
| 6876 | 6877 | rl: ResultLoc, |
| 6877 | 6878 | node: Ast.Node.Index, |
| 6878 | params: []const Ast.Node.Index, | |
| 6879 | args: []const Ast.Node.Index, | |
| 6879 | 6880 | ) InnerError!Zir.Inst.Ref { |
| 6880 | if (params.len < 1) { | |
| 6881 | if (args.len < 1) { | |
| 6881 | 6882 | return gz.astgen.failNode(node, "expected at least 1 argument, found 0", .{}); |
| 6882 | 6883 | } |
| 6883 | if (params.len == 1) { | |
| 6884 | const expr_result = try reachableExpr(gz, scope, .none, params[0], node); | |
| 6885 | const result = try gz.addUnNode(.typeof, expr_result, node); | |
| 6886 | return rvalue(gz, rl, result, node); | |
| 6884 | const gpa = gz.astgen.gpa; | |
| 6885 | if (args.len == 1) { | |
| 6886 | const typeof_inst = try gz.makeBlockInst(.typeof_builtin, node); | |
| 6887 | ||
| 6888 | var typeof_scope = gz.makeSubBlock(scope); | |
| 6889 | typeof_scope.force_comptime = false; | |
| 6890 | defer typeof_scope.unstack(); | |
| 6891 | ||
| 6892 | const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .none, args[0], node); | |
| 6893 | if (!gz.refIsNoReturn(ty_expr)) { | |
| 6894 | _ = try typeof_scope.addBreak(.break_inline, typeof_inst, ty_expr); | |
| 6895 | } | |
| 6896 | try typeof_scope.setBlockBody(typeof_inst); | |
| 6897 | ||
| 6898 | // typeof_scope unstacked now, can add new instructions to gz | |
| 6899 | try gz.instructions.append(gpa, typeof_inst); | |
| 6900 | return rvalue(gz, rl, indexToRef(typeof_inst), node); | |
| 6887 | 6901 | } |
| 6902 | const payload_size: u32 = std.meta.fields(Zir.Inst.TypeOfPeer).len; | |
| 6903 | const payload_index = try reserveExtra(gz.astgen, payload_size + args.len); | |
| 6904 | var args_index = payload_index + payload_size; | |
| 6905 | ||
| 6906 | const typeof_inst = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, args.len); | |
| 6888 | 6907 | |
| 6889 | const payload_index = try addExtra(gz.astgen, Zir.Inst.NodeMultiOp{ | |
| 6908 | var typeof_scope = gz.makeSubBlock(scope); | |
| 6909 | typeof_scope.force_comptime = false; | |
| 6910 | ||
| 6911 | for (args) |arg, i| { | |
| 6912 | const param_ref = try reachableExpr(&typeof_scope, &typeof_scope.base, .none, arg, node); | |
| 6913 | gz.astgen.extra.items[args_index + i] = @enumToInt(param_ref); | |
| 6914 | } | |
| 6915 | _ = try typeof_scope.addBreak(.break_inline, refToIndex(typeof_inst).?, .void_value); | |
| 6916 | ||
| 6917 | const body = typeof_scope.instructionsSlice(); | |
| 6918 | gz.astgen.setExtra(payload_index, Zir.Inst.TypeOfPeer{ | |
| 6919 | .body_len = @intCast(u32, body.len), | |
| 6920 | .body_index = @intCast(u32, gz.astgen.extra.items.len), | |
| 6890 | 6921 | .src_node = gz.nodeIndexToRelative(node), |
| 6891 | 6922 | }); |
| 6892 | var extra_index = try reserveExtra(gz.astgen, params.len); | |
| 6893 | for (params) |param| { | |
| 6894 | const param_ref = try reachableExpr(gz, scope, .none, param, node); | |
| 6895 | gz.astgen.extra.items[extra_index] = @enumToInt(param_ref); | |
| 6896 | extra_index += 1; | |
| 6897 | } | |
| 6923 | try gz.astgen.extra.appendSlice(gpa, body); | |
| 6924 | typeof_scope.unstack(); | |
| 6898 | 6925 | |
| 6899 | const result = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, params.len); | |
| 6900 | return rvalue(gz, rl, result, node); | |
| 6926 | return rvalue(gz, rl, typeof_inst, node); | |
| 6901 | 6927 | } |
| 6902 | 6928 | |
| 6903 | 6929 | fn builtinCall( |
src/Sema.zig+43-1| ... | ... | @@ -124,6 +124,7 @@ pub const Block = struct { |
| 124 | 124 | runtime_index: u32 = 0, |
| 125 | 125 | |
| 126 | 126 | is_comptime: bool, |
| 127 | is_typeof: bool = false, | |
| 127 | 128 | |
| 128 | 129 | /// when null, it is determined by build mode, changed by @setRuntimeSafety |
| 129 | 130 | want_safety: ?bool = null, |
| ... | ... | @@ -181,6 +182,7 @@ pub const Block = struct { |
| 181 | 182 | .label = null, |
| 182 | 183 | .inlining = parent.inlining, |
| 183 | 184 | .is_comptime = parent.is_comptime, |
| 185 | .is_typeof = parent.is_typeof, | |
| 184 | 186 | .runtime_cond = parent.runtime_cond, |
| 185 | 187 | .runtime_loop = parent.runtime_loop, |
| 186 | 188 | .runtime_index = parent.runtime_index, |
| ... | ... | @@ -682,6 +684,7 @@ fn analyzeBodyInner( |
| 682 | 684 | .size_of => try sema.zirSizeOf(block, inst), |
| 683 | 685 | .bit_size_of => try sema.zirBitSizeOf(block, inst), |
| 684 | 686 | .typeof => try sema.zirTypeof(block, inst), |
| 687 | .typeof_builtin => try sema.zirTypeofBuiltin(block, inst), | |
| 685 | 688 | .log2_int_type => try sema.zirLog2IntType(block, inst), |
| 686 | 689 | .typeof_log2_int_type => try sema.zirTypeofLog2IntType(block, inst), |
| 687 | 690 | .xor => try sema.zirBitwise(block, inst, .xor), |
| ... | ... | @@ -10574,6 +10577,29 @@ fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. |
| 10574 | 10577 | return sema.addType(operand_ty); |
| 10575 | 10578 | } |
| 10576 | 10579 | |
| 10580 | fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 10581 | const pl_node = sema.code.instructions.items(.data)[inst].pl_node; | |
| 10582 | const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); | |
| 10583 | const body = sema.code.extra[extra.end..][0..extra.data.body_len]; | |
| 10584 | ||
| 10585 | var child_block: Block = .{ | |
| 10586 | .parent = block, | |
| 10587 | .sema = sema, | |
| 10588 | .src_decl = block.src_decl, | |
| 10589 | .namespace = block.namespace, | |
| 10590 | .wip_capture_scope = block.wip_capture_scope, | |
| 10591 | .instructions = .{}, | |
| 10592 | .inlining = block.inlining, | |
| 10593 | .is_comptime = false, | |
| 10594 | .is_typeof = true, | |
| 10595 | }; | |
| 10596 | defer child_block.instructions.deinit(sema.gpa); | |
| 10597 | ||
| 10598 | const operand = try sema.resolveBody(&child_block, body, inst); | |
| 10599 | const operand_ty = sema.typeOf(operand); | |
| 10600 | return sema.addType(operand_ty); | |
| 10601 | } | |
| 10602 | ||
| 10577 | 10603 | fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 10578 | 10604 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 10579 | 10605 | const src = inst_data.src(); |
| ... | ... | @@ -10624,8 +10650,24 @@ fn zirTypeofPeer( |
| 10624 | 10650 | const tracy = trace(@src()); |
| 10625 | 10651 | defer tracy.end(); |
| 10626 | 10652 | |
| 10627 | const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); | |
| 10653 | const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand); | |
| 10628 | 10654 | const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; |
| 10655 | const body = sema.code.extra[extra.data.body_index..][0..extra.data.body_len]; | |
| 10656 | ||
| 10657 | var child_block: Block = .{ | |
| 10658 | .parent = block, | |
| 10659 | .sema = sema, | |
| 10660 | .src_decl = block.src_decl, | |
| 10661 | .namespace = block.namespace, | |
| 10662 | .wip_capture_scope = block.wip_capture_scope, | |
| 10663 | .instructions = .{}, | |
| 10664 | .inlining = block.inlining, | |
| 10665 | .is_comptime = false, | |
| 10666 | .is_typeof = true, | |
| 10667 | }; | |
| 10668 | defer child_block.instructions.deinit(sema.gpa); | |
| 10669 | _ = try sema.analyzeBody(&child_block, body); | |
| 10670 | ||
| 10629 | 10671 | const args = sema.code.refSlice(extra.end, extended.small); |
| 10630 | 10672 | |
| 10631 | 10673 | const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len); |
src/Zir.zig+11| ... | ... | @@ -547,6 +547,9 @@ pub const Inst = struct { |
| 547 | 547 | /// Returns the type of a value. |
| 548 | 548 | /// Uses the `un_node` field. |
| 549 | 549 | typeof, |
| 550 | /// Implements `@TypeOf` for one operand. | |
| 551 | /// Uses the `pl_node` field. | |
| 552 | typeof_builtin, | |
| 550 | 553 | /// Given a value, look at the type of it, which must be an integer type. |
| 551 | 554 | /// Returns the integer type for the RHS of a shift operation. |
| 552 | 555 | /// Uses the `un_node` field. |
| ... | ... | @@ -1067,6 +1070,7 @@ pub const Inst = struct { |
| 1067 | 1070 | .negate, |
| 1068 | 1071 | .negate_wrap, |
| 1069 | 1072 | .typeof, |
| 1073 | .typeof_builtin, | |
| 1070 | 1074 | .xor, |
| 1071 | 1075 | .optional_type, |
| 1072 | 1076 | .optional_payload_safe, |
| ... | ... | @@ -1429,6 +1433,7 @@ pub const Inst = struct { |
| 1429 | 1433 | .ptr_cast = .pl_node, |
| 1430 | 1434 | .truncate = .pl_node, |
| 1431 | 1435 | .align_cast = .pl_node, |
| 1436 | .typeof_builtin = .pl_node, | |
| 1432 | 1437 | |
| 1433 | 1438 | .has_decl = .pl_node, |
| 1434 | 1439 | .has_field = .pl_node, |
| ... | ... | @@ -2391,6 +2396,12 @@ pub const Inst = struct { |
| 2391 | 2396 | }; |
| 2392 | 2397 | }; |
| 2393 | 2398 | |
| 2399 | pub const TypeOfPeer = struct { | |
| 2400 | src_node: i32, | |
| 2401 | body_len: u32, | |
| 2402 | body_index: u32, | |
| 2403 | }; | |
| 2404 | ||
| 2394 | 2405 | pub const BuiltinCall = struct { |
| 2395 | 2406 | options: Ref, |
| 2396 | 2407 | callee: Ref, |
src/print_zir.zig+16-3| ... | ... | @@ -374,6 +374,7 @@ const Writer = struct { |
| 374 | 374 | .validate_array_init, |
| 375 | 375 | .validate_array_init_comptime, |
| 376 | 376 | .c_import, |
| 377 | .typeof_builtin, | |
| 377 | 378 | => try self.writePlNodeBlock(stream, inst), |
| 378 | 379 | |
| 379 | 380 | .condbr, |
| ... | ... | @@ -458,9 +459,8 @@ const Writer = struct { |
| 458 | 459 | .variable => try self.writeVarExtended(stream, extended), |
| 459 | 460 | .alloc => try self.writeAllocExtended(stream, extended), |
| 460 | 461 | |
| 461 | .compile_log, | |
| 462 | .typeof_peer, | |
| 463 | => try self.writeNodeMultiOp(stream, extended), | |
| 462 | .compile_log => try self.writeNodeMultiOp(stream, extended), | |
| 463 | .typeof_peer => try self.writeTypeofPeer(stream, extended), | |
| 464 | 464 | |
| 465 | 465 | .add_with_overflow, |
| 466 | 466 | .sub_with_overflow, |
| ... | ... | @@ -1966,6 +1966,19 @@ const Writer = struct { |
| 1966 | 1966 | try self.writeSrc(stream, src); |
| 1967 | 1967 | } |
| 1968 | 1968 | |
| 1969 | fn writeTypeofPeer(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { | |
| 1970 | const extra = self.code.extraData(Zir.Inst.TypeOfPeer, extended.operand); | |
| 1971 | const body = self.code.extra[extra.data.body_index..][0..extra.data.body_len]; | |
| 1972 | try self.writeBracedBody(stream, body); | |
| 1973 | try stream.writeAll(",["); | |
| 1974 | const args = self.code.refSlice(extra.end, extended.small); | |
| 1975 | for (args) |arg, i| { | |
| 1976 | if (i != 0) try stream.writeAll(", "); | |
| 1977 | try self.writeInstRef(stream, arg); | |
| 1978 | } | |
| 1979 | try stream.writeAll("])"); | |
| 1980 | } | |
| 1981 | ||
| 1969 | 1982 | fn writeBoolBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { |
| 1970 | 1983 | const inst_data = self.code.instructions.items(.data)[inst].bool_br; |
| 1971 | 1984 | const extra = self.code.extraData(Zir.Inst.Block, inst_data.payload_index); |
test/behavior.zig+1-1| ... | ... | @@ -102,6 +102,7 @@ test { |
| 102 | 102 | builtin.zig_backend != .stage2_wasm) |
| 103 | 103 | { |
| 104 | 104 | // Tests that pass for stage1, llvm backend, C backend |
| 105 | _ = @import("behavior/bugs/5474.zig"); | |
| 105 | 106 | _ = @import("behavior/bugs/9584.zig"); |
| 106 | 107 | _ = @import("behavior/bugs/10970.zig"); |
| 107 | 108 | _ = @import("behavior/cast_int.zig"); |
| ... | ... | @@ -152,7 +153,6 @@ test { |
| 152 | 153 | _ = @import("behavior/bugs/4328.zig"); |
| 153 | 154 | _ = @import("behavior/bugs/5398.zig"); |
| 154 | 155 | _ = @import("behavior/bugs/5413.zig"); |
| 155 | _ = @import("behavior/bugs/5474.zig"); | |
| 156 | 156 | _ = @import("behavior/bugs/5487.zig"); |
| 157 | 157 | _ = @import("behavior/bugs/6456.zig"); |
| 158 | 158 | _ = @import("behavior/bugs/6781.zig"); |
test/behavior/bugs/5474.zig+26-22| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | |
| 2 | 3 | |
| 3 | 4 | // baseline (control) struct with array of scalar |
| 4 | 5 | const Box0 = struct { |
| ... | ... | @@ -25,33 +26,36 @@ const Box2 = struct { |
| 25 | 26 | }; |
| 26 | 27 | }; |
| 27 | 28 | |
| 28 | fn doTest() !void { | |
| 29 | // var | |
| 30 | { | |
| 31 | var box0: Box0 = .{ .items = undefined }; | |
| 32 | try std.testing.expect(@typeInfo(@TypeOf(box0.items[0..])).Pointer.is_const == false); | |
| 29 | fn mutable() !void { | |
| 30 | var box0: Box0 = .{ .items = undefined }; | |
| 31 | try std.testing.expect(@typeInfo(@TypeOf(box0.items[0..])).Pointer.is_const == false); | |
| 33 | 32 | |
| 34 | var box1: Box1 = .{ .items = undefined }; | |
| 35 | try std.testing.expect(@typeInfo(@TypeOf(box1.items[0..])).Pointer.is_const == false); | |
| 33 | var box1: Box1 = .{ .items = undefined }; | |
| 34 | try std.testing.expect(@typeInfo(@TypeOf(box1.items[0..])).Pointer.is_const == false); | |
| 36 | 35 | |
| 37 | var box2: Box2 = .{ .items = undefined }; | |
| 38 | try std.testing.expect(@typeInfo(@TypeOf(box2.items[0..])).Pointer.is_const == false); | |
| 39 | } | |
| 36 | var box2: Box2 = .{ .items = undefined }; | |
| 37 | try std.testing.expect(@typeInfo(@TypeOf(box2.items[0..])).Pointer.is_const == false); | |
| 38 | } | |
| 39 | ||
| 40 | fn constant() !void { | |
| 41 | const box0: Box0 = .{ .items = undefined }; | |
| 42 | try std.testing.expect(@typeInfo(@TypeOf(box0.items[0..])).Pointer.is_const == true); | |
| 40 | 43 | |
| 41 | // const | |
| 42 | { | |
| 43 | const box0: Box0 = .{ .items = undefined }; | |
| 44 | try std.testing.expect(@typeInfo(@TypeOf(box0.items[0..])).Pointer.is_const == true); | |
| 44 | const box1: Box1 = .{ .items = undefined }; | |
| 45 | try std.testing.expect(@typeInfo(@TypeOf(box1.items[0..])).Pointer.is_const == true); | |
| 45 | 46 | |
| 46 | const box1: Box1 = .{ .items = undefined }; | |
| 47 | try std.testing.expect(@typeInfo(@TypeOf(box1.items[0..])).Pointer.is_const == true); | |
| 47 | const box2: Box2 = .{ .items = undefined }; | |
| 48 | try std.testing.expect(@typeInfo(@TypeOf(box2.items[0..])).Pointer.is_const == true); | |
| 49 | } | |
| 48 | 50 | |
| 49 | const box2: Box2 = .{ .items = undefined }; | |
| 50 | try std.testing.expect(@typeInfo(@TypeOf(box2.items[0..])).Pointer.is_const == true); | |
| 51 | } | |
| 51 | test "pointer-to-array constness for zero-size elements, var" { | |
| 52 | try mutable(); | |
| 53 | comptime try mutable(); | |
| 52 | 54 | } |
| 53 | 55 | |
| 54 | test "pointer-to-array constness for zero-size elements" { | |
| 55 | try doTest(); | |
| 56 | comptime try doTest(); | |
| 56 | test "pointer-to-array constness for zero-size elements, const" { | |
| 57 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 58 | ||
| 59 | try constant(); | |
| 60 | comptime try constant(); | |
| 57 | 61 | } |