authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-08-12 22:30:14+03:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-13 08:12:17-07:00
log4a40282391f0b92a83a6a8c269c27a32be92884a
tree45cf255fec1638c947d48628a91d5222d1282c06
parent5c1fe5861389462f309e3f0b69096a85f330dc20

stage2: implement unwrap optional


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}
20182018
2019pub 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
2019pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {2041pub 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 }
24962518
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}
307308
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 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())
309pub 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 {
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 }
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.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,
8586
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 }
129131
...@@ -420,6 +422,26 @@ pub const Inst = struct {...@@ -420,6 +422,26 @@ pub const Inst = struct {
420 }422 }
421 };423 };
422424
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};
424446
425pub const Body = struct {447pub 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,
217219
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 }
306309
...@@ -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,
380384
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};
820836
821pub const ErrorMsg = struct {837pub 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}
112113
...@@ -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
306307
307fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {308fn 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}
313325
...@@ -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}
651663
664fn 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
652fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {692fn 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);
654694
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 \\