Commit b1aa2857ff

Vexu <git@vexu.eu>
2020-08-25 18:22:00
stage2: astgen for loops
1 parent 7d0bb07
Changed files (2)
src-self-hosted
src-self-hosted/astgen.zig
@@ -273,6 +273,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
         .AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)),
         .ErrorSetDecl => return errorSetDecl(mod, scope, rl, node.castTag(.ErrorSetDecl).?),
         .ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)),
+        .For => return forExpr(mod, scope, rl, node.castTag(.For).?),
 
         .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
         .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}),
@@ -288,7 +289,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
         .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),
         .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}),
         .Switch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Switch", .{}),
-        .For => return mod.failNode(scope, node, "TODO implement astgen.expr for .For", .{}),
         .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),
         .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}),
         .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
@@ -497,7 +497,7 @@ fn varDecl(
             const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTrailer("type_node")) |type_node| a: {
                 const type_inst = try typeExpr(mod, scope, type_node);
                 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
-                break :a .{ .alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst), .result_loc = .{ .ptr = alloc } };
+                break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
             } else a: {
                 const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred);
                 break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? } };
@@ -624,7 +624,7 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo,
             .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type,
             .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type,
             .C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type,
-            .Slice => if (mutable) T.mut_slice_type else T.mut_slice_type,
+            .Slice => if (mutable) T.mut_slice_type else T.const_slice_type,
         }, child_type);
     }
 
@@ -794,9 +794,7 @@ fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfix
     const lhs = try expr(mod, scope, .ref, node.lhs);
     const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);
 
-    const pointer = try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{});
-    if (rl == .ref) return pointer;
-    return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, pointer));
+    return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{}));
 }
 
 fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
@@ -1080,6 +1078,12 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
         }
     }
 
+    if (while_node.label) |tok|
+        return mod.failTok(scope, tok, "TODO labeled while", .{});
+
+    if (while_node.inline_token) |tok|
+        return mod.failTok(scope, tok, "TODO inline while", .{});
+
     var expr_scope: Scope.GenZIR = .{
         .parent = scope,
         .decl = scope.decl().?,
@@ -1198,6 +1202,179 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
     return &while_block.base;
 }
 
+fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) InnerError!*zir.Inst {
+    if (for_node.label) |tok|
+        return mod.failTok(scope, tok, "TODO labeled for", .{});
+
+    if (for_node.inline_token) |tok|
+        return mod.failTok(scope, tok, "TODO inline for", .{});
+
+    var for_scope: Scope.GenZIR = .{
+        .parent = scope,
+        .decl = scope.decl().?,
+        .arena = scope.arena(),
+        .instructions = .{},
+    };
+    defer for_scope.instructions.deinit(mod.gpa);
+
+    // setup variables and constants
+    const tree = scope.tree();
+    const for_src = tree.token_locs[for_node.for_token].start;
+    const index_ptr = blk: {
+        const usize_type = try addZIRInstConst(mod, &for_scope.base, for_src, .{
+            .ty = Type.initTag(.type),
+            .val = Value.initTag(.usize_type),
+        });
+        const index_ptr = try addZIRUnOp(mod, &for_scope.base, for_src, .alloc, usize_type);
+        // initialize to zero
+        const zero = try addZIRInstConst(mod, &for_scope.base, for_src, .{
+            .ty = Type.initTag(.usize),
+            .val = Value.initTag(.zero),
+        });
+        _ = try addZIRBinOp(mod, &for_scope.base, for_src, .store, index_ptr, zero);
+        break :blk index_ptr;
+    };
+    const array_ptr = try expr(mod, &for_scope.base, .ref, for_node.array_expr);
+    const cond_src = tree.token_locs[for_node.array_expr.firstToken()].start;
+    const len_ptr = try addZIRInst(mod, &for_scope.base, cond_src, zir.Inst.FieldPtr, .{
+        .object_ptr = array_ptr,
+        .field_name = try addZIRInst(mod, &for_scope.base, cond_src, zir.Inst.Str, .{ .bytes = "len" }, .{}),
+    }, .{});
+
+    var loop_scope: Scope.GenZIR = .{
+        .parent = &for_scope.base,
+        .decl = for_scope.decl,
+        .arena = for_scope.arena,
+        .instructions = .{},
+    };
+    defer loop_scope.instructions.deinit(mod.gpa);
+
+    var cond_scope: Scope.GenZIR = .{
+        .parent = &loop_scope.base,
+        .decl = loop_scope.decl,
+        .arena = loop_scope.arena,
+        .instructions = .{},
+    };
+    defer cond_scope.instructions.deinit(mod.gpa);
+
+    // check condition i < array_expr.len
+    const index = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, index_ptr);
+    const len = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, len_ptr);
+    const cond = try addZIRBinOp(mod, &cond_scope.base, cond_src, .cmp_lt, index, len);
+
+    const condbr = try addZIRInstSpecial(mod, &cond_scope.base, for_src, zir.Inst.CondBr, .{
+        .condition = cond,
+        .then_body = undefined, // populated below
+        .else_body = undefined, // populated below
+    }, .{});
+    const cond_block = try addZIRInstBlock(mod, &loop_scope.base, for_src, .{
+        .instructions = try loop_scope.arena.dupe(*zir.Inst, cond_scope.instructions.items),
+    });
+
+    // increment index variable
+    const one = try addZIRInstConst(mod, &loop_scope.base, for_src, .{
+        .ty = Type.initTag(.usize),
+        .val = Value.initTag(.one),
+    });
+    const index_plus_one = try addZIRBinOp(mod, &loop_scope.base, for_src, .add, index, one);
+    _ = try addZIRBinOp(mod, &loop_scope.base, for_src, .store, index_ptr, index_plus_one);
+
+    // looping stuff
+    const loop = try addZIRInstLoop(mod, &for_scope.base, for_src, .{
+        .instructions = try for_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
+    });
+    const for_block = try addZIRInstBlock(mod, scope, for_src, .{
+        .instructions = try scope.arena().dupe(*zir.Inst, for_scope.instructions.items),
+    });
+
+    // while body
+    const then_src = tree.token_locs[for_node.body.lastToken()].start;
+    var then_scope: Scope.GenZIR = .{
+        .parent = &cond_scope.base,
+        .decl = cond_scope.decl,
+        .arena = cond_scope.arena,
+        .instructions = .{},
+    };
+    defer then_scope.instructions.deinit(mod.gpa);
+
+    // 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
+    // branches.
+    const branch_rl: ResultLoc = switch (rl) {
+        .discard, .none, .ty, .ptr, .ref => rl,
+        .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = for_block },
+    };
+
+    var index_scope: Scope.LocalPtr = undefined;
+    const then_sub_scope = blk: {
+        const payload = for_node.payload.castTag(.PointerIndexPayload).?;
+        const is_ptr = payload.ptr_token != null;
+        const value_name = tree.tokenSlice(payload.value_symbol.firstToken());
+        if (!mem.eql(u8, value_name, "_")) {
+            return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement for value payload", .{});
+        } else if (is_ptr) {
+            return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
+        }
+
+        const index_symbol_node = payload.index_symbol orelse
+            break :blk &then_scope.base;
+
+        const index_name = tree.tokenSlice(index_symbol_node.firstToken());
+        if (mem.eql(u8, index_name, "_")) {
+            break :blk &then_scope.base;
+        }
+        // TODO ensure this is const
+        index_scope = .{
+            .parent = &then_scope.base,
+            .gen_zir = &then_scope,
+            .name = index_name,
+            .ptr = index_ptr,
+        };
+        break :blk &index_scope.base;
+    };
+
+    const then_result = try expr(mod, then_sub_scope, branch_rl, for_node.body);
+    if (!then_result.tag.isNoReturn()) {
+        _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
+            .block = cond_block,
+            .operand = then_result,
+        }, .{});
+    }
+    condbr.positionals.then_body = .{
+        .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
+    };
+
+    // else branch
+    var else_scope: Scope.GenZIR = .{
+        .parent = &cond_scope.base,
+        .decl = cond_scope.decl,
+        .arena = cond_scope.arena,
+        .instructions = .{},
+    };
+    defer else_scope.instructions.deinit(mod.gpa);
+
+    if (for_node.@"else") |else_node| {
+        const else_src = tree.token_locs[else_node.body.lastToken()].start;
+        const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body);
+        if (!else_result.tag.isNoReturn()) {
+            _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.Break, .{
+                .block = for_block,
+                .operand = else_result,
+            }, .{});
+        }
+    } else {
+        const else_src = tree.token_locs[for_node.lastToken()].start;
+        _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
+            .block = for_block,
+        }, .{});
+    }
+    condbr.positionals.else_body = .{
+        .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
+    };
+    return &for_block.base;
+}
+
 fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
     const tree = scope.tree();
     const src = tree.token_locs[cfe.ltoken].start;
