From d62c12e077e4e3993864317bf8af5f0fcc631515 Mon Sep 17 00:00:00 2001 From: Vexu Date: Sat, 22 Aug 2020 15:48:41 +0300 Subject: [PATCH 1/7] stage2: astgen prefix ops --- src-self-hosted/astgen.zig | 30 ++++++++++++++++++++++++++---- src-self-hosted/zir.zig | 2 ++ src-self-hosted/zir_sema.zig | 1 + 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 8276b191cb..210ba0008e 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -232,6 +232,11 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?), .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?), + .BoolNot => return rlWrap(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)), + .BitNot => return rlWrap(mod, scope, rl, try bitNot(mod, scope, node.castTag(.BitNot).?)), + .Negation => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.Negation).?, .sub)), + .NegationWrap => return rlWrap(mod, scope, rl, try negation(mod, scope, node.castTag(.NegationWrap).?, .subwrap)), + .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?), .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)), .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)), @@ -244,7 +249,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .While => return whileExpr(mod, scope, rl, node.castTag(.While).?), .Period => return rlWrap(mod, scope, rl, try field(mod, scope, node.castTag(.Period).?)), .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)), - .BoolNot => return rlWrap(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)), .AddressOf => return rlWrap(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)), .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)), .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)), @@ -271,9 +275,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}), .OrElse => return mod.failNode(scope, node, "TODO implement astgen.expr for .OrElse", .{}), .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}), - .BitNot => return mod.failNode(scope, node, "TODO implement astgen.expr for .BitNot", .{}), - .Negation => return mod.failNode(scope, node, "TODO implement astgen.expr for .Negation", .{}), - .NegationWrap => return mod.failNode(scope, node, "TODO implement astgen.expr for .NegationWrap", .{}), .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}), .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), .Slice => return mod.failNode(scope, node, "TODO implement astgen.expr for .Slice", .{}), @@ -554,6 +555,27 @@ fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerErr return addZIRUnOp(mod, scope, src, .boolnot, operand); } +fn bitNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.token_locs[node.op_token].start; + const operand = try expr(mod, scope, .none, node.rhs); + return addZIRUnOp(mod, scope, src, .bitnot, operand); +} + +fn negation(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.token_locs[node.op_token].start; + + const lhs = addZIRInstConst(mod, scope, src, .{ + .ty = Type.initTag(.comptime_int), + .val = Value.initTag(.zero), + }); + const rhs = try expr(mod, scope, .none, node.rhs); + + const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); + return rlWrap(mod, scope, rl, result); +} + fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { return expr(mod, scope, .ref, node.rhs); } diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index a3ea1f11ab..a5d91aa690 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -70,6 +70,8 @@ pub const Inst = struct { /// A typed result location pointer is bitcasted to a new result location pointer. /// The new result location pointer has an inferred type. bitcast_result_ptr, + /// Bitwise NOT. `~` + bitnot, /// Bitwise OR. `|` bitor, /// A labeled block of code, which can return a value. diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index 75ec7f6306..6ac29e02c9 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -97,6 +97,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .array_cat => return analyzeInstArrayCat(mod, scope, old_inst.castTag(.array_cat).?), .array_mul => return analyzeInstArrayMul(mod, scope, old_inst.castTag(.array_mul).?), .bitand => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitand).?), + .bitnot => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitnot).?), .bitor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitor).?), .xor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.xor).?), .shl => return analyzeInstShl(mod, scope, old_inst.castTag(.shl).?), From 16d7db59ed9b0b1be6790bdb80777fbe5f80c7ed Mon Sep 17 00:00:00 2001 From: Vexu Date: Sat, 22 Aug 2020 16:30:57 +0300 Subject: [PATCH 2/7] stage2: anyframe and error union types --- src-self-hosted/Module.zig | 22 +++++ src-self-hosted/astgen.zig | 41 ++++++-- src-self-hosted/type.zig | 179 +++++++++++++++++++++++++++++++++-- src-self-hosted/value.zig | 14 +++ src-self-hosted/zir.zig | 14 +++ src-self-hosted/zir_sema.zig | 30 +++++- 6 files changed, 287 insertions(+), 13 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index c0d1d0d654..f97254f29a 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -3309,6 +3309,28 @@ pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_ return Type.initPayload(&payload.base); } +pub fn errorUnionType(self: *Module, scope: *Scope, error_set: Type, payload: Type) Allocator.Error!Type { + assert(error_set.zigTypeTag() == .ErrorSet); + if (error_set.eql(Type.initTag(.anyerror)) and payload.eql(Type.initTag(.void))) { + return Type.initTag(.anyerror_void_error_union); + } + + const result = try scope.arena().create(Type.Payload.ErrorUnion); + result.* = .{ + .error_set = error_set, + .payload = payload, + }; + return Type.initPayload(&result.base); +} + +pub fn anyframeType(self: *Module, scope: *Scope, return_type: Type) Allocator.Error!Type { + const result = try scope.arena().create(Type.Payload.AnyFrame); + result.* = .{ + .return_type = return_type, + }; + return Type.initPayload(&result.base); +} + pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { const zir_module = scope.namespace(); const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source"); diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 210ba0008e..32041fbc2b 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -267,11 +267,12 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .MultilineStringLiteral => return rlWrap(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)), .CharLiteral => return rlWrap(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)), .SliceType => return rlWrap(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)), + .ErrorUnion => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)), + .MergeErrorSets => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)), + .AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)), .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}), - .ErrorUnion => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorUnion", .{}), - .MergeErrorSets => return mod.failNode(scope, node, "TODO implement astgen.expr for .MergeErrorSets", .{}), .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}), .OrElse => return mod.failNode(scope, node, "TODO implement astgen.expr for .OrElse", .{}), .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}), @@ -290,7 +291,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}), .ErrorType => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorType", .{}), .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}), - .AnyFrameType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyFrameType", .{}), .ErrorSetDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorSetDecl", .{}), .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}), .Comptime => return mod.failNode(scope, node, "TODO implement astgen.expr for .Comptime", .{}), @@ -566,14 +566,13 @@ fn negation(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp, op_inst const tree = scope.tree(); const src = tree.token_locs[node.op_token].start; - const lhs = addZIRInstConst(mod, scope, src, .{ + const lhs = try addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.comptime_int), .val = Value.initTag(.zero), }); const rhs = try expr(mod, scope, .none, node.rhs); - const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); - return rlWrap(mod, scope, rl, result); + return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs); } fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { @@ -703,6 +702,36 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti }, .{}); } +fn anyFrameType(mod: *Module, scope: *Scope, node: *ast.Node.AnyFrameType) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.token_locs[node.anyframe_token].start; + if (node.result) |some| { + const meta_type = try addZIRInstConst(mod, scope, src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.type_type), + }); + const return_type = try expr(mod, scope, .{ .ty = meta_type}, some.return_type); + return addZIRUnOp(mod, scope, src, .anyframe_type, return_type); + } else { + return addZIRInstConst(mod, scope, src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.anyframe_type), + }); + } +} + +fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.token_locs[node.op_token].start; + const meta_type = try addZIRInstConst(mod, scope, src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.type_type), + }); + const error_set = try expr(mod, scope, .{ .ty = meta_type }, node.lhs); + const payload = try expr(mod, scope, .{ .ty = meta_type }, node.rhs); + return addZIRBinOp(mod, scope, src, op_inst_tag, error_set, payload); +} + fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.name].start; diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 37b13c1d15..37f9f0c638 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -84,6 +84,10 @@ pub const Type = extern union { .optional_single_mut_pointer, => return .Optional, .enum_literal => return .EnumLiteral, + + .anyerror_void_error_union, .error_union => return .ErrorUnion, + + .anyframe_T, .@"anyframe" => return .AnyFrame, } } @@ -151,6 +155,9 @@ pub const Type = extern union { .ComptimeInt => return true, .Undefined => return true, .Null => return true, + .AnyFrame => { + return a.elemType().eql(b.elemType()); + }, .Pointer => { // Hot path for common case: if (a.castPointer()) |a_payload| { @@ -225,7 +232,6 @@ pub const Type = extern union { .BoundFn, .Opaque, .Frame, - .AnyFrame, .Vector, => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }), } @@ -343,6 +349,8 @@ pub const Type = extern union { .single_const_pointer_to_comptime_int, .const_slice_u8, .enum_literal, + .anyerror_void_error_union, + .@"anyframe", => unreachable, .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0), @@ -397,6 +405,7 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, => return self.copyPayloadSingleField(allocator, Payload.PointerSimple, "pointee_type"), + .anyframe_T => return self.copyPayloadSingleField(allocator, Payload.AnyFrame, "return_type"), .pointer => { const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise); @@ -416,6 +425,17 @@ pub const Type = extern union { }; return Type{ .ptr_otherwise = &new_payload.base }; }, + .error_union => { + const payload = @fieldParentPtr(Payload.ErrorUnion, "base", self.ptr_otherwise); + const new_payload = try allocator.create(Payload.ErrorUnion); + new_payload.* = .{ + .base = payload.base, + + .error_set = try payload.error_set.copy(allocator), + .payload = try payload.payload.copy(allocator), + }; + return Type{ .ptr_otherwise = &new_payload.base }; + }, } } @@ -482,6 +502,8 @@ pub const Type = extern union { .@"null" => return out_stream.writeAll("@TypeOf(null)"), .@"undefined" => return out_stream.writeAll("@TypeOf(undefined)"), + .@"anyframe" => return out_stream.writeAll("anyframe"), + .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"), .const_slice_u8 => return out_stream.writeAll("[]const u8"), .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), .fn_void_no_args => return out_stream.writeAll("fn() void"), @@ -500,6 +522,12 @@ pub const Type = extern union { continue; }, + .anyframe_T => { + const payload = @fieldParentPtr(Payload.AnyFrame, "base", ty.ptr_otherwise); + try out_stream.print("anyframe->", .{}); + ty = payload.return_type; + continue; + }, .array_u8 => { const payload = @fieldParentPtr(Payload.Array_u8, "base", ty.ptr_otherwise); return out_stream.print("[{}]u8", .{payload.len}); @@ -622,6 +650,13 @@ pub const Type = extern union { ty = payload.pointee_type; continue; }, + .error_union => { + const payload = @fieldParentPtr(Payload.ErrorUnion, "base", ty.ptr_otherwise); + try payload.error_set.format("", .{}, out_stream); + try out_stream.writeAll("!"); + ty = payload.payload; + continue; + }, } unreachable; } @@ -715,6 +750,9 @@ pub const Type = extern union { .optional, .optional_single_mut_pointer, .optional_single_const_pointer, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => true, // TODO lazy types .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0, @@ -723,6 +761,11 @@ pub const Type = extern union { .int_signed => self.cast(Payload.IntSigned).?.bits == 0, .int_unsigned => self.cast(Payload.IntUnsigned).?.bits == 0, + .error_union => { + const payload = self.cast(Payload.ErrorUnion).?; + return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits(); + }, + .c_void, .void, .type, @@ -779,6 +822,8 @@ pub const Type = extern union { .mut_slice, .optional_single_const_pointer, .optional_single_mut_pointer, + .@"anyframe", + .anyframe_T, => return @divExact(target.cpu.arch.ptrBitWidth(), 8), .pointer => { @@ -803,7 +848,7 @@ pub const Type = extern union { .f128 => return 16, .c_longdouble => return 16, - .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type + .anyerror_void_error_union, .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type .array, .array_sentinel => return self.elemType().abiAlignment(target), @@ -829,6 +874,16 @@ pub const Type = extern union { return child_type.abiAlignment(target); }, + .error_union => { + const payload = self.cast(Payload.ErrorUnion).?; + if (!payload.error_set.hasCodeGenBits()) { + return payload.payload.abiAlignment(target); + } else if (!payload.payload.hasCodeGenBits()) { + return payload.error_set.abiAlignment(target); + } + @panic("TODO abiAlignment error union"); + }, + .c_void, .void, .type, @@ -882,12 +937,15 @@ pub const Type = extern union { .i32, .u32 => return 4, .i64, .u64 => return 8, - .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8), + .@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8), .const_slice, .mut_slice, - .const_slice_u8, - => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2, + => { + if (self.elemType().hasCodeGenBits()) return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2; + return @divExact(target.cpu.arch.ptrBitWidth(), 8); + }, + .const_slice_u8 => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2, .optional_single_const_pointer, .optional_single_mut_pointer, @@ -923,7 +981,7 @@ pub const Type = extern union { .f128 => return 16, .c_longdouble => return 16, - .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type + .anyerror_void_error_union, .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type .int_signed, .int_unsigned => { const bits: u16 = if (self.cast(Payload.IntSigned)) |pl| @@ -950,6 +1008,18 @@ pub const Type = extern union { // to the child type's ABI alignment. return child_type.abiAlignment(target) + child_type.abiSize(target); }, + + .error_union => { + const payload = self.cast(Payload.ErrorUnion).?; + if (!payload.error_set.hasCodeGenBits() and !payload.payload.hasCodeGenBits()) { + return 0; + } else if (!payload.error_set.hasCodeGenBits()) { + return payload.payload.abiSize(target); + } else if (!payload.payload.hasCodeGenBits()) { + return payload.error_set.abiSize(target); + } + @panic("TODO abiSize error union"); + }, }; } @@ -1010,6 +1080,10 @@ pub const Type = extern union { .c_mut_pointer, .const_slice, .mut_slice, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => false, .single_const_pointer, @@ -1078,6 +1152,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => false, .const_slice, @@ -1143,6 +1221,10 @@ pub const Type = extern union { .optional_single_const_pointer, .enum_literal, .mut_slice, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => false, .single_const_pointer, @@ -1217,6 +1299,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => false, .pointer => { @@ -1328,6 +1414,10 @@ pub const Type = extern union { .optional_single_const_pointer, .optional_single_mut_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => unreachable, .array => self.cast(Payload.Array).?.elem_type, @@ -1449,6 +1539,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => unreachable, .array => self.cast(Payload.Array).?.len, @@ -1516,6 +1610,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => unreachable, .array, .array_u8 => return null, @@ -1581,6 +1679,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => false, .int_signed, @@ -1649,6 +1751,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => false, .int_unsigned, @@ -1707,6 +1813,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => unreachable, .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits }, @@ -1783,6 +1893,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => false, .usize, @@ -1888,6 +2002,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => unreachable, }; } @@ -1959,6 +2077,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => unreachable, } } @@ -2029,6 +2151,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => unreachable, } } @@ -2099,6 +2225,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => unreachable, }; } @@ -2166,6 +2296,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => unreachable, }; } @@ -2233,6 +2367,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => unreachable, }; } @@ -2300,6 +2438,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => false, }; } @@ -2351,6 +2493,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .anyerror_void_error_union, + .anyframe_T, + .@"anyframe", + .error_union, => return null, .void => return Value.initTag(.void_value), @@ -2454,6 +2600,10 @@ pub const Type = extern union { .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, + .error_union, + .@"anyframe", + .anyframe_T, + .anyerror_void_error_union, => return false, .c_const_pointer, @@ -2511,6 +2661,8 @@ pub const Type = extern union { fn_naked_noreturn_no_args, fn_ccc_void_no_args, single_const_pointer_to_comptime_int, + anyerror_void_error_union, + @"anyframe", const_slice_u8, // See last_no_payload_tag below. // After this, the tag requires a payload. @@ -2533,6 +2685,8 @@ pub const Type = extern union { optional, optional_single_mut_pointer, optional_single_const_pointer, + error_union, + anyframe_T, pub const last_no_payload_tag = Tag.const_slice_u8; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -2614,6 +2768,19 @@ pub const Type = extern union { @"volatile": bool, size: std.builtin.TypeInfo.Pointer.Size, }; + + pub const ErrorUnion = struct { + base: Payload = .{ .tag = .error_union }, + + error_set: Type, + payload: Type, + }; + + pub const AnyFrame = struct { + base: Payload = .{ .tag = .anyframe_T }, + + return_type: Type, + }; }; }; diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index 95a0746e19..7b43c66200 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -61,6 +61,7 @@ pub const Value = extern union { single_const_pointer_to_comptime_int_type, const_slice_u8_type, enum_literal_type, + anyframe_type, undef, zero, @@ -168,6 +169,7 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, .enum_literal_type, + .anyframe_type, .undef, .zero, .void_value, @@ -300,6 +302,7 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), .const_slice_u8_type => return out_stream.writeAll("[]const u8"), .enum_literal_type => return out_stream.writeAll("@TypeOf(.EnumLiteral)"), + .anyframe_type => return out_stream.writeAll("anyframe"), .null_value => return out_stream.writeAll("null"), .undef => return out_stream.writeAll("undefined"), @@ -408,6 +411,7 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), .const_slice_u8_type => Type.initTag(.const_slice_u8), .enum_literal_type => Type.initTag(.enum_literal), + .anyframe_type => Type.initTag(.@"anyframe"), .undef, .zero, @@ -482,6 +486,7 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, .enum_literal_type, + .anyframe_type, .null_value, .function, .variable, @@ -560,6 +565,7 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, .enum_literal_type, + .anyframe_type, .null_value, .function, .variable, @@ -638,6 +644,7 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, .enum_literal_type, + .anyframe_type, .null_value, .function, .variable, @@ -742,6 +749,7 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, .enum_literal_type, + .anyframe_type, .null_value, .function, .variable, @@ -825,6 +833,7 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, .enum_literal_type, + .anyframe_type, .null_value, .function, .variable, @@ -988,6 +997,7 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, .enum_literal_type, + .anyframe_type, .bool_true, .bool_false, .null_value, @@ -1063,6 +1073,7 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, .enum_literal_type, + .anyframe_type, .null_value, .function, .variable, @@ -1197,6 +1208,7 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, .enum_literal_type, + .anyframe_type, .zero, .bool_true, .bool_false, @@ -1276,6 +1288,7 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, .enum_literal_type, + .anyframe_type, .zero, .bool_true, .bool_false, @@ -1372,6 +1385,7 @@ pub const Value = extern union { .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, .enum_literal_type, + .anyframe_type, .zero, .empty_array, .bool_true, diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index a5d91aa690..9460e3b2bb 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -43,6 +43,8 @@ pub const Inst = struct { alloc, /// Same as `alloc` except the type is inferred. alloc_inferred, + /// Create an `anyframe->T`. + anyframe_type, /// Array concatenation. `a ++ b` array_cat, /// Array multiplication `a ** b` @@ -135,6 +137,8 @@ pub const Inst = struct { ensure_result_used, /// Emits a compile error if an error is ignored. ensure_result_non_error, + /// Create a `E!T` type. + error_union_type, /// Export the provided Decl as the provided name in the compilation's output object file. @"export", /// Given a pointer to a struct or object that contains virtual fields, returns a pointer @@ -162,6 +166,8 @@ pub const Inst = struct { /// 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, + /// Merge two error sets into one, `E1 || E2`. + merge_error_sets, /// Ambiguously remainder division or modulus. If the computation would possibly have /// a different value depending on whether the operation is remainder division or modulus, /// a compile error is emitted. Otherwise the computation is performed. @@ -288,6 +294,8 @@ pub const Inst = struct { .unwrap_err_safe, .unwrap_err_unsafe, .ensure_err_payload_void, + .anyframe_type, + .bitnot, => UnOp, .add, @@ -318,6 +326,8 @@ pub const Inst = struct { .bitcast, .coerce_result_ptr, .xor, + .error_union_type, + .merge_error_sets, => BinOp, .arg => Arg, @@ -440,6 +450,10 @@ pub const Inst = struct { .ptr_type, .ensure_err_payload_void, .enum_literal, + .merge_error_sets, + .anyframe_type, + .error_union_type, + .bitnot, => false, .@"break", diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index 6ac29e02c9..bfe58daf7e 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -97,7 +97,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .array_cat => return analyzeInstArrayCat(mod, scope, old_inst.castTag(.array_cat).?), .array_mul => return analyzeInstArrayMul(mod, scope, old_inst.castTag(.array_mul).?), .bitand => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitand).?), - .bitnot => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitnot).?), + .bitnot => return analyzeInstBitNot(mod, scope, old_inst.castTag(.bitnot).?), .bitor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.bitor).?), .xor => return analyzeInstBitwise(mod, scope, old_inst.castTag(.xor).?), .shl => return analyzeInstShl(mod, scope, old_inst.castTag(.shl).?), @@ -123,6 +123,9 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?), .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?), .enum_literal => return analyzeInstEnumLiteral(mod, scope, old_inst.castTag(.enum_literal).?), + .merge_error_sets => return analyzeInstMergeErrorSets(mod, scope, old_inst.castTag(.merge_error_sets).?), + .error_union_type => return analyzeInstErrorUnionType(mod, scope, old_inst.castTag(.error_union_type).?), + .anyframe_type => return analyzeInstAnyframeType(mod, scope, old_inst.castTag(.anyframe_type).?), } } @@ -717,6 +720,27 @@ fn analyzeInstArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.Ar return mod.constType(scope, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), sentinel.val, elem_type)); } +fn analyzeInstErrorUnionType(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const error_union = try resolveType(mod, scope, inst.positionals.lhs); + const payload = try resolveType(mod, scope, inst.positionals.rhs); + + if (error_union.zigTypeTag() != .ErrorSet) { + return mod.fail(scope, inst.base.src, "expected error set type, found {}", .{error_union.elemType()}); + } + + return mod.constType(scope, inst.base.src, try mod.errorUnionType(scope, error_union, payload)); +} + +fn analyzeInstAnyframeType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const return_type = try resolveType(mod, scope, inst.positionals.operand); + + return mod.constType(scope, inst.base.src, try mod.anyframeType(scope, return_type)); +} + +fn analyzeInstMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement merge_error_sets", .{}); +} + fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst { const payload = try scope.arena().create(Value.Payload.Bytes); payload.* = .{ @@ -984,6 +1008,10 @@ fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerE return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitwise", .{}); } +fn analyzeInstBitNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitNot", .{}); +} + fn analyzeInstArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{}); } From e9b15ac9a099fd17e6b68d0f04ec42a2dbfda0ca Mon Sep 17 00:00:00 2001 From: Vexu Date: Sun, 23 Aug 2020 15:50:36 +0300 Subject: [PATCH 3/7] stage2: error set declarations --- src-self-hosted/Module.zig | 17 ++++++++ src-self-hosted/astgen.zig | 75 ++++++++++++++---------------------- src-self-hosted/value.zig | 30 +++++++++++++++ src-self-hosted/zir.zig | 39 +++++++++++++++++++ src-self-hosted/zir_sema.zig | 28 +++++++++++++- 5 files changed, 142 insertions(+), 47 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index f97254f29a..9b3e72e7aa 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -80,6 +80,9 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{}, root_name: []u8, keep_source_files_loaded: bool, +/// Error tags and their values, tag names are duped with mod.gpa. +global_error_set: std.StringHashMapUnmanaged(u16) = .{}, + pub const InnerError = error{ OutOfMemory, AnalysisFail }; const WorkItem = union(enum) { @@ -928,6 +931,11 @@ pub fn deinit(self: *Module) void { self.symbol_exports.deinit(gpa); self.root_scope.destroy(gpa); + + for (self.global_error_set.items()) |entry| { + gpa.free(entry.key); + } + self.global_error_set.deinit(gpa); self.* = undefined; } @@ -2072,6 +2080,15 @@ fn createNewDecl( return new_decl; } +/// Get error value for error tag `name`. +pub fn getErrorValue(self: *Module, name: []const u8) !u16 { + const new_val = @intCast(u16, self.global_error_set.items().len); + if (self.global_error_set.get(name)) |some| return some; + + try self.global_error_set.put(self.gpa, try self.gpa.dupe(u8, name), new_val); + return new_val; +} + /// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites. pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block { return scope.cast(Scope.Block) orelse diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 32041fbc2b..b77cf97dfc 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -270,6 +270,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .ErrorUnion => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)), .MergeErrorSets => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)), .AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)), + .ErrorSetDecl => return rlWrap(mod, scope, rl, try errorSetDecl(mod, scope, node.castTag(.ErrorSetDecl).?)), .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}), @@ -291,7 +292,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}), .ErrorType => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorType", .{}), .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}), - .ErrorSetDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorSetDecl", .{}), .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}), .Comptime => return mod.failNode(scope, node, "TODO implement astgen.expr for .Comptime", .{}), .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}), @@ -459,7 +459,9 @@ fn varDecl( const tree = scope.tree(); const name_src = tree.token_locs[node.name_token].start; const ident_name = try identifierTokenString(mod, scope, node.name_token); - const init_node = node.getTrailer("init_node").?; + const init_node = node.getTrailer("init_node") orelse + return mod.fail(scope, name_src, "variables must be initialized", .{}); + switch (tree.token_ids[node.mut_token]) { .Keyword_const => { // Depending on the type of AST the initialization expression is, we may need an lvalue @@ -582,11 +584,7 @@ fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerE fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.op_token].start; - const meta_type = try addZIRInstConst(mod, scope, src, .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.type_type), - }); - const operand = try expr(mod, scope, .{ .ty = meta_type }, node.rhs); + const operand = try typeExpr(mod, scope, node.rhs); return addZIRUnOp(mod, scope, src, .optional_type, operand); } @@ -611,18 +609,13 @@ fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir } fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, rhs: *ast.Node, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*zir.Inst { - const meta_type = try addZIRInstConst(mod, scope, src, .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.type_type), - }); - const simple = ptr_info.allowzero_token == null and ptr_info.align_info == null and ptr_info.volatile_token == null and ptr_info.sentinel == null; if (simple) { - const child_type = try expr(mod, scope, .{ .ty = meta_type }, rhs); + const child_type = try typeExpr(mod, scope, rhs); const mutable = ptr_info.const_token == null; // TODO stage1 type inference bug const T = zir.Inst.Tag; @@ -650,7 +643,7 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, kw_args.sentinel = try expr(mod, scope, .none, some); } - const child_type = try expr(mod, scope, .{ .ty = meta_type }, rhs); + const child_type = try typeExpr(mod, scope, rhs); if (kw_args.sentinel) |some| { kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some); } @@ -661,10 +654,6 @@ fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.op_token].start; - const meta_type = try addZIRInstConst(mod, scope, src, .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.type_type), - }); const usize_type = try addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type), @@ -672,18 +661,14 @@ fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst // TODO check for [_]T const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr); - const child_type = try expr(mod, scope, .{ .ty = meta_type }, node.rhs); + const elem_type = try typeExpr(mod, scope, node.rhs); - return addZIRBinOp(mod, scope, src, .array_type, len, child_type); + return addZIRBinOp(mod, scope, src, .array_type, len, elem_type); } fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.op_token].start; - const meta_type = try addZIRInstConst(mod, scope, src, .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.type_type), - }); const usize_type = try addZIRInstConst(mod, scope, src, .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type), @@ -692,7 +677,7 @@ fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSenti // TODO check for [_]T const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr); const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel); - const elem_type = try expr(mod, scope, .{ .ty = meta_type }, node.rhs); + const elem_type = try typeExpr(mod, scope, node.rhs); const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted); return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{ @@ -706,11 +691,7 @@ fn anyFrameType(mod: *Module, scope: *Scope, node: *ast.Node.AnyFrameType) Inner const tree = scope.tree(); const src = tree.token_locs[node.anyframe_token].start; if (node.result) |some| { - const meta_type = try addZIRInstConst(mod, scope, src, .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.type_type), - }); - const return_type = try expr(mod, scope, .{ .ty = meta_type}, some.return_type); + const return_type = try typeExpr(mod, scope, some.return_type); return addZIRUnOp(mod, scope, src, .anyframe_type, return_type); } else { return addZIRInstConst(mod, scope, src, .{ @@ -723,12 +704,8 @@ fn anyFrameType(mod: *Module, scope: *Scope, node: *ast.Node.AnyFrameType) Inner fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.op_token].start; - const meta_type = try addZIRInstConst(mod, scope, src, .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.type_type), - }); - const error_set = try expr(mod, scope, .{ .ty = meta_type }, node.lhs); - const payload = try expr(mod, scope, .{ .ty = meta_type }, node.rhs); + const error_set = try typeExpr(mod, scope, node.lhs); + const payload = try typeExpr(mod, scope, node.rhs); return addZIRBinOp(mod, scope, src, op_inst_tag, error_set, payload); } @@ -751,6 +728,20 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, unwrapped_ptr)); } +fn errorSetDecl(mod: *Module, scope: *Scope, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.token_locs[node.error_token].start; + const decls = node.decls(); + const fields = try scope.arena().alloc([]const u8, decls.len); + + for (decls) |decl, i| { + const tag = decl.castTag(.ErrorTag).?; + fields[i] = try identifierTokenString(mod, scope, tag.name_token); + } + + return addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{}); +} + /// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating. /// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used. fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool { @@ -1517,12 +1508,8 @@ fn simpleCast( try ensureBuiltinParamCount(mod, scope, call, 2); const tree = scope.tree(); const src = tree.token_locs[call.builtin_token].start; - const type_type = try addZIRInstConst(mod, scope, src, .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.type_type), - }); const params = call.params(); - const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]); + const dest_type = try typeExpr(mod, scope, params[0]); const rhs = try expr(mod, scope, .none, params[1]); const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs); return rlWrap(mod, scope, rl, result); @@ -1584,12 +1571,8 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa try ensureBuiltinParamCount(mod, scope, call, 2); const tree = scope.tree(); const src = tree.token_locs[call.builtin_token].start; - const type_type = try addZIRInstConst(mod, scope, src, .{ - .ty = Type.initTag(.type), - .val = Value.initTag(.type_type), - }); const params = call.params(); - const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]); + const dest_type = try typeExpr(mod, scope, params[0]); switch (rl) { .none => { const operand = try expr(mod, scope, .none, params[1]); diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index 7b43c66200..f7917b9d44 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -91,6 +91,7 @@ pub const Value = extern union { float_64, float_128, enum_literal, + error_set, pub const last_no_payload_tag = Tag.bool_false; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -243,6 +244,9 @@ pub const Value = extern union { }; return Value{ .ptr_otherwise = &new_payload.base }; }, + + // memory is managed by the declaration + .error_set => return self, } } @@ -346,6 +350,14 @@ pub const Value = extern union { .float_32 => return out_stream.print("{}", .{val.cast(Payload.Float_32).?.val}), .float_64 => return out_stream.print("{}", .{val.cast(Payload.Float_64).?.val}), .float_128 => return out_stream.print("{}", .{val.cast(Payload.Float_128).?.val}), + .error_set => { + const error_set = val.cast(Payload.ErrorSet).?; + try out_stream.writeAll("error{"); + for (error_set.fields.items()) |entry| { + try out_stream.print("{},", .{entry.value}); + } + return out_stream.writeAll("}"); + }, }; } @@ -437,6 +449,7 @@ pub const Value = extern union { .float_64, .float_128, .enum_literal, + .error_set, => unreachable, }; } @@ -503,6 +516,7 @@ pub const Value = extern union { .unreachable_value, .empty_array, .enum_literal, + .error_set, => unreachable, .undef => unreachable, @@ -582,6 +596,7 @@ pub const Value = extern union { .unreachable_value, .empty_array, .enum_literal, + .error_set, => unreachable, .undef => unreachable, @@ -661,6 +676,7 @@ pub const Value = extern union { .unreachable_value, .empty_array, .enum_literal, + .error_set, => unreachable, .undef => unreachable, @@ -767,6 +783,7 @@ pub const Value = extern union { .unreachable_value, .empty_array, .enum_literal, + .error_set, => unreachable, .zero, @@ -850,6 +867,7 @@ pub const Value = extern union { .unreachable_value, .empty_array, .enum_literal, + .error_set, => unreachable, .zero, @@ -1017,6 +1035,7 @@ pub const Value = extern union { .void_value, .unreachable_value, .enum_literal, + .error_set, => unreachable, .zero => false, @@ -1087,6 +1106,7 @@ pub const Value = extern union { .unreachable_value, .empty_array, .enum_literal, + .error_set, => unreachable, .zero, @@ -1230,6 +1250,7 @@ pub const Value = extern union { .unreachable_value, .empty_array, .enum_literal, + .error_set, => unreachable, .ref_val => self.cast(Payload.RefVal).?.val, @@ -1310,6 +1331,7 @@ pub const Value = extern union { .void_value, .unreachable_value, .enum_literal, + .error_set, => unreachable, .empty_array => unreachable, // out of bounds array index @@ -1407,6 +1429,7 @@ pub const Value = extern union { .float_128, .void_value, .enum_literal, + .error_set, => false, .undef => unreachable, @@ -1536,6 +1559,13 @@ pub const Value = extern union { base: Payload = .{ .tag = .float_128 }, val: f128, }; + + pub const ErrorSet = struct { + base: Payload = .{ .tag = .error_set }, + + // TODO revisit this when we have the concept of the error tag type + fields: std.StringHashMapUnmanaged(u16), + }; }; /// Big enough to fit any non-BigInt value diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 9460e3b2bb..079dc13c35 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -139,6 +139,8 @@ pub const Inst = struct { ensure_result_non_error, /// Create a `E!T` type. error_union_type, + /// Create an error set. + error_set, /// Export the provided Decl as the provided name in the compilation's output object file. @"export", /// Given a pointer to a struct or object that contains virtual fields, returns a pointer @@ -359,6 +361,7 @@ pub const Inst = struct { .condbr => CondBr, .ptr_type => PtrType, .enum_literal => EnumLiteral, + .error_set => ErrorSet, }; } @@ -454,6 +457,7 @@ pub const Inst = struct { .anyframe_type, .error_union_type, .bitnot, + .error_set, => false, .@"break", @@ -924,6 +928,16 @@ pub const Inst = struct { }, kw_args: struct {}, }; + + pub const ErrorSet = struct { + pub const base_tag = Tag.error_set; + base: Inst, + + positionals: struct { + fields: [][]const u8, + }, + kw_args: struct {}, + }; }; pub const ErrorMsg = struct { @@ -1158,6 +1172,16 @@ const Writer = struct { const name = self.loop_table.get(param).?; return std.zig.renderStringLiteral(name, stream); }, + [][]const u8 => { + try stream.writeByte('['); + for (param) |str, i| { + if (i != 0) { + try stream.writeAll(", "); + } + try std.zig.renderStringLiteral(str, stream); + } + try stream.writeByte(']'); + }, else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)), } } @@ -1555,6 +1579,21 @@ const Parser = struct { const name = try self.parseStringLiteral(); return self.loop_table.get(name).?; }, + [][]const u8 => { + try requireEatBytes(self, "["); + skipSpace(self); + if (eatByte(self, ']')) return &[0][]const u8{}; + + var strings = std.ArrayList([]const u8).init(&self.arena.allocator); + while (true) { + skipSpace(self); + try strings.append(try self.parseStringLiteral()); + skipSpace(self); + if (!eatByte(self, ',')) break; + } + try requireEatBytes(self, "]"); + return strings.toOwnedSlice(); + }, else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), } return self.fail("TODO parse parameter {}", .{@typeName(T)}); diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index bfe58daf7e..6b30d58fdb 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -126,6 +126,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .merge_error_sets => return analyzeInstMergeErrorSets(mod, scope, old_inst.castTag(.merge_error_sets).?), .error_union_type => return analyzeInstErrorUnionType(mod, scope, old_inst.castTag(.error_union_type).?), .anyframe_type => return analyzeInstAnyframeType(mod, scope, old_inst.castTag(.anyframe_type).?), + .error_set => return analyzeInstErrorSet(mod, scope, old_inst.castTag(.error_set).?), } } @@ -435,6 +436,7 @@ fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerErr // The bytes references memory inside the ZIR module, which can get deallocated // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena. var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); + errdefer new_decl_arena.deinit(); const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes); const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0); @@ -737,6 +739,30 @@ fn analyzeInstAnyframeType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In return mod.constType(scope, inst.base.src, try mod.anyframeType(scope, return_type)); } +fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) InnerError!*Inst { + // The bytes references memory inside the ZIR module, which can get deallocated + // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena. + var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); + errdefer new_decl_arena.deinit(); + + const payload = try scope.arena().create(Value.Payload.ErrorSet); + payload.* = .{ .fields = .{} }; + try payload.fields.ensureCapacity(&new_decl_arena.allocator, inst.positionals.fields.len); + + for (inst.positionals.fields) |field_name| { + const value = try mod.getErrorValue(field_name); + if (payload.fields.fetchPutAssumeCapacity(field_name, value)) |prev| { + return mod.fail(scope, inst.base.src, "duplicate error: '{}'", .{field_name}); + } + } + // TODO create name in format "error:line:column" + const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{ + .ty = Type.initTag(.type), + .val = Value.initPayload(&payload.base), + }); + return mod.analyzeDeclRef(scope, inst.base.src, new_decl); +} + fn analyzeInstMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { return mod.fail(scope, inst.base.src, "TODO implement merge_error_sets", .{}); } @@ -1377,7 +1403,7 @@ fn analyzeInstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.PtrType) Inne if (host_size != 0 and bit_offset >= host_size * 8) return mod.fail(scope, inst.base.src, "bit offset starts after end of host integer", .{}); - + const sentinel = if (inst.kw_args.sentinel) |some| (try resolveInstConst(mod, scope, some)).val else From 1520e084cb5fee08ddaf670046c38fd34d3d2cff Mon Sep 17 00:00:00 2001 From: Vexu Date: Sun, 23 Aug 2020 20:19:05 +0300 Subject: [PATCH 4/7] stage2: implement accessing error values --- src-self-hosted/Module.zig | 11 +++++--- src-self-hosted/astgen.zig | 51 ++++++++++++++++++++---------------- src-self-hosted/value.zig | 28 ++++++++++++++++++-- src-self-hosted/zir_sema.zig | 39 ++++++++++++++++++++++++--- 4 files changed, 96 insertions(+), 33 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 9b3e72e7aa..f23e09d44c 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -2081,12 +2081,15 @@ fn createNewDecl( } /// Get error value for error tag `name`. -pub fn getErrorValue(self: *Module, name: []const u8) !u16 { +pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanaged(u16).Entry { const new_val = @intCast(u16, self.global_error_set.items().len); - if (self.global_error_set.get(name)) |some| return some; + if (self.global_error_set.getEntry(name)) |some| return some.*; - try self.global_error_set.put(self.gpa, try self.gpa.dupe(u8, name), new_val); - return new_val; + const duped = try self.gpa.dupe(u8, name); + errdefer self.gpa.free(duped); + + try self.global_error_set.put(self.gpa, duped, new_val); + return self.global_error_set.getEntry(duped).?.*; } /// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites. diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index b77cf97dfc..cd5639d791 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -247,7 +247,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .Return => return ret(mod, scope, node.castTag(.Return).?), .If => return ifExpr(mod, scope, rl, node.castTag(.If).?), .While => return whileExpr(mod, scope, rl, node.castTag(.While).?), - .Period => return rlWrap(mod, scope, rl, try field(mod, scope, node.castTag(.Period).?)), + .Period => return field(mod, scope, rl, node.castTag(.Period).?), .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)), .AddressOf => return rlWrap(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)), .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)), @@ -270,7 +270,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .ErrorUnion => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)), .MergeErrorSets => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)), .AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)), - .ErrorSetDecl => return rlWrap(mod, scope, rl, try errorSetDecl(mod, scope, node.castTag(.ErrorSetDecl).?)), + .ErrorSetDecl => return errorSetDecl(mod, scope, rl, node.castTag(.ErrorSetDecl).?), + .ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)), .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), .Catch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Catch", .{}), @@ -290,7 +291,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .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", .{}), - .ErrorType => return mod.failNode(scope, node, "TODO implement astgen.expr for .ErrorType", .{}), .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}), .ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}), .Comptime => return mod.failNode(scope, node, "TODO implement astgen.expr for .Comptime", .{}), @@ -722,13 +722,10 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si const src = tree.token_locs[node.rtoken].start; const operand = try expr(mod, scope, .ref, node.lhs); - const unwrapped_ptr = try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand); - if (rl == .lvalue or rl == .ref) return unwrapped_ptr; - - return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, unwrapped_ptr)); + return rlWrapPtr(mod, scope, rl, try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand)); } -fn errorSetDecl(mod: *Module, scope: *Scope, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst { +fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.error_token].start; const decls = node.decls(); @@ -739,7 +736,17 @@ fn errorSetDecl(mod: *Module, scope: *Scope, node: *ast.Node.ErrorSetDecl) Inner fields[i] = try identifierTokenString(mod, scope, tag.name_token); } - return addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{}); + // analyzing the error set results in a decl ref, so we might need to dereference it + return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{})); +} + +fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.token_locs[node.token].start; + return addZIRInstConst(mod, scope, src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.anyerror_type), + }); } /// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating. @@ -779,16 +786,16 @@ pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToke return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{}); } -fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst { - // TODO introduce lvalues +fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.op_token].start; - const lhs = try expr(mod, scope, .none, node.lhs); + 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 }, .{}); - return addZIRUnOp(mod, scope, src, .deref, pointer); + if (rl == .ref or rl == .lvalue) return pointer; + return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, pointer)); } fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst { @@ -1274,12 +1281,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo .local_ptr => { const local_ptr = s.cast(Scope.LocalPtr).?; if (mem.eql(u8, local_ptr.name, ident_name)) { - if (rl == .lvalue or rl == .ref) { - return local_ptr.ptr; - } else { - const result = try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr); - return rlWrap(mod, scope, rl, result); - } + return rlWrapPtr(mod, scope, rl, local_ptr.ptr); } s = local_ptr.parent; }, @@ -1289,10 +1291,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo } if (mod.lookupDeclName(scope, ident_name)) |decl| { - const result = try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}); - if (rl == .lvalue or rl == .ref) - return result; - return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, result)); + return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{})); } return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name}); @@ -1886,6 +1885,12 @@ fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, resul return rlWrap(mod, scope, rl, void_inst); } +fn rlWrapPtr(mod: *Module, scope: *Scope, rl: ResultLoc, ptr: *zir.Inst) InnerError!*zir.Inst { + if (rl == .lvalue or rl == .ref) return ptr; + + return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, ptr.src, .deref, ptr)); +} + pub fn addZIRInstSpecial( mod: *Module, scope: *Scope, diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index f7917b9d44..eacbd4e9ab 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -92,6 +92,7 @@ pub const Value = extern union { float_128, enum_literal, error_set, + @"error", pub const last_no_payload_tag = Tag.bool_false; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -244,9 +245,10 @@ pub const Value = extern union { }; return Value{ .ptr_otherwise = &new_payload.base }; }, + .@"error" => return self.copyPayloadShallow(allocator, Payload.Error), // memory is managed by the declaration - .error_set => return self, + .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), } } @@ -358,6 +360,7 @@ pub const Value = extern union { } return out_stream.writeAll("}"); }, + .@"error" => return out_stream.print("error.{}", .{val.cast(Payload.Error).?.name}), }; } @@ -424,6 +427,7 @@ pub const Value = extern union { .const_slice_u8_type => Type.initTag(.const_slice_u8), .enum_literal_type => Type.initTag(.enum_literal), .anyframe_type => Type.initTag(.@"anyframe"), + .error_set => @panic("TODO error set to type"), .undef, .zero, @@ -449,7 +453,7 @@ pub const Value = extern union { .float_64, .float_128, .enum_literal, - .error_set, + .@"error", => unreachable, }; } @@ -517,6 +521,7 @@ pub const Value = extern union { .empty_array, .enum_literal, .error_set, + .@"error", => unreachable, .undef => unreachable, @@ -597,6 +602,7 @@ pub const Value = extern union { .empty_array, .enum_literal, .error_set, + .@"error", => unreachable, .undef => unreachable, @@ -677,6 +683,7 @@ pub const Value = extern union { .empty_array, .enum_literal, .error_set, + .@"error", => unreachable, .undef => unreachable, @@ -784,6 +791,7 @@ pub const Value = extern union { .empty_array, .enum_literal, .error_set, + .@"error", => unreachable, .zero, @@ -868,6 +876,7 @@ pub const Value = extern union { .empty_array, .enum_literal, .error_set, + .@"error", => unreachable, .zero, @@ -1036,6 +1045,7 @@ pub const Value = extern union { .unreachable_value, .enum_literal, .error_set, + .@"error", => unreachable, .zero => false, @@ -1107,6 +1117,7 @@ pub const Value = extern union { .empty_array, .enum_literal, .error_set, + .@"error", => unreachable, .zero, @@ -1251,6 +1262,7 @@ pub const Value = extern union { .empty_array, .enum_literal, .error_set, + .@"error", => unreachable, .ref_val => self.cast(Payload.RefVal).?.val, @@ -1332,6 +1344,7 @@ pub const Value = extern union { .unreachable_value, .enum_literal, .error_set, + .@"error", => unreachable, .empty_array => unreachable, // out of bounds array index @@ -1430,6 +1443,7 @@ pub const Value = extern union { .void_value, .enum_literal, .error_set, + .@"error", => false, .undef => unreachable, @@ -1566,6 +1580,16 @@ pub const Value = extern union { // TODO revisit this when we have the concept of the error tag type fields: std.StringHashMapUnmanaged(u16), }; + + pub const Error = struct { + base: Payload = .{ .tag = .@"error" }, + + // TODO revisit this when we have the concept of the error tag type + /// `name` is owned by `Module` and will be valid for the entire + /// duration of the compilation. + name: []const u8, + value: u16, + }; }; /// Big enough to fit any non-BigInt value diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index 6b30d58fdb..acf5ca641f 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -740,8 +740,7 @@ fn analyzeInstAnyframeType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In } fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) InnerError!*Inst { - // The bytes references memory inside the ZIR module, which can get deallocated - // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena. + // The declarations arena will store the hashmap. var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); errdefer new_decl_arena.deinit(); @@ -750,8 +749,8 @@ fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) In try payload.fields.ensureCapacity(&new_decl_arena.allocator, inst.positionals.fields.len); for (inst.positionals.fields) |field_name| { - const value = try mod.getErrorValue(field_name); - if (payload.fields.fetchPutAssumeCapacity(field_name, value)) |prev| { + const entry = try mod.getErrorValue(field_name); + if (payload.fields.fetchPutAssumeCapacity(entry.key, entry.value)) |prev| { return mod.fail(scope, inst.base.src, "duplicate error: '{}'", .{field_name}); } } @@ -909,6 +908,38 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr ); } }, + .Type => { + _ = try mod.resolveConstValue(scope, object_ptr); + const result = try mod.analyzeDeref(scope, fieldptr.base.src, object_ptr, object_ptr.src); + const val = result.value().?; + const child_type = val.toType(); + switch (child_type.zigTypeTag()) { + .ErrorSet => { + // TODO resolve inferred error sets + const entry = if (val.cast(Value.Payload.ErrorSet)) |payload| + (payload.fields.getEntry(field_name) orelse + return mod.fail(scope, fieldptr.base.src, "no error named '{}' in '{}'", .{ field_name, child_type })).* + else + try mod.getErrorValue(field_name); + + const error_payload = try scope.arena().create(Value.Payload.Error); + error_payload.* = .{ + .name = entry.key, + .value = entry.value, + }; + + const ref_payload = try scope.arena().create(Value.Payload.RefVal); + ref_payload.* = .{ .val = Value.initPayload(&error_payload.base) }; + + // TODO if this is accessing the global error set create a `error{field_name}` type + return mod.constInst(scope, fieldptr.base.src, .{ + .ty = try mod.simplePtrType(scope, fieldptr.base.src, child_type, false, .One), + .val = Value.initPayload(&ref_payload.base), + }); + }, + else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{child_type}), + } + }, else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}), } } From bc1d55a1d19e1b4ad3826d58127396f27e96242c Mon Sep 17 00:00:00 2001 From: Vexu Date: Mon, 24 Aug 2020 16:05:34 +0300 Subject: [PATCH 5/7] stage2: fix field access of array pointers --- src-self-hosted/zir_sema.zig | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index acf5ca641f..85e6a764fb 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -908,6 +908,33 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr ); } }, + .Pointer => { + const ptr_child = elem_ty.elemType(); + switch (ptr_child.zigTypeTag()) { + .Array => { + if (mem.eql(u8, field_name, "len")) { + const len_payload = try scope.arena().create(Value.Payload.Int_u64); + len_payload.* = .{ .int = ptr_child.arrayLen() }; + + const ref_payload = try scope.arena().create(Value.Payload.RefVal); + ref_payload.* = .{ .val = Value.initPayload(&len_payload.base) }; + + return mod.constInst(scope, fieldptr.base.src, .{ + .ty = Type.initTag(.single_const_pointer_to_comptime_int), + .val = Value.initPayload(&ref_payload.base), + }); + } else { + return mod.fail( + scope, + fieldptr.positionals.field_name.src, + "no member named '{}' in '{}'", + .{ field_name, elem_ty }, + ); + } + }, + else => {}, + } + }, .Type => { _ = try mod.resolveConstValue(scope, object_ptr); const result = try mod.analyzeDeref(scope, fieldptr.base.src, object_ptr, object_ptr.src); @@ -940,8 +967,9 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{child_type}), } }, - else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}), + else => {}, } + return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty}); } fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { From 5de9aac7491958806164f46c356fce983b1c8624 Mon Sep 17 00:00:00 2001 From: Vexu Date: Mon, 24 Aug 2020 16:24:23 +0300 Subject: [PATCH 6/7] stage2: error set types --- src-self-hosted/type.zig | 82 ++++++++++++++++++++++++++++++++++-- src-self-hosted/value.zig | 25 +++++++++-- src-self-hosted/zir.zig | 2 +- src-self-hosted/zir_sema.zig | 26 ++++++++---- 4 files changed, 118 insertions(+), 17 deletions(-) diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 37f9f0c638..eb8fa2acd7 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -3,6 +3,7 @@ const Value = @import("value.zig").Value; const assert = std.debug.assert; const Allocator = std.mem.Allocator; const Target = std.Target; +const Module = @import("Module.zig"); /// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication. /// It's important for this type to be small. @@ -52,7 +53,7 @@ pub const Type = extern union { .bool => return .Bool, .void => return .Void, .type => return .Type, - .anyerror => return .ErrorSet, + .error_set, .error_set_single, .anyerror => return .ErrorSet, .comptime_int => return .ComptimeInt, .comptime_float => return .ComptimeFloat, .noreturn => return .NoReturn, @@ -436,6 +437,8 @@ pub const Type = extern union { }; return Type{ .ptr_otherwise = &new_payload.base }; }, + .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), + .error_set_single => return self.copyPayloadShallow(allocator, Payload.ErrorSetSingle), } } @@ -657,6 +660,14 @@ pub const Type = extern union { ty = payload.payload; continue; }, + .error_set => { + const payload = @fieldParentPtr(Payload.ErrorSet, "base", ty.ptr_otherwise); + return out_stream.writeAll(std.mem.spanZ(payload.decl.name)); + }, + .error_set_single => { + const payload = @fieldParentPtr(Payload.ErrorSetSingle, "base", ty.ptr_otherwise); + return out_stream.print("error{{{}}}", .{payload.name}); + }, } unreachable; } @@ -753,6 +764,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => true, // TODO lazy types .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0, @@ -848,7 +861,11 @@ pub const Type = extern union { .f128 => return 16, .c_longdouble => return 16, - .anyerror_void_error_union, .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type + .error_set, + .error_set_single, + .anyerror_void_error_union, + .anyerror, + => return 2, // TODO revisit this when we have the concept of the error tag type .array, .array_sentinel => return self.elemType().abiAlignment(target), @@ -981,7 +998,11 @@ pub const Type = extern union { .f128 => return 16, .c_longdouble => return 16, - .anyerror_void_error_union, .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type + .error_set, + .error_set_single, + .anyerror_void_error_union, + .anyerror, + => return 2, // TODO revisit this when we have the concept of the error tag type .int_signed, .int_unsigned => { const bits: u16 = if (self.cast(Payload.IntSigned)) |pl| @@ -1084,6 +1105,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => false, .single_const_pointer, @@ -1156,6 +1179,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => false, .const_slice, @@ -1225,6 +1250,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => false, .single_const_pointer, @@ -1303,6 +1330,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => false, .pointer => { @@ -1418,6 +1447,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => unreachable, .array => self.cast(Payload.Array).?.elem_type, @@ -1543,6 +1574,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => unreachable, .array => self.cast(Payload.Array).?.len, @@ -1614,6 +1647,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => unreachable, .array, .array_u8 => return null, @@ -1683,6 +1718,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => false, .int_signed, @@ -1755,6 +1792,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => false, .int_unsigned, @@ -1817,6 +1856,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => unreachable, .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits }, @@ -1897,6 +1938,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => false, .usize, @@ -2006,6 +2049,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => unreachable, }; } @@ -2081,6 +2126,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => unreachable, } } @@ -2155,6 +2202,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => unreachable, } } @@ -2229,6 +2278,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => unreachable, }; } @@ -2300,6 +2351,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => unreachable, }; } @@ -2371,6 +2424,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => unreachable, }; } @@ -2442,6 +2497,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => false, }; } @@ -2497,6 +2554,8 @@ pub const Type = extern union { .anyframe_T, .@"anyframe", .error_union, + .error_set, + .error_set_single, => return null, .void => return Value.initTag(.void_value), @@ -2604,6 +2663,8 @@ pub const Type = extern union { .@"anyframe", .anyframe_T, .anyerror_void_error_union, + .error_set, + .error_set_single, => return false, .c_const_pointer, @@ -2687,6 +2748,8 @@ pub const Type = extern union { optional_single_const_pointer, error_union, anyframe_T, + error_set, + error_set_single, pub const last_no_payload_tag = Tag.const_slice_u8; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -2781,6 +2844,19 @@ pub const Type = extern union { return_type: Type, }; + + pub const ErrorSet = struct { + base: Payload = .{ .tag = .error_set }, + + decl: *Module.Decl, + }; + + pub const ErrorSetSingle = struct { + base: Payload = .{ .tag = .error_set_single }, + + /// memory is owned by `Module` + name: []const u8, + }; }; }; diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index eacbd4e9ab..f2e36b59a4 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -381,11 +381,9 @@ pub const Value = extern union { } /// Asserts that the value is representable as a type. - pub fn toType(self: Value) Type { + pub fn toType(self: Value, allocator: *Allocator) !Type { return switch (self.tag()) { .ty => self.cast(Payload.Ty).?.ty, - .int_type => @panic("TODO int type to type"), - .u8_type => Type.initTag(.u8), .i8_type => Type.initTag(.i8), .u16_type => Type.initTag(.u16), @@ -427,7 +425,25 @@ pub const Value = extern union { .const_slice_u8_type => Type.initTag(.const_slice_u8), .enum_literal_type => Type.initTag(.enum_literal), .anyframe_type => Type.initTag(.@"anyframe"), - .error_set => @panic("TODO error set to type"), + + .int_type => { + const payload = self.cast(Payload.IntType).?; + if (payload.signed) { + const new = try allocator.create(Type.Payload.IntSigned); + new.* = .{ .bits = payload.bits }; + return Type.initPayload(&new.base); + } else { + const new = try allocator.create(Type.Payload.IntUnsigned); + new.* = .{ .bits = payload.bits }; + return Type.initPayload(&new.base); + } + }, + .error_set => { + const payload = self.cast(Payload.ErrorSet).?; + const new = try allocator.create(Type.Payload.ErrorSet); + new.* = .{ .decl = payload.decl }; + return Type.initPayload(&new.base); + }, .undef, .zero, @@ -1579,6 +1595,7 @@ pub const Value = extern union { // TODO revisit this when we have the concept of the error tag type fields: std.StringHashMapUnmanaged(u16), + decl: *Module.Decl, }; pub const Error = struct { diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 079dc13c35..3557c88f4e 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -2016,7 +2016,7 @@ const EmitZIR = struct { return self.emitUnnamedDecl(&as_inst.base); }, .Type => { - const ty = typed_value.val.toType(); + const ty = try typed_value.val.toType(&self.arena.allocator); return self.emitType(src, ty); }, .Fn => { diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index 85e6a764fb..7106bda090 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -150,7 +150,7 @@ pub fn analyzeBodyValueAsType(mod: *Module, block_scope: *Scope.Block, body: zir for (block_scope.instructions.items) |inst| { if (inst.castTag(.ret)) |ret| { const val = try mod.resolveConstValue(&block_scope.base, ret.operand); - return val.toType(); + return val.toType(block_scope.base.arena()); } else { return mod.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{}); } @@ -275,7 +275,7 @@ fn resolveType(mod: *Module, scope: *Scope, old_inst: *zir.Inst) !Type { const wanted_type = Type.initTag(.@"type"); const coerced_inst = try mod.coerce(scope, wanted_type, new_inst); const val = try mod.resolveConstValue(scope, coerced_inst); - return val.toType(); + return val.toType(scope.arena()); } fn resolveInt(mod: *Module, scope: *Scope, old_inst: *zir.Inst, dest_type: Type) !u64 { @@ -745,7 +745,10 @@ fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) In errdefer new_decl_arena.deinit(); const payload = try scope.arena().create(Value.Payload.ErrorSet); - payload.* = .{ .fields = .{} }; + payload.* = .{ + .fields = .{}, + .decl = undefined, // populated below + }; try payload.fields.ensureCapacity(&new_decl_arena.allocator, inst.positionals.fields.len); for (inst.positionals.fields) |field_name| { @@ -759,6 +762,7 @@ fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) In .ty = Type.initTag(.type), .val = Value.initPayload(&payload.base), }); + payload.decl = new_decl; return mod.analyzeDeclRef(scope, inst.base.src, new_decl); } @@ -939,18 +943,17 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr _ = try mod.resolveConstValue(scope, object_ptr); const result = try mod.analyzeDeref(scope, fieldptr.base.src, object_ptr, object_ptr.src); const val = result.value().?; - const child_type = val.toType(); + const child_type = try val.toType(scope.arena()); switch (child_type.zigTypeTag()) { .ErrorSet => { // TODO resolve inferred error sets const entry = if (val.cast(Value.Payload.ErrorSet)) |payload| (payload.fields.getEntry(field_name) orelse return mod.fail(scope, fieldptr.base.src, "no error named '{}' in '{}'", .{ field_name, child_type })).* - else - try mod.getErrorValue(field_name); + else try mod.getErrorValue(field_name); const error_payload = try scope.arena().create(Value.Payload.Error); - error_payload.* = .{ + error_payload.* = .{ .name = entry.key, .value = entry.value, }; @@ -958,9 +961,14 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr const ref_payload = try scope.arena().create(Value.Payload.RefVal); ref_payload.* = .{ .val = Value.initPayload(&error_payload.base) }; - // TODO if this is accessing the global error set create a `error{field_name}` type + const result_type = if (child_type.tag() == .anyerror) blk: { + const result_payload = try scope.arena().create(Type.Payload.ErrorSetSingle); + result_payload.* = .{ .name = entry.key }; + break :blk Type.initPayload(&result_payload.base); + } else child_type; + return mod.constInst(scope, fieldptr.base.src, .{ - .ty = try mod.simplePtrType(scope, fieldptr.base.src, child_type, false, .One), + .ty = try mod.simplePtrType(scope, fieldptr.base.src, result_type, false, .One), .val = Value.initPayload(&ref_payload.base), }); }, From 16d54c70eb35b58e871c538bf172991aa81191fe Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 24 Aug 2020 15:41:59 -0700 Subject: [PATCH 7/7] stage2: getErrorValue takes advantage of HashMap getOrPut API --- src-self-hosted/Module.zig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index f23e09d44c..82029c1e9f 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -2082,14 +2082,14 @@ fn createNewDecl( /// Get error value for error tag `name`. pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanaged(u16).Entry { - const new_val = @intCast(u16, self.global_error_set.items().len); - if (self.global_error_set.getEntry(name)) |some| return some.*; + const gop = try self.global_error_set.getOrPut(self.gpa, name); + if (gop.found_existing) + return gop.entry.*; + errdefer self.global_error_set.removeAssertDiscard(name); - const duped = try self.gpa.dupe(u8, name); - errdefer self.gpa.free(duped); - - try self.global_error_set.put(self.gpa, duped, new_val); - return self.global_error_set.getEntry(duped).?.*; + gop.entry.key = try self.gpa.dupe(u8, name); + gop.entry.value = @intCast(u16, self.global_error_set.items().len - 1); + return gop.entry.*; } /// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites.