authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-21 11:28:40-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-12-21 11:28:40-08:00
log8df540aeef33b9b02e98aebe311299c101ce44b9
tree95e6f1de6f6225ebb2e254ec9421240ec52af338
parent7e16bb36d82cf45cd5f6f4da38fba512554f66ed
parente106e18d96595bdc4bc037e0b36900992a576160
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10370 from Snektron/stage2-inferred-error-sets-2

stage2: Make page_allocator work

21 files changed, 1158 insertions(+), 381 deletions(-)

lib/std/math/big/int.zig+23-13
......@@ -443,12 +443,12 @@ pub const Mutable = struct {
443443 }
444444 }
445445
446 /// r = a + b with 2s-complement wrapping semantics.
446 /// r = a + b with 2s-complement wrapping semantics. Returns whether overflow occurred.
447447 /// r, a and b may be aliases
448448 ///
449449 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
450450 /// r is `calcTwosCompLimbCount(bit_count)`.
451 pub fn addWrap(r: *Mutable, a: Const, b: Const, signedness: Signedness, bit_count: usize) void {
451 pub fn addWrap(r: *Mutable, a: Const, b: Const, signedness: Signedness, bit_count: usize) bool {
452452 const req_limbs = calcTwosCompLimbCount(bit_count);
453453
454454 // Slice of the upper bits if they exist, these will be ignored and allows us to use addCarry to determine
......@@ -463,6 +463,7 @@ pub const Mutable = struct {
463463 .limbs = b.limbs[0..math.min(req_limbs, b.limbs.len)],
464464 };
465465
466 var carry_truncated = false;
466467 if (r.addCarry(x, y)) {
467468 // There are two possibilities here:
468469 // - We overflowed req_limbs. In this case, the carry is ignored, as it would be removed by
......@@ -473,10 +474,17 @@ pub const Mutable = struct {
473474 if (msl < req_limbs) {
474475 r.limbs[msl] = 1;
475476 r.len = req_limbs;
477 } else {
478 carry_truncated = true;
476479 }
477480 }
478481
479 r.truncate(r.toConst(), signedness, bit_count);
482 if (!r.toConst().fitsInTwosComp(signedness, bit_count)) {
483 r.truncate(r.toConst(), signedness, bit_count);
484 return true;
485 }
486
487 return carry_truncated;
480488 }
481489
482490 /// r = a + b with 2s-complement saturating semantics.
......@@ -581,13 +589,13 @@ pub const Mutable = struct {
581589 r.add(a, b.negate());
582590 }
583591
584 /// r = a - b with 2s-complement wrapping semantics.
592 /// r = a - b with 2s-complement wrapping semantics. Returns whether any overflow occured.
585593 ///
586594 /// r, a and b may be aliases
587595 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
588596 /// r is `calcTwosCompLimbCount(bit_count)`.
589 pub fn subWrap(r: *Mutable, a: Const, b: Const, signedness: Signedness, bit_count: usize) void {
590 r.addWrap(a, b.negate(), signedness, bit_count);
597 pub fn subWrap(r: *Mutable, a: Const, b: Const, signedness: Signedness, bit_count: usize) bool {
598 return r.addWrap(a, b.negate(), signedness, bit_count);
591599 }
592600
593601 /// r = a - b with 2s-complement saturating semantics.
......@@ -1039,7 +1047,7 @@ pub const Mutable = struct {
10391047 pub fn bitNotWrap(r: *Mutable, a: Const, signedness: Signedness, bit_count: usize) void {
10401048 r.copy(a.negate());
10411049 const negative_one = Const{ .limbs = &.{1}, .positive = false };
1042 r.addWrap(r.toConst(), negative_one, signedness, bit_count);
1050 _ = r.addWrap(r.toConst(), negative_one, signedness, bit_count);
10431051 }
10441052
10451053 /// r = a | b under 2s complement semantics.
......@@ -2443,17 +2451,18 @@ pub const Managed = struct {
24432451 r.setMetadata(m.positive, m.len);
24442452 }
24452453
2446 /// r = a + b with 2s-complement wrapping semantics.
2454 /// r = a + b with 2s-complement wrapping semantics. Returns whether any overflow occured.
24472455 ///
24482456 /// r, a and b may be aliases. If r aliases a or b, then caller must call
24492457 /// `r.ensureTwosCompCapacity` prior to calling `add`.
24502458 ///
24512459 /// Returns an error if memory could not be allocated.
2452 pub fn addWrap(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!void {
2460 pub fn addWrap(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!bool {
24532461 try r.ensureTwosCompCapacity(bit_count);
24542462 var m = r.toMutable();
2455 m.addWrap(a, b, signedness, bit_count);
2463 const wrapped = m.addWrap(a, b, signedness, bit_count);
24562464 r.setMetadata(m.positive, m.len);
2465 return wrapped;
24572466 }
24582467
24592468 /// r = a + b with 2s-complement saturating semantics.
......@@ -2481,17 +2490,18 @@ pub const Managed = struct {
24812490 r.setMetadata(m.positive, m.len);
24822491 }
24832492
2484 /// r = a - b with 2s-complement wrapping semantics.
2493 /// r = a - b with 2s-complement wrapping semantics. Returns whether any overflow occured.
24852494 ///
24862495 /// r, a and b may be aliases. If r aliases a or b, then caller must call
24872496 /// `r.ensureTwosCompCapacity` prior to calling `add`.
24882497 ///
24892498 /// Returns an error if memory could not be allocated.
2490 pub fn subWrap(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!void {
2499 pub fn subWrap(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!bool {
24912500 try r.ensureTwosCompCapacity(bit_count);
24922501 var m = r.toMutable();
2493 m.subWrap(a, b, signedness, bit_count);
2502 const wrapped = m.subWrap(a, b, signedness, bit_count);
24942503 r.setMetadata(m.positive, m.len);
2504 return wrapped;
24952505 }
24962506
24972507 /// r = a - b with 2s-complement saturating semantics.
lib/std/math/big/int_test.zig+16-8
......@@ -590,8 +590,9 @@ test "big.int addWrap single-single, unsigned" {
590590 var b = try Managed.initSet(testing.allocator, 10);
591591 defer b.deinit();
592592
593 try a.addWrap(a.toConst(), b.toConst(), .unsigned, 17);
593 const wrapped = try a.addWrap(a.toConst(), b.toConst(), .unsigned, 17);
594594
595 try testing.expect(wrapped);
595596 try testing.expect((try a.to(u17)) == 9);
596597}
597598
......@@ -602,8 +603,9 @@ test "big.int subWrap single-single, unsigned" {
602603 var b = try Managed.initSet(testing.allocator, maxInt(u17));
603604 defer b.deinit();
604605
605 try a.subWrap(a.toConst(), b.toConst(), .unsigned, 17);
606 const wrapped = try a.subWrap(a.toConst(), b.toConst(), .unsigned, 17);
606607
608 try testing.expect(wrapped);
607609 try testing.expect((try a.to(u17)) == 1);
608610}
609611
......@@ -614,8 +616,9 @@ test "big.int addWrap multi-multi, unsigned, limb aligned" {
614616 var b = try Managed.initSet(testing.allocator, maxInt(DoubleLimb));
615617 defer b.deinit();
616618
617 try a.addWrap(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb));
619 const wrapped = try a.addWrap(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb));
618620
621 try testing.expect(wrapped);
619622 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) - 1);
620623}
621624
......@@ -626,8 +629,9 @@ test "big.int subWrap single-multi, unsigned, limb aligned" {
626629 var b = try Managed.initSet(testing.allocator, maxInt(DoubleLimb) + 100);
627630 defer b.deinit();
628631
629 try a.subWrap(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb));
632 const wrapped = try a.subWrap(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb));
630633
634 try testing.expect(wrapped);
631635 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) - 88);
632636}
633637
......@@ -638,8 +642,9 @@ test "big.int addWrap single-single, signed" {
638642 var b = try Managed.initSet(testing.allocator, 1 + 1 + maxInt(u21));
639643 defer b.deinit();
640644
641 try a.addWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(i21));
645 const wrapped = try a.addWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(i21));
642646
647 try testing.expect(wrapped);
643648 try testing.expect((try a.to(i21)) == minInt(i21));
644649}
645650
......@@ -650,8 +655,9 @@ test "big.int subWrap single-single, signed" {
650655 var b = try Managed.initSet(testing.allocator, 1);
651656 defer b.deinit();
652657
653 try a.subWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(i21));
658 const wrapped = try a.subWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(i21));
654659
660 try testing.expect(wrapped);
655661 try testing.expect((try a.to(i21)) == maxInt(i21));
656662}
657663
......@@ -662,8 +668,9 @@ test "big.int addWrap multi-multi, signed, limb aligned" {
662668 var b = try Managed.initSet(testing.allocator, maxInt(SignedDoubleLimb));
663669 defer b.deinit();
664670
665 try a.addWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb));
671 const wrapped = try a.addWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb));
666672
673 try testing.expect(wrapped);
667674 try testing.expect((try a.to(SignedDoubleLimb)) == -2);
668675}
669676
......@@ -674,8 +681,9 @@ test "big.int subWrap single-multi, signed, limb aligned" {
674681 var b = try Managed.initSet(testing.allocator, 1);
675682 defer b.deinit();
676683
677 try a.subWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb));
684 const wrapped = try a.subWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb));
678685
686 try testing.expect(wrapped);
679687 try testing.expect((try a.to(SignedDoubleLimb)) == maxInt(SignedDoubleLimb));
680688}
681689
lib/std/os.zig+5-1
......@@ -4968,7 +4968,11 @@ pub fn toPosixPath(file_path: []const u8) ![MAX_PATH_BYTES - 1:0]u8 {
49684968/// if this happens the fix is to add the error code to the corresponding
49694969/// switch expression, possibly introduce a new error in the error set, and
49704970/// send a patch to Zig.
4971pub const unexpected_error_tracing = builtin.mode == .Debug;
4971/// The self-hosted compiler is not fully capable of handle the related code.
4972/// Until then, unexpected error tracing is disabled for the self-hosted compiler.
4973/// TODO remove this once self-hosted is capable enough to handle printing and
4974/// stack trace dumping.
4975pub const unexpected_error_tracing = !builtin.zig_is_stage2 and builtin.mode == .Debug;
49724976
49734977pub const UnexpectedError = error{
49744978 /// The Operating System returned an undocumented error code.
src/Air.zig+34
......@@ -135,6 +135,30 @@ pub const Inst = struct {
135135 /// is the same as both operands.
136136 /// Uses the `bin_op` field.
137137 min,
138 /// Integer addition with overflow. Both operands are guaranteed to be the same type,
139 /// and the result is bool. The wrapped value is written to the pointer given by the in
140 /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types
141 /// of the operation.
142 /// Uses the `pl_op` field with payload `Bin`.
143 add_with_overflow,
144 /// Integer subtraction with overflow. Both operands are guaranteed to be the same type,
145 /// and the result is bool. The wrapped value is written to the pointer given by the in
146 /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types
147 /// of the operation.
148 /// Uses the `pl_op` field with payload `Bin`.
149 sub_with_overflow,
150 /// Integer multiplication with overflow. Both operands are guaranteed to be the same type,
151 /// and the result is bool. The wrapped value is written to the pointer given by the in
152 /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types
153 /// of the operation.
154 /// Uses the `pl_op` field with payload `Bin`.
155 mul_with_overflow,
156 /// Integer left-shift with overflow. Both operands are guaranteed to be the same type,
157 /// and the result is bool. The wrapped value is written to the pointer given by the in
158 /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types
159 /// of the operation.
160 /// Uses the `pl_op` field with payload `Bin`.
161 shl_with_overflow,
138162 /// Allocates stack local memory.
139163 /// Uses the `ty` field.
140164 alloc,
......@@ -189,6 +213,9 @@ pub const Inst = struct {
189213 /// Lowers to a hardware trap instruction, or the next best thing.
190214 /// Result type is always void.
191215 breakpoint,
216 /// Yields the return address of the current function.
217 /// Uses the `no_op` field.
218 ret_addr,
192219 /// Function call.
193220 /// Result type is the return type of the function being called.
194221 /// Uses the `pl_op` field with the `Call` payload. operand is the callee.
......@@ -779,6 +806,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
779806
780807 .ptrtoint,
781808 .slice_len,
809 .ret_addr,
782810 => return Type.initTag(.usize),
783811
784812 .bool_to_int => return Type.initTag(.u1),
......@@ -804,6 +832,12 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
804832 const ptr_ty = air.typeOf(datas[inst].pl_op.operand);
805833 return ptr_ty.elemType();
806834 },
835
836 .add_with_overflow,
837 .sub_with_overflow,
838 .mul_with_overflow,
839 .shl_with_overflow,
840 => return Type.initTag(.bool),
807841 }
808842}
809843
src/AstGen.zig+24-13
......@@ -984,17 +984,17 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
984984
985985 .fn_proto_simple => {
986986 var params: [1]Ast.Node.Index = undefined;
987 return fnProtoExpr(gz, scope, rl, tree.fnProtoSimple(&params, node));
987 return fnProtoExpr(gz, scope, rl, node, tree.fnProtoSimple(&params, node));
988988 },
989989 .fn_proto_multi => {
990 return fnProtoExpr(gz, scope, rl, tree.fnProtoMulti(node));
990 return fnProtoExpr(gz, scope, rl, node, tree.fnProtoMulti(node));
991991 },
992992 .fn_proto_one => {
993993 var params: [1]Ast.Node.Index = undefined;
994 return fnProtoExpr(gz, scope, rl, tree.fnProtoOne(&params, node));
994 return fnProtoExpr(gz, scope, rl, node, tree.fnProtoOne(&params, node));
995995 },
996996 .fn_proto => {
997 return fnProtoExpr(gz, scope, rl, tree.fnProto(node));
997 return fnProtoExpr(gz, scope, rl, node, tree.fnProto(node));
998998 },
999999 }
10001000}
......@@ -1101,6 +1101,7 @@ fn fnProtoExpr(
11011101 gz: *GenZir,
11021102 scope: *Scope,
11031103 rl: ResultLoc,
1104 node: Ast.Node.Index,
11041105 fn_proto: Ast.full.FnProto,
11051106) InnerError!Zir.Inst.Ref {
11061107 const astgen = gz.astgen;
......@@ -1113,6 +1114,11 @@ fn fnProtoExpr(
11131114 };
11141115 assert(!is_extern);
11151116
1117 var block_scope = gz.makeSubBlock(scope);
1118 defer block_scope.unstack();
1119
1120 const block_inst = try gz.makeBlockInst(.block_inline, node);
1121
11161122 const is_var_args = is_var_args: {
11171123 var param_type_i: usize = 0;
11181124 var it = fn_proto.iterate(tree.*);
......@@ -1144,11 +1150,11 @@ fn fnProtoExpr(
11441150 .param_anytype_comptime
11451151 else
11461152 .param_anytype;
1147 _ = try gz.addStrTok(tag, param_name, name_token);
1153 _ = try block_scope.addStrTok(tag, param_name, name_token);
11481154 } else {
11491155 const param_type_node = param.type_expr;
11501156 assert(param_type_node != 0);
1151 var param_gz = gz.makeSubBlock(scope);
1157 var param_gz = block_scope.makeSubBlock(scope);
11521158 defer param_gz.unstack();
11531159 const param_type = try expr(&param_gz, scope, coerced_type_rl, param_type_node);
11541160 const param_inst_expected = @intCast(u32, astgen.instructions.len + 1);
......@@ -1156,7 +1162,7 @@ fn fnProtoExpr(
11561162 const main_tokens = tree.nodes.items(.main_token);
11571163 const name_token = param.name_token orelse main_tokens[param_type_node];
11581164 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
1159 const param_inst = try gz.addParam(&param_gz, tag, name_token, param_name);
1165 const param_inst = try block_scope.addParam(&param_gz, tag, name_token, param_name);
11601166 assert(param_inst_expected == param_inst);
11611167 }
11621168 }
......@@ -1164,7 +1170,7 @@ fn fnProtoExpr(
11641170 };
11651171
11661172 const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
1167 break :inst try expr(gz, scope, align_rl, fn_proto.ast.align_expr);
1173 break :inst try expr(&block_scope, scope, align_rl, fn_proto.ast.align_expr);
11681174 };
11691175
11701176 if (fn_proto.ast.addrspace_expr != 0) {
......@@ -1177,7 +1183,7 @@ fn fnProtoExpr(
11771183
11781184 const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0)
11791185 try expr(
1180 gz,
1186 &block_scope,
11811187 scope,
11821188 .{ .ty = .calling_convention_type },
11831189 fn_proto.ast.callconv_expr,
......@@ -1190,14 +1196,14 @@ fn fnProtoExpr(
11901196 if (is_inferred_error) {
11911197 return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
11921198 }
1193 var ret_gz = gz.makeSubBlock(scope);
1199 var ret_gz = block_scope.makeSubBlock(scope);
11941200 defer ret_gz.unstack();
11951201 const ret_ty = try expr(&ret_gz, scope, coerced_type_rl, fn_proto.ast.return_type);
11961202 const ret_br = try ret_gz.addBreak(.break_inline, 0, ret_ty);
11971203
1198 const result = try gz.addFunc(.{
1204 const result = try block_scope.addFunc(.{
11991205 .src_node = fn_proto.ast.proto_node,
1200 .param_block = 0,
1206 .param_block = block_inst,
12011207 .ret_gz = &ret_gz,
12021208 .ret_br = ret_br,
12031209 .body_gz = null,
......@@ -1209,7 +1215,12 @@ fn fnProtoExpr(
12091215 .is_test = false,
12101216 .is_extern = false,
12111217 });
1212 return rvalue(gz, rl, result, fn_proto.ast.proto_node);
1218
1219 _ = try block_scope.addBreak(.break_inline, block_inst, result);
1220 try block_scope.setBlockBody(block_inst);
1221 try gz.instructions.append(astgen.gpa, block_inst);
1222
1223 return rvalue(gz, rl, indexToRef(block_inst), fn_proto.ast.proto_node);
12131224}
12141225
12151226fn arrayInitExpr(
src/Liveness.zig+8-1
......@@ -281,6 +281,7 @@ fn analyzeInst(
281281 .dbg_stmt,
282282 .unreach,
283283 .fence,
284 .ret_addr,
284285 => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
285286
286287 .not,
......@@ -381,7 +382,13 @@ fn analyzeInst(
381382 const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data;
382383 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.operand, .none });
383384 },
384 .memset, .memcpy => {
385 .memset,
386 .memcpy,
387 .add_with_overflow,
388 .sub_with_overflow,
389 .mul_with_overflow,
390 .shl_with_overflow,
391 => {
385392 const pl_op = inst_datas[inst].pl_op;
386393 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;
387394 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs });
src/Module.zig+70-21
......@@ -796,15 +796,11 @@ pub const ErrorSet = struct {
796796 owner_decl: *Decl,
797797 /// Offset from Decl node index, points to the error set AST node.
798798 node_offset: i32,
799 names_len: u32,
800799 /// The string bytes are stored in the owner Decl arena.
801800 /// They are in the same order they appear in the AST.
802 /// The length is given by `names_len`.
803 names_ptr: [*]const []const u8,
801 names: NameMap,
804802
805 pub fn names(self: ErrorSet) []const []const u8 {
806 return self.names_ptr[0..self.names_len];
807 }
803 pub const NameMap = std.StringArrayHashMapUnmanaged(void);
808804
809805 pub fn srcLoc(self: ErrorSet) SrcLoc {
810806 return .{
......@@ -1211,6 +1207,10 @@ pub const Fn = struct {
12111207 is_cold: bool = false,
12121208 is_noinline: bool = false,
12131209
1210 /// Any inferred error sets that this function owns, both it's own inferred error set and
1211 /// inferred error sets of any inline/comptime functions called.
1212 inferred_error_sets: InferredErrorSetList = .{},
1213
12141214 pub const Analysis = enum {
12151215 queued,
12161216 /// This function intentionally only has ZIR generated because it is marked
......@@ -1225,24 +1225,73 @@ pub const Fn = struct {
12251225 success,
12261226 };
12271227
1228 pub fn deinit(func: *Fn, gpa: Allocator) void {
1229 if (func.getInferredErrorSet()) |error_set_data| {
1230 error_set_data.map.deinit(gpa);
1231 error_set_data.functions.deinit(gpa);
1232 }
1233 }
1228 /// This struct is used to keep track of any dependencies related to functions instances
1229 /// that return inferred error sets. Note that a function may be associated to multiple different error sets,
1230 /// for example an inferred error set which this function returns, but also any inferred error sets
1231 /// of called inline or comptime functions.
1232 pub const InferredErrorSet = struct {
1233 /// The function from which this error set originates.
1234 /// Note: may be the function itself.
1235 func: *Fn,
12341236
1235 pub fn getInferredErrorSet(func: *Fn) ?*Type.Payload.ErrorSetInferred.Data {
1236 const ret_ty = func.owner_decl.ty.fnReturnType();
1237 if (ret_ty.tag() == .generic_poison) {
1238 return null;
1239 }
1240 if (ret_ty.zigTypeTag() == .ErrorUnion) {
1241 if (ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| {
1242 return &payload.data;
1237 /// All currently known errors that this error set contains. This includes direct additions
1238 /// via `return error.Foo;`, and possibly also errors that are returned from any dependent functions.
1239 /// When the inferred error set is fully resolved, this map contains all the errors that the function might return.
1240 errors: std.StringHashMapUnmanaged(void) = .{},
1241
1242 /// Other inferred error sets which this inferred error set should include.
1243 inferred_error_sets: std.AutoHashMapUnmanaged(*InferredErrorSet, void) = .{},
1244
1245 /// Whether the function returned anyerror. This is true if either of the dependent functions
1246 /// returns anyerror.
1247 is_anyerror: bool = false,
1248
1249 /// Whether this error set is already fully resolved. If true, resolving can skip resolving any dependents
1250 /// of this inferred error set.
1251 is_resolved: bool = false,
1252
1253 pub fn addErrorSet(self: *InferredErrorSet, gpa: Allocator, err_set_ty: Type) !void {
1254 switch (err_set_ty.tag()) {
1255 .error_set => {
1256 const names = err_set_ty.castTag(.error_set).?.data.names.keys();
1257 for (names) |name| {
1258 try self.errors.put(gpa, name, {});
1259 }
1260 },
1261 .error_set_single => {
1262 const name = err_set_ty.castTag(.error_set_single).?.data;
1263 try self.errors.put(gpa, name, {});
1264 },
1265 .error_set_inferred => {
1266 const set = err_set_ty.castTag(.error_set_inferred).?.data;
1267 try self.inferred_error_sets.put(gpa, set, {});
1268 },
1269 .error_set_merged => {
1270 const names = err_set_ty.castTag(.error_set_merged).?.data.keys();
1271 for (names) |name| {
1272 try self.errors.put(gpa, name, {});
1273 }
1274 },
1275 .anyerror => {
1276 self.is_anyerror = true;
1277 },
1278 else => unreachable,
12431279 }
12441280 }
1245 return null;
1281 };
1282
1283 pub const InferredErrorSetList = std.SinglyLinkedList(InferredErrorSet);
1284 pub const InferredErrorSetListNode = InferredErrorSetList.Node;
1285
1286 pub fn deinit(func: *Fn, gpa: Allocator) void {
1287 var it = func.inferred_error_sets.first;
1288 while (it) |node| {
1289 const next = node.next;
1290 node.data.errors.deinit(gpa);
1291 node.data.inferred_error_sets.deinit(gpa);
1292 gpa.destroy(node);
1293 it = next;
1294 }
12461295 }
12471296};
12481297
src/Sema.zig+432-138
......@@ -940,6 +940,15 @@ pub fn analyzeBody(
940940 const inst_data = datas[inst].pl_node;
941941 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
942942 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
943 // If this block contains a function prototype, we need to reset the
944 // current list of parameters and restore it later.
945 // Note: this probably needs to be resolved in a more general manner.
946 const prev_params = block.params;
947 block.params = .{};
948 defer {
949 block.params.deinit(sema.gpa);
950 block.params = prev_params;
951 }
943952 const break_inst = try sema.analyzeBody(block, inline_body);
944953 const break_data = datas[break_inst].@"break";
945954 if (inst == break_data.block_inst) {
......@@ -953,6 +962,15 @@ pub fn analyzeBody(
953962 const inst_data = datas[inst].pl_node;
954963 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
955964 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
965 // If this block contains a function prototype, we need to reset the
966 // current list of parameters and restore it later.
967 // Note: this probably needs to be resolved in a more general manner.
968 const prev_params = block.params;
969 block.params = .{};
970 defer {
971 block.params.deinit(sema.gpa);
972 block.params = prev_params;
973 }
956974 const break_inst = try sema.analyzeBody(block, inline_body);
957975 const break_data = datas[break_inst].@"break";
958976 if (inst == break_data.block_inst) {
......@@ -1033,10 +1051,10 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
10331051 .@"asm" => return sema.zirAsm( block, extended, inst),
10341052 .typeof_peer => return sema.zirTypeofPeer( block, extended),
10351053 .compile_log => return sema.zirCompileLog( block, extended),
1036 .add_with_overflow => return sema.zirOverflowArithmetic(block, extended),
1037 .sub_with_overflow => return sema.zirOverflowArithmetic(block, extended),
1038 .mul_with_overflow => return sema.zirOverflowArithmetic(block, extended),
1039 .shl_with_overflow => return sema.zirOverflowArithmetic(block, extended),
1054 .add_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
1055 .sub_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
1056 .mul_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
1057 .shl_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
10401058 .c_undef => return sema.zirCUndef( block, extended),
10411059 .c_include => return sema.zirCInclude( block, extended),
10421060 .c_define => return sema.zirCDefine( block, extended),
......@@ -2025,15 +2043,22 @@ fn zirErrorSetDecl(
20252043 }, type_name);
20262044 new_decl.owns_tv = true;
20272045 errdefer sema.mod.abortAnonDecl(new_decl);
2028 const names = try new_decl_arena_allocator.alloc([]const u8, fields.len);
2029 for (fields) |str_index, i| {
2030 names[i] = try new_decl_arena_allocator.dupe(u8, sema.code.nullTerminatedString(str_index));
2046
2047 var names = Module.ErrorSet.NameMap{};
2048 try names.ensureUnusedCapacity(new_decl_arena_allocator, fields.len);
2049 for (fields) |str_index| {
2050 const name = try new_decl_arena_allocator.dupe(u8, sema.code.nullTerminatedString(str_index));
2051
2052 // TODO: This check should be performed in AstGen instead.
2053 const result = names.getOrPutAssumeCapacity(name);
2054 if (result.found_existing) {
2055 return sema.fail(block, src, "duplicate error set field {s}", .{name});
2056 }
20312057 }
20322058 error_set.* = .{
20332059 .owner_decl = new_decl,
20342060 .node_offset = inst_data.src_node,
2035 .names_ptr = names.ptr,
2036 .names_len = @intCast(u32, names.len),
2061 .names = names,
20372062 };
20382063 try new_decl.finalizeNewArena(&new_decl_arena);
20392064 return sema.analyzeDeclVal(block, src, new_decl);
......@@ -3887,17 +3912,20 @@ fn analyzeCall(
38873912 const ret_ty_inst = try sema.resolveBody(&child_block, fn_info.ret_ty_body);
38883913 const ret_ty_src = func_src; // TODO better source location
38893914 const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst);
3890 // If the function has an inferred error set, `bare_return_type` is the payload type only.
3915 // Create a fresh inferred error set type for inline/comptime calls.
38913916 const fn_ret_ty = blk: {
3892 // TODO instead of reusing the function's inferred error set, this code should
3893 // create a temporary error set which is used for the comptime/inline function
3894 // call alone, independent from the runtime instantiation.
38953917 if (func_ty_info.return_type.castTag(.error_union)) |payload| {
3896 const error_set_ty = payload.data.error_set;
3897 break :blk try Type.Tag.error_union.create(sema.arena, .{
3898 .error_set = error_set_ty,
3899 .payload = bare_return_type,
3900 });
3918 if (payload.data.error_set.tag() == .error_set_inferred) {
3919 const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
3920 node.data = .{ .func = module_fn };
3921 parent_func.?.inferred_error_sets.prepend(node);
3922
3923 const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, &node.data);
3924 break :blk try Type.Tag.error_union.create(sema.arena, .{
3925 .error_set = error_set_ty,
3926 .payload = bare_return_type,
3927 });
3928 }
39013929 }
39023930 break :blk bare_return_type;
39033931 };
......@@ -4556,63 +4584,43 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
45564584 return Air.Inst.Ref.anyerror_type;
45574585 }
45584586 // Resolve both error sets now.
4559 var set: std.StringHashMapUnmanaged(void) = .{};
4560 defer set.deinit(sema.gpa);
4561
4562 switch (lhs_ty.tag()) {
4563 .error_set_single => {
4564 const name = lhs_ty.castTag(.error_set_single).?.data;
4565 try set.put(sema.gpa, name, {});
4566 },
4567 .error_set_merged => {
4568 const names = lhs_ty.castTag(.error_set_merged).?.data;
4569 for (names) |name| {
4570 try set.put(sema.gpa, name, {});
4571 }
4572 },
4573 .error_set => {
4574 const lhs_set = lhs_ty.castTag(.error_set).?.data;
4575 try set.ensureUnusedCapacity(sema.gpa, lhs_set.names_len);
4576 for (lhs_set.names_ptr[0..lhs_set.names_len]) |name| {
4577 set.putAssumeCapacityNoClobber(name, {});
4578 }
4587 const lhs_names = switch (lhs_ty.tag()) {
4588 .error_set_single => blk: {
4589 // Work around coercion problems
4590 const tmp: *const [1][]const u8 = &lhs_ty.castTag(.error_set_single).?.data;
4591 break :blk tmp;
45794592 },
4593 .error_set_merged => lhs_ty.castTag(.error_set_merged).?.data.keys(),
4594 .error_set => lhs_ty.castTag(.error_set).?.data.names.keys(),
45804595 else => unreachable,
4581 }
4582 switch (rhs_ty.tag()) {
4583 .error_set_single => {
4584 const name = rhs_ty.castTag(.error_set_single).?.data;
4585 try set.put(sema.gpa, name, {});
4586 },
4587 .error_set_merged => {
4588 const names = rhs_ty.castTag(.error_set_merged).?.data;
4589 for (names) |name| {
4590 try set.put(sema.gpa, name, {});
4591 }
4592 },
4593 .error_set => {
4594 const rhs_set = rhs_ty.castTag(.error_set).?.data;
4595 try set.ensureUnusedCapacity(sema.gpa, rhs_set.names_len);
4596 for (rhs_set.names_ptr[0..rhs_set.names_len]) |name| {
4597 set.putAssumeCapacity(name, {});
4598 }
4596 };
4597
4598 const rhs_names = switch (rhs_ty.tag()) {
4599 .error_set_single => blk: {
4600 const tmp: *const [1][]const u8 = &rhs_ty.castTag(.error_set_single).?.data;
4601 break :blk tmp;
45994602 },
4603 .error_set_merged => rhs_ty.castTag(.error_set_merged).?.data.keys(),
4604 .error_set => rhs_ty.castTag(.error_set).?.data.names.keys(),
46004605 else => unreachable,
4601 }
4606 };
46024607
46034608 // TODO do we really want to create a Decl for this?
46044609 // The reason we do it right now is for memory management.
46054610 var anon_decl = try block.startAnonDecl();
46064611 defer anon_decl.deinit();
46074612
4608 const new_names = try anon_decl.arena().alloc([]const u8, set.count());
4609 var it = set.keyIterator();
4610 var i: usize = 0;
4611 while (it.next()) |key| : (i += 1) {
4612 new_names[i] = key.*;
4613 var names = Module.ErrorSet.NameMap{};
4614 // TODO: Guess is an upper bound, but maybe this needs to be reduced by computing the exact size first.
4615 try names.ensureUnusedCapacity(anon_decl.arena(), @intCast(u32, lhs_names.len + rhs_names.len));
4616 for (lhs_names) |name| {
4617 names.putAssumeCapacityNoClobber(name, {});
4618 }
4619 for (rhs_names) |name| {
4620 names.putAssumeCapacity(name, {});
46134621 }
46144622
4615 const err_set_ty = try Type.Tag.error_set_merged.create(anon_decl.arena(), new_names);
4623 const err_set_ty = try Type.Tag.error_set_merged.create(anon_decl.arena(), names);
46164624 const err_set_decl = try anon_decl.finish(
46174625 Type.type,
46184626 try Value.Tag.ty.create(anon_decl.arena(), err_set_ty),
......@@ -5079,6 +5087,10 @@ fn funcCommon(
50795087 };
50805088 errdefer if (body_inst != 0) sema.gpa.destroy(new_func);
50815089
5090 var maybe_inferred_error_set_node: ?*Module.Fn.InferredErrorSetListNode = null;
5091 errdefer if (maybe_inferred_error_set_node) |node| sema.gpa.destroy(node);
5092 // Note: no need to errdefer since this will still be in its default state at the end of the function.
5093
50825094 const fn_ty: Type = fn_ty: {
50835095 // Hot path for some common function types.
50845096 // TODO can we eliminate some of these Type tag values? seems unnecessarily complicated.
......@@ -5120,12 +5132,11 @@ fn funcCommon(
51205132 const return_type = if (!inferred_error_set or bare_return_type.tag() == .generic_poison)
51215133 bare_return_type
51225134 else blk: {
5123 const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, .{
5124 .func = new_func,
5125 .map = .{},
5126 .functions = .{},
5127 .is_anyerror = false,
5128 });
5135 const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
5136 node.data = .{ .func = new_func };
5137 maybe_inferred_error_set_node = node;
5138
5139 const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, &node.data);
51295140 break :blk try Type.Tag.error_union.create(sema.arena, .{
51305141 .error_set = error_set_ty,
51315142 .payload = bare_return_type,
......@@ -5217,6 +5228,10 @@ fn funcCommon(
52175228 .lbrace_column = @truncate(u16, src_locs.columns),
52185229 .rbrace_column = @truncate(u16, src_locs.columns >> 16),
52195230 };
5231 if (maybe_inferred_error_set_node) |node| {
5232 new_func.inferred_error_sets.prepend(node);
5233 }
5234 maybe_inferred_error_set_node = null;
52205235 fn_payload.* = .{
52215236 .base = .{ .tag = .function },
52225237 .data = new_func,
......@@ -5368,7 +5383,7 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
53685383 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
53695384 const ptr = sema.resolveInst(inst_data.operand);
53705385 const ptr_ty = sema.typeOf(ptr);
5371 if (ptr_ty.zigTypeTag() != .Pointer) {
5386 if (!ptr_ty.isPtrAtRuntime()) {
53725387 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
53735388 return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty});
53745389 }
......@@ -7295,6 +7310,7 @@ fn zirOverflowArithmetic(
72957310 sema: *Sema,
72967311 block: *Block,
72977312 extended: Zir.Inst.Extended.InstData,
7313 zir_tag: Zir.Inst.Extended,
72987314) CompileError!Air.Inst.Ref {
72997315 const tracy = trace(@src());
73007316 defer tracy.end();
......@@ -7302,7 +7318,170 @@ fn zirOverflowArithmetic(
73027318 const extra = sema.code.extraData(Zir.Inst.OverflowArithmetic, extended.operand).data;
73037319 const src: LazySrcLoc = .{ .node_offset = extra.node };
73047320
7305 return sema.fail(block, src, "TODO implement Sema.zirOverflowArithmetic", .{});
7321 const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
7322 const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
7323 const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node };
7324
7325 const lhs = sema.resolveInst(extra.lhs);
7326 const rhs = sema.resolveInst(extra.rhs);
7327 const ptr = sema.resolveInst(extra.ptr);
7328
7329 const lhs_ty = sema.typeOf(lhs);
7330
7331 // Note, the types of lhs/rhs (also for shifting)/ptr are already correct as ensured by astgen.
7332 const dest_ty = lhs_ty;
7333 if (dest_ty.zigTypeTag() != .Int) {
7334 return sema.fail(block, src, "expected integer type, found '{}'", .{dest_ty});
7335 }
7336
7337 const target = sema.mod.getTarget();
7338
7339 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
7340 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
7341
7342 const result: struct {
7343 overflowed: enum { yes, no, undef },
7344 wrapped: Air.Inst.Ref,
7345 } = result: {
7346 switch (zir_tag) {
7347 .add_with_overflow => {
7348 // If either of the arguments is zero, `false` is returned and the other is stored
7349 // to the result, even if it is undefined..
7350 // Otherwise, if either of the argument is undefined, undefined is returned.
7351 if (maybe_lhs_val) |lhs_val| {
7352 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
7353 break :result .{ .overflowed = .no, .wrapped = rhs };
7354 }
7355 }
7356 if (maybe_rhs_val) |rhs_val| {
7357 if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) {
7358 break :result .{ .overflowed = .no, .wrapped = lhs };
7359 }
7360 }
7361 if (maybe_lhs_val) |lhs_val| {
7362 if (maybe_rhs_val) |rhs_val| {
7363 if (lhs_val.isUndef() or rhs_val.isUndef()) {
7364 break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
7365 }
7366
7367 const result = try lhs_val.intAddWithOverflow(rhs_val, dest_ty, sema.arena, target);
7368 const inst = try sema.addConstant(dest_ty, result.wrapped_result);
7369 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
7370 }
7371 }
7372 },
7373 .sub_with_overflow => {
7374 // If the rhs is zero, then the result is lhs and no overflow occured.
7375 // Otherwise, if either result is undefined, both results are undefined.
7376 if (maybe_rhs_val) |rhs_val| {
7377 if (rhs_val.isUndef()) {
7378 break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
7379 } else if (rhs_val.compareWithZero(.eq)) {
7380 break :result .{ .overflowed = .no, .wrapped = lhs };
7381 } else if (maybe_lhs_val) |lhs_val| {
7382 if (lhs_val.isUndef()) {
7383 break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
7384 }
7385
7386 const result = try lhs_val.intSubWithOverflow(rhs_val, dest_ty, sema.arena, target);
7387 const inst = try sema.addConstant(dest_ty, result.wrapped_result);
7388 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
7389 }
7390 }
7391 },
7392 .mul_with_overflow => {
7393 // If either of the arguments is zero, the result is zero and no overflow occured.
7394 // If either of the arguments is one, the result is the other and no overflow occured.
7395 // Otherwise, if either of the arguments is undefined, both results are undefined.
7396 if (maybe_lhs_val) |lhs_val| {
7397 if (!lhs_val.isUndef()) {
7398 if (lhs_val.compareWithZero(.eq)) {
7399 break :result .{ .overflowed = .no, .wrapped = lhs };
7400 } else if (lhs_val.compare(.eq, Value.one, dest_ty)) {
7401 break :result .{ .overflowed = .no, .wrapped = rhs };
7402 }
7403 }
7404 }
7405
7406 if (maybe_rhs_val) |rhs_val| {
7407 if (!rhs_val.isUndef()) {
7408 if (rhs_val.compareWithZero(.eq)) {
7409 break :result .{ .overflowed = .no, .wrapped = rhs };
7410 } else if (rhs_val.compare(.eq, Value.one, dest_ty)) {
7411 break :result .{ .overflowed = .no, .wrapped = lhs };
7412 }
7413 }
7414 }
7415
7416 if (maybe_lhs_val) |lhs_val| {
7417 if (maybe_rhs_val) |rhs_val| {
7418 if (lhs_val.isUndef() or rhs_val.isUndef()) {
7419 break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
7420 }
7421
7422 const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, target);
7423 const inst = try sema.addConstant(dest_ty, result.wrapped_result);
7424 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
7425 }
7426 }
7427 },
7428 .shl_with_overflow => {
7429 // If lhs is zero, the result is zero and no overflow occurred.
7430 // If rhs is zero, the result is lhs (even if undefined) and no overflow occurred.
7431 // Oterhwise if either of the arguments is undefined, both results are undefined.
7432 if (maybe_lhs_val) |lhs_val| {
7433 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
7434 break :result .{ .overflowed = .no, .wrapped = lhs };
7435 }
7436 }
7437 if (maybe_rhs_val) |rhs_val| {
7438 if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) {
7439 break :result .{ .overflowed = .no, .wrapped = lhs };
7440 }
7441 }
7442 if (maybe_lhs_val) |lhs_val| {
7443 if (maybe_rhs_val) |rhs_val| {
7444 if (lhs_val.isUndef() or rhs_val.isUndef()) {
7445 break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
7446 }
7447
7448 const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, target);
7449 const inst = try sema.addConstant(dest_ty, result.wrapped_result);
7450 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
7451 }
7452 }
7453 },
7454 else => unreachable,
7455 }
7456
7457 const air_tag: Air.Inst.Tag = switch (zir_tag) {
7458 .add_with_overflow => .add_with_overflow,
7459 .mul_with_overflow => .mul_with_overflow,
7460 .sub_with_overflow => .sub_with_overflow,
7461 .shl_with_overflow => .shl_with_overflow,
7462 else => unreachable,
7463 };
7464
7465 try sema.requireRuntimeBlock(block, src);
7466 return block.addInst(.{
7467 .tag = air_tag,
7468 .data = .{ .pl_op = .{
7469 .operand = ptr,
7470 .payload = try sema.addExtra(Air.Bin{
7471 .lhs = lhs,
7472 .rhs = rhs,
7473 }),
7474 } },
7475 });
7476 };
7477
7478 try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store);
7479
7480 return switch (result.overflowed) {
7481 .yes => Air.Inst.Ref.bool_true,
7482 .no => Air.Inst.Ref.bool_false,
7483 .undef => try sema.addConstUndef(Type.initTag(.bool)),
7484 };
73067485}
73077486
73087487fn analyzeArithmetic(
......@@ -8635,8 +8814,12 @@ fn zirRetAddr(
86358814 block: *Block,
86368815 extended: Zir.Inst.Extended.InstData,
86378816) CompileError!Air.Inst.Ref {
8817 const tracy = trace(@src());
8818 defer tracy.end();
8819
86388820 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
8639 return sema.fail(block, src, "TODO: implement Sema.zirRetAddr", .{});
8821 try sema.requireRuntimeBlock(block, src);
8822 return try block.addNoOp(.ret_addr);
86408823}
86418824
86428825fn zirBuiltinSrc(
......@@ -8777,7 +8960,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
87778960 },
87788961 .Pointer => {
87798962 const info = ty.ptrInfo().data;
8780 const field_values = try sema.arena.alloc(Value, 7);
8963 const field_values = try sema.arena.alloc(Value, 8);
87818964 // size: Size,
87828965 field_values[0] = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.size));
87838966 // is_const: bool,
......@@ -8786,12 +8969,14 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
87868969 field_values[2] = if (info.@"volatile") Value.initTag(.bool_true) else Value.initTag(.bool_false);
87878970 // alignment: comptime_int,
87888971 field_values[3] = try Value.Tag.int_u64.create(sema.arena, info.@"align");
8972 // address_space: AddressSpace
8973 field_values[4] = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.@"addrspace"));
87898974 // child: type,
8790 field_values[4] = try Value.Tag.ty.create(sema.arena, info.pointee_type);
8975 field_values[5] = try Value.Tag.ty.create(sema.arena, info.pointee_type);
87918976 // is_allowzero: bool,
8792 field_values[5] = if (info.@"allowzero") Value.initTag(.bool_true) else Value.initTag(.bool_false);
8977 field_values[6] = if (info.@"allowzero") Value.initTag(.bool_true) else Value.initTag(.bool_false);
87938978 // sentinel: anytype,
8794 field_values[6] = if (info.sentinel) |some| try Value.Tag.opt_payload.create(sema.arena, some) else Value.@"null";
8979 field_values[7] = if (info.sentinel) |some| try Value.Tag.opt_payload.create(sema.arena, some) else Value.@"null";
87958980
87968981 return sema.addConstant(
87978982 type_info_ty,
......@@ -8881,11 +9066,17 @@ fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) Compi
88819066 switch (operand.zigTypeTag()) {
88829067 .ComptimeInt => return Air.Inst.Ref.comptime_int_type,
88839068 .Int => {
8884 var count: u16 = 0;
8885 var s = operand.bitSize(sema.mod.getTarget()) - 1;
8886 while (s != 0) : (s >>= 1) {
8887 count += 1;
8888 }
9069 const bits = operand.bitSize(sema.mod.getTarget());
9070 const count = if (bits == 0)
9071 0
9072 else blk: {
9073 var count: u16 = 0;
9074 var s = bits - 1;
9075 while (s != 0) : (s >>= 1) {
9076 count += 1;
9077 }
9078 break :blk count;
9079 };
88899080 const res = try Module.makeIntType(sema.arena, .unsigned, count);
88909081 return sema.addType(res);
88919082 },
......@@ -11425,14 +11616,8 @@ fn fieldVal(
1142511616 switch (child_type.zigTypeTag()) {
1142611617 .ErrorSet => {
1142711618 const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: {
11428 const error_set = payload.data;
11429 // TODO this is O(N). I'm putting off solving this until we solve inferred
11430 // error sets at the same time.
11431 const names = error_set.names_ptr[0..error_set.names_len];
11432 for (names) |name| {
11433 if (mem.eql(u8, field_name, name)) {
11434 break :blk name;
11435 }
11619 if (payload.data.names.getEntry(field_name)) |entry| {
11620 break :blk entry.key_ptr.*;
1143611621 }
1143711622 return sema.fail(block, src, "no error named '{s}' in '{}'", .{
1143811623 field_name, child_type,
......@@ -11630,14 +11815,8 @@ fn fieldPtr(
1163011815 .ErrorSet => {
1163111816 // TODO resolve inferred error sets
1163211817 const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: {
11633 const error_set = payload.data;
11634 // TODO this is O(N). I'm putting off solving this until we solve inferred
11635 // error sets at the same time.
11636 const names = error_set.names_ptr[0..error_set.names_len];
11637 for (names) |name| {
11638 if (mem.eql(u8, field_name, name)) {
11639 break :blk name;
11640 }
11818 if (payload.data.names.getEntry(field_name)) |entry| {
11819 break :blk entry.key_ptr.*;
1164111820 }
1164211821 return sema.fail(block, src, "no error named '{s}' in '{}'", .{
1164311822 field_name, child_type,
......@@ -12207,7 +12386,7 @@ fn coerce(
1220712386 const arena = sema.arena;
1220812387 const target = sema.mod.getTarget();
1220912388
12210 const in_memory_result = coerceInMemoryAllowed(dest_ty, inst_ty, false, target);
12389 const in_memory_result = try sema.coerceInMemoryAllowed(dest_ty, inst_ty, false, target);
1221112390 if (in_memory_result == .ok) {
1221212391 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
1221312392 // Keep the comptime Value representation; take the new type.
......@@ -12266,7 +12445,7 @@ fn coerce(
1226612445 if (inst_ty.isConstPtr() and dest_is_mut) break :single_item;
1226712446 if (inst_ty.isVolatilePtr() and !dest_info.@"volatile") break :single_item;
1226812447 if (inst_ty.ptrAddressSpace() != dest_info.@"addrspace") break :single_item;
12269 switch (coerceInMemoryAllowed(array_elem_ty, ptr_elem_ty, dest_is_mut, target)) {
12448 switch (try sema.coerceInMemoryAllowed(array_elem_ty, ptr_elem_ty, dest_is_mut, target)) {
1227012449 .ok => {},
1227112450 .no_match => break :single_item,
1227212451 }
......@@ -12285,7 +12464,7 @@ fn coerce(
1228512464 if (inst_ty.ptrAddressSpace() != dest_info.@"addrspace") break :src_array_ptr;
1228612465
1228712466 const dst_elem_type = dest_info.pointee_type;
12288 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, target)) {
12467 switch (try sema.coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, target)) {
1228912468 .ok => {},
1229012469 .no_match => break :src_array_ptr,
1229112470 }
......@@ -12324,7 +12503,7 @@ fn coerce(
1232412503 const src_elem_ty = inst_ty.childType();
1232512504 const dest_is_mut = dest_info.mutable;
1232612505 const dst_elem_type = dest_info.pointee_type;
12327 switch (coerceInMemoryAllowed(dst_elem_type, src_elem_ty, dest_is_mut, target)) {
12506 switch (try sema.coerceInMemoryAllowed(dst_elem_type, src_elem_ty, dest_is_mut, target)) {
1232812507 .ok => {},
1232912508 .no_match => break :src_c_ptr,
1233012509 }
......@@ -12467,7 +12646,13 @@ const InMemoryCoercionResult = enum {
1246712646/// * sentinel-terminated pointers can coerce into `[*]`
1246812647/// TODO improve this function to report recursive compile errors like it does in stage1.
1246912648/// look at the function types_match_const_cast_only
12470fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult {
12649fn coerceInMemoryAllowed(
12650 sema: *Sema,
12651 dest_ty: Type,
12652 src_ty: Type,
12653 dest_is_mut: bool,
12654 target: std.Target,
12655) CompileError!InMemoryCoercionResult {
1247112656 if (dest_ty.eql(src_ty))
1247212657 return .ok;
1247312658
......@@ -12476,32 +12661,35 @@ fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target:
1247612661 var src_buf: Type.Payload.ElemType = undefined;
1247712662 if (dest_ty.ptrOrOptionalPtrTy(&dest_buf)) |dest_ptr_ty| {
1247812663 if (src_ty.ptrOrOptionalPtrTy(&src_buf)) |src_ptr_ty| {
12479 return coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target);
12664 return try sema.coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target);
1248012665 }
1248112666 }
1248212667
1248312668 // Slices
1248412669 if (dest_ty.isSlice() and src_ty.isSlice()) {
12485 return coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target);
12670 return try sema.coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target);
1248612671 }
1248712672
12673 const dest_tag = dest_ty.zigTypeTag();
12674 const src_tag = src_ty.zigTypeTag();
12675
1248812676 // Functions
12489 if (dest_ty.zigTypeTag() == .Fn and src_ty.zigTypeTag() == .Fn) {
12490 return coerceInMemoryAllowedFns(dest_ty, src_ty, target);
12677 if (dest_tag == .Fn and src_tag == .Fn) {
12678 return try sema.coerceInMemoryAllowedFns(dest_ty, src_ty, target);
1249112679 }
1249212680
1249312681 // Error Unions
12494 if (dest_ty.zigTypeTag() == .ErrorUnion and src_ty.zigTypeTag() == .ErrorUnion) {
12495 const child = coerceInMemoryAllowed(dest_ty.errorUnionPayload(), src_ty.errorUnionPayload(), dest_is_mut, target);
12682 if (dest_tag == .ErrorUnion and src_tag == .ErrorUnion) {
12683 const child = try sema.coerceInMemoryAllowed(dest_ty.errorUnionPayload(), src_ty.errorUnionPayload(), dest_is_mut, target);
1249612684 if (child == .no_match) {
1249712685 return child;
1249812686 }
12499 return coerceInMemoryAllowed(dest_ty.errorUnionSet(), src_ty.errorUnionSet(), dest_is_mut, target);
12687 return try sema.coerceInMemoryAllowed(dest_ty.errorUnionSet(), src_ty.errorUnionSet(), dest_is_mut, target);
1250012688 }
1250112689
1250212690 // Error Sets
12503 if (dest_ty.zigTypeTag() == .ErrorSet and src_ty.zigTypeTag() == .ErrorSet) {
12504 return coerceInMemoryAllowedErrorSets(dest_ty, src_ty);
12691 if (dest_tag == .ErrorSet and src_tag == .ErrorSet) {
12692 return try sema.coerceInMemoryAllowedErrorSets(dest_ty, src_ty);
1250512693 }
1250612694
1250712695 // TODO: arrays
......@@ -12512,14 +12700,16 @@ fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target:
1251212700}
1251312701
1251412702fn coerceInMemoryAllowedErrorSets(
12703 sema: *Sema,
1251512704 dest_ty: Type,
1251612705 src_ty: Type,
12517) InMemoryCoercionResult {
12518 // Coercion to `anyerror`. Note that this check can return false positives
12706) !InMemoryCoercionResult {
12707 // Coercion to `anyerror`. Note that this check can return false negatives
1251912708 // in case the error sets did not get resolved.
1252012709 if (dest_ty.isAnyError()) {
1252112710 return .ok;
1252212711 }
12712
1252312713 // If both are inferred error sets of functions, and
1252412714 // the dest includes the source function, the coercion is OK.
1252512715 // This check is important because it works without forcing a full resolution
......@@ -12529,21 +12719,85 @@ fn coerceInMemoryAllowedErrorSets(
1252912719 const src_func = src_payload.data.func;
1253012720 const dst_func = dst_payload.data.func;
1253112721
12532 if (src_func == dst_func or dst_payload.data.functions.contains(src_func)) {
12722 if (src_func == dst_func or dst_payload.data.inferred_error_sets.contains(src_payload.data)) {
1253312723 return .ok;
1253412724 }
12725 return .no_match;
12726 }
12727 }
12728
12729 if (dest_ty.castTag(.error_set_inferred)) |payload| {
12730 try sema.resolveInferredErrorSet(payload.data);
12731 // isAnyError might have changed from a false negative to a true positive after resolution.
12732 if (dest_ty.isAnyError()) {
12733 return .ok;
1253512734 }
1253612735 }
1253712736
12538 // TODO full error set resolution and compare sets by names.
12737 switch (src_ty.tag()) {
12738 .error_set_inferred => {
12739 const src_data = src_ty.castTag(.error_set_inferred).?.data;
12740
12741 try sema.resolveInferredErrorSet(src_data);
12742 // src anyerror status might have changed after the resolution.
12743 if (src_ty.isAnyError()) {
12744 // dest_ty.isAnyError() == true is already checked for at this point.
12745 return .no_match;
12746 }
12747
12748 var it = src_data.errors.keyIterator();
12749 while (it.next()) |name_ptr| {
12750 if (!dest_ty.errorSetHasField(name_ptr.*)) {
12751 return .no_match;
12752 }
12753 }
12754
12755 return .ok;
12756 },
12757 .error_set_single => {
12758 const name = src_ty.castTag(.error_set_single).?.data;
12759 if (dest_ty.errorSetHasField(name)) {
12760 return .ok;
12761 }
12762 },
12763 .error_set_merged => {
12764 const names = src_ty.castTag(.error_set_merged).?.data.keys();
12765 for (names) |name| {
12766 if (!dest_ty.errorSetHasField(name)) {
12767 return .no_match;
12768 }
12769 }
12770
12771 return .ok;
12772 },
12773 .error_set => {
12774 const names = src_ty.castTag(.error_set).?.data.names.keys();
12775 for (names) |name| {
12776 if (!dest_ty.errorSetHasField(name)) {
12777 return .no_match;
12778 }
12779 }
12780
12781 return .ok;
12782 },
12783 .anyerror => switch (dest_ty.tag()) {
12784 .error_set_inferred => return .no_match, // Caught by dest.isAnyError() above.
12785 .error_set_single, .error_set_merged, .error_set => {},
12786 .anyerror => unreachable, // Filtered out above.
12787 else => unreachable,
12788 },
12789 else => unreachable,
12790 }
12791
1253912792 return .no_match;
1254012793}
1254112794
1254212795fn coerceInMemoryAllowedFns(
12796 sema: *Sema,
1254312797 dest_ty: Type,
1254412798 src_ty: Type,
1254512799 target: std.Target,
12546) InMemoryCoercionResult {
12800) !InMemoryCoercionResult {
1254712801 const dest_info = dest_ty.fnInfo();
1254812802 const src_info = src_ty.fnInfo();
1254912803
......@@ -12556,7 +12810,7 @@ fn coerceInMemoryAllowedFns(
1255612810 }
1255712811
1255812812 if (!src_info.return_type.isNoReturn()) {
12559 const rt = coerceInMemoryAllowed(dest_info.return_type, src_info.return_type, false, target);
12813 const rt = try sema.coerceInMemoryAllowed(dest_info.return_type, src_info.return_type, false, target);
1256012814 if (rt == .no_match) {
1256112815 return rt;
1256212816 }
......@@ -12576,7 +12830,7 @@ fn coerceInMemoryAllowedFns(
1257612830 // TODO: nolias
1257712831
1257812832 // Note: Cast direction is reversed here.
12579 const param = coerceInMemoryAllowed(src_param_ty, dest_param_ty, false, target);
12833 const param = try sema.coerceInMemoryAllowed(src_param_ty, dest_param_ty, false, target);
1258012834 if (param == .no_match) {
1258112835 return param;
1258212836 }
......@@ -12590,17 +12844,18 @@ fn coerceInMemoryAllowedFns(
1259012844}
1259112845
1259212846fn coerceInMemoryAllowedPtrs(
12847 sema: *Sema,
1259312848 dest_ty: Type,
1259412849 src_ty: Type,
1259512850 dest_ptr_ty: Type,
1259612851 src_ptr_ty: Type,
1259712852 dest_is_mut: bool,
1259812853 target: std.Target,
12599) InMemoryCoercionResult {
12854) !InMemoryCoercionResult {
1260012855 const dest_info = dest_ptr_ty.ptrInfo().data;
1260112856 const src_info = src_ptr_ty.ptrInfo().data;
1260212857
12603 const child = coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target);
12858 const child = try sema.coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target);
1260412859 if (child == .no_match) {
1260512860 return child;
1260612861 }
......@@ -13321,7 +13576,7 @@ fn coerceVectorInMemory(
1332113576 const target = sema.mod.getTarget();
1332213577 const dest_elem_ty = dest_ty.childType();
1332313578 const inst_elem_ty = inst_ty.childType();
13324 const in_memory_result = coerceInMemoryAllowed(dest_elem_ty, inst_elem_ty, false, target);
13579 const in_memory_result = try sema.coerceInMemoryAllowed(dest_elem_ty, inst_elem_ty, false, target);
1332513580 if (in_memory_result != .ok) {
1332613581 // TODO recursive error notes for coerceInMemoryAllowed failure
1332713582 return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty, inst_ty });
......@@ -13916,25 +14171,28 @@ fn wrapErrorUnion(
1391614171 if (mem.eql(u8, expected_name, n)) break :ok;
1391714172 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
1391814173 },
13919 .error_set => ok: {
14174 .error_set => {
1392014175 const expected_name = val.castTag(.@"error").?.data.name;
1392114176 const error_set = dest_err_set_ty.castTag(.error_set).?.data;
13922 const names = error_set.names_ptr[0..error_set.names_len];
13923 // TODO this is O(N). I'm putting off solving this until we solve inferred
13924 // error sets at the same time.
13925 for (names) |name| {
13926 if (mem.eql(u8, expected_name, name)) break :ok;
14177 if (!error_set.names.contains(expected_name)) {
14178 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
1392714179 }
13928 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
1392914180 },
1393014181 .error_set_inferred => ok: {
13931 const err_set_payload = dest_err_set_ty.castTag(.error_set_inferred).?.data;
13932 if (err_set_payload.is_anyerror) break :ok;
1393314182 const expected_name = val.castTag(.@"error").?.data.name;
13934 if (err_set_payload.map.contains(expected_name)) break :ok;
13935 // TODO error set resolution here before emitting a compile error
14183 const data = dest_err_set_ty.castTag(.error_set_inferred).?.data;
14184 try sema.resolveInferredErrorSet(data);
14185 if (data.is_anyerror) break :ok;
14186 if (data.errors.contains(expected_name)) break :ok;
1393614187 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
1393714188 },
14189 .error_set_merged => {
14190 const expected_name = val.castTag(.@"error").?.data.name;
14191 const error_set = dest_err_set_ty.castTag(.error_set_merged).?.data;
14192 if (!error_set.contains(expected_name)) {
14193 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
14194 }
14195 },
1393814196 else => unreachable,
1393914197 }
1394014198 return sema.addConstant(dest_ty, val);
......@@ -14077,12 +14335,12 @@ fn resolvePeerTypes(
1407714335 .Optional => {
1407814336 var opt_child_buf: Type.Payload.ElemType = undefined;
1407914337 const opt_child_ty = candidate_ty.optionalChild(&opt_child_buf);
14080 if (coerceInMemoryAllowed(opt_child_ty, chosen_ty, false, target) == .ok) {
14338 if ((try sema.coerceInMemoryAllowed(opt_child_ty, chosen_ty, false, target)) == .ok) {
1408114339 chosen = candidate;
1408214340 chosen_i = candidate_i + 1;
1408314341 continue;
1408414342 }
14085 if (coerceInMemoryAllowed(chosen_ty, opt_child_ty, false, target) == .ok) {
14343 if ((try sema.coerceInMemoryAllowed(chosen_ty, opt_child_ty, false, target)) == .ok) {
1408614344 any_are_null = true;
1408714345 continue;
1408814346 }
......@@ -14105,10 +14363,10 @@ fn resolvePeerTypes(
1410514363 .Optional => {
1410614364 var opt_child_buf: Type.Payload.ElemType = undefined;
1410714365 const opt_child_ty = chosen_ty.optionalChild(&opt_child_buf);
14108 if (coerceInMemoryAllowed(opt_child_ty, candidate_ty, false, target) == .ok) {
14366 if ((try sema.coerceInMemoryAllowed(opt_child_ty, candidate_ty, false, target)) == .ok) {
1410914367 continue;
1411014368 }
14111 if (coerceInMemoryAllowed(candidate_ty, opt_child_ty, false, target) == .ok) {
14369 if ((try sema.coerceInMemoryAllowed(candidate_ty, opt_child_ty, false, target)) == .ok) {
1411214370 any_are_null = true;
1411314371 chosen = candidate;
1411414372 chosen_i = candidate_i + 1;
......@@ -14274,6 +14532,42 @@ fn resolveBuiltinTypeFields(
1427414532 return sema.resolveTypeFields(block, src, resolved_ty);
1427514533}
1427614534
14535fn resolveInferredErrorSet(sema: *Sema, inferred_error_set: *Module.Fn.InferredErrorSet) CompileError!void {
14536 // Ensuring that a particular decl is analyzed does not neccesarily mean that
14537 // it's error set is inferred, so traverse all of them to get the complete
14538 // picture.
14539 // Note: We want to skip re-resolving the current function, as recursion
14540 // doesn't change the error set. We can just check for state == .in_progress for this.
14541 // TODO: Is that correct?
14542
14543 if (inferred_error_set.is_resolved) {
14544 return;
14545 }
14546
14547 var it = inferred_error_set.inferred_error_sets.keyIterator();
14548 while (it.next()) |other_error_set_ptr| {
14549 const func = other_error_set_ptr.*.func;
14550 const decl = func.*.owner_decl;
14551
14552 if (func.*.state == .in_progress) {
14553 // Recursion, doesn't alter current error set, keep going.
14554 continue;
14555 }
14556
14557 try sema.ensureDeclAnalyzed(decl); // To ensure that all dependencies are properly added to the set.
14558 try sema.resolveInferredErrorSet(other_error_set_ptr.*);
14559
14560 var error_it = other_error_set_ptr.*.errors.keyIterator();
14561 while (error_it.next()) |entry| {
14562 try inferred_error_set.errors.put(sema.gpa, entry.*, {});
14563 }
14564 if (other_error_set_ptr.*.is_anyerror)
14565 inferred_error_set.is_anyerror = true;
14566 }
14567
14568 inferred_error_set.is_resolved = true;
14569}
14570
1427714571fn semaStructFields(
1427814572 mod: *Module,
1427914573 struct_obj: *Module.Struct,
......@@ -15236,8 +15530,8 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr
1523615530 // We have a Value that lines up in virtual memory exactly with what we want to load.
1523715531 // If the Type is in-memory coercable to `load_ty`, it may be returned without modifications.
1523815532 const coerce_in_mem_ok =
15239 coerceInMemoryAllowed(load_ty, parent.ty, false, target) == .ok or
15240 coerceInMemoryAllowed(parent.ty, load_ty, false, target) == .ok;
15533 (try sema.coerceInMemoryAllowed(load_ty, parent.ty, false, target)) == .ok or
15534 (try sema.coerceInMemoryAllowed(parent.ty, load_ty, false, target)) == .ok;
1524115535 if (coerce_in_mem_ok) {
1524215536 if (parent.is_mutable) {
1524315537 // The decl whose value we are obtaining here may be overwritten with
src/arch/aarch64/CodeGen.zig+30
......@@ -521,6 +521,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
521521 .max => try self.airMax(inst),
522522 .slice => try self.airSlice(inst),
523523
524 .add_with_overflow => try self.airAddWithOverflow(inst),
525 .sub_with_overflow => try self.airSubWithOverflow(inst),
526 .mul_with_overflow => try self.airMulWithOverflow(inst),
527 .shl_with_overflow => try self.airShlWithOverflow(inst),
528
524529 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
525530
526531 .cmp_lt => try self.airCmp(inst, .lt),
......@@ -545,6 +550,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
545550 .block => try self.airBlock(inst),
546551 .br => try self.airBr(inst),
547552 .breakpoint => try self.airBreakpoint(),
553 .ret_addr => try self.airRetAddr(),
548554 .fence => try self.airFence(),
549555 .call => try self.airCall(inst),
550556 .cond_br => try self.airCondBr(inst),
......@@ -968,6 +974,26 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
968974 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
969975}
970976
977fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
978 _ = inst;
979 return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch});
980}
981
982fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
983 _ = inst;
984 return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});
985}
986
987fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
988 _ = inst;
989 return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch});
990}
991
992fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
993 _ = inst;
994 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
995}
996
971997fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
972998 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
973999 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
......@@ -1409,6 +1435,10 @@ fn airBreakpoint(self: *Self) !void {
14091435 return self.finishAirBookkeeping();
14101436}
14111437
1438fn airRetAddr(self: *Self) !void {
1439 return self.fail("TODO implement airRetAddr for {}", .{self.target.cpu.arch});
1440}
1441
14121442fn airFence(self: *Self) !void {
14131443 return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch});
14141444 //return self.finishAirBookkeeping();
src/arch/arm/CodeGen.zig+30
......@@ -519,6 +519,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
519519 .max => try self.airMax(inst),
520520 .slice => try self.airSlice(inst),
521521
522 .add_with_overflow => try self.airAddWithOverflow(inst),
523 .sub_with_overflow => try self.airSubWithOverflow(inst),
524 .mul_with_overflow => try self.airMulWithOverflow(inst),
525 .shl_with_overflow => try self.airShlWithOverflow(inst),
526
522527 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
523528
524529 .cmp_lt => try self.airCmp(inst, .lt),
......@@ -543,6 +548,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
543548 .block => try self.airBlock(inst),
544549 .br => try self.airBr(inst),
545550 .breakpoint => try self.airBreakpoint(),
551 .ret_addr => try self.airRetAddr(),
546552 .fence => try self.airFence(),
547553 .call => try self.airCall(inst),
548554 .cond_br => try self.airCondBr(inst),
......@@ -998,6 +1004,26 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
9981004 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
9991005}
10001006
1007fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1008 _ = inst;
1009 return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch});
1010}
1011
1012fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1013 _ = inst;
1014 return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});
1015}
1016
1017fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1018 _ = inst;
1019 return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch});
1020}
1021
1022fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1023 _ = inst;
1024 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
1025}
1026
10011027fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
10021028 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
10031029 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
......@@ -1843,6 +1869,10 @@ fn airBreakpoint(self: *Self) !void {
18431869 return self.finishAirBookkeeping();
18441870}
18451871
1872fn airRetAddr(self: *Self) !void {
1873 return self.fail("TODO implement airRetAddr for {}", .{self.target.cpu.arch});
1874}
1875
18461876fn airFence(self: *Self) !void {
18471877 return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch});
18481878 //return self.finishAirBookkeeping();
src/arch/riscv64/CodeGen.zig+30
......@@ -500,6 +500,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
500500 .max => try self.airMax(inst),
501501 .slice => try self.airSlice(inst),
502502
503 .add_with_overflow => try self.airAddWithOverflow(inst),
504 .sub_with_overflow => try self.airSubWithOverflow(inst),
505 .mul_with_overflow => try self.airMulWithOverflow(inst),
506 .shl_with_overflow => try self.airShlWithOverflow(inst),
507
503508 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
504509
505510 .cmp_lt => try self.airCmp(inst, .lt),
......@@ -524,6 +529,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
524529 .block => try self.airBlock(inst),
525530 .br => try self.airBr(inst),
526531 .breakpoint => try self.airBreakpoint(),
532 .ret_addr => try self.airRetAddr(),
527533 .fence => try self.airFence(),
528534 .call => try self.airCall(inst),
529535 .cond_br => try self.airCondBr(inst),
......@@ -913,6 +919,26 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
913919 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
914920}
915921
922fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
923 _ = inst;
924 return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch});
925}
926
927fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
928 _ = inst;
929 return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});
930}
931
932fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
933 _ = inst;
934 return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch});
935}
936
937fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
938 _ = inst;
939 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
940}
941
916942fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
917943 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
918944 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
......@@ -1347,6 +1373,10 @@ fn airBreakpoint(self: *Self) !void {
13471373 return self.finishAirBookkeeping();
13481374}
13491375
1376fn airRetAddr(self: *Self) !void {
1377 return self.fail("TODO implement airRetAddr for {}", .{self.target.cpu.arch});
1378}
1379
13501380fn airFence(self: *Self) !void {
13511381 return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch});
13521382 //return self.finishAirBookkeeping();
src/arch/x86_64/CodeGen.zig+30
......@@ -553,6 +553,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
553553 .max => try self.airMax(inst),
554554 .slice => try self.airSlice(inst),
555555
556 .add_with_overflow => try self.airAddWithOverflow(inst),
557 .sub_with_overflow => try self.airSubWithOverflow(inst),
558 .mul_with_overflow => try self.airMulWithOverflow(inst),
559 .shl_with_overflow => try self.airShlWithOverflow(inst),
560
556561 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
557562
558563 .cmp_lt => try self.airCmp(inst, .lt),
......@@ -577,6 +582,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
577582 .block => try self.airBlock(inst),
578583 .br => try self.airBr(inst),
579584 .breakpoint => try self.airBreakpoint(),
585 .ret_addr => try self.airRetAddr(),
580586 .fence => try self.airFence(),
581587 .call => try self.airCall(inst),
582588 .cond_br => try self.airCondBr(inst),
......@@ -1027,6 +1033,26 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
10271033 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
10281034}
10291035
1036fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1037 _ = inst;
1038 return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch});
1039}
1040
1041fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1042 _ = inst;
1043 return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});
1044}
1045
1046fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1047 _ = inst;
1048 return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch});
1049}
1050
1051fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1052 _ = inst;
1053 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
1054}
1055
10301056fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
10311057 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
10321058 const result: MCValue = if (self.liveness.isUnused(inst))
......@@ -1832,6 +1858,10 @@ fn airBreakpoint(self: *Self) !void {
18321858 return self.finishAirBookkeeping();
18331859}
18341860
1861fn airRetAddr(self: *Self) !void {
1862 return self.fail("TODO implement airRetAddr for {}", .{self.target.cpu.arch});
1863}
1864
18351865fn airFence(self: *Self) !void {
18361866 return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch});
18371867 //return self.finishAirBookkeeping();
src/codegen/c.zig+34
......@@ -1125,6 +1125,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
11251125 .arg => airArg(f),
11261126
11271127 .breakpoint => try airBreakpoint(f),
1128 .ret_addr => try airRetAddr(f),
11281129 .unreach => try airUnreach(f),
11291130 .fence => try airFence(f, inst),
11301131
......@@ -1155,6 +1156,11 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
11551156 .mul_sat => try airSatOp(f, inst, "muls_"),
11561157 .shl_sat => try airSatOp(f, inst, "shls_"),
11571158
1159 .add_with_overflow => try airAddWithOverflow(f, inst),
1160 .sub_with_overflow => try airSubWithOverflow(f, inst),
1161 .mul_with_overflow => try airMulWithOverflow(f, inst),
1162 .shl_with_overflow => try airShlWithOverflow(f, inst),
1163
11581164 .min => try airMinMax(f, inst, "<"),
11591165 .max => try airMinMax(f, inst, ">"),
11601166
......@@ -1864,6 +1870,30 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue {
18641870 return ret;
18651871}
18661872
1873fn airAddWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue {
1874 _ = f;
1875 _ = inst;
1876 return f.fail("TODO add with overflow", .{});
1877}
1878
1879fn airSubWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue {
1880 _ = f;
1881 _ = inst;
1882 return f.fail("TODO sub with overflow", .{});
1883}
1884
1885fn airMulWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue {
1886 _ = f;
1887 _ = inst;
1888 return f.fail("TODO mul with overflow", .{});
1889}
1890
1891fn airShlWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue {
1892 _ = f;
1893 _ = inst;
1894 return f.fail("TODO shl with overflow", .{});
1895}
1896
18671897fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
18681898 if (f.liveness.isUnused(inst))
18691899 return CValue.none;
......@@ -2183,6 +2213,10 @@ fn airBreakpoint(f: *Function) !CValue {
21832213 return CValue.none;
21842214}
21852215
2216fn airRetAddr(f: *Function) !CValue {
2217 return f.fail("TODO implement codegen for airRetAddr", .{});
2218}
2219
21862220fn airFence(f: *Function, inst: Air.Inst.Index) !CValue {
21872221 const atomic_order = f.air.instructions.items(.data)[inst].fence;
21882222 const writer = f.object.writer();
src/codegen/llvm.zig+103-18
......@@ -761,21 +761,25 @@ pub const DeclGen = struct {
761761 dg.context.intType(8);
762762 return llvm_elem_ty.pointerType(llvm_addrspace);
763763 },
764 .Opaque => {
765 const gop = try dg.object.type_map.getOrPut(gpa, t);
766 if (gop.found_existing) return gop.value_ptr.*;
764 .Opaque => switch (t.tag()) {
765 .@"opaque" => {
766 const gop = try dg.object.type_map.getOrPut(gpa, t);
767 if (gop.found_existing) return gop.value_ptr.*;
767768
768 // The Type memory is ephemeral; since we want to store a longer-lived
769 // reference, we need to copy it here.
770 gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());
769 // The Type memory is ephemeral; since we want to store a longer-lived
770 // reference, we need to copy it here.
771 gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());
771772
772 const opaque_obj = t.castTag(.@"opaque").?.data;
773 const name = try opaque_obj.getFullyQualifiedName(gpa);
774 defer gpa.free(name);
773 const opaque_obj = t.castTag(.@"opaque").?.data;
774 const name = try opaque_obj.getFullyQualifiedName(gpa);
775 defer gpa.free(name);
775776
776 const llvm_struct_ty = dg.context.structCreateNamed(name);
777 gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls
778 return llvm_struct_ty;
777 const llvm_struct_ty = dg.context.structCreateNamed(name);
778 gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls
779 return llvm_struct_ty;
780 },
781 .anyopaque => return dg.context.intType(8),
782 else => unreachable,
779783 },
780784 .Array => {
781785 const elem_type = try dg.llvmType(t.childType());
......@@ -1714,6 +1718,11 @@ pub const FuncGen = struct {
17141718 .max => try self.airMax(inst),
17151719 .slice => try self.airSlice(inst),
17161720
1721 .add_with_overflow => try self.airOverflow(inst, "llvm.sadd.with.overflow", "llvm.uadd.with.overflow"),
1722 .sub_with_overflow => try self.airOverflow(inst, "llvm.ssub.with.overflow", "llvm.usub.with.overflow"),
1723 .mul_with_overflow => try self.airOverflow(inst, "llvm.smul.with.overflow", "llvm.umul.with.overflow"),
1724 .shl_with_overflow => try self.airShlWithOverflow(inst),
1725
17171726 .bit_and, .bool_and => try self.airAnd(inst),
17181727 .bit_or, .bool_or => try self.airOr(inst),
17191728 .xor => try self.airXor(inst),
......@@ -1745,6 +1754,7 @@ pub const FuncGen = struct {
17451754 .br => try self.airBr(inst),
17461755 .switch_br => try self.airSwitchBr(inst),
17471756 .breakpoint => try self.airBreakpoint(inst),
1757 .ret_addr => try self.airRetAddr(inst),
17481758 .call => try self.airCall(inst),
17491759 .cond_br => try self.airCondBr(inst),
17501760 .intcast => try self.airIntCast(inst),
......@@ -3133,6 +3143,75 @@ pub const FuncGen = struct {
31333143 }
31343144 }
31353145
3146 fn airOverflow(
3147 self: *FuncGen,
3148 inst: Air.Inst.Index,
3149 signed_intrinsic: []const u8,
3150 unsigned_intrinsic: []const u8,
3151 ) !?*const llvm.Value {
3152 if (self.liveness.isUnused(inst))
3153 return null;
3154
3155 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3156 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
3157
3158 const ptr = try self.resolveInst(pl_op.operand);
3159 const lhs = try self.resolveInst(extra.lhs);
3160 const rhs = try self.resolveInst(extra.rhs);
3161
3162 const ptr_ty = self.air.typeOf(pl_op.operand);
3163 const lhs_ty = self.air.typeOf(extra.lhs);
3164
3165 const intrinsic_name = if (lhs_ty.isSignedInt()) signed_intrinsic else unsigned_intrinsic;
3166
3167 const llvm_lhs_ty = try self.dg.llvmType(lhs_ty);
3168
3169 const llvm_fn = self.getIntrinsic(intrinsic_name, &.{llvm_lhs_ty});
3170 const result_struct = self.builder.buildCall(llvm_fn, &[_]*const llvm.Value{ lhs, rhs }, 2, .Fast, .Auto, "");
3171
3172 const result = self.builder.buildExtractValue(result_struct, 0, "");
3173 const overflow_bit = self.builder.buildExtractValue(result_struct, 1, "");
3174
3175 self.store(ptr, ptr_ty, result, .NotAtomic);
3176
3177 return overflow_bit;
3178 }
3179
3180 fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
3181 if (self.liveness.isUnused(inst))
3182 return null;
3183
3184 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3185 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
3186
3187 const ptr = try self.resolveInst(pl_op.operand);
3188 const lhs = try self.resolveInst(extra.lhs);
3189 const rhs = try self.resolveInst(extra.rhs);
3190
3191 const ptr_ty = self.air.typeOf(pl_op.operand);
3192 const lhs_ty = self.air.typeOf(extra.lhs);
3193 const rhs_ty = self.air.typeOf(extra.rhs);
3194
3195 const tg = self.dg.module.getTarget();
3196
3197 const casted_rhs = if (rhs_ty.bitSize(tg) < lhs_ty.bitSize(tg))
3198 self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "")
3199 else
3200 rhs;
3201
3202 const result = self.builder.buildShl(lhs, casted_rhs, "");
3203 const reconstructed = if (lhs_ty.isSignedInt())
3204 self.builder.buildAShr(result, casted_rhs, "")
3205 else
3206 self.builder.buildLShr(result, casted_rhs, "");
3207
3208 const overflow_bit = self.builder.buildICmp(.NE, lhs, reconstructed, "");
3209
3210 self.store(ptr, ptr_ty, result, .NotAtomic);
3211
3212 return overflow_bit;
3213 }
3214
31363215 fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
31373216 if (self.liveness.isUnused(inst))
31383217 return null;
......@@ -3511,11 +3590,20 @@ pub const FuncGen = struct {
35113590
35123591 fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
35133592 _ = inst;
3514 const llvm_fn = self.getIntrinsic("llvm.debugtrap");
3593 const llvm_fn = self.getIntrinsic("llvm.debugtrap", &.{});
35153594 _ = self.builder.buildCall(llvm_fn, undefined, 0, .C, .Auto, "");
35163595 return null;
35173596 }
35183597
3598 fn airRetAddr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
3599 _ = inst;
3600 const i32_zero = self.context.intType(32).constNull();
3601 const usize_llvm_ty = try self.dg.llvmType(Type.usize);
3602 const llvm_fn = self.getIntrinsic("llvm.returnaddress", &.{});
3603 const ptr_val = self.builder.buildCall(llvm_fn, &[_]*const llvm.Value{i32_zero}, 1, .Fast, .Auto, "");
3604 return self.builder.buildPtrToInt(ptr_val, usize_llvm_ty, "");
3605 }
3606
35193607 fn airFence(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
35203608 const atomic_order = self.air.instructions.items(.data)[inst].fence;
35213609 const llvm_memory_order = toLlvmAtomicOrdering(atomic_order);
......@@ -3946,13 +4034,10 @@ pub const FuncGen = struct {
39464034 return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
39474035 }
39484036
3949 fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value {
4037 fn getIntrinsic(self: *FuncGen, name: []const u8, types: []*const llvm.Type) *const llvm.Value {
39504038 const id = llvm.lookupIntrinsicID(name.ptr, name.len);
39514039 assert(id != 0);
3952 // TODO: add support for overload intrinsics by passing the prefix of the intrinsic
3953 // to `lookupIntrinsicID` and then passing the correct types to
3954 // `getIntrinsicDeclaration`
3955 return self.llvmModule().getIntrinsicDeclaration(id, null, 0);
4040 return self.llvmModule().getIntrinsicDeclaration(id, types.ptr, types.len);
39564041 }
39574042
39584043 fn load(self: *FuncGen, ptr: *const llvm.Value, ptr_ty: Type) ?*const llvm.Value {
src/print_air.zig+18
......@@ -159,6 +159,7 @@ const Writer = struct {
159159
160160 .breakpoint,
161161 .unreach,
162 .ret_addr,
162163 => try w.writeNoOp(s, inst),
163164
164165 .const_ty,
......@@ -228,6 +229,12 @@ const Writer = struct {
228229 .atomic_rmw => try w.writeAtomicRmw(s, inst),
229230 .memcpy => try w.writeMemcpy(s, inst),
230231 .memset => try w.writeMemset(s, inst),
232
233 .add_with_overflow,
234 .sub_with_overflow,
235 .mul_with_overflow,
236 .shl_with_overflow,
237 => try w.writeOverflow(s, inst),
231238 }
232239 }
233240
......@@ -348,6 +355,17 @@ const Writer = struct {
348355 try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) });
349356 }
350357
358 fn writeOverflow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
359 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
360 const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
361
362 try w.writeOperand(s, inst, 0, pl_op.operand);
363 try s.writeAll(", ");
364 try w.writeOperand(s, inst, 1, extra.lhs);
365 try s.writeAll(", ");
366 try w.writeOperand(s, inst, 2, extra.rhs);
367 }
368
351369 fn writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
352370 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
353371 const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
src/type.zig+39-52
......@@ -627,7 +627,7 @@ pub const Type = extern union {
627627 }
628628
629629 if (a.tag() == .error_set_inferred and b.tag() == .error_set_inferred) {
630 return a.castTag(.error_set_inferred).?.data.func == b.castTag(.error_set_inferred).?.data.func;
630 return a.castTag(.error_set_inferred).?.data == b.castTag(.error_set_inferred).?.data;
631631 }
632632
633633 if (a.tag() == .error_set_single and b.tag() == .error_set_single) {
......@@ -904,10 +904,11 @@ pub const Type = extern union {
904904 });
905905 },
906906 .error_set_merged => {
907 const names = self.castTag(.error_set_merged).?.data;
908 const duped_names = try allocator.alloc([]const u8, names.len);
909 for (duped_names) |*name, i| {
910 name.* = try allocator.dupe(u8, names[i]);
907 const names = self.castTag(.error_set_merged).?.data.keys();
908 var duped_names = Module.ErrorSet.NameMap{};
909 try duped_names.ensureTotalCapacity(allocator, names.len);
910 for (names) |name| {
911 duped_names.putAssumeCapacityNoClobber(name, .{});
911912 }
912913 return Tag.error_set_merged.create(allocator, duped_names);
913914 },
......@@ -1206,7 +1207,7 @@ pub const Type = extern union {
12061207 return writer.print("(inferred error set of {s})", .{func.owner_decl.name});
12071208 },
12081209 .error_set_merged => {
1209 const names = ty.castTag(.error_set_merged).?.data;
1210 const names = ty.castTag(.error_set_merged).?.data.keys();
12101211 try writer.writeAll("error{");
12111212 for (names) |name, i| {
12121213 if (i != 0) try writer.writeByte(',');
......@@ -1574,6 +1575,7 @@ pub const Type = extern union {
15741575 .extern_options,
15751576 .@"anyframe",
15761577 .anyframe_T,
1578 .anyopaque,
15771579 .@"opaque",
15781580 .single_const_pointer,
15791581 .single_mut_pointer,
......@@ -1653,7 +1655,6 @@ pub const Type = extern union {
16531655 return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();
16541656 },
16551657
1656 .anyopaque,
16571658 .void,
16581659 .type,
16591660 .comptime_int,
......@@ -2873,6 +2874,35 @@ pub const Type = extern union {
28732874 };
28742875 }
28752876
2877 /// Returns whether ty, which must be an error set, includes an error `name`.
2878 /// Might return a false negative if `ty` is an inferred error set and not fully
2879 /// resolved yet.
2880 pub fn errorSetHasField(ty: Type, name: []const u8) bool {
2881 if (ty.isAnyError()) {
2882 return true;
2883 }
2884
2885 switch (ty.tag()) {
2886 .error_set_single => {
2887 const data = ty.castTag(.error_set_single).?.data;
2888 return std.mem.eql(u8, data, name);
2889 },
2890 .error_set_inferred => {
2891 const data = ty.castTag(.error_set_inferred).?.data;
2892 return data.errors.contains(name);
2893 },
2894 .error_set_merged => {
2895 const data = ty.castTag(.error_set_merged).?.data;
2896 return data.contains(name);
2897 },
2898 .error_set => {
2899 const data = ty.castTag(.error_set).?.data;
2900 return data.names.contains(name);
2901 },
2902 else => unreachable,
2903 }
2904 }
2905
28762906 /// Asserts the type is an array or vector.
28772907 pub fn arrayLen(ty: Type) u64 {
28782908 return switch (ty.tag()) {
......@@ -4148,57 +4178,14 @@ pub const Type = extern union {
41484178 pub const base_tag = Tag.error_set_merged;
41494179
41504180 base: Payload = Payload{ .tag = base_tag },
4151 data: []const []const u8,
4181 data: Module.ErrorSet.NameMap,
41524182 };
41534183
41544184 pub const ErrorSetInferred = struct {
41554185 pub const base_tag = Tag.error_set_inferred;
41564186
41574187 base: Payload = Payload{ .tag = base_tag },
4158 data: Data,
4159
4160 pub const Data = struct {
4161 func: *Module.Fn,
4162 /// Direct additions to the inferred error set via `return error.Foo;`.
4163 map: std.StringHashMapUnmanaged(void),
4164 /// Other functions with inferred error sets which this error set includes.
4165 functions: std.AutoHashMapUnmanaged(*Module.Fn, void),
4166 is_anyerror: bool,
4167
4168 pub fn addErrorSet(self: *Data, gpa: Allocator, err_set_ty: Type) !void {
4169 switch (err_set_ty.tag()) {
4170 .error_set => {
4171 const names = err_set_ty.castTag(.error_set).?.data.names();
4172 for (names) |name| {
4173 try self.map.put(gpa, name, {});
4174 }
4175 },
4176 .error_set_single => {
4177 const name = err_set_ty.castTag(.error_set_single).?.data;
4178 try self.map.put(gpa, name, {});
4179 },
4180 .error_set_inferred => {
4181 const func = err_set_ty.castTag(.error_set_inferred).?.data.func;
4182 try self.functions.put(gpa, func, {});
4183 var it = func.owner_decl.ty.fnReturnType().errorUnionSet()
4184 .castTag(.error_set_inferred).?.data.map.iterator();
4185 while (it.next()) |entry| {
4186 try self.map.put(gpa, entry.key_ptr.*, {});
4187 }
4188 },
4189 .error_set_merged => {
4190 const names = err_set_ty.castTag(.error_set_merged).?.data;
4191 for (names) |name| {
4192 try self.map.put(gpa, name, {});
4193 }
4194 },
4195 .anyerror => {
4196 self.is_anyerror = true;
4197 },
4198 else => unreachable,
4199 }
4200 }
4201 };
4188 data: *Module.Fn.InferredErrorSet,
42024189 };
42034190
42044191 pub const Pointer = struct {
src/value.zig+121-37
......@@ -1969,20 +1969,18 @@ pub const Value = extern union {
19691969 return @divFloor(@floatToInt(std.math.big.Limb, std.math.log2(w_value)), @typeInfo(std.math.big.Limb).Int.bits) + 1;
19701970 }
19711971
1972 /// Supports both floats and ints; handles undefined.
1973 pub fn numberAddWrap(
1972 pub const OverflowArithmeticResult = struct {
1973 overflowed: bool,
1974 wrapped_result: Value,
1975 };
1976
1977 pub fn intAddWithOverflow(
19741978 lhs: Value,
19751979 rhs: Value,
19761980 ty: Type,
19771981 arena: Allocator,
19781982 target: Target,
1979 ) !Value {
1980 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1981
1982 if (ty.isAnyFloat()) {
1983 return floatAdd(lhs, rhs, ty, arena);
1984 }
1985
1983 ) !OverflowArithmeticResult {
19861984 const info = ty.intInfo(target);
19871985
19881986 var lhs_space: Value.BigIntSpace = undefined;
......@@ -1994,8 +1992,30 @@ pub const Value = extern union {
19941992 std.math.big.int.calcTwosCompLimbCount(info.bits),
19951993 );
19961994 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1997 result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1998 return fromBigInt(arena, result_bigint.toConst());
1995 const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1996 const result = try fromBigInt(arena, result_bigint.toConst());
1997 return OverflowArithmeticResult{
1998 .overflowed = overflowed,
1999 .wrapped_result = result,
2000 };
2001 }
2002
2003 /// Supports both floats and ints; handles undefined.
2004 pub fn numberAddWrap(
2005 lhs: Value,
2006 rhs: Value,
2007 ty: Type,
2008 arena: Allocator,
2009 target: Target,
2010 ) !Value {
2011 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
2012
2013 if (ty.isAnyFloat()) {
2014 return floatAdd(lhs, rhs, ty, arena);
2015 }
2016
2017 const overflow_result = try intAddWithOverflow(lhs, rhs, ty, arena, target);
2018 return overflow_result.wrapped_result;
19992019 }
20002020
20012021 fn fromBigInt(arena: Allocator, big_int: BigIntConst) !Value {
......@@ -2040,20 +2060,13 @@ pub const Value = extern union {
20402060 return fromBigInt(arena, result_bigint.toConst());
20412061 }
20422062
2043 /// Supports both floats and ints; handles undefined.
2044 pub fn numberSubWrap(
2063 pub fn intSubWithOverflow(
20452064 lhs: Value,
20462065 rhs: Value,
20472066 ty: Type,
20482067 arena: Allocator,
20492068 target: Target,
2050 ) !Value {
2051 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
2052
2053 if (ty.isAnyFloat()) {
2054 return floatSub(lhs, rhs, ty, arena);
2055 }
2056
2069 ) !OverflowArithmeticResult {
20572070 const info = ty.intInfo(target);
20582071
20592072 var lhs_space: Value.BigIntSpace = undefined;
......@@ -2065,8 +2078,30 @@ pub const Value = extern union {
20652078 std.math.big.int.calcTwosCompLimbCount(info.bits),
20662079 );
20672080 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2068 result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
2069 return fromBigInt(arena, result_bigint.toConst());
2081 const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
2082 const wrapped_result = try fromBigInt(arena, result_bigint.toConst());
2083 return OverflowArithmeticResult{
2084 .overflowed = overflowed,
2085 .wrapped_result = wrapped_result,
2086 };
2087 }
2088
2089 /// Supports both floats and ints; handles undefined.
2090 pub fn numberSubWrap(
2091 lhs: Value,
2092 rhs: Value,
2093 ty: Type,
2094 arena: Allocator,
2095 target: Target,
2096 ) !Value {
2097 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
2098
2099 if (ty.isAnyFloat()) {
2100 return floatSub(lhs, rhs, ty, arena);
2101 }
2102
2103 const overflow_result = try intSubWithOverflow(lhs, rhs, ty, arena, target);
2104 return overflow_result.wrapped_result;
20702105 }
20712106
20722107 /// Supports integers only; asserts neither operand is undefined.
......@@ -2095,20 +2130,13 @@ pub const Value = extern union {
20952130 return fromBigInt(arena, result_bigint.toConst());
20962131 }
20972132
2098 /// Supports both floats and ints; handles undefined.
2099 pub fn numberMulWrap(
2133 pub fn intMulWithOverflow(
21002134 lhs: Value,
21012135 rhs: Value,
21022136 ty: Type,
21032137 arena: Allocator,
21042138 target: Target,
2105 ) !Value {
2106 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
2107
2108 if (ty.isAnyFloat()) {
2109 return floatMul(lhs, rhs, ty, arena);
2110 }
2111
2139 ) !OverflowArithmeticResult {
21122140 const info = ty.intInfo(target);
21132141
21142142 var lhs_space: Value.BigIntSpace = undefined;
......@@ -2117,16 +2145,42 @@ pub const Value = extern union {
21172145 const rhs_bigint = rhs.toBigInt(&rhs_space);
21182146 const limbs = try arena.alloc(
21192147 std.math.big.Limb,
2120 std.math.big.int.calcTwosCompLimbCount(info.bits),
2148 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
21212149 );
21222150 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
21232151 var limbs_buffer = try arena.alloc(
21242152 std.math.big.Limb,
2125 std.math.big.int.calcMulWrapLimbsBufferLen(info.bits, lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
2153 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
21262154 );
2127 defer arena.free(limbs_buffer);
2128 result_bigint.mulWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits, limbs_buffer, arena);
2129 return fromBigInt(arena, result_bigint.toConst());
2155 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena);
2156
2157 const overflowed = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);
2158 if (overflowed) {
2159 result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
2160 }
2161
2162 return OverflowArithmeticResult{
2163 .overflowed = overflowed,
2164 .wrapped_result = try fromBigInt(arena, result_bigint.toConst()),
2165 };
2166 }
2167
2168 /// Supports both floats and ints; handles undefined.
2169 pub fn numberMulWrap(
2170 lhs: Value,
2171 rhs: Value,
2172 ty: Type,
2173 arena: Allocator,
2174 target: Target,
2175 ) !Value {
2176 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
2177
2178 if (ty.isAnyFloat()) {
2179 return floatMul(lhs, rhs, ty, arena);
2180 }
2181
2182 const overflow_result = try intMulWithOverflow(lhs, rhs, ty, arena, target);
2183 return overflow_result.wrapped_result;
21302184 }
21312185
21322186 /// Supports integers only; asserts neither operand is undefined.
......@@ -2159,7 +2213,6 @@ pub const Value = extern union {
21592213 std.math.big.Limb,
21602214 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
21612215 );
2162 defer arena.free(limbs_buffer);
21632216 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena);
21642217 result_bigint.saturate(result_bigint.toConst(), info.signedness, info.bits);
21652218 return fromBigInt(arena, result_bigint.toConst());
......@@ -2495,6 +2548,37 @@ pub const Value = extern union {
24952548 return fromBigInt(allocator, result_bigint.toConst());
24962549 }
24972550
2551 pub fn shlWithOverflow(
2552 lhs: Value,
2553 rhs: Value,
2554 ty: Type,
2555 allocator: Allocator,
2556 target: Target,
2557 ) !OverflowArithmeticResult {
2558 const info = ty.intInfo(target);
2559 var lhs_space: Value.BigIntSpace = undefined;
2560 const lhs_bigint = lhs.toBigInt(&lhs_space);
2561 const shift = @intCast(usize, rhs.toUnsignedInt());
2562 const limbs = try allocator.alloc(
2563 std.math.big.Limb,
2564 lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1,
2565 );
2566 var result_bigint = BigIntMutable{
2567 .limbs = limbs,
2568 .positive = undefined,
2569 .len = undefined,
2570 };
2571 result_bigint.shiftLeft(lhs_bigint, shift);
2572 const overflowed = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);
2573 if (overflowed) {
2574 result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
2575 }
2576 return OverflowArithmeticResult{
2577 .overflowed = overflowed,
2578 .wrapped_result = try fromBigInt(allocator, result_bigint.toConst()),
2579 };
2580 }
2581
24982582 pub fn shlSat(
24992583 lhs: Value,
25002584 rhs: Value,
test/behavior/eval.zig+16
......@@ -451,3 +451,19 @@ test "comptime bitwise operators" {
451451 try expect(~@as(u128, 0) == 0xffffffffffffffffffffffffffffffff);
452452 }
453453}
454
455test "comptime shlWithOverflow" {
456 const ct_shifted: u64 = comptime amt: {
457 var amt = @as(u64, 0);
458 _ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt);
459 break :amt amt;
460 };
461
462 const rt_shifted: u64 = amt: {
463 var amt = @as(u64, 0);
464 _ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt);
465 break :amt amt;
466 };
467
468 try expect(ct_shifted == rt_shifted);
469}
test/behavior/eval_stage1.zig-16
......@@ -162,22 +162,6 @@ test "const ptr to comptime mutable data is not memoized" {
162162 }
163163}
164164
165test "comptime shlWithOverflow" {
166 const ct_shifted: u64 = comptime amt: {
167 var amt = @as(u64, 0);
168 _ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt);
169 break :amt amt;
170 };
171
172 const rt_shifted: u64 = amt: {
173 var amt = @as(u64, 0);
174 _ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt);
175 break :amt amt;
176 };
177
178 try expect(ct_shifted == rt_shifted);
179}
180
181165test "runtime 128 bit integer division" {
182166 var a: u128 = 152313999999999991610955792383;
183167 var b: u128 = 10000000000000000000;
test/behavior/math.zig+95
......@@ -444,3 +444,98 @@ test "128-bit multiplication" {
444444 var c = a * b;
445445 try expect(c == 6);
446446}
447
448test "@addWithOverflow" {
449 var result: u8 = undefined;
450 try expect(@addWithOverflow(u8, 250, 100, &result));
451 try expect(result == 94);
452 try expect(!@addWithOverflow(u8, 100, 150, &result));
453 try expect(result == 250);
454
455 var a: u8 = 200;
456 var b: u8 = 99;
457 try expect(@addWithOverflow(u8, a, b, &result));
458 try expect(result == 43);
459 b = 55;
460 try expect(!@addWithOverflow(u8, a, b, &result));
461 try expect(result == 255);
462}
463
464test "small int addition" {
465 var x: u2 = 0;
466 try expect(x == 0);
467
468 x += 1;
469 try expect(x == 1);
470
471 x += 1;
472 try expect(x == 2);
473
474 x += 1;
475 try expect(x == 3);
476
477 var result: @TypeOf(x) = 3;
478 try expect(@addWithOverflow(@TypeOf(x), x, 1, &result));
479
480 try expect(result == 0);
481}
482
483test "@mulWithOverflow" {
484 var result: u8 = undefined;
485 try expect(@mulWithOverflow(u8, 86, 3, &result));
486 try expect(result == 2);
487 try expect(!@mulWithOverflow(u8, 85, 3, &result));
488 try expect(result == 255);
489
490 var a: u8 = 123;
491 var b: u8 = 2;
492 try expect(!@mulWithOverflow(u8, a, b, &result));
493 try expect(result == 246);
494 b = 4;
495 try expect(@mulWithOverflow(u8, a, b, &result));
496 try expect(result == 236);
497}
498
499test "@subWithOverflow" {
500 var result: u8 = undefined;
501 try expect(@subWithOverflow(u8, 1, 2, &result));
502 try expect(result == 255);
503 try expect(!@subWithOverflow(u8, 1, 1, &result));
504 try expect(result == 0);
505
506 var a: u8 = 1;
507 var b: u8 = 2;
508 try expect(@subWithOverflow(u8, a, b, &result));
509 try expect(result == 255);
510 b = 1;
511 try expect(!@subWithOverflow(u8, a, b, &result));
512 try expect(result == 0);
513}
514
515test "@shlWithOverflow" {
516 var result: u16 = undefined;
517 try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));
518 try expect(result == 0b0111111111111000);
519 try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result));
520 try expect(result == 0b1011111111111100);
521
522 var a: u16 = 0b0000_0000_0000_0011;
523 var b: u4 = 15;
524 try expect(@shlWithOverflow(u16, a, b, &result));
525 try expect(result == 0b1000_0000_0000_0000);
526 b = 14;
527 try expect(!@shlWithOverflow(u16, a, b, &result));
528 try expect(result == 0b1100_0000_0000_0000);
529}
530
531test "overflow arithmetic with u0 values" {
532 var result: u0 = undefined;
533 try expect(!@addWithOverflow(u0, 0, 0, &result));
534 try expect(result == 0);
535 try expect(!@subWithOverflow(u0, 0, 0, &result));
536 try expect(result == 0);
537 try expect(!@mulWithOverflow(u0, 0, 0, &result));
538 try expect(result == 0);
539 try expect(!@shlWithOverflow(u0, 0, 0, &result));
540 try expect(result == 0);
541}
test/behavior/math_stage1.zig-63
......@@ -6,50 +6,6 @@ const maxInt = std.math.maxInt;
66const minInt = std.math.minInt;
77const mem = std.mem;
88
9test "@addWithOverflow" {
10 var result: u8 = undefined;
11 try expect(@addWithOverflow(u8, 250, 100, &result));
12 try expect(result == 94);
13 try expect(!@addWithOverflow(u8, 100, 150, &result));
14 try expect(result == 250);
15}
16
17test "@mulWithOverflow" {
18 var result: u8 = undefined;
19 try expect(@mulWithOverflow(u8, 86, 3, &result));
20 try expect(result == 2);
21 try expect(!@mulWithOverflow(u8, 85, 3, &result));
22 try expect(result == 255);
23}
24
25test "@subWithOverflow" {
26 var result: u8 = undefined;
27 try expect(@subWithOverflow(u8, 1, 2, &result));
28 try expect(result == 255);
29 try expect(!@subWithOverflow(u8, 1, 1, &result));
30 try expect(result == 0);
31}
32
33test "@shlWithOverflow" {
34 var result: u16 = undefined;
35 try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));
36 try expect(result == 0b0111111111111000);
37 try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result));
38 try expect(result == 0b1011111111111100);
39}
40
41test "overflow arithmetic with u0 values" {
42 var result: u0 = undefined;
43 try expect(!@addWithOverflow(u0, 0, 0, &result));
44 try expect(result == 0);
45 try expect(!@subWithOverflow(u0, 0, 0, &result));
46 try expect(result == 0);
47 try expect(!@mulWithOverflow(u0, 0, 0, &result));
48 try expect(result == 0);
49 try expect(!@shlWithOverflow(u0, 0, 0, &result));
50 try expect(result == 0);
51}
52
539test "@clz vectors" {
5410 try testClzVectors();
5511 comptime try testClzVectors();
......@@ -90,25 +46,6 @@ fn testCtzVectors() !void {
9046 try expectEqual(@ctz(u16, @splat(64, @as(u16, 0b00000000))), @splat(64, @as(u5, 16)));
9147}
9248
93test "small int addition" {
94 var x: u2 = 0;
95 try expect(x == 0);
96
97 x += 1;
98 try expect(x == 1);
99
100 x += 1;
101 try expect(x == 2);
102
103 x += 1;
104 try expect(x == 3);
105
106 var result: @TypeOf(x) = 3;
107 try expect(@addWithOverflow(@TypeOf(x), x, 1, &result));
108
109 try expect(result == 0);
110}
111
11249test "allow signed integer division/remainder when values are comptime known and positive or exact" {
11350 try expect(5 / 3 == 1);
11451 try expect(-5 / -3 == 1);