src-self-hosted/value.zig
@@ -65,6 +65,7 @@ pub const Value = extern union {
 
         undef,
         zero,
+        one,
         void_value,
         unreachable_value,
         empty_array,
@@ -174,6 +175,7 @@ pub const Value = extern union {
             .anyframe_type,
             .undef,
             .zero,
+            .one,
             .void_value,
             .unreachable_value,
             .empty_array,
@@ -313,6 +315,7 @@ pub const Value = extern union {
             .null_value => return out_stream.writeAll("null"),
             .undef => return out_stream.writeAll("undefined"),
             .zero => return out_stream.writeAll("0"),
+            .one => return out_stream.writeAll("1"),
             .void_value => return out_stream.writeAll("{}"),
             .unreachable_value => return out_stream.writeAll("unreachable"),
             .bool_true => return out_stream.writeAll("true"),
@@ -447,6 +450,7 @@ pub const Value = extern union {
 
             .undef,
             .zero,
+            .one,
             .void_value,
             .unreachable_value,
             .empty_array,
@@ -546,7 +550,9 @@ pub const Value = extern union {
             .bool_false,
             => return BigIntMutable.init(&space.limbs, 0).toConst(),
 
-            .bool_true => return BigIntMutable.init(&space.limbs, 1).toConst(),
+            .one,
+            .bool_true,
+            => return BigIntMutable.init(&space.limbs, 1).toConst(),
 
             .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
             .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
@@ -627,7 +633,9 @@ pub const Value = extern union {
             .bool_false,
             => return 0,
 
-            .bool_true => return 1,
+            .one,
+            .bool_true,
+            => return 1,
 
             .int_u64 => return self.cast(Payload.Int_u64).?.int,
             .int_i64 => return @intCast(u64, self.cast(Payload.Int_i64).?.int),
@@ -708,7 +716,9 @@ pub const Value = extern union {
             .bool_false,
             => return 0,
 
-            .bool_true => return 1,
+            .one,
+            .bool_true,
+            => return 1,
 
             .int_u64 => return @intCast(i64, self.cast(Payload.Int_u64).?.int),
             .int_i64 => return self.cast(Payload.Int_i64).?.int,
@@ -734,6 +744,7 @@ pub const Value = extern union {
             .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val),
 
             .zero => 0,
+            .one => 1,
             .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int),
             .int_i64 => @intToFloat(T, self.cast(Payload.Int_i64).?.int),
 
@@ -814,7 +825,9 @@ pub const Value = extern union {
             .bool_false,
             => return 0,
 
-            .bool_true => return 1,
+            .one,
+            .bool_true,
+            => return 1,
 
             .int_u64 => {
                 const x = self.cast(Payload.Int_u64).?.int;
@@ -900,7 +913,9 @@ pub const Value = extern union {
             .bool_false,
             => return true,
 
-            .bool_true => {
+            .one,
+            .bool_true,
+            => {
                 const info = ty.intInfo(target);
                 if (info.signed) {
                     return info.bits >= 2;
@@ -1064,7 +1079,9 @@ pub const Value = extern union {
             .@"error",
             => unreachable,
 
-            .zero => false,
+            .zero,
+            .one,
+            => false,
 
             .float_16 => @rem(self.cast(Payload.Float_16).?.val, 1) != 0,
             .float_32 => @rem(self.cast(Payload.Float_32).?.val, 1) != 0,
@@ -1140,7 +1157,9 @@ pub const Value = extern union {
             .bool_false,
             => .eq,
 
-            .bool_true => .gt,
+            .one,
+            .bool_true,
+            => .gt,
 
             .int_u64 => std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
             .int_i64 => std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
@@ -1257,6 +1276,7 @@ pub const Value = extern union {
             .enum_literal_type,
             .anyframe_type,
             .zero,
+            .one,
             .bool_true,
             .bool_false,
             .null_value,
@@ -1339,6 +1359,7 @@ pub const Value = extern union {
             .enum_literal_type,
             .anyframe_type,
             .zero,
+            .one,
             .bool_true,
             .bool_false,
             .null_value,
@@ -1438,6 +1459,7 @@ pub const Value = extern union {
             .enum_literal_type,
             .anyframe_type,
             .zero,
+            .one,
             .empty_array,
             .bool_true,
             .bool_false,