Commit db77b6b4e7

Vexu <git@vexu.eu>
2020-08-15 18:17:50
stage2: astgen for if and while with optionals
1 parent 012fac2
Changed files (1)
src-self-hosted
src-self-hosted/astgen.zig
@@ -594,12 +594,55 @@ fn simpleBinOp(
     return rlWrap(mod, scope, rl, result);
 }
 
-fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst {
-    if (if_node.payload) |payload| {
-        return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for optionals", .{});
+const CondKind = union(enum) {
+    bool,
+    optional: ?*zir.Inst,
+    err_union: ?*zir.Inst,
+
+    fn cond(self: *CondKind, mod: *Module, block_scope: *Scope.GenZIR, src: usize, cond_node: *ast.Node) !*zir.Inst {
+        switch (self.*) {
+            .bool => {
+                const bool_type = try addZIRInstConst(mod, &block_scope.base, src, .{
+                    .ty = Type.initTag(.type),
+                    .val = Value.initTag(.bool_type),
+                });
+                return try expr(mod, &block_scope.base, .{ .ty = bool_type }, cond_node);
+            },
+            .optional => {
+                const cond_ptr = try expr(mod, &block_scope.base, .lvalue, cond_node);
+                self.* = .{ .optional = cond_ptr };
+                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,
+        }
+    }
+
+    fn thenSubScope(self: CondKind, mod: *Module, then_scope: *Scope.GenZIR, payload_node: ?*ast.Node) !*Scope {
+        if (self == .bool) return &then_scope.base;
+
+        const payload = payload_node.?.castTag(.PointerPayload).?;
+        const is_ptr = payload.ptr_token != null;
+        const ident_node = payload.value_symbol.castTag(.Identifier).?;
+        const ident_name = try identifierTokenString(mod, &then_scope.base, ident_node.token);
+        if (mem.eql(u8, ident_name, "_")) {
+            if (is_ptr)
+                return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
+            return &then_scope.base;
+        }
+
+        return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement payload symbols", .{});
     }
+};
+
+fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst {
+    var cond_kind: CondKind = .bool;
+    if (if_node.payload) |_| cond_kind = .{ .optional = null };
     if (if_node.@"else") |else_node| {
         if (else_node.payload) |payload| {
+            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", .{});
         }
     }
@@ -613,11 +656,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
 
     const tree = scope.tree();
     const if_src = tree.token_locs[if_node.if_token].start;
-    const bool_type = try addZIRInstConst(mod, scope, if_src, .{
-        .ty = Type.initTag(.type),
-        .val = Value.initTag(.bool_type),
-    });
-    const cond = try expr(mod, &block_scope.base, .{ .ty = bool_type }, if_node.condition);
+    const cond = try cond_kind.cond(mod, &block_scope, if_src, if_node.condition);
 
     const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{
         .condition = cond,
@@ -636,6 +675,9 @@ 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);
+
     // Most result location types can be forwarded directly; however
     // if we need to write to a pointer which has an inferred type,
     // proper type inference requires peer type resolution on the if's
@@ -645,10 +687,10 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
         .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
     };
 
-    const then_result = try expr(mod, &then_scope.base, branch_rl, if_node.body);
+    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_scope.base, then_src, zir.Inst.Break, .{
+        _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
             .block = block,
             .operand = then_result,
         }, .{});
@@ -690,11 +732,13 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
 }
 
 fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.While) InnerError!*zir.Inst {
-    if (while_node.payload) |payload| {
-        return mod.failNode(scope, payload, "TODO implement astgen.whileExpr for optionals", .{});
-    }
+    var cond_kind: CondKind = .bool;
+    if (while_node.payload) |_| cond_kind = .{ .optional = null };
     if (while_node.@"else") |else_node| {
         if (else_node.payload) |payload| {
+            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", .{});
         }
     }
@@ -725,15 +769,11 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
 
     const tree = scope.tree();
     const while_src = tree.token_locs[while_node.while_token].start;
-    const bool_type = try addZIRInstConst(mod, scope, while_src, .{
-        .ty = Type.initTag(.type),
-        .val = Value.initTag(.bool_type),
-    });
     const void_type = try addZIRInstConst(mod, scope, while_src, .{
         .ty = Type.initTag(.type),
         .val = Value.initTag(.void_type),
     });
-    const cond = try expr(mod, &continue_scope.base, .{ .ty = bool_type }, while_node.condition);
+    const cond = try cond_kind.cond(mod, &continue_scope, while_src, while_node.condition);
 
     const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{
         .condition = cond,
@@ -764,6 +804,9 @@ 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);
+
     // Most result location types can be forwarded directly; however
     // if we need to write to a pointer which has an inferred type,
     // proper type inference requires peer type resolution on the while's
@@ -773,10 +816,10 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
         .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = while_block },
     };
 
-    const then_result = try expr(mod, &then_scope.base, branch_rl, while_node.body);
+    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_scope.base, then_src, zir.Inst.Break, .{
+        _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
             .block = cond_block,
             .operand = then_result,
         }, .{});