From 16d7db59ed9b0b1be6790bdb80777fbe5f80c7ed Mon Sep 17 00:00:00 2001 From: Vexu Date: Sat, 22 Aug 2020 16:30:57 +0300 Subject: stage2: anyframe and error union types --- src-self-hosted/Module.zig | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'src-self-hosted/Module.zig') 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"); -- cgit v1.2.3 From e9b15ac9a099fd17e6b68d0f04ec42a2dbfda0ca Mon Sep 17 00:00:00 2001 From: Vexu Date: Sun, 23 Aug 2020 15:50:36 +0300 Subject: 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(-) (limited to 'src-self-hosted/Module.zig') 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 -- cgit v1.2.3 From 1520e084cb5fee08ddaf670046c38fd34d3d2cff Mon Sep 17 00:00:00 2001 From: Vexu Date: Sun, 23 Aug 2020 20:19:05 +0300 Subject: 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(-) (limited to 'src-self-hosted/Module.zig') 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}), } } -- cgit v1.2.3 From 16d54c70eb35b58e871c538bf172991aa81191fe Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 24 Aug 2020 15:41:59 -0700 Subject: stage2: getErrorValue takes advantage of HashMap getOrPut API --- src-self-hosted/Module.zig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src-self-hosted/Module.zig') 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 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).?.*; + 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); + + 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. -- cgit v1.2.3