From 1520e084cb5fee08ddaf670046c38fd34d3d2cff Mon Sep 17 00:00:00 2001 From: Vexu Date: Sun, 23 Aug 2020 20:19:05 +0300 Subject: [PATCH] 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}), } }