InternPool: replace untyped values with typed values (#1023)
* InternPool: replace untyped values with typed values * InternPool: remove `indexToTag` * InternPool: rework representation of optional values * add representation for unknown values and types * ComptimeInterpreter: use InternPool typed-values * ComptimeInterpreter: field access test * ComptimeInterpreter: improve handling of if expressions * InternPool: fix typeOf on a comptime float * ComptimeInterpreter: implement TypeOf with multiple parameters
This commit is contained in:
@@ -14,39 +14,50 @@ const offsets = zls.offsets;
|
||||
const allocator: std.mem.Allocator = std.testing.allocator;
|
||||
|
||||
test "ComptimeInterpreter - primitive types" {
|
||||
try testExpr("true", .{ .simple_type = .bool }, .{ .simple_value = .bool_true });
|
||||
try testExpr("false", .{ .simple_type = .bool }, .{ .simple_value = .bool_false });
|
||||
try testExpr("5", .{ .simple_type = .comptime_int }, .{ .int_u64_value = 5 });
|
||||
// TODO try testExpr("-2", .{ .simple_type = .comptime_int }, .{ .int_i64_value = -2 });
|
||||
try testExpr("3.0", .{ .simple_type = .comptime_float }, null);
|
||||
try testExpr("true", .{ .simple_value = .bool_true });
|
||||
try testExpr("false", .{ .simple_value = .bool_false });
|
||||
try testExpr("5", .{ .int_u64_value = .{ .ty = .comptime_int_type, .int = 5 } });
|
||||
// TODO try testExpr("-2", .{ .int_i64_value = .{ .ty = .comptime_int, .int = -2 } });
|
||||
try testExpr("3.0", .{ .float_comptime_value = 3.0 });
|
||||
|
||||
try testExpr("null", .{ .simple_type = .null_type }, .{ .simple_value = .null_value });
|
||||
try testExpr("void", .{ .simple_type = .type }, .{ .simple_type = .void });
|
||||
try testExpr("undefined", .{ .simple_type = .undefined_type }, .{ .simple_value = .undefined_value });
|
||||
try testExpr("noreturn", .{ .simple_type = .type }, .{ .simple_type = .noreturn });
|
||||
try testExpr("null", .{ .simple_value = .null_value });
|
||||
try testExpr("void", .{ .simple_type = .void });
|
||||
try testExpr("undefined", .{ .simple_value = .undefined_value });
|
||||
try testExpr("noreturn", .{ .simple_type = .noreturn });
|
||||
}
|
||||
|
||||
test "ComptimeInterpreter - expressions" {
|
||||
if (true) return error.SkipZigTest; // TODO
|
||||
try testExpr("5 + 3", .{ .simple_type = .comptime_int }, .{ .int_u64_value = 8 });
|
||||
try testExpr("5.2 + 4.2", .{ .simple_type = .comptime_float }, null);
|
||||
try testExpr("5 + 3", .{ .int_u64_value = .{ .ty = .comptime_int_type, .int = 8 } });
|
||||
// try testExpr("5.2 + 4.2", .{ .simple_type = .comptime_float }, null);
|
||||
|
||||
try testExpr("3 == 3", .{ .simple_type = .bool }, .{ .simple_valueclear = .bool_true });
|
||||
try testExpr("5.2 == 2.1", .{ .simple_type = .bool }, .{ .simple_value = .bool_false });
|
||||
try testExpr("3 == 3", .{ .simple_valueclear = .bool_true });
|
||||
try testExpr("5.2 == 2.1", .{ .simple_value = .bool_false });
|
||||
|
||||
try testExpr("@as(?bool, null) orelse true", .{ .simple_type = .bool }, .{ .simple_value = .bool_true });
|
||||
try testExpr("@as(?bool, null) orelse true", .{ .simple_value = .bool_true });
|
||||
}
|
||||
|
||||
test "ComptimeInterpreter - builtins" {
|
||||
if (true) return error.SkipZigTest; // TODO
|
||||
try testExpr("@as(bool, true)", .{ .simple_type = .bool }, .{ .simple_value = .bool_true });
|
||||
try testExpr("@as(u32, 3)", .{ .int_type = .{
|
||||
.signedness = .unsigned,
|
||||
.bits = 32,
|
||||
} }, .{ .int_u64_value = 3 });
|
||||
try testExpr("@as(bool, true)", .{ .simple_value = .bool_true });
|
||||
try testExpr("@as(u32, 3)", .{ .int_u64_value = .{ .ty = .u32_type, .int = 3 } });
|
||||
}
|
||||
|
||||
test "ComptimeInterpreter - @TypeOf" {
|
||||
try testExpr("@TypeOf(bool)", .{ .simple_type = .type });
|
||||
try testExpr("@TypeOf(5)", .{ .simple_type = .comptime_int });
|
||||
try testExpr("@TypeOf(3.14)", .{ .simple_type = .comptime_float });
|
||||
|
||||
try testExpr("@TypeOf(bool, u32)", .{ .simple_type = .type });
|
||||
try testExpr("@TypeOf(true, false)", .{ .simple_type = .bool });
|
||||
try testExpr("@TypeOf(3, 2)", .{ .simple_type = .comptime_int });
|
||||
try testExpr("@TypeOf(3.14, 2)", .{ .simple_type = .comptime_float });
|
||||
|
||||
try testExpr("@TypeOf(null, 2)", .{ .optional_type = .{ .payload_type = .comptime_int_type } });
|
||||
}
|
||||
|
||||
test "ComptimeInterpreter - string literal" {
|
||||
if (true) return error.SkipZigTest; // TODO
|
||||
var context = try Context.init(
|
||||
\\const foobarbaz = "hello world!";
|
||||
\\
|
||||
@@ -64,12 +75,12 @@ test "ComptimeInterpreter - labeled block" {
|
||||
\\blk: {
|
||||
\\ break :blk true;
|
||||
\\}
|
||||
, .{ .simple_type = .bool }, .{ .simple_value = .bool_true });
|
||||
, .{ .simple_value = .bool_true });
|
||||
try testExpr(
|
||||
\\blk: {
|
||||
\\ break :blk 3;
|
||||
\\}
|
||||
, .{ .simple_type = .comptime_int }, .{ .int_u64_value = 3 });
|
||||
, .{ .int_u64_value = .{ .ty = .comptime_int_type, .int = 3 } });
|
||||
}
|
||||
|
||||
test "ComptimeInterpreter - if" {
|
||||
@@ -77,18 +88,18 @@ test "ComptimeInterpreter - if" {
|
||||
\\blk: {
|
||||
\\ break :blk if (true) true else false;
|
||||
\\}
|
||||
, .{ .simple_type = .bool }, .{ .simple_value = .bool_true });
|
||||
, .{ .simple_value = .bool_true });
|
||||
try testExpr(
|
||||
\\blk: {
|
||||
\\ break :blk if (false) true else false;
|
||||
\\}
|
||||
, .{ .simple_type = .bool }, .{ .simple_value = .bool_false });
|
||||
, .{ .simple_value = .bool_false });
|
||||
try testExpr(
|
||||
\\blk: {
|
||||
\\ if (false) break :blk true;
|
||||
\\ break :blk false;
|
||||
\\}
|
||||
, .{ .simple_type = .bool }, .{ .simple_value = .bool_false });
|
||||
, .{ .simple_value = .bool_false });
|
||||
// TODO
|
||||
// try testExpr(
|
||||
// \\outer: {
|
||||
@@ -97,7 +108,7 @@ test "ComptimeInterpreter - if" {
|
||||
// \\ }) break :outer true;
|
||||
// \\ break :outer false;
|
||||
// \\}
|
||||
// , .{ .simple_type = .bool }, .{ .simple_value = .bool_true });
|
||||
// , .{ .simple_value = .bool_true });
|
||||
}
|
||||
|
||||
test "ComptimeInterpreter - variable lookup" {
|
||||
@@ -106,7 +117,7 @@ test "ComptimeInterpreter - variable lookup" {
|
||||
\\ var foo = 42;
|
||||
\\ break :blk foo;
|
||||
\\}
|
||||
, .{ .simple_type = .comptime_int }, .{ .int_u64_value = 42 });
|
||||
, .{ .int_u64_value = .{ .ty = .comptime_int_type, .int = 42 } });
|
||||
try testExpr(
|
||||
\\blk: {
|
||||
\\ var foo = 1;
|
||||
@@ -114,7 +125,7 @@ test "ComptimeInterpreter - variable lookup" {
|
||||
\\ var baz = 3;
|
||||
\\ break :blk bar;
|
||||
\\}
|
||||
, .{ .simple_type = .comptime_int }, .{ .int_u64_value = 2 });
|
||||
, .{ .int_u64_value = .{ .ty = .comptime_int_type, .int = 2 } });
|
||||
|
||||
var context = try Context.init(
|
||||
\\const bar = foo;
|
||||
@@ -123,26 +134,25 @@ test "ComptimeInterpreter - variable lookup" {
|
||||
defer context.deinit();
|
||||
|
||||
const result = try context.interpret(context.findVar("bar"));
|
||||
try expectEqualKey(context.interpreter.ip, .{ .simple_type = .comptime_int }, result.ty);
|
||||
try expectEqualKey(context.interpreter.ip, .{ .int_u64_value = 3 }, result.val);
|
||||
try expectEqualKey(context.interpreter.ip, .{ .int_u64_value = .{ .ty = .comptime_int_type, .int = 3 } }, result.val);
|
||||
}
|
||||
|
||||
test "ComptimeInterpreter - field access" {
|
||||
try testExpr(
|
||||
\\blk: {
|
||||
\\ const foo: struct {alpha: u64, beta: bool} = undefined;
|
||||
\\ break :blk foo.beta;
|
||||
\\ break :blk @TypeOf(foo.beta);
|
||||
\\}
|
||||
, .{ .simple_type = .bool }, null);
|
||||
, .{ .simple_type = .bool });
|
||||
try testExpr(
|
||||
\\blk: {
|
||||
\\ const foo: struct {alpha: u64, beta: bool} = undefined;
|
||||
\\ break :blk foo.alpha;
|
||||
\\ break :blk @TypeOf(foo.alpha);
|
||||
\\}
|
||||
, .{ .int_type = .{
|
||||
.signedness = .unsigned,
|
||||
.bits = 64,
|
||||
} }, null);
|
||||
} });
|
||||
}
|
||||
|
||||
test "ComptimeInterpreter - optional operations" {
|
||||
@@ -152,13 +162,13 @@ test "ComptimeInterpreter - optional operations" {
|
||||
\\ const foo: ?bool = true;
|
||||
\\ break :blk foo.?;
|
||||
\\}
|
||||
, .{ .simple_type = .bool }, .{ .simple_value = .bool_true });
|
||||
, .{ .simple_value = .bool_true });
|
||||
try testExpr(
|
||||
\\blk: {
|
||||
\\ const foo: ?bool = true;
|
||||
\\ break :blk foo == null;
|
||||
\\}
|
||||
, .{ .simple_type = .bool }, .{ .simple_value = .bool_false });
|
||||
, .{ .simple_value = .bool_false });
|
||||
}
|
||||
|
||||
test "ComptimeInterpreter - pointer operations" {
|
||||
@@ -168,20 +178,20 @@ test "ComptimeInterpreter - pointer operations" {
|
||||
\\ const foo: []const u8 = "";
|
||||
\\ break :blk foo.len;
|
||||
\\}
|
||||
, .{ .simple_type = .usize }, .{ .bytes = "" });
|
||||
, .{ .int_u64_value = .{ .ty = .usize_type, .int = 0 } });
|
||||
try testExpr(
|
||||
\\blk: {
|
||||
\\ const foo = true;
|
||||
\\ break :blk &foo;
|
||||
\\}
|
||||
, @panic("TODO"), .{ .simple_value = .bool_true });
|
||||
, .{ .simple_value = .bool_true });
|
||||
try testExpr(
|
||||
\\blk: {
|
||||
\\ const foo = true;
|
||||
\\ const bar = &foo;
|
||||
\\ break :blk bar.*;
|
||||
\\}
|
||||
, @panic("TODO"), .{ .simple_value = .bool_true });
|
||||
, .{ .simple_value = .bool_true });
|
||||
}
|
||||
|
||||
test "ComptimeInterpreter - call return primitive type" {
|
||||
@@ -357,20 +367,24 @@ const Context = struct {
|
||||
args[i] = .{
|
||||
.interpreter = self.interpreter,
|
||||
.node_idx = 0,
|
||||
.ty = try self.interpreter.ip.get(self.interpreter.allocator, argument.ty),
|
||||
.val = if (argument.val) |val| try self.interpreter.ip.get(self.interpreter.allocator, val) else .none,
|
||||
.index = if (argument.val) |val|
|
||||
try self.interpreter.ip.get(self.interpreter.allocator, val)
|
||||
else
|
||||
try self.interpreter.ip.get(self.interpreter.allocator, .{
|
||||
.unknown_value = .{ .ty = try self.interpreter.ip.get(self.interpreter.allocator, argument.ty) },
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
const namespace = @intToEnum(ComptimeInterpreter.Namespace.Index, 0); // root namespace
|
||||
const result = (try self.interpreter.call(namespace, func_node, args, .{})).result;
|
||||
|
||||
try std.testing.expect(result == .value);
|
||||
try std.testing.expect(result.value.ty != .none);
|
||||
const val = self.interpreter.ip.indexToKey(result.value.index);
|
||||
const ty = self.interpreter.ip.indexToKey(val.typeOf());
|
||||
|
||||
return KV{
|
||||
.ty = self.interpreter.ip.indexToKey(result.value.ty),
|
||||
.val = if (result.value.val == .none) null else self.interpreter.ip.indexToKey(result.value.val),
|
||||
.ty = ty,
|
||||
.val = val,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -378,11 +392,12 @@ const Context = struct {
|
||||
const namespace = @intToEnum(ComptimeInterpreter.Namespace.Index, 0); // root namespace
|
||||
const result = try (try self.interpreter.interpret(node, namespace, .{})).getValue();
|
||||
|
||||
try std.testing.expect(result.ty != .none);
|
||||
const val = self.interpreter.ip.indexToKey(result.index);
|
||||
const ty = self.interpreter.ip.indexToKey(val.typeOf());
|
||||
|
||||
return KV{
|
||||
.ty = self.interpreter.ip.indexToKey(result.ty),
|
||||
.val = if (result.val == .none) null else self.interpreter.ip.indexToKey(result.val),
|
||||
.ty = ty,
|
||||
.val = val,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -428,8 +443,7 @@ fn testCall(
|
||||
|
||||
fn testExpr(
|
||||
expr: []const u8,
|
||||
expected_ty: Key,
|
||||
expected_val: ?Key,
|
||||
expected: Key,
|
||||
) !void {
|
||||
const source = try std.fmt.allocPrint(allocator,
|
||||
\\const foobarbaz = {s};
|
||||
@@ -441,34 +455,19 @@ fn testExpr(
|
||||
|
||||
const result = try context.interpret(context.findVar("foobarbaz"));
|
||||
|
||||
try expectEqualKey(context.interpreter.ip, expected_ty, result.ty);
|
||||
if (expected_val) |expected| {
|
||||
try expectEqualKey(context.interpreter.ip, expected, result.val);
|
||||
}
|
||||
try expectEqualKey(context.interpreter.ip, expected, result.val);
|
||||
}
|
||||
|
||||
/// TODO refactor this code
|
||||
fn expectEqualKey(ip: InternPool, expected: Key, actual: ?Key) !void {
|
||||
if (actual) |actual_key| {
|
||||
if (expected.eql(actual_key)) return;
|
||||
|
||||
if (expected.isType() and actual_key.isType()) {
|
||||
std.debug.print("expected type `{}`, found type `{}`\n", .{ expected.fmtType(ip), actual_key.fmtType(ip) });
|
||||
} else if (expected.isType()) {
|
||||
std.debug.print("expected type `{}`, found value ({})\n", .{ expected.fmtType(ip), actual_key });
|
||||
} else if (actual_key.isType()) {
|
||||
std.debug.print("expected value ({}), found type `{}`\n", .{ expected, actual_key.fmtType(ip) });
|
||||
} else {
|
||||
std.debug.print("expected value ({}), found value ({})\n", .{ expected, actual_key }); // TODO print value
|
||||
if (!expected.eql(actual_key)) {
|
||||
std.debug.print("expected `{}`, found `{}`\n", .{ expected.fmt(ip), actual_key.fmt(ip) });
|
||||
return error.TestExpectedEqual;
|
||||
}
|
||||
} else {
|
||||
if (expected.isType()) {
|
||||
std.debug.print("expected type `{}`, found null\n", .{expected.fmtType(ip)});
|
||||
} else {
|
||||
std.debug.print("expected value ({}), found null\n", .{expected});
|
||||
}
|
||||
std.debug.print("expected `{}`, found null\n", .{expected.fmt(ip)});
|
||||
return error.TestExpectedEqual;
|
||||
}
|
||||
return error.TestExpectedEqual;
|
||||
}
|
||||
|
||||
fn interpretReportErrors(
|
||||
|
||||
Reference in New Issue
Block a user