mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
stage2: implement accessing error values
This commit is contained in:
parent
e9b15ac9a0
commit
1520e084cb
@ -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.
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}),
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user