From ece4a2fc512bffba3845bf611370f5ea436c57b4 Mon Sep 17 00:00:00 2001 From: Vexu Date: Sat, 15 Aug 2020 19:39:11 +0300 Subject: [PATCH] stage2: astgen for if and while with error unions --- src-self-hosted/astgen.zig | 56 +++++++++++++++++++++++++++--------- src-self-hosted/codegen.zig | 7 +++++ src-self-hosted/ir.zig | 2 ++ src-self-hosted/zir.zig | 13 +++++++++ src-self-hosted/zir_sema.zig | 11 +++++++ 5 files changed, 75 insertions(+), 14 deletions(-) diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 3dad37487e..5335504ed2 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -614,11 +614,16 @@ const CondKind = union(enum) { const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, cond_ptr); return try addZIRUnOp(mod, &block_scope.base, src, .isnonnull, result); }, - .err_union => unreachable, + .err_union => { + const err_ptr = try expr(mod, &block_scope.base, .lvalue, cond_node); + self.* = .{ .err_union = err_ptr }; + const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, err_ptr); + return try addZIRUnOp(mod, &block_scope.base, src, .iserr, result); + }, } } - fn thenSubScope(self: CondKind, mod: *Module, then_scope: *Scope.GenZIR, payload_node: ?*ast.Node) !*Scope { + fn thenSubScope(self: CondKind, mod: *Module, then_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope { if (self == .bool) return &then_scope.base; const payload = payload_node.?.castTag(.PointerPayload).?; @@ -633,6 +638,21 @@ const CondKind = union(enum) { return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement payload symbols", .{}); } + + fn elseSubScope(self: CondKind, mod: *Module, else_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope { + if (self != .err_union) return &else_scope.base; + + const payload_ptr = try addZIRUnOp(mod, &else_scope.base, src, .unwrap_err_unsafe, self.err_union.?); + + const payload = payload_node.?.castTag(.Payload).?; + const ident_node = payload.error_symbol.castTag(.Identifier).?; + const ident_name = try identifierTokenString(mod, &else_scope.base, ident_node.token); + if (mem.eql(u8, ident_name, "_")) { + return &else_scope.base; + } + + return mod.failNode(&else_scope.base, payload.error_symbol, "TODO implement payload symbols", .{}); + } }; fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst { @@ -643,7 +663,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn if (cond_kind != .optional) { return mod.failNode(scope, payload, "else payload invalid on bool conditions", .{}); } - return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for error unions", .{}); + cond_kind = .{ .err_union = null }; } } var block_scope: Scope.GenZIR = .{ @@ -667,6 +687,8 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn const block = try addZIRInstBlock(mod, scope, if_src, .{ .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), }); + + const then_src = tree.token_locs[if_node.body.lastToken()].start; var then_scope: Scope.GenZIR = .{ .parent = scope, .decl = block_scope.decl, @@ -676,7 +698,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn defer then_scope.instructions.deinit(mod.gpa); // declare payload to the then_scope - const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, if_node.payload); + const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, if_node.payload); // Most result location types can be forwarded directly; however // if we need to write to a pointer which has an inferred type, @@ -689,7 +711,6 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn const then_result = try expr(mod, then_sub_scope, branch_rl, if_node.body); if (!then_result.tag.isNoReturn()) { - const then_src = tree.token_locs[if_node.body.lastToken()].start; _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{ .block = block, .operand = then_result, @@ -708,10 +729,13 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn defer else_scope.instructions.deinit(mod.gpa); if (if_node.@"else") |else_node| { - const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body); + const else_src = tree.token_locs[else_node.body.lastToken()].start; + // declare payload to the then_scope + const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload); + + const else_result = try expr(mod, else_sub_scope, branch_rl, else_node.body); if (!else_result.tag.isNoReturn()) { - const else_src = tree.token_locs[else_node.body.lastToken()].start; - _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.Break, .{ + _ = try addZIRInst(mod, else_sub_scope, else_src, zir.Inst.Break, .{ .block = block, .operand = else_result, }, .{}); @@ -739,7 +763,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W if (cond_kind != .optional) { return mod.failNode(scope, payload, "else payload invalid on bool conditions", .{}); } - return mod.failNode(scope, payload, "TODO implement astgen.whileExpr for error unions", .{}); + cond_kind = .{ .err_union = null }; } } @@ -796,6 +820,8 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W const while_block = try addZIRInstBlock(mod, scope, while_src, .{ .instructions = try expr_scope.arena.dupe(*zir.Inst, expr_scope.instructions.items), }); + + const then_src = tree.token_locs[while_node.body.lastToken()].start; var then_scope: Scope.GenZIR = .{ .parent = &continue_scope.base, .decl = continue_scope.decl, @@ -805,7 +831,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W defer then_scope.instructions.deinit(mod.gpa); // declare payload to the then_scope - const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, while_node.payload); + const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, while_node.payload); // Most result location types can be forwarded directly; however // if we need to write to a pointer which has an inferred type, @@ -818,7 +844,6 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W const then_result = try expr(mod, then_sub_scope, branch_rl, while_node.body); if (!then_result.tag.isNoReturn()) { - const then_src = tree.token_locs[while_node.body.lastToken()].start; _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{ .block = cond_block, .operand = then_result, @@ -837,10 +862,13 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W defer else_scope.instructions.deinit(mod.gpa); if (while_node.@"else") |else_node| { - const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body); + const else_src = tree.token_locs[else_node.body.lastToken()].start; + // declare payload to the then_scope + const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload); + + const else_result = try expr(mod, else_sub_scope, branch_rl, else_node.body); if (!else_result.tag.isNoReturn()) { - const else_src = tree.token_locs[else_node.body.lastToken()].start; - _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.Break, .{ + _ = try addZIRInst(mod, else_sub_scope, else_src, zir.Inst.Break, .{ .block = while_block, .operand = else_result, }, .{}); diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 58b7c97f7b..d37145a275 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -671,6 +671,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .intcast => return self.genIntCast(inst.castTag(.intcast).?), .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?), .isnull => return self.genIsNull(inst.castTag(.isnull).?), + .iserr => return self.genIsErr(inst.castTag(.iserr).?), .load => return self.genLoad(inst.castTag(.load).?), .loop => return self.genLoop(inst.castTag(.loop).?), .not => return self.genNot(inst.castTag(.not).?), @@ -1391,6 +1392,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } } + fn genIsErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue { + switch (arch) { + else => return self.fail(inst.base.src, "TODO implement iserr for {}", .{self.target.cpu.arch}), + } + } + fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue { // A loop is a setup to be able to jump back to the beginning. const start_index = self.code.items.len; diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 4f83fa7030..91dfad45d7 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -68,6 +68,7 @@ pub const Inst = struct { dbg_stmt, isnonnull, isnull, + iserr, /// Read a value from a pointer. load, loop, @@ -100,6 +101,7 @@ pub const Inst = struct { .not, .isnonnull, .isnull, + .iserr, .ptrtoint, .floatcast, .intcast, diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 695cf0013f..276db6d522 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -151,6 +151,8 @@ pub const Inst = struct { isnonnull, /// Return a boolean true if an optional is null. `x == null` isnull, + /// Return a boolean true if value is an error + iserr, /// A labeled block of code that loops forever. At the end of the body it is implied /// to repeat; no explicit "repeat" instruction terminates loop bodies. loop, @@ -219,6 +221,10 @@ pub const Inst = struct { unwrap_optional_safe, /// Same as previous, but without safety checks. Used for orelse, if and while unwrap_optional_unsafe, + /// Gets the payload of an error union + unwrap_err_safe, + /// Same as previous, but without safety checks. Used for orelse, if and while + unwrap_err_unsafe, pub fn Type(tag: Tag) type { return switch (tag) { @@ -237,6 +243,7 @@ pub const Inst = struct { .@"return", .isnull, .isnonnull, + .iserr, .ptrtoint, .alloc, .ensure_result_used, @@ -250,6 +257,8 @@ pub const Inst = struct { .optional_type, .unwrap_optional_safe, .unwrap_optional_unsafe, + .unwrap_err_safe, + .unwrap_err_unsafe, => UnOp, .add, @@ -363,6 +372,7 @@ pub const Inst = struct { .inttype, .isnonnull, .isnull, + .iserr, .mod_rem, .mul, .mulwrap, @@ -385,6 +395,8 @@ pub const Inst = struct { .optional_type, .unwrap_optional_safe, .unwrap_optional_unsafe, + .unwrap_err_safe, + .unwrap_err_unsafe, .ptr_type, => false, @@ -2014,6 +2026,7 @@ const EmitZIR = struct { .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint), .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull), .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull), + .iserr => try self.emitUnOp(inst.src, new_body, inst.castTag(.iserr).?, .iserr), .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref), .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref), .unwrap_optional => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional).?, .unwrap_optional_unsafe), diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index 281bc75b1d..362dbe7909 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -104,11 +104,14 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?), .isnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnull).?, true), .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false), + .iserr => return analyzeInstIsErr(mod, scope, old_inst.castTag(.iserr).?, true), .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?), .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?), .optional_type => return analyzeInstOptionalType(mod, scope, old_inst.castTag(.optional_type).?), .unwrap_optional_safe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_safe).?, true), .unwrap_optional_unsafe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_unsafe).?, false), + .unwrap_err_safe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_safe).?, true), + .unwrap_err_unsafe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_unsafe).?, false), } } @@ -729,6 +732,10 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp return mod.addUnOp(b, unwrap.base.src, child_pointer, .unwrap_optional, operand); } +fn analyzeInstUnwrapErr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { + return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErr", .{}); +} + fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { const return_type = try resolveType(mod, scope, fntype.positionals.return_type); @@ -1169,6 +1176,10 @@ fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, inver return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic); } +fn analyzeInstIsErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement analyzeInstIsErr", .{}); +} + fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst { const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition); const cond = try mod.coerce(scope, Type.initTag(.bool), uncasted_cond);