mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
stage2: implement sema for @errorToInt and @intToError
This commit is contained in:
parent
f80f8a7a78
commit
0005b34637
@ -1237,6 +1237,8 @@ fn blockExprStmts(
|
||||
.bit_not,
|
||||
.error_set,
|
||||
.error_value,
|
||||
.error_to_int,
|
||||
.int_to_error,
|
||||
.slice_start,
|
||||
.slice_end,
|
||||
.slice_sentinel,
|
||||
@ -3370,6 +3372,16 @@ fn builtinCall(
|
||||
const result = try gz.addUnNode(.import, target, node);
|
||||
return rvalue(gz, scope, rl, result, node);
|
||||
},
|
||||
.error_to_int => {
|
||||
const target = try expr(gz, scope, .none, params[0]);
|
||||
const result = try gz.addUnNode(.error_to_int, target, node);
|
||||
return rvalue(gz, scope, rl, result, node);
|
||||
},
|
||||
.int_to_error => {
|
||||
const target = try expr(gz, scope, .{ .ty = .u16_type }, params[0]);
|
||||
const result = try gz.addUnNode(.int_to_error, target, node);
|
||||
return rvalue(gz, scope, rl, result, node);
|
||||
},
|
||||
.compile_error => {
|
||||
const target = try expr(gz, scope, .none, params[0]);
|
||||
const result = try gz.addUnNode(.compile_error, target, node);
|
||||
@ -3439,7 +3451,6 @@ fn builtinCall(
|
||||
.enum_to_int,
|
||||
.error_name,
|
||||
.error_return_trace,
|
||||
.error_to_int,
|
||||
.err_set_cast,
|
||||
.@"export",
|
||||
.fence,
|
||||
@ -3448,7 +3459,6 @@ fn builtinCall(
|
||||
.has_decl,
|
||||
.has_field,
|
||||
.int_to_enum,
|
||||
.int_to_error,
|
||||
.int_to_float,
|
||||
.int_to_ptr,
|
||||
.memcpy,
|
||||
|
||||
@ -941,6 +941,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
};
|
||||
|
||||
const module = try arena.create(Module);
|
||||
errdefer module.deinit();
|
||||
module.* = .{
|
||||
.gpa = gpa,
|
||||
.comp = comp,
|
||||
@ -948,7 +949,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
|
||||
.root_scope = root_scope,
|
||||
.zig_cache_artifact_directory = zig_cache_artifact_directory,
|
||||
.emit_h = options.emit_h,
|
||||
.error_name_list = try std.ArrayListUnmanaged([]const u8).initCapacity(gpa, 1),
|
||||
};
|
||||
module.error_name_list.appendAssumeCapacity("(no error)");
|
||||
break :blk module;
|
||||
} else blk: {
|
||||
if (options.emit_h != null) return error.NoZigModuleForCHeader;
|
||||
|
||||
@ -80,6 +80,9 @@ deletion_set: ArrayListUnmanaged(*Decl) = .{},
|
||||
/// Error tags and their values, tag names are duped with mod.gpa.
|
||||
global_error_set: std.StringHashMapUnmanaged(u16) = .{},
|
||||
|
||||
/// error u16 -> []const u8 for fast lookups for @intToError at comptime
|
||||
error_name_list: ArrayListUnmanaged([]const u8) = .{},
|
||||
|
||||
/// Keys are fully qualified paths
|
||||
import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{},
|
||||
|
||||
@ -1570,7 +1573,22 @@ pub const SrcLoc = struct {
|
||||
const token_starts = tree.tokens.items(.start);
|
||||
return token_starts[tok_index];
|
||||
},
|
||||
.node_offset_builtin_call_arg0 => @panic("TODO"),
|
||||
.node_offset_builtin_call_arg0 => |node_off| {
|
||||
const decl = src_loc.container.decl;
|
||||
const tree = decl.container.file_scope.base.tree();
|
||||
const node_datas = tree.nodes.items(.data);
|
||||
const node_tags = tree.nodes.items(.tag);
|
||||
const node = decl.relativeToNodeIndex(node_off);
|
||||
const param = switch (node_tags[node]) {
|
||||
.builtin_call_two, .builtin_call_two_comma => node_datas[node].lhs,
|
||||
.builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs],
|
||||
else => unreachable,
|
||||
};
|
||||
const main_tokens = tree.nodes.items(.main_token);
|
||||
const tok_index = main_tokens[param];
|
||||
const token_starts = tree.tokens.items(.start);
|
||||
return token_starts[tok_index];
|
||||
},
|
||||
.node_offset_builtin_call_arg1 => @panic("TODO"),
|
||||
.node_offset_builtin_call_argn => unreachable, // Handled specially in `Sema`.
|
||||
.node_offset_array_access_index => @panic("TODO"),
|
||||
@ -1893,6 +1911,8 @@ pub fn deinit(mod: *Module) void {
|
||||
}
|
||||
mod.global_error_set.deinit(gpa);
|
||||
|
||||
mod.error_name_list.deinit(gpa);
|
||||
|
||||
for (mod.import_table.items()) |entry| {
|
||||
entry.value.destroy(gpa);
|
||||
}
|
||||
@ -3346,10 +3366,12 @@ pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged
|
||||
const gop = try mod.global_error_set.getOrPut(mod.gpa, name);
|
||||
if (gop.found_existing)
|
||||
return gop.entry.*;
|
||||
errdefer mod.global_error_set.removeAssertDiscard(name);
|
||||
|
||||
errdefer mod.global_error_set.removeAssertDiscard(name);
|
||||
try mod.error_name_list.ensureCapacity(mod.gpa, mod.error_name_list.items.len + 1);
|
||||
gop.entry.key = try mod.gpa.dupe(u8, name);
|
||||
gop.entry.value = @intCast(u16, mod.global_error_set.count() - 1);
|
||||
gop.entry.value = @intCast(u16, mod.error_name_list.items.len);
|
||||
mod.error_name_list.appendAssumeCapacity(gop.entry.key);
|
||||
return gop.entry.*;
|
||||
}
|
||||
|
||||
|
||||
62
src/Sema.zig
62
src/Sema.zig
@ -177,6 +177,8 @@ pub fn analyzeBody(
|
||||
.error_set => try sema.zirErrorSet(block, inst),
|
||||
.error_union_type => try sema.zirErrorUnionType(block, inst),
|
||||
.error_value => try sema.zirErrorValue(block, inst),
|
||||
.error_to_int => try sema.zirErrorToInt(block, inst),
|
||||
.int_to_error => try sema.zirIntToError(block, inst),
|
||||
.field_ptr => try sema.zirFieldPtr(block, inst),
|
||||
.field_ptr_named => try sema.zirFieldPtrNamed(block, inst),
|
||||
.field_val => try sema.zirFieldVal(block, inst),
|
||||
@ -1460,6 +1462,65 @@ fn zirErrorValue(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
|
||||
});
|
||||
}
|
||||
|
||||
fn zirErrorToInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const src = inst_data.src();
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
|
||||
const op = try sema.resolveInst(inst_data.operand);
|
||||
const op_coerced = try sema.coerce(block, Type.initTag(.anyerror), op, operand_src);
|
||||
|
||||
if (op_coerced.value()) |val| {
|
||||
const payload = try sema.arena.create(Value.Payload.U64);
|
||||
payload.* = .{
|
||||
.base = .{ .tag = .int_u64 },
|
||||
.data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,
|
||||
};
|
||||
return sema.mod.constInst(sema.arena, src, .{
|
||||
.ty = Type.initTag(.u16),
|
||||
.val = Value.initPayload(&payload.base),
|
||||
});
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addUnOp(src, Type.initTag(.u16), .error_to_int, op_coerced);
|
||||
}
|
||||
|
||||
fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const src = inst_data.src();
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
|
||||
|
||||
const op = try sema.resolveInst(inst_data.operand);
|
||||
|
||||
if (try sema.resolveDefinedValue(block, operand_src, op)) |value| {
|
||||
const int = value.toUnsignedInt();
|
||||
if (int > sema.mod.global_error_set.count() or int == 0)
|
||||
return sema.mod.fail(&block.base, operand_src, "integer value {d} represents no error", .{int});
|
||||
const payload = try sema.arena.create(Value.Payload.Error);
|
||||
payload.* = .{
|
||||
.base = .{ .tag = .@"error" },
|
||||
.data = .{ .name = sema.mod.error_name_list.items[int] },
|
||||
};
|
||||
return sema.mod.constInst(sema.arena, src, .{
|
||||
.ty = Type.initTag(.anyerror),
|
||||
.val = Value.initPayload(&payload.base),
|
||||
});
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
if (block.wantSafety()) {
|
||||
return sema.mod.fail(&block.base, src, "TODO: get max errors in compilation", .{});
|
||||
// const is_gt_max = @panic("TODO get max errors in compilation");
|
||||
// try sema.addSafetyCheck(block, is_gt_max, .invalid_error_code);
|
||||
}
|
||||
return block.addUnOp(src, Type.initTag(.anyerror), .int_to_error, op);
|
||||
}
|
||||
|
||||
fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
@ -3242,6 +3303,7 @@ pub const PanicId = enum {
|
||||
unreach,
|
||||
unwrap_null,
|
||||
unwrap_errunion,
|
||||
invalid_error_code,
|
||||
};
|
||||
|
||||
fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
|
||||
|
||||
@ -898,6 +898,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?),
|
||||
.is_err => return self.genIsErr(inst.castTag(.is_err).?),
|
||||
.is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?),
|
||||
.error_to_int => return self.genErrorToInt(inst.castTag(.error_to_int).?),
|
||||
.int_to_error => return self.genIntToError(inst.castTag(.int_to_error).?),
|
||||
.load => return self.genLoad(inst.castTag(.load).?),
|
||||
.loop => return self.genLoop(inst.castTag(.loop).?),
|
||||
.not => return self.genNot(inst.castTag(.not).?),
|
||||
@ -2557,6 +2559,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{});
|
||||
}
|
||||
|
||||
fn genErrorToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
|
||||
return self.resolveInst(inst.operand);
|
||||
}
|
||||
|
||||
fn genIntToError(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
|
||||
return self.resolveInst(inst.operand);
|
||||
}
|
||||
|
||||
fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {
|
||||
// A loop is a setup to be able to jump back to the beginning.
|
||||
const start_index = self.code.items.len;
|
||||
|
||||
@ -569,6 +569,8 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
|
||||
.optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?),
|
||||
.is_err => try genIsErr(o, inst.castTag(.is_err).?),
|
||||
.is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?),
|
||||
.error_to_int => try genErrorToInt(o, inst.castTag(.error_to_int).?),
|
||||
.int_to_error => try genIntToError(o, inst.castTag(.int_to_error).?),
|
||||
.unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),
|
||||
.unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),
|
||||
.unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?),
|
||||
@ -1072,6 +1074,14 @@ fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue {
|
||||
return local;
|
||||
}
|
||||
|
||||
fn genIntToError(o: *Object, inst: *Inst.UnOp) !CValue {
|
||||
return o.resolveInst(inst.operand);
|
||||
}
|
||||
|
||||
fn genErrorToInt(o: *Object, inst: *Inst.UnOp) !CValue {
|
||||
return o.resolveInst(inst.operand);
|
||||
}
|
||||
|
||||
fn IndentWriter(comptime UnderlyingWriter: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
10
src/ir.zig
10
src/ir.zig
@ -92,6 +92,10 @@ pub const Inst = struct {
|
||||
is_err,
|
||||
/// *E!T => bool
|
||||
is_err_ptr,
|
||||
/// E => u16
|
||||
error_to_int,
|
||||
/// u16 => E
|
||||
int_to_error,
|
||||
bool_and,
|
||||
bool_or,
|
||||
/// Read a value from a pointer.
|
||||
@ -152,6 +156,8 @@ pub const Inst = struct {
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_err_ptr,
|
||||
.int_to_error,
|
||||
.error_to_int,
|
||||
.ptrtoint,
|
||||
.floatcast,
|
||||
.intcast,
|
||||
@ -696,6 +702,8 @@ const DumpTzir = struct {
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_err_ptr,
|
||||
.error_to_int,
|
||||
.int_to_error,
|
||||
.ptrtoint,
|
||||
.floatcast,
|
||||
.intcast,
|
||||
@ -817,6 +825,8 @@ const DumpTzir = struct {
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_err_ptr,
|
||||
.error_to_int,
|
||||
.int_to_error,
|
||||
.ptrtoint,
|
||||
.floatcast,
|
||||
.intcast,
|
||||
|
||||
@ -185,8 +185,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
|
||||
if (module.global_error_set.size == 0) break :render_errors;
|
||||
var it = module.global_error_set.iterator();
|
||||
while (it.next()) |entry| {
|
||||
// + 1 because 0 represents no error
|
||||
try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value + 1 });
|
||||
try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value });
|
||||
}
|
||||
try err_typedef_writer.writeByte('\n');
|
||||
}
|
||||
|
||||
@ -365,6 +365,10 @@ pub const Inst = struct {
|
||||
/// Make an integer type out of signedness and bit count.
|
||||
/// Payload is `int_type`
|
||||
int_type,
|
||||
/// Convert an error type to `u16`
|
||||
error_to_int,
|
||||
/// Convert a `u16` to `anyerror`
|
||||
int_to_error,
|
||||
/// Return a boolean false if an optional is null. `x != null`
|
||||
/// Uses the `un_node` field.
|
||||
is_non_null,
|
||||
@ -728,6 +732,8 @@ pub const Inst = struct {
|
||||
.err_union_payload_unsafe_ptr,
|
||||
.err_union_code,
|
||||
.err_union_code_ptr,
|
||||
.error_to_int,
|
||||
.int_to_error,
|
||||
.ptr_type,
|
||||
.ptr_type_simple,
|
||||
.ensure_err_payload_void,
|
||||
@ -1414,6 +1420,8 @@ const Writer = struct {
|
||||
.err_union_payload_unsafe_ptr,
|
||||
.err_union_code,
|
||||
.err_union_code_ptr,
|
||||
.int_to_error,
|
||||
.error_to_int,
|
||||
.is_non_null,
|
||||
.is_null,
|
||||
.is_non_null_ptr,
|
||||
|
||||
@ -54,6 +54,42 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
, "Hello, world!" ++ std.cstr.line_sep);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exeFromCompiledC("@intToError", .{});
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub export fn main() c_int {
|
||||
\\ // comptime checks
|
||||
\\ const a = error.A;
|
||||
\\ const b = error.B;
|
||||
\\ const c = @intToError(2);
|
||||
\\ const d = @intToError(1);
|
||||
\\ if (!(c == b)) unreachable;
|
||||
\\ if (!(a == d)) unreachable;
|
||||
\\ // runtime checks
|
||||
\\ var x = error.A;
|
||||
\\ var y = error.B;
|
||||
\\ var z = @intToError(2);
|
||||
\\ var f = @intToError(1);
|
||||
\\ if (!(y == z)) unreachable;
|
||||
\\ if (!(x == f)) unreachable;
|
||||
\\ return 0;
|
||||
\\}
|
||||
, "");
|
||||
case.addError(
|
||||
\\pub export fn main() c_int {
|
||||
\\ const c = @intToError(0);
|
||||
\\ return 0;
|
||||
\\}
|
||||
, &.{":2:27: error: integer value 0 represents no error"});
|
||||
case.addError(
|
||||
\\pub export fn main() c_int {
|
||||
\\ const c = @intToError(3);
|
||||
\\ return 0;
|
||||
\\}
|
||||
, &.{":2:27: error: integer value 3 represents no error"});
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user