| author | |
| committer | |
| log | 6e0fb060109349b4fb7855c1587b6d9c396d926b |
| tree | bbf9a1aed1c8a160caffe68821f71ffd5c1d6fec |
| parent | cb06d62603c764a91aeb9bcedc0b9472d746f9e5 |
| parent | ec4953504a07f3025d5f32344180dd9b6a4de8ae |
closes #60428 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 | 2219 | }; |
| 2220 | 2220 | } |
| 2221 | 2221 | |
| 2222 | pub 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 | ||
| 2227 | 2222 | pub fn analyzeIsNull( |
| 2228 | 2223 | self: *Module, |
| 2229 | 2224 | scope: *Scope, |
| ... | ... | @@ -2476,6 +2471,24 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst |
| 2476 | 2471 | } |
| 2477 | 2472 | assert(inst.ty.zigTypeTag() != .Undefined); |
| 2478 | 2473 | |
| 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 | 2492 | // *[N]T to []T |
| 2480 | 2493 | if (inst.ty.isSinglePointer() and dest_type.isSlice() and |
| 2481 | 2494 | (!inst.ty.isConstPtr() or dest_type.isConstPtr())) |
| ... | ... | @@ -2884,3 +2897,70 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { |
| 2884 | 2897 | }); |
| 2885 | 2898 | } |
| 2886 | 2899 | } |
| 2900 | ||
| 2901 | pub const PanicId = enum { | |
| 2902 | unreach, | |
| 2903 | unwrap_null, | |
| 2904 | }; | |
| 2905 | ||
| 2906 | pub 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 | ||
| 2962 | pub 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 | 105 | .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)), |
| 106 | 106 | .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)), |
| 107 | 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 | 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 | 295 | return addZIRUnOp(mod, scope, src, .boolnot, operand); |
| 294 | 296 | } |
| 295 | 297 | |
| 298 | fn 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 | ||
| 309 | fn 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 | 320 | /// Identifier token -> String (allocated in scope.arena()) |
| 297 | 321 | pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { |
| 298 | 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 | 668 | .store => return self.genStore(inst.castTag(.store).?), |
| 669 | 669 | .sub => return self.genSub(inst.castTag(.sub).?), |
| 670 | 670 | .unreach => return MCValue{ .unreach = {} }, |
| 671 | .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?), | |
| 671 | 672 | } |
| 672 | 673 | } |
| 673 | 674 | |
| ... | ... | @@ -817,6 +818,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 817 | 818 | } |
| 818 | 819 | } |
| 819 | 820 | |
| 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 | 830 | fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue { |
| 821 | 831 | const elem_ty = inst.base.ty; |
| 822 | 832 | if (!elem_ty.hasCodeGenBits()) |
src-self-hosted/ir.zig+2-1| ... | ... | @@ -82,6 +82,7 @@ pub const Inst = struct { |
| 82 | 82 | not, |
| 83 | 83 | floatcast, |
| 84 | 84 | intcast, |
| 85 | unwrap_optional, | |
| 85 | 86 | |
| 86 | 87 | pub fn Type(tag: Tag) type { |
| 87 | 88 | return switch (tag) { |
| ... | ... | @@ -102,6 +103,7 @@ pub const Inst = struct { |
| 102 | 103 | .floatcast, |
| 103 | 104 | .intcast, |
| 104 | 105 | .load, |
| 106 | .unwrap_optional, | |
| 105 | 107 | => UnOp, |
| 106 | 108 | |
| 107 | 109 | .add, |
| ... | ... | @@ -419,7 +421,6 @@ pub const Inst = struct { |
| 419 | 421 | return null; |
| 420 | 422 | } |
| 421 | 423 | }; |
| 422 | ||
| 423 | 424 | }; |
| 424 | 425 | |
| 425 | 426 | pub const Body = struct { |
src-self-hosted/type.zig+161-26| ... | ... | @@ -70,6 +70,11 @@ pub const Type = extern union { |
| 70 | 70 | .single_mut_pointer => return .Pointer, |
| 71 | 71 | .single_const_pointer_to_comptime_int => return .Pointer, |
| 72 | 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 | } |
| 75 | 80 | |
| ... | ... | @@ -179,9 +184,11 @@ pub const Type = extern union { |
| 179 | 184 | } |
| 180 | 185 | return true; |
| 181 | 186 | }, |
| 187 | .Optional => { | |
| 188 | return a.elemType().eql(b.elemType()); | |
| 189 | }, | |
| 182 | 190 | .Float, |
| 183 | 191 | .Struct, |
| 184 | .Optional, | |
| 185 | 192 | .ErrorUnion, |
| 186 | 193 | .ErrorSet, |
| 187 | 194 | .Enum, |
| ... | ... | @@ -241,9 +248,11 @@ pub const Type = extern union { |
| 241 | 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 | 254 | .Float, |
| 245 | 255 | .Struct, |
| 246 | .Optional, | |
| 247 | 256 | .ErrorUnion, |
| 248 | 257 | .ErrorSet, |
| 249 | 258 | .Enum, |
| ... | ... | @@ -317,24 +326,8 @@ pub const Type = extern union { |
| 317 | 326 | }; |
| 318 | 327 | return Type{ .ptr_otherwise = &new_payload.base }; |
| 319 | 328 | }, |
| 320 | .single_const_pointer => { | |
| 321 | const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", self.ptr_otherwise); | |
| 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 | }, | |
| 329 | .single_const_pointer => return self.copyPayloadSingleField(allocator, Payload.SingleConstPointer, "pointee_type"), | |
| 330 | .single_mut_pointer => return self.copyPayloadSingleField(allocator, Payload.SingleMutPointer, "pointee_type"), | |
| 338 | 331 | .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned), |
| 339 | 332 | .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned), |
| 340 | 333 | .function => { |
| ... | ... | @@ -352,6 +345,9 @@ pub const Type = extern union { |
| 352 | 345 | }; |
| 353 | 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 | } |
| 357 | 353 | |
| ... | ... | @@ -362,6 +358,14 @@ pub const Type = extern union { |
| 362 | 358 | return Type{ .ptr_otherwise = &new_payload.base }; |
| 363 | 359 | } |
| 364 | 360 | |
| 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 | 369 | pub fn format( |
| 366 | 370 | self: Type, |
| 367 | 371 | comptime fmt: []const u8, |
| ... | ... | @@ -456,6 +460,24 @@ pub const Type = extern union { |
| 456 | 460 | const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise); |
| 457 | 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 | 482 | unreachable; |
| 461 | 483 | } |
| ... | ... | @@ -545,12 +567,16 @@ pub const Type = extern union { |
| 545 | 567 | .single_const_pointer_to_comptime_int, |
| 546 | 568 | .const_slice_u8, |
| 547 | 569 | .array_u8_sentinel_0, |
| 548 | .array, // TODO check for zero bits | |
| 549 | .single_const_pointer, | |
| 550 | .single_mut_pointer, | |
| 551 | .int_signed, // TODO check for zero bits | |
| 552 | .int_unsigned, // TODO check for zero bits | |
| 570 | .optional, | |
| 571 | .optional_single_mut_pointer, | |
| 572 | .optional_single_const_pointer, | |
| 553 | 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, | |
| 554 | 580 | |
| 555 | 581 | .c_void, |
| 556 | 582 | .void, |
| ... | ... | @@ -597,6 +623,8 @@ pub const Type = extern union { |
| 597 | 623 | .const_slice_u8, |
| 598 | 624 | .single_const_pointer, |
| 599 | 625 | .single_mut_pointer, |
| 626 | .optional_single_const_pointer, | |
| 627 | .optional_single_mut_pointer, | |
| 600 | 628 | => return @divExact(target.cpu.arch.ptrBitWidth(), 8), |
| 601 | 629 | |
| 602 | 630 | .c_short => return @divExact(CType.short.sizeInBits(target), 8), |
| ... | ... | @@ -629,6 +657,16 @@ pub const Type = extern union { |
| 629 | 657 | return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8); |
| 630 | 658 | }, |
| 631 | 659 | |
| 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 | 670 | .c_void, |
| 633 | 671 | .void, |
| 634 | 672 | .type, |
| ... | ... | @@ -679,6 +717,8 @@ pub const Type = extern union { |
| 679 | 717 | .const_slice_u8, |
| 680 | 718 | .single_const_pointer, |
| 681 | 719 | .single_mut_pointer, |
| 720 | .optional_single_const_pointer, | |
| 721 | .optional_single_mut_pointer, | |
| 682 | 722 | => return @divExact(target.cpu.arch.ptrBitWidth(), 8), |
| 683 | 723 | |
| 684 | 724 | .c_short => return @divExact(CType.short.sizeInBits(target), 8), |
| ... | ... | @@ -708,6 +748,20 @@ pub const Type = extern union { |
| 708 | 748 | |
| 709 | 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 | } |
| 713 | 767 | |
| ... | ... | @@ -756,6 +810,9 @@ pub const Type = extern union { |
| 756 | 810 | .function, |
| 757 | 811 | .int_unsigned, |
| 758 | 812 | .int_signed, |
| 813 | .optional, | |
| 814 | .optional_single_mut_pointer, | |
| 815 | .optional_single_const_pointer, | |
| 759 | 816 | => false, |
| 760 | 817 | |
| 761 | 818 | .single_const_pointer, |
| ... | ... | @@ -812,6 +869,9 @@ pub const Type = extern union { |
| 812 | 869 | .function, |
| 813 | 870 | .int_unsigned, |
| 814 | 871 | .int_signed, |
| 872 | .optional, | |
| 873 | .optional_single_mut_pointer, | |
| 874 | .optional_single_const_pointer, | |
| 815 | 875 | => false, |
| 816 | 876 | |
| 817 | 877 | .const_slice_u8 => true, |
| ... | ... | @@ -863,6 +923,9 @@ pub const Type = extern union { |
| 863 | 923 | .int_unsigned, |
| 864 | 924 | .int_signed, |
| 865 | 925 | .single_mut_pointer, |
| 926 | .optional, | |
| 927 | .optional_single_mut_pointer, | |
| 928 | .optional_single_const_pointer, | |
| 866 | 929 | => false, |
| 867 | 930 | |
| 868 | 931 | .single_const_pointer, |
| ... | ... | @@ -920,11 +983,14 @@ pub const Type = extern union { |
| 920 | 983 | .single_const_pointer, |
| 921 | 984 | .single_const_pointer_to_comptime_int, |
| 922 | 985 | .const_slice_u8, |
| 986 | .optional, | |
| 987 | .optional_single_mut_pointer, | |
| 988 | .optional_single_const_pointer, | |
| 923 | 989 | => false, |
| 924 | 990 | }; |
| 925 | 991 | } |
| 926 | 992 | |
| 927 | /// Asserts the type is a pointer or array type. | |
| 993 | /// Asserts the type is a pointer, optional or array type. | |
| 928 | 994 | pub fn elemType(self: Type) Type { |
| 929 | 995 | return switch (self.tag()) { |
| 930 | 996 | .u8, |
| ... | ... | @@ -974,6 +1040,9 @@ pub const Type = extern union { |
| 974 | 1040 | .single_mut_pointer => self.cast(Payload.SingleMutPointer).?.pointee_type, |
| 975 | 1041 | .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8), |
| 976 | 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 | } |
| 979 | 1048 | |
| ... | ... | @@ -1024,6 +1093,9 @@ pub const Type = extern union { |
| 1024 | 1093 | .const_slice_u8, |
| 1025 | 1094 | .int_unsigned, |
| 1026 | 1095 | .int_signed, |
| 1096 | .optional, | |
| 1097 | .optional_single_mut_pointer, | |
| 1098 | .optional_single_const_pointer, | |
| 1027 | 1099 | => unreachable, |
| 1028 | 1100 | |
| 1029 | 1101 | .array => self.cast(Payload.Array).?.len, |
| ... | ... | @@ -1078,6 +1150,9 @@ pub const Type = extern union { |
| 1078 | 1150 | .const_slice_u8, |
| 1079 | 1151 | .int_unsigned, |
| 1080 | 1152 | .int_signed, |
| 1153 | .optional, | |
| 1154 | .optional_single_mut_pointer, | |
| 1155 | .optional_single_const_pointer, | |
| 1081 | 1156 | => unreachable, |
| 1082 | 1157 | |
| 1083 | 1158 | .array => return null, |
| ... | ... | @@ -1129,6 +1204,9 @@ pub const Type = extern union { |
| 1129 | 1204 | .u16, |
| 1130 | 1205 | .u32, |
| 1131 | 1206 | .u64, |
| 1207 | .optional, | |
| 1208 | .optional_single_mut_pointer, | |
| 1209 | .optional_single_const_pointer, | |
| 1132 | 1210 | => false, |
| 1133 | 1211 | |
| 1134 | 1212 | .int_signed, |
| ... | ... | @@ -1184,6 +1262,9 @@ pub const Type = extern union { |
| 1184 | 1262 | .i16, |
| 1185 | 1263 | .i32, |
| 1186 | 1264 | .i64, |
| 1265 | .optional, | |
| 1266 | .optional_single_mut_pointer, | |
| 1267 | .optional_single_const_pointer, | |
| 1187 | 1268 | => false, |
| 1188 | 1269 | |
| 1189 | 1270 | .int_unsigned, |
| ... | ... | @@ -1229,6 +1310,9 @@ pub const Type = extern union { |
| 1229 | 1310 | .single_const_pointer_to_comptime_int, |
| 1230 | 1311 | .array_u8_sentinel_0, |
| 1231 | 1312 | .const_slice_u8, |
| 1313 | .optional, | |
| 1314 | .optional_single_mut_pointer, | |
| 1315 | .optional_single_const_pointer, | |
| 1232 | 1316 | => unreachable, |
| 1233 | 1317 | |
| 1234 | 1318 | .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits }, |
| ... | ... | @@ -1292,6 +1376,9 @@ pub const Type = extern union { |
| 1292 | 1376 | .i32, |
| 1293 | 1377 | .u64, |
| 1294 | 1378 | .i64, |
| 1379 | .optional, | |
| 1380 | .optional_single_mut_pointer, | |
| 1381 | .optional_single_const_pointer, | |
| 1295 | 1382 | => false, |
| 1296 | 1383 | |
| 1297 | 1384 | .usize, |
| ... | ... | @@ -1384,6 +1471,9 @@ pub const Type = extern union { |
| 1384 | 1471 | .c_ulonglong, |
| 1385 | 1472 | .int_unsigned, |
| 1386 | 1473 | .int_signed, |
| 1474 | .optional, | |
| 1475 | .optional_single_mut_pointer, | |
| 1476 | .optional_single_const_pointer, | |
| 1387 | 1477 | => unreachable, |
| 1388 | 1478 | }; |
| 1389 | 1479 | } |
| ... | ... | @@ -1442,6 +1532,9 @@ pub const Type = extern union { |
| 1442 | 1532 | .c_ulonglong, |
| 1443 | 1533 | .int_unsigned, |
| 1444 | 1534 | .int_signed, |
| 1535 | .optional, | |
| 1536 | .optional_single_mut_pointer, | |
| 1537 | .optional_single_const_pointer, | |
| 1445 | 1538 | => unreachable, |
| 1446 | 1539 | } |
| 1447 | 1540 | } |
| ... | ... | @@ -1499,6 +1592,9 @@ pub const Type = extern union { |
| 1499 | 1592 | .c_ulonglong, |
| 1500 | 1593 | .int_unsigned, |
| 1501 | 1594 | .int_signed, |
| 1595 | .optional, | |
| 1596 | .optional_single_mut_pointer, | |
| 1597 | .optional_single_const_pointer, | |
| 1502 | 1598 | => unreachable, |
| 1503 | 1599 | } |
| 1504 | 1600 | } |
| ... | ... | @@ -1556,6 +1652,9 @@ pub const Type = extern union { |
| 1556 | 1652 | .c_ulonglong, |
| 1557 | 1653 | .int_unsigned, |
| 1558 | 1654 | .int_signed, |
| 1655 | .optional, | |
| 1656 | .optional_single_mut_pointer, | |
| 1657 | .optional_single_const_pointer, | |
| 1559 | 1658 | => unreachable, |
| 1560 | 1659 | }; |
| 1561 | 1660 | } |
| ... | ... | @@ -1610,6 +1709,9 @@ pub const Type = extern union { |
| 1610 | 1709 | .c_ulonglong, |
| 1611 | 1710 | .int_unsigned, |
| 1612 | 1711 | .int_signed, |
| 1712 | .optional, | |
| 1713 | .optional_single_mut_pointer, | |
| 1714 | .optional_single_const_pointer, | |
| 1613 | 1715 | => unreachable, |
| 1614 | 1716 | }; |
| 1615 | 1717 | } |
| ... | ... | @@ -1664,6 +1766,9 @@ pub const Type = extern union { |
| 1664 | 1766 | .c_ulonglong, |
| 1665 | 1767 | .int_unsigned, |
| 1666 | 1768 | .int_signed, |
| 1769 | .optional, | |
| 1770 | .optional_single_mut_pointer, | |
| 1771 | .optional_single_const_pointer, | |
| 1667 | 1772 | => unreachable, |
| 1668 | 1773 | }; |
| 1669 | 1774 | } |
| ... | ... | @@ -1718,6 +1823,9 @@ pub const Type = extern union { |
| 1718 | 1823 | .single_const_pointer_to_comptime_int, |
| 1719 | 1824 | .array_u8_sentinel_0, |
| 1720 | 1825 | .const_slice_u8, |
| 1826 | .optional, | |
| 1827 | .optional_single_mut_pointer, | |
| 1828 | .optional_single_const_pointer, | |
| 1721 | 1829 | => false, |
| 1722 | 1830 | }; |
| 1723 | 1831 | } |
| ... | ... | @@ -1762,6 +1870,9 @@ pub const Type = extern union { |
| 1762 | 1870 | .array_u8_sentinel_0, |
| 1763 | 1871 | .const_slice_u8, |
| 1764 | 1872 | .c_void, |
| 1873 | .optional, | |
| 1874 | .optional_single_mut_pointer, | |
| 1875 | .optional_single_const_pointer, | |
| 1765 | 1876 | => return null, |
| 1766 | 1877 | |
| 1767 | 1878 | .void => return Value.initTag(.void_value), |
| ... | ... | @@ -1851,6 +1962,9 @@ pub const Type = extern union { |
| 1851 | 1962 | .array, |
| 1852 | 1963 | .single_const_pointer, |
| 1853 | 1964 | .single_mut_pointer, |
| 1965 | .optional, | |
| 1966 | .optional_single_mut_pointer, | |
| 1967 | .optional_single_const_pointer, | |
| 1854 | 1968 | => return false, |
| 1855 | 1969 | }; |
| 1856 | 1970 | } |
| ... | ... | @@ -1911,6 +2025,9 @@ pub const Type = extern union { |
| 1911 | 2025 | int_signed, |
| 1912 | 2026 | int_unsigned, |
| 1913 | 2027 | function, |
| 2028 | optional, | |
| 2029 | optional_single_mut_pointer, | |
| 2030 | optional_single_const_pointer, | |
| 1914 | 2031 | |
| 1915 | 2032 | pub const last_no_payload_tag = Tag.const_slice_u8; |
| 1916 | 2033 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | ... | @@ -1963,6 +2080,24 @@ pub const Type = extern union { |
| 1963 | 2080 | return_type: Type, |
| 1964 | 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 | }; |
| 1968 | 2103 |
src-self-hosted/zir.zig+27| ... | ... | @@ -212,6 +212,12 @@ pub const Inst = struct { |
| 212 | 212 | @"unreachable", |
| 213 | 213 | /// Bitwise XOR. `^` |
| 214 | 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, | |
| 215 | 221 | |
| 216 | 222 | pub fn Type(tag: Tag) type { |
| 217 | 223 | return switch (tag) { |
| ... | ... | @@ -240,6 +246,9 @@ pub const Inst = struct { |
| 240 | 246 | .typeof, |
| 241 | 247 | .single_const_ptr_type, |
| 242 | 248 | .single_mut_ptr_type, |
| 249 | .optional_type, | |
| 250 | .unwrap_optional_safe, | |
| 251 | .unwrap_optional_unsafe, | |
| 243 | 252 | => UnOp, |
| 244 | 253 | |
| 245 | 254 | .add, |
| ... | ... | @@ -372,6 +381,9 @@ pub const Inst = struct { |
| 372 | 381 | .subwrap, |
| 373 | 382 | .typeof, |
| 374 | 383 | .xor, |
| 384 | .optional_type, | |
| 385 | .unwrap_optional_safe, | |
| 386 | .unwrap_optional_unsafe, | |
| 375 | 387 | => false, |
| 376 | 388 | |
| 377 | 389 | .@"break", |
| ... | ... | @@ -1915,6 +1927,7 @@ const EmitZIR = struct { |
| 1915 | 1927 | .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull), |
| 1916 | 1928 | .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref), |
| 1917 | 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), | |
| 1918 | 1931 | |
| 1919 | 1932 | .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add), |
| 1920 | 1933 | .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub), |
| ... | ... | @@ -2242,6 +2255,20 @@ const EmitZIR = struct { |
| 2242 | 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 | 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 | 68 | .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?), |
| 69 | 69 | .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?), |
| 70 | 70 | .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?), |
| 71 | .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?), | |
| 72 | .unreach_nocheck => return analyzeInstUnreachNoChk(mod, scope, old_inst.castTag(.unreach_nocheck).?), | |
| 71 | .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?, true), | |
| 72 | .unreach_nocheck => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.unreach_nocheck).?, false), | |
| 73 | 73 | .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?), |
| 74 | 74 | .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?), |
| 75 | 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 | 106 | .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false), |
| 107 | 107 | .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?), |
| 108 | 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 | } |
| 111 | 114 | |
| ... | ... | @@ -305,8 +308,19 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr |
| 305 | 308 | |
| 306 | 309 | fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { |
| 307 | 310 | const operand = try resolveInst(mod, scope, inst.positionals.operand); |
| 308 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | |
| 309 | 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 | 324 | return mod.addUnOp(b, inst.base.src, ptr_type, .ref, operand); |
| 311 | 325 | } |
| 312 | 326 | |
| ... | ... | @@ -620,6 +634,66 @@ fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) I |
| 620 | 634 | return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{}); |
| 621 | 635 | } |
| 622 | 636 | |
| 637 | fn 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 | ||
| 665 | fn 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 | ||
| 623 | 697 | fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { |
| 624 | 698 | const return_type = try resolveType(mod, scope, fntype.positionals.return_type); |
| 625 | 699 | |
| ... | ... | @@ -1094,18 +1168,19 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE |
| 1094 | 1168 | return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body); |
| 1095 | 1169 | } |
| 1096 | 1170 | |
| 1097 | fn analyzeInstUnreachNoChk(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst { | |
| 1098 | return mod.analyzeUnreach(scope, unreach.base.src); | |
| 1099 | } | |
| 1100 | ||
| 1101 | fn analyzeInstUnreachable(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst { | |
| 1171 | fn analyzeInstUnreachable( | |
| 1172 | mod: *Module, | |
| 1173 | scope: *Scope, | |
| 1174 | unreach: *zir.Inst.NoOp, | |
| 1175 | safety_check: bool, | |
| 1176 | ) InnerError!*Inst { | |
| 1102 | 1177 | const b = try mod.requireRuntimeBlock(scope, unreach.base.src); |
| 1103 | 1178 | // TODO Add compile error for @optimizeFor occurring too late in a scope. |
| 1104 | if (mod.wantSafety(scope)) { | |
| 1105 | // TODO Once we have a panic function to call, call it here instead of this. | |
| 1106 | _ = try mod.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint); | |
| 1179 | if (safety_check and mod.wantSafety(scope)) { | |
| 1180 | return mod.safetyPanic(b, unreach.base.src, .unreach); | |
| 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 | } |
| 1110 | 1185 | |
| 1111 | 1186 | fn 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 | 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 | } |