authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-13 10:05:20-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-13 10:05:20-07:00
log6e0fb060109349b4fb7855c1587b6d9c396d926b
treebbf9a1aed1c8a160caffe68821f71ffd5c1d6fec
parentcb06d62603c764a91aeb9bcedc0b9472d746f9e5
parentec4953504a07f3025d5f32344180dd9b6a4de8ae

Merge branch 'Vexu-stage2'

closes #6042

8 files changed, 420 insertions(+), 44 deletions(-)

src-self-hosted/Module.zig+85-5
...@@ -2219,11 +2219,6 @@ pub fn wantSafety(self: *Module, scope: *Scope) bool {...@@ -2219,11 +2219,6 @@ pub fn wantSafety(self: *Module, scope: *Scope) bool {
2219 };2219 };
2220}2220}
22212221
2222pub fn analyzeUnreach(self: *Module, scope: *Scope, src: usize) InnerError!*Inst {
2223 const b = try self.requireRuntimeBlock(scope, src);
2224 return self.addNoOp(b, src, Type.initTag(.noreturn), .unreach);
2225}
2226
2227pub fn analyzeIsNull(2222pub fn analyzeIsNull(
2228 self: *Module,2223 self: *Module,
2229 scope: *Scope,2224 scope: *Scope,
...@@ -2476,6 +2471,24 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst...@@ -2476,6 +2471,24 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
2476 }2471 }
2477 assert(inst.ty.zigTypeTag() != .Undefined);2472 assert(inst.ty.zigTypeTag() != .Undefined);
24782473
2474 // null to ?T
2475 if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) {
2476 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
2477 }
2478
2479 // T to ?T
2480 if (dest_type.zigTypeTag() == .Optional) {
2481 const child_type = dest_type.elemType();
2482 if (inst.value()) |val| {
2483 if (child_type.eql(inst.ty)) {
2484 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2485 }
2486 return self.fail(scope, inst.src, "TODO optional wrap {} to {}", .{ val, dest_type });
2487 } else if (child_type.eql(inst.ty)) {
2488 return self.fail(scope, inst.src, "TODO optional wrap {}", .{dest_type});
2489 }
2490 }
2491
2479 // *[N]T to []T2492 // *[N]T to []T
2480 if (inst.ty.isSinglePointer() and dest_type.isSlice() and2493 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
2481 (!inst.ty.isConstPtr() or dest_type.isConstPtr()))2494 (!inst.ty.isConstPtr() or dest_type.isConstPtr()))
...@@ -2884,3 +2897,70 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {...@@ -2884,3 +2897,70 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
2884 });2897 });
2885 }2898 }
2886}2899}
2900
2901pub const PanicId = enum {
2902 unreach,
2903 unwrap_null,
2904};
2905
2906pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
2907 const block_inst = try parent_block.arena.create(Inst.Block);
2908 block_inst.* = .{
2909 .base = .{
2910 .tag = Inst.Block.base_tag,
2911 .ty = Type.initTag(.void),
2912 .src = ok.src,
2913 },
2914 .body = .{
2915 .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr.
2916 },
2917 };
2918
2919 const ok_body: ir.Body = .{
2920 .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the brvoid.
2921 };
2922 const brvoid = try parent_block.arena.create(Inst.BrVoid);
2923 brvoid.* = .{
2924 .base = .{
2925 .tag = .brvoid,
2926 .ty = Type.initTag(.noreturn),
2927 .src = ok.src,
2928 },
2929 .block = block_inst,
2930 };
2931 ok_body.instructions[0] = &brvoid.base;
2932
2933 var fail_block: Scope.Block = .{
2934 .parent = parent_block,
2935 .func = parent_block.func,
2936 .decl = parent_block.decl,
2937 .instructions = .{},
2938 .arena = parent_block.arena,
2939 };
2940 defer fail_block.instructions.deinit(mod.gpa);
2941
2942 _ = try mod.safetyPanic(&fail_block, ok.src, panic_id);
2943
2944 const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) };
2945
2946 const condbr = try parent_block.arena.create(Inst.CondBr);
2947 condbr.* = .{
2948 .base = .{
2949 .tag = .condbr,
2950 .ty = Type.initTag(.noreturn),
2951 .src = ok.src,
2952 },
2953 .condition = ok,
2954 .then_body = ok_body,
2955 .else_body = fail_body,
2956 };
2957 block_inst.body.instructions[0] = &condbr.base;
2958
2959 try parent_block.instructions.append(mod.gpa, &block_inst.base);
2960}
2961
2962pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: PanicId) !*Inst {
2963 // TODO Once we have a panic function to call, call it here instead of breakpoint.
2964 _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint);
2965 return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach);
2966}
src-self-hosted/astgen.zig+24
...@@ -105,6 +105,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -105,6 +105,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
105 .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),105 .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),
106 .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)),106 .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)),
107 .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)),107 .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)),
108 .OptionalType => return rlWrap(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)),
109 .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?),
108 else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}),110 else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}),
109 }111 }
110}112}
...@@ -293,6 +295,28 @@ fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerErr...@@ -293,6 +295,28 @@ fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerErr
293 return addZIRUnOp(mod, scope, src, .boolnot, operand);295 return addZIRUnOp(mod, scope, src, .boolnot, operand);
294}296}
295297
298fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
299 const tree = scope.tree();
300 const src = tree.token_locs[node.op_token].start;
301 const meta_type = try addZIRInstConst(mod, scope, src, .{
302 .ty = Type.initTag(.type),
303 .val = Value.initTag(.type_type),
304 });
305 const operand = try expr(mod, scope, .{ .ty = meta_type }, node.rhs);
306 return addZIRUnOp(mod, scope, src, .optional_type, operand);
307}
308
309fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
310 const tree = scope.tree();
311 const src = tree.token_locs[node.rtoken].start;
312
313 const operand = try expr(mod, scope, .lvalue, node.lhs);
314 const unwrapped_ptr = try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand);
315 if (rl == .lvalue) return unwrapped_ptr;
316
317 return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, unwrapped_ptr));
318}
319
296/// Identifier token -> String (allocated in scope.arena())320/// Identifier token -> String (allocated in scope.arena())
297pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {321pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
298 const tree = scope.tree();322 const tree = scope.tree();
src-self-hosted/codegen.zig+10
...@@ -668,6 +668,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -668,6 +668,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
668 .store => return self.genStore(inst.castTag(.store).?),668 .store => return self.genStore(inst.castTag(.store).?),
669 .sub => return self.genSub(inst.castTag(.sub).?),669 .sub => return self.genSub(inst.castTag(.sub).?),
670 .unreach => return MCValue{ .unreach = {} },670 .unreach => return MCValue{ .unreach = {} },
671 .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?),
671 }672 }
672 }673 }
673674
...@@ -817,6 +818,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -817,6 +818,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
817 }818 }
818 }819 }
819820
821 fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
822 // No side effects, so if it's unreferenced, do nothing.
823 if (inst.base.isUnused())
824 return MCValue.dead;
825 switch (arch) {
826 else => return self.fail(inst.base.src, "TODO implement unwrap optional for {}", .{self.target.cpu.arch}),
827 }
828 }
829
820 fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {830 fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
821 const elem_ty = inst.base.ty;831 const elem_ty = inst.base.ty;
822 if (!elem_ty.hasCodeGenBits())832 if (!elem_ty.hasCodeGenBits())
src-self-hosted/ir.zig+2-1
...@@ -82,6 +82,7 @@ pub const Inst = struct {...@@ -82,6 +82,7 @@ pub const Inst = struct {
82 not,82 not,
83 floatcast,83 floatcast,
84 intcast,84 intcast,
85 unwrap_optional,
8586
86 pub fn Type(tag: Tag) type {87 pub fn Type(tag: Tag) type {
87 return switch (tag) {88 return switch (tag) {
...@@ -102,6 +103,7 @@ pub const Inst = struct {...@@ -102,6 +103,7 @@ pub const Inst = struct {
102 .floatcast,103 .floatcast,
103 .intcast,104 .intcast,
104 .load,105 .load,
106 .unwrap_optional,
105 => UnOp,107 => UnOp,
106108
107 .add,109 .add,
...@@ -419,7 +421,6 @@ pub const Inst = struct {...@@ -419,7 +421,6 @@ pub const Inst = struct {
419 return null;421 return null;
420 }422 }
421 };423 };
422
423};424};
424425
425pub const Body = struct {426pub const Body = struct {
src-self-hosted/type.zig+161-26
...@@ -70,6 +70,11 @@ pub const Type = extern union {...@@ -70,6 +70,11 @@ pub const Type = extern union {
70 .single_mut_pointer => return .Pointer,70 .single_mut_pointer => return .Pointer,
71 .single_const_pointer_to_comptime_int => return .Pointer,71 .single_const_pointer_to_comptime_int => return .Pointer,
72 .const_slice_u8 => return .Pointer,72 .const_slice_u8 => return .Pointer,
73
74 .optional,
75 .optional_single_const_pointer,
76 .optional_single_mut_pointer,
77 => return .Optional,
73 }78 }
74 }79 }
7580
...@@ -179,9 +184,11 @@ pub const Type = extern union {...@@ -179,9 +184,11 @@ pub const Type = extern union {
179 }184 }
180 return true;185 return true;
181 },186 },
187 .Optional => {
188 return a.elemType().eql(b.elemType());
189 },
182 .Float,190 .Float,
183 .Struct,191 .Struct,
184 .Optional,
185 .ErrorUnion,192 .ErrorUnion,
186 .ErrorSet,193 .ErrorSet,
187 .Enum,194 .Enum,
...@@ -241,9 +248,11 @@ pub const Type = extern union {...@@ -241,9 +248,11 @@ pub const Type = extern union {
241 std.hash.autoHash(&hasher, self.fnParamType(i).hash());248 std.hash.autoHash(&hasher, self.fnParamType(i).hash());
242 }249 }
243 },250 },
251 .Optional => {
252 std.hash.autoHash(&hasher, self.elemType().hash());
253 },
244 .Float,254 .Float,
245 .Struct,255 .Struct,
246 .Optional,
247 .ErrorUnion,256 .ErrorUnion,
248 .ErrorSet,257 .ErrorSet,
249 .Enum,258 .Enum,
...@@ -317,24 +326,8 @@ pub const Type = extern union {...@@ -317,24 +326,8 @@ pub const Type = extern union {
317 };326 };
318 return Type{ .ptr_otherwise = &new_payload.base };327 return Type{ .ptr_otherwise = &new_payload.base };
319 },328 },
320 .single_const_pointer => {329 .single_const_pointer => return self.copyPayloadSingleField(allocator, Payload.SingleConstPointer, "pointee_type"),
321 const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", self.ptr_otherwise);330 .single_mut_pointer => return self.copyPayloadSingleField(allocator, Payload.SingleMutPointer, "pointee_type"),
322 const new_payload = try allocator.create(Payload.SingleConstPointer);
323 new_payload.* = .{
324 .base = payload.base,
325 .pointee_type = try payload.pointee_type.copy(allocator),
326 };
327 return Type{ .ptr_otherwise = &new_payload.base };
328 },
329 .single_mut_pointer => {
330 const payload = @fieldParentPtr(Payload.SingleMutPointer, "base", self.ptr_otherwise);
331 const new_payload = try allocator.create(Payload.SingleMutPointer);
332 new_payload.* = .{
333 .base = payload.base,
334 .pointee_type = try payload.pointee_type.copy(allocator),
335 };
336 return Type{ .ptr_otherwise = &new_payload.base };
337 },
338 .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),331 .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
339 .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),332 .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
340 .function => {333 .function => {
...@@ -352,6 +345,9 @@ pub const Type = extern union {...@@ -352,6 +345,9 @@ pub const Type = extern union {
352 };345 };
353 return Type{ .ptr_otherwise = &new_payload.base };346 return Type{ .ptr_otherwise = &new_payload.base };
354 },347 },
348 .optional => return self.copyPayloadSingleField(allocator, Payload.Optional, "child_type"),
349 .optional_single_mut_pointer => return self.copyPayloadSingleField(allocator, Payload.OptionalSingleMutPointer, "pointee_type"),
350 .optional_single_const_pointer => return self.copyPayloadSingleField(allocator, Payload.OptionalSingleConstPointer, "pointee_type"),
355 }351 }
356 }352 }
357353
...@@ -362,6 +358,14 @@ pub const Type = extern union {...@@ -362,6 +358,14 @@ pub const Type = extern union {
362 return Type{ .ptr_otherwise = &new_payload.base };358 return Type{ .ptr_otherwise = &new_payload.base };
363 }359 }
364360
361 fn copyPayloadSingleField(self: Type, allocator: *Allocator, comptime T: type, comptime field_name: []const u8) error{OutOfMemory}!Type {
362 const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
363 const new_payload = try allocator.create(T);
364 new_payload.base = payload.base;
365 @field(new_payload, field_name) = try @field(payload, field_name).copy(allocator);
366 return Type{ .ptr_otherwise = &new_payload.base };
367 }
368
365 pub fn format(369 pub fn format(
366 self: Type,370 self: Type,
367 comptime fmt: []const u8,371 comptime fmt: []const u8,
...@@ -456,6 +460,24 @@ pub const Type = extern union {...@@ -456,6 +460,24 @@ pub const Type = extern union {
456 const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise);460 const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise);
457 return out_stream.print("u{}", .{payload.bits});461 return out_stream.print("u{}", .{payload.bits});
458 },462 },
463 .optional => {
464 const payload = @fieldParentPtr(Payload.Optional, "base", ty.ptr_otherwise);
465 try out_stream.writeByte('?');
466 ty = payload.child_type;
467 continue;
468 },
469 .optional_single_const_pointer => {
470 const payload = @fieldParentPtr(Payload.OptionalSingleConstPointer, "base", ty.ptr_otherwise);
471 try out_stream.writeAll("?*const ");
472 ty = payload.pointee_type;
473 continue;
474 },
475 .optional_single_mut_pointer => {
476 const payload = @fieldParentPtr(Payload.OptionalSingleMutPointer, "base", ty.ptr_otherwise);
477 try out_stream.writeAll("?*");
478 ty = payload.pointee_type;
479 continue;
480 },
459 }481 }
460 unreachable;482 unreachable;
461 }483 }
...@@ -545,12 +567,16 @@ pub const Type = extern union {...@@ -545,12 +567,16 @@ pub const Type = extern union {
545 .single_const_pointer_to_comptime_int,567 .single_const_pointer_to_comptime_int,
546 .const_slice_u8,568 .const_slice_u8,
547 .array_u8_sentinel_0,569 .array_u8_sentinel_0,
548 .array, // TODO check for zero bits570 .optional,
549 .single_const_pointer,571 .optional_single_mut_pointer,
550 .single_mut_pointer,572 .optional_single_const_pointer,
551 .int_signed, // TODO check for zero bits
552 .int_unsigned, // TODO check for zero bits
553 => true,573 => true,
574 // TODO lazy types
575 .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
576 .single_const_pointer => self.elemType().hasCodeGenBits(),
577 .single_mut_pointer => self.elemType().hasCodeGenBits(),
578 .int_signed => self.cast(Payload.IntSigned).?.bits == 0,
579 .int_unsigned => self.cast(Payload.IntUnsigned).?.bits == 0,
554580
555 .c_void,581 .c_void,
556 .void,582 .void,
...@@ -597,6 +623,8 @@ pub const Type = extern union {...@@ -597,6 +623,8 @@ pub const Type = extern union {
597 .const_slice_u8,623 .const_slice_u8,
598 .single_const_pointer,624 .single_const_pointer,
599 .single_mut_pointer,625 .single_mut_pointer,
626 .optional_single_const_pointer,
627 .optional_single_mut_pointer,
600 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),628 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
601629
602 .c_short => return @divExact(CType.short.sizeInBits(target), 8),630 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
...@@ -629,6 +657,16 @@ pub const Type = extern union {...@@ -629,6 +657,16 @@ pub const Type = extern union {
629 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);657 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
630 },658 },
631659
660 .optional => {
661 const child_type = self.cast(Payload.Optional).?.child_type;
662 if (!child_type.hasCodeGenBits()) return 1;
663
664 if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr())
665 return @divExact(target.cpu.arch.ptrBitWidth(), 8);
666
667 return child_type.abiAlignment(target);
668 },
669
632 .c_void,670 .c_void,
633 .void,671 .void,
634 .type,672 .type,
...@@ -679,6 +717,8 @@ pub const Type = extern union {...@@ -679,6 +717,8 @@ pub const Type = extern union {
679 .const_slice_u8,717 .const_slice_u8,
680 .single_const_pointer,718 .single_const_pointer,
681 .single_mut_pointer,719 .single_mut_pointer,
720 .optional_single_const_pointer,
721 .optional_single_mut_pointer,
682 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),722 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
683723
684 .c_short => return @divExact(CType.short.sizeInBits(target), 8),724 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
...@@ -708,6 +748,20 @@ pub const Type = extern union {...@@ -708,6 +748,20 @@ pub const Type = extern union {
708748
709 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);749 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
710 },750 },
751
752 .optional => {
753 const child_type = self.cast(Payload.Optional).?.child_type;
754 if (!child_type.hasCodeGenBits()) return 1;
755
756 if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr())
757 return @divExact(target.cpu.arch.ptrBitWidth(), 8);
758
759 // Optional types are represented as a struct with the child type as the first
760 // field and a boolean as the second. Since the child type's abi alignment is
761 // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal
762 // to the child type's ABI alignment.
763 return child_type.abiAlignment(target) + child_type.abiSize(target);
764 },
711 };765 };
712 }766 }
713767
...@@ -756,6 +810,9 @@ pub const Type = extern union {...@@ -756,6 +810,9 @@ pub const Type = extern union {
756 .function,810 .function,
757 .int_unsigned,811 .int_unsigned,
758 .int_signed,812 .int_signed,
813 .optional,
814 .optional_single_mut_pointer,
815 .optional_single_const_pointer,
759 => false,816 => false,
760817
761 .single_const_pointer,818 .single_const_pointer,
...@@ -812,6 +869,9 @@ pub const Type = extern union {...@@ -812,6 +869,9 @@ pub const Type = extern union {
812 .function,869 .function,
813 .int_unsigned,870 .int_unsigned,
814 .int_signed,871 .int_signed,
872 .optional,
873 .optional_single_mut_pointer,
874 .optional_single_const_pointer,
815 => false,875 => false,
816876
817 .const_slice_u8 => true,877 .const_slice_u8 => true,
...@@ -863,6 +923,9 @@ pub const Type = extern union {...@@ -863,6 +923,9 @@ pub const Type = extern union {
863 .int_unsigned,923 .int_unsigned,
864 .int_signed,924 .int_signed,
865 .single_mut_pointer,925 .single_mut_pointer,
926 .optional,
927 .optional_single_mut_pointer,
928 .optional_single_const_pointer,
866 => false,929 => false,
867930
868 .single_const_pointer,931 .single_const_pointer,
...@@ -920,11 +983,14 @@ pub const Type = extern union {...@@ -920,11 +983,14 @@ pub const Type = extern union {
920 .single_const_pointer,983 .single_const_pointer,
921 .single_const_pointer_to_comptime_int,984 .single_const_pointer_to_comptime_int,
922 .const_slice_u8,985 .const_slice_u8,
986 .optional,
987 .optional_single_mut_pointer,
988 .optional_single_const_pointer,
923 => false,989 => false,
924 };990 };
925 }991 }
926992
927 /// Asserts the type is a pointer or array type.993 /// Asserts the type is a pointer, optional or array type.
928 pub fn elemType(self: Type) Type {994 pub fn elemType(self: Type) Type {
929 return switch (self.tag()) {995 return switch (self.tag()) {
930 .u8,996 .u8,
...@@ -974,6 +1040,9 @@ pub const Type = extern union {...@@ -974,6 +1040,9 @@ pub const Type = extern union {
974 .single_mut_pointer => self.cast(Payload.SingleMutPointer).?.pointee_type,1040 .single_mut_pointer => self.cast(Payload.SingleMutPointer).?.pointee_type,
975 .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),1041 .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
976 .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),1042 .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
1043 .optional => self.cast(Payload.Optional).?.child_type,
1044 .optional_single_mut_pointer => self.cast(Payload.OptionalSingleMutPointer).?.pointee_type,
1045 .optional_single_const_pointer => self.cast(Payload.OptionalSingleConstPointer).?.pointee_type,
977 };1046 };
978 }1047 }
9791048
...@@ -1024,6 +1093,9 @@ pub const Type = extern union {...@@ -1024,6 +1093,9 @@ pub const Type = extern union {
1024 .const_slice_u8,1093 .const_slice_u8,
1025 .int_unsigned,1094 .int_unsigned,
1026 .int_signed,1095 .int_signed,
1096 .optional,
1097 .optional_single_mut_pointer,
1098 .optional_single_const_pointer,
1027 => unreachable,1099 => unreachable,
10281100
1029 .array => self.cast(Payload.Array).?.len,1101 .array => self.cast(Payload.Array).?.len,
...@@ -1078,6 +1150,9 @@ pub const Type = extern union {...@@ -1078,6 +1150,9 @@ pub const Type = extern union {
1078 .const_slice_u8,1150 .const_slice_u8,
1079 .int_unsigned,1151 .int_unsigned,
1080 .int_signed,1152 .int_signed,
1153 .optional,
1154 .optional_single_mut_pointer,
1155 .optional_single_const_pointer,
1081 => unreachable,1156 => unreachable,
10821157
1083 .array => return null,1158 .array => return null,
...@@ -1129,6 +1204,9 @@ pub const Type = extern union {...@@ -1129,6 +1204,9 @@ pub const Type = extern union {
1129 .u16,1204 .u16,
1130 .u32,1205 .u32,
1131 .u64,1206 .u64,
1207 .optional,
1208 .optional_single_mut_pointer,
1209 .optional_single_const_pointer,
1132 => false,1210 => false,
11331211
1134 .int_signed,1212 .int_signed,
...@@ -1184,6 +1262,9 @@ pub const Type = extern union {...@@ -1184,6 +1262,9 @@ pub const Type = extern union {
1184 .i16,1262 .i16,
1185 .i32,1263 .i32,
1186 .i64,1264 .i64,
1265 .optional,
1266 .optional_single_mut_pointer,
1267 .optional_single_const_pointer,
1187 => false,1268 => false,
11881269
1189 .int_unsigned,1270 .int_unsigned,
...@@ -1229,6 +1310,9 @@ pub const Type = extern union {...@@ -1229,6 +1310,9 @@ pub const Type = extern union {
1229 .single_const_pointer_to_comptime_int,1310 .single_const_pointer_to_comptime_int,
1230 .array_u8_sentinel_0,1311 .array_u8_sentinel_0,
1231 .const_slice_u8,1312 .const_slice_u8,
1313 .optional,
1314 .optional_single_mut_pointer,
1315 .optional_single_const_pointer,
1232 => unreachable,1316 => unreachable,
12331317
1234 .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },1318 .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },
...@@ -1292,6 +1376,9 @@ pub const Type = extern union {...@@ -1292,6 +1376,9 @@ pub const Type = extern union {
1292 .i32,1376 .i32,
1293 .u64,1377 .u64,
1294 .i64,1378 .i64,
1379 .optional,
1380 .optional_single_mut_pointer,
1381 .optional_single_const_pointer,
1295 => false,1382 => false,
12961383
1297 .usize,1384 .usize,
...@@ -1384,6 +1471,9 @@ pub const Type = extern union {...@@ -1384,6 +1471,9 @@ pub const Type = extern union {
1384 .c_ulonglong,1471 .c_ulonglong,
1385 .int_unsigned,1472 .int_unsigned,
1386 .int_signed,1473 .int_signed,
1474 .optional,
1475 .optional_single_mut_pointer,
1476 .optional_single_const_pointer,
1387 => unreachable,1477 => unreachable,
1388 };1478 };
1389 }1479 }
...@@ -1442,6 +1532,9 @@ pub const Type = extern union {...@@ -1442,6 +1532,9 @@ pub const Type = extern union {
1442 .c_ulonglong,1532 .c_ulonglong,
1443 .int_unsigned,1533 .int_unsigned,
1444 .int_signed,1534 .int_signed,
1535 .optional,
1536 .optional_single_mut_pointer,
1537 .optional_single_const_pointer,
1445 => unreachable,1538 => unreachable,
1446 }1539 }
1447 }1540 }
...@@ -1499,6 +1592,9 @@ pub const Type = extern union {...@@ -1499,6 +1592,9 @@ pub const Type = extern union {
1499 .c_ulonglong,1592 .c_ulonglong,
1500 .int_unsigned,1593 .int_unsigned,
1501 .int_signed,1594 .int_signed,
1595 .optional,
1596 .optional_single_mut_pointer,
1597 .optional_single_const_pointer,
1502 => unreachable,1598 => unreachable,
1503 }1599 }
1504 }1600 }
...@@ -1556,6 +1652,9 @@ pub const Type = extern union {...@@ -1556,6 +1652,9 @@ pub const Type = extern union {
1556 .c_ulonglong,1652 .c_ulonglong,
1557 .int_unsigned,1653 .int_unsigned,
1558 .int_signed,1654 .int_signed,
1655 .optional,
1656 .optional_single_mut_pointer,
1657 .optional_single_const_pointer,
1559 => unreachable,1658 => unreachable,
1560 };1659 };
1561 }1660 }
...@@ -1610,6 +1709,9 @@ pub const Type = extern union {...@@ -1610,6 +1709,9 @@ pub const Type = extern union {
1610 .c_ulonglong,1709 .c_ulonglong,
1611 .int_unsigned,1710 .int_unsigned,
1612 .int_signed,1711 .int_signed,
1712 .optional,
1713 .optional_single_mut_pointer,
1714 .optional_single_const_pointer,
1613 => unreachable,1715 => unreachable,
1614 };1716 };
1615 }1717 }
...@@ -1664,6 +1766,9 @@ pub const Type = extern union {...@@ -1664,6 +1766,9 @@ pub const Type = extern union {
1664 .c_ulonglong,1766 .c_ulonglong,
1665 .int_unsigned,1767 .int_unsigned,
1666 .int_signed,1768 .int_signed,
1769 .optional,
1770 .optional_single_mut_pointer,
1771 .optional_single_const_pointer,
1667 => unreachable,1772 => unreachable,
1668 };1773 };
1669 }1774 }
...@@ -1718,6 +1823,9 @@ pub const Type = extern union {...@@ -1718,6 +1823,9 @@ pub const Type = extern union {
1718 .single_const_pointer_to_comptime_int,1823 .single_const_pointer_to_comptime_int,
1719 .array_u8_sentinel_0,1824 .array_u8_sentinel_0,
1720 .const_slice_u8,1825 .const_slice_u8,
1826 .optional,
1827 .optional_single_mut_pointer,
1828 .optional_single_const_pointer,
1721 => false,1829 => false,
1722 };1830 };
1723 }1831 }
...@@ -1762,6 +1870,9 @@ pub const Type = extern union {...@@ -1762,6 +1870,9 @@ pub const Type = extern union {
1762 .array_u8_sentinel_0,1870 .array_u8_sentinel_0,
1763 .const_slice_u8,1871 .const_slice_u8,
1764 .c_void,1872 .c_void,
1873 .optional,
1874 .optional_single_mut_pointer,
1875 .optional_single_const_pointer,
1765 => return null,1876 => return null,
17661877
1767 .void => return Value.initTag(.void_value),1878 .void => return Value.initTag(.void_value),
...@@ -1851,6 +1962,9 @@ pub const Type = extern union {...@@ -1851,6 +1962,9 @@ pub const Type = extern union {
1851 .array,1962 .array,
1852 .single_const_pointer,1963 .single_const_pointer,
1853 .single_mut_pointer,1964 .single_mut_pointer,
1965 .optional,
1966 .optional_single_mut_pointer,
1967 .optional_single_const_pointer,
1854 => return false,1968 => return false,
1855 };1969 };
1856 }1970 }
...@@ -1911,6 +2025,9 @@ pub const Type = extern union {...@@ -1911,6 +2025,9 @@ pub const Type = extern union {
1911 int_signed,2025 int_signed,
1912 int_unsigned,2026 int_unsigned,
1913 function,2027 function,
2028 optional,
2029 optional_single_mut_pointer,
2030 optional_single_const_pointer,
19142031
1915 pub const last_no_payload_tag = Tag.const_slice_u8;2032 pub const last_no_payload_tag = Tag.const_slice_u8;
1916 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;2033 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
...@@ -1963,6 +2080,24 @@ pub const Type = extern union {...@@ -1963,6 +2080,24 @@ pub const Type = extern union {
1963 return_type: Type,2080 return_type: Type,
1964 cc: std.builtin.CallingConvention,2081 cc: std.builtin.CallingConvention,
1965 };2082 };
2083
2084 pub const Optional = struct {
2085 base: Payload = Payload{ .tag = .optional },
2086
2087 child_type: Type,
2088 };
2089
2090 pub const OptionalSingleConstPointer = struct {
2091 base: Payload = Payload{ .tag = .optional_single_const_pointer },
2092
2093 pointee_type: Type,
2094 };
2095
2096 pub const OptionalSingleMutPointer = struct {
2097 base: Payload = Payload{ .tag = .optional_single_mut_pointer },
2098
2099 pointee_type: Type,
2100 };
1966 };2101 };
1967};2102};
19682103
src-self-hosted/zir.zig+27
...@@ -212,6 +212,12 @@ pub const Inst = struct {...@@ -212,6 +212,12 @@ pub const Inst = struct {
212 @"unreachable",212 @"unreachable",
213 /// Bitwise XOR. `^`213 /// Bitwise XOR. `^`
214 xor,214 xor,
215 /// Create an optional type '?T'
216 optional_type,
217 /// Unwraps an optional value 'lhs.?'
218 unwrap_optional_safe,
219 /// Same as previous, but without safety checks. Used for orelse, if and while
220 unwrap_optional_unsafe,
215221
216 pub fn Type(tag: Tag) type {222 pub fn Type(tag: Tag) type {
217 return switch (tag) {223 return switch (tag) {
...@@ -240,6 +246,9 @@ pub const Inst = struct {...@@ -240,6 +246,9 @@ pub const Inst = struct {
240 .typeof,246 .typeof,
241 .single_const_ptr_type,247 .single_const_ptr_type,
242 .single_mut_ptr_type,248 .single_mut_ptr_type,
249 .optional_type,
250 .unwrap_optional_safe,
251 .unwrap_optional_unsafe,
243 => UnOp,252 => UnOp,
244253
245 .add,254 .add,
...@@ -372,6 +381,9 @@ pub const Inst = struct {...@@ -372,6 +381,9 @@ pub const Inst = struct {
372 .subwrap,381 .subwrap,
373 .typeof,382 .typeof,
374 .xor,383 .xor,
384 .optional_type,
385 .unwrap_optional_safe,
386 .unwrap_optional_unsafe,
375 => false,387 => false,
376388
377 .@"break",389 .@"break",
...@@ -1915,6 +1927,7 @@ const EmitZIR = struct {...@@ -1915,6 +1927,7 @@ const EmitZIR = struct {
1915 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull),1927 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull),
1916 .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref),1928 .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref),
1917 .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref),1929 .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref),
1930 .unwrap_optional => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional).?, .unwrap_optional_unsafe),
19181931
1919 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),1932 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),
1920 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),1933 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),
...@@ -2242,6 +2255,20 @@ const EmitZIR = struct {...@@ -2242,6 +2255,20 @@ const EmitZIR = struct {
2242 std.debug.panic("TODO implement emitType for {}", .{ty});2255 std.debug.panic("TODO implement emitType for {}", .{ty});
2243 }2256 }
2244 },2257 },
2258 .Optional => {
2259 const inst = try self.arena.allocator.create(Inst.UnOp);
2260 inst.* = .{
2261 .base = .{
2262 .src = src,
2263 .tag = .optional_type,
2264 },
2265 .positionals = .{
2266 .operand = (try self.emitType(src, ty.elemType())).inst,
2267 },
2268 .kw_args = .{},
2269 };
2270 return self.emitUnnamedDecl(&inst.base);
2271 },
2245 else => std.debug.panic("TODO implement emitType for {}", .{ty}),2272 else => std.debug.panic("TODO implement emitType for {}", .{ty}),
2246 },2273 },
2247 }2274 }
src-self-hosted/zir_sema.zig+87-12
...@@ -68,8 +68,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -68,8 +68,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
68 .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?),68 .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?),
69 .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?),69 .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?),
70 .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?),70 .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?),
71 .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?),71 .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?, true),
72 .unreach_nocheck => return analyzeInstUnreachNoChk(mod, scope, old_inst.castTag(.unreach_nocheck).?),72 .unreach_nocheck => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.unreach_nocheck).?, false),
73 .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?),73 .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?),
74 .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?),74 .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?),
75 .@"fn" => return analyzeInstFn(mod, scope, old_inst.castTag(.@"fn").?),75 .@"fn" => return analyzeInstFn(mod, scope, old_inst.castTag(.@"fn").?),
...@@ -106,6 +106,9 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -106,6 +106,9 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
106 .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false),106 .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false),
107 .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?),107 .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?),
108 .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?),108 .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?),
109 .optional_type => return analyzeInstOptionalType(mod, scope, old_inst.castTag(.optional_type).?),
110 .unwrap_optional_safe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_safe).?, true),
111 .unwrap_optional_unsafe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_unsafe).?, false),
109 }112 }
110}113}
111114
...@@ -305,8 +308,19 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr...@@ -305,8 +308,19 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr
305308
306fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {309fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
307 const operand = try resolveInst(mod, scope, inst.positionals.operand);310 const operand = try resolveInst(mod, scope, inst.positionals.operand);
308 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
309 const ptr_type = try mod.singleConstPtrType(scope, inst.base.src, operand.ty);311 const ptr_type = try mod.singleConstPtrType(scope, inst.base.src, operand.ty);
312
313 if (operand.value()) |val| {
314 const ref_payload = try scope.arena().create(Value.Payload.RefVal);
315 ref_payload.* = .{ .val = val };
316
317 return mod.constInst(scope, inst.base.src, .{
318 .ty = ptr_type,
319 .val = Value.initPayload(&ref_payload.base),
320 });
321 }
322
323 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
310 return mod.addUnOp(b, inst.base.src, ptr_type, .ref, operand);324 return mod.addUnOp(b, inst.base.src, ptr_type, .ref, operand);
311}325}
312326
...@@ -620,6 +634,66 @@ fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) I...@@ -620,6 +634,66 @@ fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) I
620 return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{});634 return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{});
621}635}
622636
637fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerError!*Inst {
638 const child_type = try resolveType(mod, scope, optional.positionals.operand);
639
640 return mod.constType(scope, optional.base.src, Type.initPayload(switch (child_type.tag()) {
641 .single_const_pointer => blk: {
642 const payload = try scope.arena().create(Type.Payload.OptionalSingleConstPointer);
643 payload.* = .{
644 .pointee_type = child_type.elemType(),
645 };
646 break :blk &payload.base;
647 },
648 .single_mut_pointer => blk: {
649 const payload = try scope.arena().create(Type.Payload.OptionalSingleMutPointer);
650 payload.* = .{
651 .pointee_type = child_type.elemType(),
652 };
653 break :blk &payload.base;
654 },
655 else => blk: {
656 const payload = try scope.arena().create(Type.Payload.Optional);
657 payload.* = .{
658 .child_type = child_type,
659 };
660 break :blk &payload.base;
661 },
662 }));
663}
664
665fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {
666 const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
667 assert(operand.ty.zigTypeTag() == .Pointer);
668
669 if (operand.ty.elemType().zigTypeTag() != .Optional) {
670 return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{operand.ty.elemType()});
671 }
672
673 const child_type = operand.ty.elemType().elemType();
674 const child_pointer = if (operand.ty.isConstPtr())
675 try mod.singleConstPtrType(scope, unwrap.base.src, child_type)
676 else
677 try mod.singleMutPtrType(scope, unwrap.base.src, child_type);
678
679 if (operand.value()) |val| {
680 if (val.isNull()) {
681 return mod.fail(scope, unwrap.base.src, "unable to unwrap null", .{});
682 }
683 return mod.constInst(scope, unwrap.base.src, .{
684 .ty = child_pointer,
685 .val = val,
686 });
687 }
688
689 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
690 if (safety_check and mod.wantSafety(scope)) {
691 const is_non_null = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .isnonnull, operand);
692 try mod.addSafetyCheck(b, is_non_null, .unwrap_null);
693 }
694 return mod.addUnOp(b, unwrap.base.src, child_pointer, .unwrap_optional, operand);
695}
696
623fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {697fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
624 const return_type = try resolveType(mod, scope, fntype.positionals.return_type);698 const return_type = try resolveType(mod, scope, fntype.positionals.return_type);
625699
...@@ -1094,18 +1168,19 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE...@@ -1094,18 +1168,19 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
1094 return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);1168 return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);
1095}1169}
10961170
1097fn analyzeInstUnreachNoChk(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {1171fn analyzeInstUnreachable(
1098 return mod.analyzeUnreach(scope, unreach.base.src);1172 mod: *Module,
1099}1173 scope: *Scope,
11001174 unreach: *zir.Inst.NoOp,
1101fn analyzeInstUnreachable(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {1175 safety_check: bool,
1176) InnerError!*Inst {
1102 const b = try mod.requireRuntimeBlock(scope, unreach.base.src);1177 const b = try mod.requireRuntimeBlock(scope, unreach.base.src);
1103 // TODO Add compile error for @optimizeFor occurring too late in a scope.1178 // TODO Add compile error for @optimizeFor occurring too late in a scope.
1104 if (mod.wantSafety(scope)) {1179 if (safety_check and mod.wantSafety(scope)) {
1105 // TODO Once we have a panic function to call, call it here instead of this.1180 return mod.safetyPanic(b, unreach.base.src, .unreach);
1106 _ = try mod.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint);1181 } else {
1182 return mod.addNoOp(b, unreach.base.src, Type.initTag(.noreturn), .unreach);
1107 }1183 }
1108 return mod.analyzeUnreach(scope, unreach.base.src);
1109}1184}
11101185
1111fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {1186fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
test/stage2/compare_output.zig+24
...@@ -441,5 +441,29 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -441,5 +441,29 @@ pub fn addCases(ctx: *TestContext) !void {
441 ,441 ,
442 "",442 "",
443 );443 );
444
445 // Optionals
446 case.addCompareOutput(
447 \\export fn _start() noreturn {
448 \\ const a: u32 = 2;
449 \\ const b: ?u32 = a;
450 \\ const c = b.?;
451 \\ if (c != 2) unreachable;
452 \\
453 \\ exit();
454 \\}
455 \\
456 \\fn exit() noreturn {
457 \\ asm volatile ("syscall"
458 \\ :
459 \\ : [number] "{rax}" (231),
460 \\ [arg1] "{rdi}" (0)
461 \\ : "rcx", "r11", "memory"
462 \\ );
463 \\ unreachable;
464 \\}
465 ,
466 "",
467 );
444 }468 }
445}469}