stage2: implement accessing error values

This commit is contained in:
Vexu 2020-08-23 20:19:05 +03:00 committed by Andrew Kelley
parent e9b15ac9a0
commit 1520e084cb
4 changed files with 96 additions and 33 deletions

View File

@ -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.

View File

@ -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,

View File

@ -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

View File

@ -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}),
}
}