diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 23e8c9527a..cb35b0070a 100644 --- a/src-self-hosted/astgen.zig +++ b/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; diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index f2e36b59a4..6a39371ebe 100644 --- a/src-self-hosted/value.zig +++ b/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,