| author | |
| committer | |
| log | 4a40282391f0b92a83a6a8c269c27a32be92884a |
| tree | 45cf255fec1638c947d48628a91d5222d1282c06 |
| parent | 5c1fe5861389462f309e3f0b69096a85f330dc20 |
7 files changed, 149 insertions(+), 3 deletions(-)
src-self-hosted/Module.zig+24-2| ... | @@ -2016,6 +2016,28 @@ pub fn addCall( | ... | @@ -2016,6 +2016,28 @@ pub fn addCall( |
| 2016 | return &inst.base; | 2016 | return &inst.base; |
| 2017 | } | 2017 | } |
| 2018 | 2018 | ||
| 2019 | pub fn addUnwrapOptional( | ||
| 2020 | self: *Module, | ||
| 2021 | block: *Scope.Block, | ||
| 2022 | src: usize, | ||
| 2023 | ty: Type, | ||
| 2024 | operand: *Inst, | ||
| 2025 | safety_check: bool, | ||
| 2026 | ) !*Inst { | ||
| 2027 | const inst = try block.arena.create(Inst.UnwrapOptional); | ||
| 2028 | inst.* = .{ | ||
| 2029 | .base = .{ | ||
| 2030 | .tag = .unwrap_optional, | ||
| 2031 | .ty = ty, | ||
| 2032 | .src = src, | ||
| 2033 | }, | ||
| 2034 | .operand = operand, | ||
| 2035 | .safety_check = safety_check, | ||
| 2036 | }; | ||
| 2037 | try block.instructions.append(self.gpa, &inst.base); | ||
| 2038 | return &inst.base; | ||
| 2039 | } | ||
| 2040 | |||
| 2019 | pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst { | 2041 | pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst { |
| 2020 | const const_inst = try scope.arena().create(Inst.Constant); | 2042 | const const_inst = try scope.arena().create(Inst.Constant); |
| 2021 | const_inst.* = .{ | 2043 | const_inst.* = .{ |
| ... | @@ -2488,9 +2510,9 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst | ... | @@ -2488,9 +2510,9 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst |
| 2488 | if (child_type.eql(inst.ty)) { | 2510 | if (child_type.eql(inst.ty)) { |
| 2489 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | 2511 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); |
| 2490 | } | 2512 | } |
| 2491 | return self.fail(scope, inst.src, "TODO optional wrap {} to {}", .{ val, inst.ty }); | 2513 | return self.fail(scope, inst.src, "TODO optional wrap {} to {}", .{ val, dest_type }); |
| 2492 | } else if (child_type.eql(inst.ty)) { | 2514 | } else if (child_type.eql(inst.ty)) { |
| 2493 | return self.fail(scope, inst.src, "TODO optional wrap {}", .{inst.ty}); | 2515 | return self.fail(scope, inst.src, "TODO optional wrap {}", .{dest_type}); |
| 2494 | } | 2516 | } |
| 2495 | } | 2517 | } |
| 2496 | 2518 |
src-self-hosted/astgen.zig+12| ... | @@ -106,6 +106,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr | ... | @@ -106,6 +106,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 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).?)), | 108 | .OptionalType => return rlWrap(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)), |
| 109 | .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?), | ||
| 109 | 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)}), |
| 110 | } | 111 | } |
| 111 | } | 112 | } |
| ... | @@ -305,6 +306,17 @@ fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) Inn | ... | @@ -305,6 +306,17 @@ fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) Inn |
| 305 | return addZIRUnOp(mod, scope, src, .optional_type, operand); | 306 | return addZIRUnOp(mod, scope, src, .optional_type, operand); |
| 306 | } | 307 | } |
| 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 addZIRInst(mod, scope, src, zir.Inst.UnwrapOptional, .{ .operand = 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 | |||
| 308 | /// Identifier token -> String (allocated in scope.arena()) | 320 | /// Identifier token -> String (allocated in scope.arena()) |
| 309 | pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { | 321 | pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { |
| 310 | 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 | } |
| 673 | 674 | ||
| ... | @@ -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 | } |
| 819 | 820 | ||
| 821 | fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnwrapOptional) !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+22| ... | @@ -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, | ||
| 85 | 86 | ||
| 86 | pub fn Type(tag: Tag) type { | 87 | pub fn Type(tag: Tag) type { |
| 87 | return switch (tag) { | 88 | return switch (tag) { |
| ... | @@ -124,6 +125,7 @@ pub const Inst = struct { | ... | @@ -124,6 +125,7 @@ pub const Inst = struct { |
| 124 | .condbr => CondBr, | 125 | .condbr => CondBr, |
| 125 | .constant => Constant, | 126 | .constant => Constant, |
| 126 | .loop => Loop, | 127 | .loop => Loop, |
| 128 | .unwrap_optional => UnwrapOptional, | ||
| 127 | }; | 129 | }; |
| 128 | } | 130 | } |
| 129 | 131 | ||
| ... | @@ -420,6 +422,26 @@ pub const Inst = struct { | ... | @@ -420,6 +422,26 @@ pub const Inst = struct { |
| 420 | } | 422 | } |
| 421 | }; | 423 | }; |
| 422 | 424 | ||
| 425 | pub const UnwrapOptional = struct { | ||
| 426 | pub const base_tag = Tag.unwrap_optional; | ||
| 427 | base: Inst, | ||
| 428 | |||
| 429 | operand: *Inst, | ||
| 430 | safety_check: bool, | ||
| 431 | |||
| 432 | pub fn operandCount(self: *const UnwrapOptional) usize { | ||
| 433 | return 1; | ||
| 434 | } | ||
| 435 | pub fn getOperand(self: *const UnwrapOptional, index: usize) ?*Inst { | ||
| 436 | var i = index; | ||
| 437 | |||
| 438 | if (i < 1) | ||
| 439 | return self.operand; | ||
| 440 | i -= 1; | ||
| 441 | |||
| 442 | return null; | ||
| 443 | } | ||
| 444 | }; | ||
| 423 | }; | 445 | }; |
| 424 | 446 | ||
| 425 | pub const Body = struct { | 447 | pub const Body = struct { |
src-self-hosted/zir.zig+35| ... | @@ -214,6 +214,8 @@ pub const Inst = struct { | ... | @@ -214,6 +214,8 @@ pub const Inst = struct { |
| 214 | xor, | 214 | xor, |
| 215 | /// Create an optional type '?T' | 215 | /// Create an optional type '?T' |
| 216 | optional_type, | 216 | optional_type, |
| 217 | /// Unwraps an optional value 'lhs.?' | ||
| 218 | unwrap_optional, | ||
| 217 | 219 | ||
| 218 | pub fn Type(tag: Tag) type { | 220 | pub fn Type(tag: Tag) type { |
| 219 | return switch (tag) { | 221 | return switch (tag) { |
| ... | @@ -301,6 +303,7 @@ pub const Inst = struct { | ... | @@ -301,6 +303,7 @@ pub const Inst = struct { |
| 301 | .fntype => FnType, | 303 | .fntype => FnType, |
| 302 | .elemptr => ElemPtr, | 304 | .elemptr => ElemPtr, |
| 303 | .condbr => CondBr, | 305 | .condbr => CondBr, |
| 306 | .unwrap_optional => UnwrapOptional, | ||
| 304 | }; | 307 | }; |
| 305 | } | 308 | } |
| 306 | 309 | ||
| ... | @@ -376,6 +379,7 @@ pub const Inst = struct { | ... | @@ -376,6 +379,7 @@ pub const Inst = struct { |
| 376 | .typeof, | 379 | .typeof, |
| 377 | .xor, | 380 | .xor, |
| 378 | .optional_type, | 381 | .optional_type, |
| 382 | .unwrap_optional, | ||
| 379 | => false, | 383 | => false, |
| 380 | 384 | ||
| 381 | .@"break", | 385 | .@"break", |
| ... | @@ -816,6 +820,18 @@ pub const Inst = struct { | ... | @@ -816,6 +820,18 @@ pub const Inst = struct { |
| 816 | }, | 820 | }, |
| 817 | kw_args: struct {}, | 821 | kw_args: struct {}, |
| 818 | }; | 822 | }; |
| 823 | |||
| 824 | pub const UnwrapOptional = struct { | ||
| 825 | pub const base_tag = Tag.unwrap_optional; | ||
| 826 | base: Inst, | ||
| 827 | |||
| 828 | positionals: struct { | ||
| 829 | operand: *Inst, | ||
| 830 | }, | ||
| 831 | kw_args: struct { | ||
| 832 | safety_check: bool = true, | ||
| 833 | }, | ||
| 834 | }; | ||
| 819 | }; | 835 | }; |
| 820 | 836 | ||
| 821 | pub const ErrorMsg = struct { | 837 | pub const ErrorMsg = struct { |
| ... | @@ -2141,6 +2157,25 @@ const EmitZIR = struct { | ... | @@ -2141,6 +2157,25 @@ const EmitZIR = struct { |
| 2141 | }; | 2157 | }; |
| 2142 | break :blk &new_inst.base; | 2158 | break :blk &new_inst.base; |
| 2143 | }, | 2159 | }, |
| 2160 | |||
| 2161 | .unwrap_optional => blk: { | ||
| 2162 | const old_inst = inst.castTag(.unwrap_optional).?; | ||
| 2163 | |||
| 2164 | const new_inst = try self.arena.allocator.create(Inst.UnwrapOptional); | ||
| 2165 | new_inst.* = .{ | ||
| 2166 | .base = .{ | ||
| 2167 | .src = inst.src, | ||
| 2168 | .tag = Inst.UnwrapOptional.base_tag, | ||
| 2169 | }, | ||
| 2170 | .positionals = .{ | ||
| 2171 | .operand = try self.resolveInst(new_body, old_inst.operand), | ||
| 2172 | }, | ||
| 2173 | .kw_args = .{ | ||
| 2174 | .safety_check = old_inst.safety_check, | ||
| 2175 | }, | ||
| 2176 | }; | ||
| 2177 | break :blk &new_inst.base; | ||
| 2178 | }, | ||
| 2144 | }; | 2179 | }; |
| 2145 | try instructions.append(new_inst); | 2180 | try instructions.append(new_inst); |
| 2146 | try inst_table.put(inst, new_inst); | 2181 | try inst_table.put(inst, new_inst); |
src-self-hosted/zir_sema.zig+41-1| ... | @@ -107,6 +107,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! | ... | @@ -107,6 +107,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 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).?), | 109 | .optional_type => return analyzeInstOptionalType(mod, scope, old_inst.castTag(.optional_type).?), |
| 110 | .unwrap_optional => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional).?), | ||
| 110 | } | 111 | } |
| 111 | } | 112 | } |
| 112 | 113 | ||
| ... | @@ -306,8 +307,19 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr | ... | @@ -306,8 +307,19 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr |
| 306 | 307 | ||
| 307 | fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | 308 | fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { |
| 308 | const operand = try resolveInst(mod, scope, inst.positionals.operand); | 309 | const operand = try resolveInst(mod, scope, inst.positionals.operand); |
| 309 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | ||
| 310 | const ptr_type = try mod.singleConstPtrType(scope, inst.base.src, operand.ty); | 310 | const ptr_type = try mod.singleConstPtrType(scope, inst.base.src, operand.ty); |
| 311 | |||
| 312 | if (operand.value()) |val| { | ||
| 313 | const ref_payload = try scope.arena().create(Value.Payload.RefVal); | ||
| 314 | ref_payload.* = .{ .val = val }; | ||
| 315 | |||
| 316 | return mod.constInst(scope, inst.base.src, .{ | ||
| 317 | .ty = ptr_type, | ||
| 318 | .val = Value.initPayload(&ref_payload.base), | ||
| 319 | }); | ||
| 320 | } | ||
| 321 | |||
| 322 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | ||
| 311 | return mod.addUnOp(b, inst.base.src, ptr_type, .ref, operand); | 323 | return mod.addUnOp(b, inst.base.src, ptr_type, .ref, operand); |
| 312 | } | 324 | } |
| 313 | 325 | ||
| ... | @@ -649,6 +661,34 @@ fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp | ... | @@ -649,6 +661,34 @@ fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp |
| 649 | })); | 661 | })); |
| 650 | } | 662 | } |
| 651 | 663 | ||
| 664 | fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnwrapOptional) InnerError!*Inst { | ||
| 665 | const operand = try resolveInst(mod, scope, unwrap.positionals.operand); | ||
| 666 | assert(operand.ty.zigTypeTag() == .Pointer); | ||
| 667 | |||
| 668 | if (operand.ty.elemType().zigTypeTag() != .Optional) { | ||
| 669 | return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{operand.ty.elemType()}); | ||
| 670 | } | ||
| 671 | |||
| 672 | const child_type = operand.ty.elemType().elemType(); | ||
| 673 | const child_pointer = if (operand.ty.isConstPtr()) | ||
| 674 | try mod.singleConstPtrType(scope, unwrap.base.src, child_type) | ||
| 675 | else | ||
| 676 | try mod.singleMutPtrType(scope, unwrap.base.src, child_type); | ||
| 677 | |||
| 678 | if (operand.value()) |val| { | ||
| 679 | if (val.tag() == .null_value) { | ||
| 680 | return mod.fail(scope, unwrap.base.src, "unable to unwrap null", .{}); | ||
| 681 | } | ||
| 682 | return mod.constInst(scope, unwrap.base.src, .{ | ||
| 683 | .ty = child_pointer, | ||
| 684 | .val = val, | ||
| 685 | }); | ||
| 686 | } | ||
| 687 | |||
| 688 | const b = try mod.requireRuntimeBlock(scope, unwrap.base.src); | ||
| 689 | return mod.addUnwrapOptional(b, unwrap.base.src, child_pointer, operand, unwrap.kw_args.safety_check); | ||
| 690 | } | ||
| 691 | |||
| 652 | fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { | 692 | fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { |
| 653 | const return_type = try resolveType(mod, scope, fntype.positionals.return_type); | 693 | const return_type = try resolveType(mod, scope, fntype.positionals.return_type); |
| 654 | 694 |
test/stage2/compare_output.zig+5| ... | @@ -31,6 +31,11 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -31,6 +31,11 @@ pub fn addCases(ctx: *TestContext) !void { |
| 31 | \\export fn _start() noreturn { | 31 | \\export fn _start() noreturn { |
| 32 | \\ print(); | 32 | \\ print(); |
| 33 | \\ | 33 | \\ |
| 34 | \\ const a: u32 = 2; | ||
| 35 | \\ const b: ?u32 = a; | ||
| 36 | \\ const c = b.?; | ||
| 37 | \\ if (c != 2) unreachable; | ||
| 38 | \\ | ||
| 34 | \\ exit(); | 39 | \\ exit(); |
| 35 | \\} | 40 | \\} |
| 36 | \\ | 41 | \\ |