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:
parent
89ab9fdf70
commit
30869d7d87
@ -70,19 +70,12 @@ pub fn deinit(interpreter: *ComptimeInterpreter) void {
|
||||
interpreter.namespaces.deinit(interpreter.allocator);
|
||||
}
|
||||
|
||||
pub const Type = struct {
|
||||
interpreter: *ComptimeInterpreter,
|
||||
|
||||
node_idx: Ast.Node.Index,
|
||||
ty: Index,
|
||||
};
|
||||
|
||||
pub const Value = struct {
|
||||
interpreter: *ComptimeInterpreter,
|
||||
|
||||
node_idx: Ast.Node.Index,
|
||||
ty: Index,
|
||||
val: Index,
|
||||
/// this stores both the type and the value
|
||||
index: Index,
|
||||
};
|
||||
// pub const Comptimeness = enum { @"comptime", runtime };
|
||||
|
||||
@ -234,19 +227,20 @@ pub fn interpret(
|
||||
continue;
|
||||
};
|
||||
|
||||
var init_type_value = try (try interpreter.interpret(container_field.ast.type_expr, container_namespace, .{})).getValue();
|
||||
var init_value = try (try interpreter.interpret(container_field.ast.type_expr, container_namespace, .{})).getValue();
|
||||
|
||||
var default_value = if (container_field.ast.value_expr == 0)
|
||||
Index.none
|
||||
else
|
||||
(try (try interpreter.interpret(container_field.ast.value_expr, container_namespace, .{})).getValue()).val; // TODO check ty
|
||||
(try (try interpreter.interpret(container_field.ast.value_expr, container_namespace, .{})).getValue()).index; // TODO check ty
|
||||
|
||||
if (init_type_value.ty != Index.type_type) {
|
||||
const init_value_ty = interpreter.ip.indexToKey(init_value.index).typeOf();
|
||||
if (init_value_ty != .type_type) {
|
||||
try interpreter.recordError(
|
||||
container_field.ast.type_expr,
|
||||
"expected_type",
|
||||
"expected type 'type', found '{}'",
|
||||
.{init_type_value.ty.fmtType(interpreter.ip)},
|
||||
.{init_value_ty.fmt(interpreter.ip)},
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@ -254,7 +248,7 @@ pub fn interpret(
|
||||
const field_name = tree.tokenSlice(container_field.ast.main_token);
|
||||
|
||||
try struct_info.fields.put(interpreter.allocator, field_name, .{
|
||||
.ty = init_type_value.val,
|
||||
.ty = init_value.index,
|
||||
.default_value = default_value,
|
||||
.alignment = 0, // TODO,
|
||||
.is_comptime = false, // TODO
|
||||
@ -267,8 +261,7 @@ pub fn interpret(
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = Index.type_type,
|
||||
.val = struct_type,
|
||||
.index = struct_type,
|
||||
} };
|
||||
},
|
||||
.error_set_decl => {
|
||||
@ -286,8 +279,7 @@ pub fn interpret(
|
||||
const decl_index = try interpreter.ip.createDecl(interpreter.allocator, .{
|
||||
.name = name,
|
||||
.node_idx = node_idx,
|
||||
.ty = .none,
|
||||
.val = .none,
|
||||
.index = .none,
|
||||
.alignment = 0, // TODO
|
||||
.address_space = .generic, // TODO
|
||||
.is_pub = true, // TODO
|
||||
@ -315,12 +307,16 @@ pub fn interpret(
|
||||
if (type_value == null and init_value == null) return InterpretResult{ .nothing = {} };
|
||||
|
||||
if (type_value) |v| {
|
||||
if (v.ty != Index.type_type) return InterpretResult{ .nothing = {} };
|
||||
if (interpreter.ip.indexToKey(v.index).typeOf() != .type_type) {
|
||||
return InterpretResult{ .nothing = {} };
|
||||
}
|
||||
}
|
||||
// TODO coerce `init_value` into `type_value`
|
||||
|
||||
const decl = interpreter.ip.getDecl(decl_index);
|
||||
decl.ty = if (type_value) |v| v.val else init_value.?.ty;
|
||||
decl.val = if (init_value) |init| init.val else .none;
|
||||
decl.index = if (type_value) |v| try interpreter.ip.get(interpreter.allocator, .{
|
||||
.unknown_value = .{ .ty = v.index },
|
||||
}) else init_value.?.index;
|
||||
|
||||
// TODO: Am I a dumbo shrimp? (e.g. is this tree shaking correct? works on my machine so like...)
|
||||
|
||||
@ -416,20 +412,10 @@ pub fn interpret(
|
||||
});
|
||||
|
||||
if (simples.get(identifier)) |index| {
|
||||
const ty: Index = switch (index) {
|
||||
.undefined_value => .undefined_type,
|
||||
.void_value => .void_type,
|
||||
.unreachable_value => .noreturn_type,
|
||||
.null_value => .null_type,
|
||||
.bool_true => .bool_type,
|
||||
.bool_false => .bool_type,
|
||||
else => .type_type,
|
||||
};
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = ty,
|
||||
.val = index,
|
||||
.index = index,
|
||||
} };
|
||||
}
|
||||
|
||||
@ -437,8 +423,7 @@ pub fn interpret(
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = Index.type_type,
|
||||
.val = try interpreter.ip.get(interpreter.allocator, Key{ .int_type = .{
|
||||
.index = try interpreter.ip.get(interpreter.allocator, Key{ .int_type = .{
|
||||
.signedness = if (identifier[0] == 'u') .unsigned else .signed,
|
||||
.bits = std.fmt.parseInt(u16, identifier[1..], 10) catch break :blk,
|
||||
} }),
|
||||
@ -448,12 +433,11 @@ pub fn interpret(
|
||||
// Logic to find identifiers in accessible scopes
|
||||
if (interpreter.huntItDown(namespace, identifier, options)) |decl_index| {
|
||||
const decl = interpreter.ip.getDecl(decl_index);
|
||||
if (decl.ty == .none) return InterpretResult{ .nothing = {} };
|
||||
if (decl.index == .none) return InterpretResult{ .nothing = {} };
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = decl.node_idx,
|
||||
.ty = decl.ty,
|
||||
.val = decl.val,
|
||||
.index = decl.index,
|
||||
} };
|
||||
}
|
||||
|
||||
@ -471,31 +455,39 @@ pub fn interpret(
|
||||
const field_name = tree.tokenSlice(data[node_idx].rhs);
|
||||
|
||||
var ir = try interpreter.interpret(data[node_idx].lhs, namespace, options);
|
||||
var irv = try ir.getValue();
|
||||
var ir_value = try ir.getValue();
|
||||
|
||||
const lhs = interpreter.ip.indexToKey(irv.ty);
|
||||
const inner_lhs = switch (lhs) {
|
||||
.pointer_type => |info| if (info.size == .One) interpreter.ip.indexToKey(info.elem_type) else lhs,
|
||||
else => lhs,
|
||||
const val_index = ir_value.index;
|
||||
const val = interpreter.ip.indexToKey(val_index);
|
||||
std.debug.assert(val.typeOf() != .none);
|
||||
const ty = interpreter.ip.indexToKey(val.typeOf());
|
||||
|
||||
const inner_ty = switch (ty) {
|
||||
.pointer_type => |info| if (info.size == .One) interpreter.ip.indexToKey(info.elem_type) else ty,
|
||||
else => ty,
|
||||
};
|
||||
|
||||
const can_have_fields: bool = switch (inner_lhs) {
|
||||
const can_have_fields: bool = switch (inner_ty) {
|
||||
.simple_type => |simple| switch (simple) {
|
||||
.type => blk: {
|
||||
if (irv.val == .none) break :blk true;
|
||||
|
||||
const ty_key = interpreter.ip.indexToKey(irv.val);
|
||||
if (interpreter.huntItDown(ty_key.getNamespace(interpreter.ip), field_name, options)) |decl_index| {
|
||||
if (interpreter.huntItDown(val.getNamespace(interpreter.ip), field_name, options)) |decl_index| {
|
||||
const decl = interpreter.ip.getDecl(decl_index);
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = decl.ty,
|
||||
.val = decl.val,
|
||||
.index = decl.index,
|
||||
} };
|
||||
}
|
||||
|
||||
switch (ty_key) {
|
||||
if (val == .unknown_value) {
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.index = .unknown_unknown,
|
||||
} };
|
||||
}
|
||||
|
||||
switch (val) {
|
||||
.error_set_type => |error_set_info| { // TODO
|
||||
_ = error_set_info;
|
||||
},
|
||||
@ -508,8 +500,7 @@ pub fn interpret(
|
||||
.value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.ty = irv.val,
|
||||
.val = .none, // TODO resolve enum value
|
||||
.index = .unknown_unknown, // TODO
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -529,8 +520,10 @@ pub fn interpret(
|
||||
.value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.ty = try interpreter.ip.get(interpreter.allocator, many_ptr_info),
|
||||
.val = .none, // TODO resolve ptr of Slice
|
||||
// TODO resolve ptr of Slice
|
||||
.index = try interpreter.ip.get(interpreter.allocator, .{
|
||||
.unknown_value = .{ .ty = try interpreter.ip.get(interpreter.allocator, many_ptr_info) },
|
||||
}),
|
||||
},
|
||||
};
|
||||
} else if (std.mem.eql(u8, field_name, "len")) {
|
||||
@ -538,8 +531,10 @@ pub fn interpret(
|
||||
.value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.ty = Index.usize_type,
|
||||
.val = .none, // TODO resolve length of Slice
|
||||
// TODO resolve length of Slice
|
||||
.index = try interpreter.ip.get(interpreter.allocator, .{
|
||||
.unknown_value = .{ .ty = Index.usize_type },
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -549,8 +544,10 @@ pub fn interpret(
|
||||
.value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.ty = Index.usize_type,
|
||||
.val = .none, // TODO resolve length of Slice
|
||||
// TODO resolve length of Slice
|
||||
.index = try interpreter.ip.get(interpreter.allocator, .{
|
||||
.unknown_value = .{ .ty = Index.usize_type },
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -558,53 +555,58 @@ pub fn interpret(
|
||||
break :blk true;
|
||||
},
|
||||
.array_type => |array_info| blk: {
|
||||
const len_value = try interpreter.ip.get(interpreter.allocator, .{ .int_u64_value = array_info.len });
|
||||
|
||||
if (std.mem.eql(u8, field_name, "len")) {
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.ty = Index.comptime_int_type,
|
||||
.val = len_value,
|
||||
.index = try interpreter.ip.get(interpreter.allocator, .{ .int_u64_value = .{
|
||||
.ty = .comptime_int_type,
|
||||
.int = array_info.len,
|
||||
} }),
|
||||
} };
|
||||
}
|
||||
break :blk true;
|
||||
},
|
||||
.optional_type => |optional_info| blk: {
|
||||
if (!std.mem.eql(u8, field_name, "?")) break :blk false;
|
||||
if (irv.val == Index.null_value) {
|
||||
|
||||
if (val_index == .type_type) {
|
||||
try interpreter.recordError(
|
||||
node_idx,
|
||||
"null_unwrap",
|
||||
"tried to unwrap optional of type `{}` which was null",
|
||||
.{irv.ty.fmtType(interpreter.ip)},
|
||||
.{optional_info.payload_type.fmt(interpreter.ip)},
|
||||
);
|
||||
return error.InvalidOperation;
|
||||
} else {
|
||||
}
|
||||
const result = switch (val) {
|
||||
.optional_value => |optional_val| optional_val.val,
|
||||
.unknown_value => val_index,
|
||||
else => return error.InvalidOperation,
|
||||
};
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.ty = optional_info.payload_type,
|
||||
.val = irv.val,
|
||||
.index = result,
|
||||
} };
|
||||
}
|
||||
},
|
||||
.struct_type => |struct_index| blk: {
|
||||
const struct_info = interpreter.ip.getStruct(struct_index);
|
||||
if (struct_info.fields.getIndex(field_name)) |field_index| {
|
||||
const field = struct_info.fields.values()[field_index];
|
||||
const val = found_val: {
|
||||
if (irv.val == .none) break :found_val .none;
|
||||
const val_key = interpreter.ip.indexToKey(irv.val);
|
||||
if (val_key != .aggregate) break :found_val .none;
|
||||
break :found_val val_key.aggregate[field_index];
|
||||
|
||||
const result = switch (val) {
|
||||
.aggregate => |aggregate| aggregate.values[field_index],
|
||||
.unknown_value => try interpreter.ip.get(interpreter.allocator, .{
|
||||
.unknown_value = .{ .ty = field.ty },
|
||||
}),
|
||||
else => return error.InvalidOperation,
|
||||
};
|
||||
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = data[node_idx].rhs,
|
||||
.ty = field.ty,
|
||||
.val = val,
|
||||
.index = result,
|
||||
} };
|
||||
}
|
||||
break :blk true;
|
||||
@ -617,27 +619,52 @@ pub fn interpret(
|
||||
_ = union_info;
|
||||
break :blk true;
|
||||
},
|
||||
else => false,
|
||||
.int_type,
|
||||
.error_union_type,
|
||||
.error_set_type,
|
||||
.function_type,
|
||||
.tuple_type,
|
||||
.vector_type,
|
||||
.anyframe_type,
|
||||
=> false,
|
||||
|
||||
.simple_value,
|
||||
.int_u64_value,
|
||||
.int_i64_value,
|
||||
.int_big_value,
|
||||
.float_16_value,
|
||||
.float_32_value,
|
||||
.float_64_value,
|
||||
.float_80_value,
|
||||
.float_128_value,
|
||||
.float_comptime_value,
|
||||
=> unreachable,
|
||||
|
||||
.bytes,
|
||||
.optional_value,
|
||||
.slice,
|
||||
.aggregate,
|
||||
.union_value,
|
||||
.unknown_value,
|
||||
=> unreachable,
|
||||
};
|
||||
|
||||
const accessed_ty = if (inner_lhs == .simple_type and inner_lhs.simple_type == .type) irv.val else irv.ty;
|
||||
if (accessed_ty != .none) {
|
||||
const accessed_ty = if (inner_ty == .simple_type and inner_ty.simple_type == .type) val else inner_ty;
|
||||
if (can_have_fields) {
|
||||
try interpreter.recordError(
|
||||
node_idx,
|
||||
"undeclared_identifier",
|
||||
"`{}` has no member '{s}'",
|
||||
.{ accessed_ty.fmtType(interpreter.ip), field_name },
|
||||
.{ accessed_ty.fmt(interpreter.ip), field_name },
|
||||
);
|
||||
} else {
|
||||
try interpreter.recordError(
|
||||
node_idx,
|
||||
"invalid_field_access",
|
||||
"`{}` does not support field access",
|
||||
.{accessed_ty.fmtType(interpreter.ip)},
|
||||
.{accessed_ty.fmt(interpreter.ip)},
|
||||
);
|
||||
}
|
||||
}
|
||||
return error.InvalidOperation;
|
||||
},
|
||||
.grouped_expression => {
|
||||
@ -660,19 +687,39 @@ pub fn interpret(
|
||||
.if_simple,
|
||||
=> {
|
||||
const if_info = ast.fullIf(tree, node_idx).?;
|
||||
// TODO: Don't evaluate runtime ifs
|
||||
// if (options.observe_values) {
|
||||
const ir = try interpreter.interpret(if_info.ast.cond_expr, namespace, options);
|
||||
|
||||
const condition = (try ir.getValue()).val;
|
||||
std.debug.assert(condition == Index.bool_false or condition == Index.bool_true);
|
||||
if (condition == Index.bool_true) {
|
||||
const condition = (try ir.getValue()).index;
|
||||
const condition_val = interpreter.ip.indexToKey(condition);
|
||||
const condition_ty = condition_val.typeOf();
|
||||
|
||||
switch (condition_ty) {
|
||||
.bool_type => {},
|
||||
.unknown_type => return InterpretResult{ .nothing = {} },
|
||||
else => {
|
||||
try interpreter.recordError(
|
||||
if_info.ast.cond_expr,
|
||||
"invalid_if_condition",
|
||||
"expected `bool` but found `{}`",
|
||||
.{condition_ty.fmt(interpreter.ip)},
|
||||
);
|
||||
return error.InvalidOperation;
|
||||
},
|
||||
}
|
||||
if (condition_val == .unknown_value) {
|
||||
return InterpretResult{ .nothing = {} };
|
||||
}
|
||||
|
||||
std.debug.assert(condition == .bool_false or condition == .bool_true);
|
||||
if (condition == .bool_true) {
|
||||
return try interpreter.interpret(if_info.ast.then_expr, namespace, options);
|
||||
} else {
|
||||
if (if_info.ast.else_expr != 0) {
|
||||
return try interpreter.interpret(if_info.ast.else_expr, namespace, options);
|
||||
} else return InterpretResult{ .nothing = {} };
|
||||
}
|
||||
}
|
||||
return InterpretResult{ .nothing = {} };
|
||||
},
|
||||
.equal_equal => {
|
||||
var a = try interpreter.interpret(data[node_idx].lhs, namespace, options);
|
||||
@ -683,8 +730,7 @@ pub fn interpret(
|
||||
.value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = Index.bool_type,
|
||||
.val = if (a_value.val == b_value.val) Index.bool_true else Index.bool_false, // TODO eql function required?
|
||||
.index = if (a_value.index == b_value.index) .bool_true else .bool_false, // TODO eql function required?
|
||||
},
|
||||
};
|
||||
},
|
||||
@ -700,19 +746,26 @@ pub fn interpret(
|
||||
interpreter.allocator,
|
||||
switch (nl) {
|
||||
.float => Key{
|
||||
.float_128_value = try std.fmt.parseFloat(f128, s),
|
||||
.float_comptime_value = try std.fmt.parseFloat(f128, s),
|
||||
},
|
||||
.int => if (s[0] == '-') Key{
|
||||
.int_i64_value = try std.fmt.parseInt(i64, s, 0),
|
||||
.int_i64_value = .{
|
||||
.ty = number_type,
|
||||
.int = try std.fmt.parseInt(i64, s, 0),
|
||||
},
|
||||
} else Key{
|
||||
.int_u64_value = try std.fmt.parseInt(u64, s, 0),
|
||||
.int_u64_value = .{
|
||||
.ty = number_type,
|
||||
.int = try std.fmt.parseInt(u64, s, 0),
|
||||
},
|
||||
},
|
||||
.big_int => |base| blk: {
|
||||
var big_int = try std.math.big.int.Managed.init(interpreter.allocator);
|
||||
defer big_int.deinit();
|
||||
const prefix_length: usize = if (base != .decimal) 2 else 0;
|
||||
try big_int.setString(@enumToInt(base), s[prefix_length..]);
|
||||
break :blk Key{ .int_big_value = big_int.toConst() };
|
||||
std.debug.assert(number_type == .comptime_int_type);
|
||||
break :blk Key{ .int_big_value = .{ .ty = number_type, .int = big_int.toConst() } };
|
||||
},
|
||||
.failure => return error.CriticalAstFailure,
|
||||
},
|
||||
@ -721,8 +774,7 @@ pub fn interpret(
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = number_type,
|
||||
.val = value,
|
||||
.index = value,
|
||||
} };
|
||||
},
|
||||
.assign,
|
||||
@ -747,12 +799,17 @@ pub fn interpret(
|
||||
return InterpretResult{ .nothing = {} };
|
||||
}
|
||||
|
||||
var ir = try interpreter.interpret(data[node_idx].lhs, namespace, options);
|
||||
var to_value = try ir.getValue();
|
||||
var from_value = (try (try interpreter.interpret(data[node_idx].rhs, namespace, options)).getValue());
|
||||
const lhs = try interpreter.interpret(data[node_idx].lhs, namespace, options);
|
||||
const rhs = try interpreter.interpret(data[node_idx].rhs, namespace, options);
|
||||
|
||||
const to_val = try lhs.getValue();
|
||||
const from_val = try rhs.getValue();
|
||||
|
||||
const to_ty = interpreter.ip.indexToKey(to_val.index).typeOf();
|
||||
const from_ty = interpreter.ip.indexToKey(from_val.index).typeOf();
|
||||
|
||||
// TODO report error
|
||||
_ = try interpreter.ip.cast(interpreter.allocator, to_value.ty, from_value.ty, builtin.target);
|
||||
_ = try interpreter.ip.cast(interpreter.allocator, to_ty, from_ty, builtin.target);
|
||||
|
||||
return InterpretResult{ .nothing = {} };
|
||||
},
|
||||
@ -786,11 +843,14 @@ pub fn interpret(
|
||||
try writer.writeAll("log: ");
|
||||
|
||||
for (params, 0..) |param, index| {
|
||||
var value = (try interpreter.interpret(param, namespace, options)).maybeGetValue() orelse {
|
||||
const ir_value = (try interpreter.interpret(param, namespace, options)).maybeGetValue() orelse {
|
||||
try writer.writeAll("indeterminate");
|
||||
continue;
|
||||
};
|
||||
try writer.print("@as({}, {})", .{ value.ty.fmtType(interpreter.ip), value.val.fmtValue(value.ty, interpreter.ip) });
|
||||
const val = interpreter.ip.indexToKey(ir_value.index);
|
||||
const ty = val.typeOf();
|
||||
|
||||
try writer.print("@as({}, {})", .{ ty.fmt(interpreter.ip), val.fmt(interpreter.ip) });
|
||||
if (index != params.len - 1)
|
||||
try writer.writeAll(", ");
|
||||
}
|
||||
@ -827,8 +887,9 @@ pub fn interpret(
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = try interpreter.ip.get(interpreter.allocator, Key{ .struct_type = struct_index }),
|
||||
.val = Index.undefined_value,
|
||||
.index = try interpreter.ip.get(interpreter.allocator, .{ .unknown_value = .{
|
||||
.ty = try interpreter.ip.get(interpreter.allocator, .{ .struct_type = struct_index }),
|
||||
} }),
|
||||
} };
|
||||
}
|
||||
|
||||
@ -842,38 +903,59 @@ pub fn interpret(
|
||||
.value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = Index.type_type,
|
||||
.val = .none, // TODO
|
||||
.index = try interpreter.ip.get(interpreter.allocator, .{ .unknown_value = .{ .ty = .type_type } }),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, call_name, "@TypeOf")) {
|
||||
if (params.len != 1) return error.InvalidBuiltin;
|
||||
if (params.len == 0) return error.InvalidBuiltin;
|
||||
|
||||
const types: []Index = try interpreter.allocator.alloc(Index, params.len);
|
||||
defer interpreter.allocator.free(types);
|
||||
|
||||
for (params, types) |param, *out_type| {
|
||||
const value = try (try interpreter.interpret(param, namespace, options)).getValue();
|
||||
out_type.* = interpreter.ip.indexToKey(value.index).typeOf();
|
||||
}
|
||||
|
||||
const peer_type = try interpreter.ip.resolvePeerTypes(interpreter.allocator, types, builtin.target);
|
||||
|
||||
if (peer_type == .none) {
|
||||
var output = std.ArrayListUnmanaged(u8){};
|
||||
var writer = output.writer(interpreter.allocator);
|
||||
try writer.writeAll("incompatible types: ");
|
||||
for (types, 0..) |ty, i| {
|
||||
if (i != 0) try writer.writeAll(", ");
|
||||
try writer.print("`{}`", .{ty.fmt(interpreter.ip)});
|
||||
}
|
||||
|
||||
try interpreter.recordError(node_idx, "invalid_typeof", "{s}", .{output.items});
|
||||
return error.InvalidOperation;
|
||||
}
|
||||
|
||||
const value = try (try interpreter.interpret(params[0], namespace, options)).getValue();
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = Index.type_type,
|
||||
.val = value.ty,
|
||||
.index = peer_type,
|
||||
} };
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, call_name, "@hasDecl")) {
|
||||
if (params.len != 2) return error.InvalidBuiltin;
|
||||
|
||||
const value = try (try interpreter.interpret(params[0], namespace, options)).getValue();
|
||||
const ir_value = try (try interpreter.interpret(params[0], namespace, options)).getValue();
|
||||
const field_name = try (try interpreter.interpret(params[1], namespace, options)).getValue();
|
||||
|
||||
if (value.ty != Index.type_type or value.ty == .none) return error.InvalidBuiltin;
|
||||
if (interpreter.ip.indexToKey(field_name.ty) != .pointer_type) return error.InvalidBuiltin; // Check if it's a []const u8
|
||||
if (value.val == .none) return error.InvalidBuiltin;
|
||||
const val = interpreter.ip.indexToKey(ir_value.index);
|
||||
const ty = val.typeOf();
|
||||
|
||||
const value_namespace = interpreter.ip.indexToKey(value.val).getNamespace(interpreter.ip);
|
||||
if (ty != .type_type) return error.InvalidBuiltin;
|
||||
|
||||
const value_namespace = interpreter.ip.indexToKey(ty).getNamespace(interpreter.ip);
|
||||
if (value_namespace == .none) return error.InvalidBuiltin;
|
||||
|
||||
const name = interpreter.ip.indexToKey(field_name.val).bytes; // TODO add checks
|
||||
const name = interpreter.ip.indexToKey(field_name.index).bytes; // TODO add checks
|
||||
|
||||
const decls = interpreter.namespaces.items(.decls)[@enumToInt(value_namespace)];
|
||||
const has_decl = decls.contains(name);
|
||||
@ -881,8 +963,7 @@ pub fn interpret(
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = Index.bool_type,
|
||||
.val = if (has_decl) Index.bool_true else Index.bool_false,
|
||||
.index = if (has_decl) .bool_true else .bool_false,
|
||||
} };
|
||||
}
|
||||
|
||||
@ -890,16 +971,20 @@ pub fn interpret(
|
||||
if (params.len != 2) return error.InvalidBuiltin;
|
||||
|
||||
const as_type = try (try interpreter.interpret(params[0], namespace, options)).getValue();
|
||||
const value = try (try interpreter.interpret(params[1], namespace, options)).getValue();
|
||||
// const value = try (try interpreter.interpret(params[1], namespace, options)).getValue();
|
||||
|
||||
if (as_type.ty != Index.type_type) return error.InvalidBuiltin;
|
||||
if (interpreter.ip.indexToKey(as_type.index).typeOf() != .type_type) {
|
||||
return error.InvalidBuiltin;
|
||||
}
|
||||
|
||||
return InterpretResult{
|
||||
.value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = as_type.val,
|
||||
.val = value.val, // TODO port Sema.coerceExtra to InternPool
|
||||
// TODO port Sema.coerceExtra to InternPool
|
||||
.index = try interpreter.ip.get(interpreter.allocator, .{
|
||||
.unknown_value = .{ .ty = as_type.index },
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -910,26 +995,25 @@ pub fn interpret(
|
||||
.string_literal => {
|
||||
const str = tree.getNodeSource(node_idx)[1 .. tree.getNodeSource(node_idx).len - 1];
|
||||
|
||||
const string_literal_type = try interpreter.ip.get(interpreter.allocator, Key{ .pointer_type = .{
|
||||
.elem_type = try interpreter.ip.get(interpreter.allocator, Key{ .array_type = .{
|
||||
.child = Index.u8_type,
|
||||
.len = @intCast(u64, str.len),
|
||||
.sentinel = try interpreter.ip.get(interpreter.allocator, Key{ .int_u64_value = 0 }),
|
||||
} }),
|
||||
.sentinel = .none,
|
||||
.alignment = 0,
|
||||
.size = .One,
|
||||
.is_const = true,
|
||||
.is_volatile = false,
|
||||
.is_allowzero = false,
|
||||
.address_space = .generic,
|
||||
} });
|
||||
// const string_literal_type = try interpreter.ip.get(interpreter.allocator, Key{ .pointer_type = .{
|
||||
// .elem_type = try interpreter.ip.get(interpreter.allocator, Key{ .array_type = .{
|
||||
// .child = Index.u8_type,
|
||||
// .len = @intCast(u64, str.len),
|
||||
// .sentinel = try interpreter.ip.get(interpreter.allocator, Key{ .int_u64_value = .{ .ty = .u8_type, .int = 0 } }),
|
||||
// } }),
|
||||
// .sentinel = .none,
|
||||
// .alignment = 0,
|
||||
// .size = .One,
|
||||
// .is_const = true,
|
||||
// .is_volatile = false,
|
||||
// .is_allowzero = false,
|
||||
// .address_space = .generic,
|
||||
// } });
|
||||
|
||||
return InterpretResult{ .value = Value{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = string_literal_type,
|
||||
.val = try interpreter.ip.get(interpreter.allocator, Key{ .bytes = str }),
|
||||
.index = try interpreter.ip.get(interpreter.allocator, Key{ .bytes = str }),
|
||||
} };
|
||||
},
|
||||
// TODO: Add comptime autodetection; e.g. const MyArrayList = std.ArrayList(u8)
|
||||
@ -989,8 +1073,7 @@ pub fn interpret(
|
||||
const decl_index = try interpreter.ip.createDecl(interpreter.allocator, .{
|
||||
.name = name,
|
||||
.node_idx = node_idx,
|
||||
.ty = Index.type_type,
|
||||
.val = function_type,
|
||||
.index = function_type,
|
||||
.alignment = 0, // TODO
|
||||
.address_space = .generic, // TODO
|
||||
.is_pub = false, // TODO
|
||||
@ -1034,24 +1117,36 @@ pub fn interpret(
|
||||
},
|
||||
.bool_not => {
|
||||
const result = try interpreter.interpret(data[node_idx].lhs, namespace, .{});
|
||||
const value = try result.getValue();
|
||||
const ir_value = try result.getValue();
|
||||
|
||||
if (value.ty != Index.bool_type) {
|
||||
const val = ir_value.index;
|
||||
const ty = interpreter.ip.indexToKey(ir_value.index).typeOf();
|
||||
|
||||
if (ty == .unknown_type) {
|
||||
return InterpretResult{ .value = .{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.index = try interpreter.ip.get(interpreter.allocator, .{
|
||||
.unknown_value = .{ .ty = .bool_type },
|
||||
}),
|
||||
} };
|
||||
}
|
||||
|
||||
if (ty != .bool_type) {
|
||||
try interpreter.recordError(
|
||||
node_idx,
|
||||
"invalid_deref",
|
||||
"expected type `bool` but got `{}`",
|
||||
.{value.ty.fmtType(interpreter.ip)},
|
||||
.{ty.fmt(interpreter.ip)},
|
||||
);
|
||||
return error.InvalidOperation;
|
||||
}
|
||||
|
||||
std.debug.assert(value.val == Index.bool_false or value.val == Index.bool_true);
|
||||
std.debug.assert(val == .bool_false or val == .bool_true);
|
||||
return InterpretResult{ .value = .{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = Index.bool_type,
|
||||
.val = if (value.val == Index.bool_false) Index.bool_true else Index.bool_false,
|
||||
.index = if (val == .bool_false) .bool_true else .bool_false,
|
||||
} };
|
||||
},
|
||||
.address_of => {
|
||||
@ -1059,10 +1154,12 @@ pub fn interpret(
|
||||
// variables are the only non-const(?)
|
||||
|
||||
const result = try interpreter.interpret(data[node_idx].lhs, namespace, .{});
|
||||
const value = (try result.getValue());
|
||||
const ir_value = try result.getValue();
|
||||
|
||||
const ty = interpreter.ip.indexToKey(ir_value.index).typeOf();
|
||||
|
||||
const pointer_type = try interpreter.ip.get(interpreter.allocator, Key{ .pointer_type = .{
|
||||
.elem_type = value.ty,
|
||||
.elem_type = ty,
|
||||
.sentinel = .none,
|
||||
.alignment = 0,
|
||||
.size = .One,
|
||||
@ -1075,15 +1172,26 @@ pub fn interpret(
|
||||
return InterpretResult{ .value = .{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = pointer_type,
|
||||
.val = value.val,
|
||||
.index = try interpreter.ip.get(interpreter.allocator, .{
|
||||
.unknown_value = .{ .ty = pointer_type },
|
||||
}),
|
||||
} };
|
||||
},
|
||||
.deref => {
|
||||
const result = try interpreter.interpret(data[node_idx].lhs, namespace, .{});
|
||||
const value = (try result.getValue());
|
||||
const ir_value = (try result.getValue());
|
||||
|
||||
const type_key = interpreter.ip.indexToKey(value.ty);
|
||||
const ty = interpreter.ip.indexToKey(ir_value.index).typeOf();
|
||||
|
||||
if (ty == .unknown_type) {
|
||||
return InterpretResult{ .value = .{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.index = .unknown_unknown,
|
||||
} };
|
||||
}
|
||||
|
||||
const type_key = interpreter.ip.indexToKey(ty);
|
||||
|
||||
if (type_key != .pointer_type) {
|
||||
try interpreter.recordError(node_idx, "invalid_deref", "cannot deference non-pointer", .{});
|
||||
@ -1093,8 +1201,9 @@ pub fn interpret(
|
||||
return InterpretResult{ .value = .{
|
||||
.interpreter = interpreter,
|
||||
.node_idx = node_idx,
|
||||
.ty = type_key.pointer_type.elem_type,
|
||||
.val = value.val,
|
||||
.index = try interpreter.ip.get(interpreter.allocator, .{
|
||||
.unknown_value = .{ .ty = type_key.pointer_type.elem_type },
|
||||
}),
|
||||
} };
|
||||
},
|
||||
else => {
|
||||
@ -1144,15 +1253,17 @@ pub fn call(
|
||||
while (ast.nextFnParam(&arg_it)) |param| {
|
||||
if (arg_index >= arguments.len) return error.MissingArguments;
|
||||
var tex = try (try interpreter.interpret(param.type_expr, fn_namespace, options)).getValue();
|
||||
if (tex.ty != Index.type_type) {
|
||||
const tex_ty = interpreter.ip.indexToKey(tex.index).typeOf();
|
||||
if (tex_ty != .type_type) {
|
||||
try interpreter.recordError(
|
||||
param.type_expr,
|
||||
"expected_type",
|
||||
"expected type 'type', found '{}'",
|
||||
.{tex.ty.fmtType(interpreter.ip)},
|
||||
.{tex_ty.fmt(interpreter.ip)},
|
||||
);
|
||||
return error.InvalidCast;
|
||||
}
|
||||
// TODO validate that `arguments[arg_index].index`'s types matches tex.index
|
||||
if (param.name_token) |name_token| {
|
||||
const name = offsets.tokenToSlice(tree, name_token);
|
||||
|
||||
@ -1160,8 +1271,7 @@ pub fn call(
|
||||
const decl_index = try interpreter.ip.createDecl(interpreter.allocator, .{
|
||||
.name = name,
|
||||
.node_idx = name_token,
|
||||
.ty = tex.val,
|
||||
.val = arguments[arg_index].val,
|
||||
.index = arguments[arg_index].index,
|
||||
.alignment = 0, // TODO
|
||||
.address_space = .generic, // TODO
|
||||
.is_pub = true, // TODO
|
||||
|
@ -598,13 +598,14 @@ fn typeToCompletion(
|
||||
null,
|
||||
),
|
||||
.primitive, .array_index => {},
|
||||
.@"comptime" => |co| {
|
||||
if (type_handle.type.is_type_val) {
|
||||
try analyser.completions.dotCompletions(allocator, list, &co.interpreter.ip, co.value.ty, co.value.val, co.value.node_idx);
|
||||
} else {
|
||||
try analyser.completions.dotCompletions(allocator, list, &co.interpreter.ip, co.value.val, .none, co.value.node_idx);
|
||||
}
|
||||
},
|
||||
.@"comptime" => |co| try analyser.completions.dotCompletions(
|
||||
allocator,
|
||||
list,
|
||||
&co.interpreter.ip,
|
||||
co.value.index,
|
||||
type_handle.type.is_type_val,
|
||||
co.value.node_idx,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -921,7 +922,7 @@ fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle) error{OutO
|
||||
|
||||
const resolved_type_str = if (resolved_type) |rt|
|
||||
if (rt.type.is_type_val) switch (rt.type.data) {
|
||||
.@"comptime" => |co| try std.fmt.allocPrint(server.arena.allocator(), "{}", .{co.value.ty.fmtType(co.interpreter.ip)}),
|
||||
.@"comptime" => |co| try std.fmt.allocPrint(server.arena.allocator(), "{}", .{co.value.index.fmt(co.interpreter.ip)}),
|
||||
else => "type",
|
||||
} else switch (rt.type.data) { // TODO: Investigate random weird numbers like 897 that cause index of bounds
|
||||
.pointer,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,31 +4,36 @@ const types = @import("../lsp.zig");
|
||||
|
||||
const Ast = std.zig.Ast;
|
||||
|
||||
/// generates a list of dot completions for the given typed-value in `index`
|
||||
/// the given `index` must belong to the given InternPool
|
||||
pub fn dotCompletions(
|
||||
arena: std.mem.Allocator,
|
||||
completions: *std.ArrayListUnmanaged(types.CompletionItem),
|
||||
ip: *InternPool,
|
||||
ty: InternPool.Index,
|
||||
val: InternPool.Index,
|
||||
index: InternPool.Index,
|
||||
is_type_val: bool,
|
||||
node: ?Ast.Node.Index,
|
||||
) error{OutOfMemory}!void {
|
||||
std.debug.assert(index != .none);
|
||||
_ = node;
|
||||
|
||||
const key = ip.indexToKey(ty);
|
||||
const inner_key = switch (key) {
|
||||
.pointer_type => |info| if (info.size == .One) ip.indexToKey(info.elem_type) else key,
|
||||
else => key,
|
||||
const index_key = ip.indexToKey(index);
|
||||
const val: InternPool.Key = if (is_type_val) index_key else .{ .unknown_value = .{ .ty = index } };
|
||||
const ty: InternPool.Key = if (is_type_val) ip.indexToKey(index_key.typeOf()) else index_key;
|
||||
|
||||
const inner_ty = switch (ty) {
|
||||
.pointer_type => |info| if (info.size == .One) ip.indexToKey(info.elem_type) else ty,
|
||||
else => ty,
|
||||
};
|
||||
|
||||
switch (inner_key) {
|
||||
switch (inner_ty) {
|
||||
.simple_type => |simple| switch (simple) {
|
||||
.type => {
|
||||
const ty_key = ip.indexToKey(val);
|
||||
const namespace = ty_key.getNamespace(ip.*);
|
||||
const namespace = val.getNamespace(ip.*);
|
||||
if (namespace != .none) {
|
||||
// TODO lookup in namespace
|
||||
}
|
||||
switch (ty_key) {
|
||||
switch (val) {
|
||||
.error_set_type => |error_set_info| {
|
||||
for (error_set_info.names) |name| {
|
||||
const error_name = ip.indexToKey(name).bytes;
|
||||
@ -64,7 +69,7 @@ pub fn dotCompletions(
|
||||
try completions.append(arena, .{
|
||||
.label = "ptr",
|
||||
.kind = .Field,
|
||||
.detail = try std.fmt.allocPrint(arena, "ptr: {}", .{many_ptr_info.fmtType(ip.*)}),
|
||||
.detail = try std.fmt.allocPrint(arena, "ptr: {}", .{many_ptr_info.fmt(ip.*)}),
|
||||
});
|
||||
try completions.append(arena, .{
|
||||
.label = "len",
|
||||
@ -80,7 +85,7 @@ pub fn dotCompletions(
|
||||
}
|
||||
},
|
||||
.array_type => |array_info| {
|
||||
try completions.append(arena, types.CompletionItem{
|
||||
try completions.append(arena, .{
|
||||
.label = "len",
|
||||
.kind = .Field,
|
||||
.detail = try std.fmt.allocPrint(arena, "const len: usize ({d})", .{array_info.len}), // TODO how should this be displayed
|
||||
@ -93,7 +98,7 @@ pub fn dotCompletions(
|
||||
while (field_it.next()) |entry| {
|
||||
const label = entry.key_ptr.*;
|
||||
const field = entry.value_ptr.*;
|
||||
completions.appendAssumeCapacity(types.CompletionItem{
|
||||
completions.appendAssumeCapacity(.{
|
||||
.label = label,
|
||||
.kind = .Field,
|
||||
.detail = try std.fmt.allocPrint(arena, "{s}: {}", .{
|
||||
@ -107,7 +112,7 @@ pub fn dotCompletions(
|
||||
try completions.append(arena, .{
|
||||
.label = "?",
|
||||
.kind = .Operator,
|
||||
.detail = try std.fmt.allocPrint(arena, "{}", .{optional_info.payload_type.fmtType(ip.*)}),
|
||||
.detail = try std.fmt.allocPrint(arena, "{}", .{optional_info.payload_type.fmt(ip.*)}),
|
||||
});
|
||||
},
|
||||
.enum_type => |enum_index| {
|
||||
@ -116,7 +121,7 @@ pub fn dotCompletions(
|
||||
try completions.append(arena, .{
|
||||
.label = field_name,
|
||||
.kind = .Field,
|
||||
.detail = try std.fmt.allocPrint(arena, "{}", .{field_value.fmtValue(enum_info.tag_type, ip.*)}),
|
||||
.detail = try std.fmt.allocPrint(arena, "{}", .{field_value.fmt(ip.*)}),
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -130,9 +135,9 @@ pub fn dotCompletions(
|
||||
.label = label,
|
||||
.kind = .Field,
|
||||
.detail = if (field.alignment != 0)
|
||||
try std.fmt.allocPrint(arena, "{s}: align({d}) {}", .{ label, field.alignment, field.ty.fmtType(ip.*) })
|
||||
try std.fmt.allocPrint(arena, "{s}: align({d}) {}", .{ label, field.alignment, field.ty.fmt(ip.*) })
|
||||
else
|
||||
try std.fmt.allocPrint(arena, "{s}: {}", .{ label, field.ty.fmtType(ip.*) }),
|
||||
try std.fmt.allocPrint(arena, "{s}: {}", .{ label, field.ty.fmt(ip.*) }),
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -141,7 +146,7 @@ pub fn dotCompletions(
|
||||
try completions.append(arena, .{
|
||||
.label = try std.fmt.allocPrint(arena, "{d}", .{i}),
|
||||
.kind = .Field,
|
||||
.detail = try std.fmt.allocPrint(arena, "{d}: {}", .{ i, tuple_ty.fmtType(ip.*) }),
|
||||
.detail = try std.fmt.allocPrint(arena, "{d}: {}", .{ i, tuple_ty.fmt(ip.*) }),
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -162,11 +167,15 @@ pub fn dotCompletions(
|
||||
.float_64_value,
|
||||
.float_80_value,
|
||||
.float_128_value,
|
||||
.float_comptime_value,
|
||||
=> unreachable,
|
||||
|
||||
.bytes,
|
||||
.optional_value,
|
||||
.slice,
|
||||
.aggregate,
|
||||
.union_value,
|
||||
.unknown_value,
|
||||
=> unreachable,
|
||||
}
|
||||
}
|
||||
@ -194,9 +203,9 @@ fn formatFieldDetail(
|
||||
if (field.alignment != 0) {
|
||||
try writer.print("align({d}) ", .{field.alignment});
|
||||
}
|
||||
try writer.print("{}", .{field.ty.fmtType(ctx.ip.*)});
|
||||
try writer.print("{}", .{field.ty.fmt(ctx.ip.*)});
|
||||
if (field.default_value != .none) {
|
||||
try writer.print(" = {},", .{field.default_value.fmtValue(field.ty, ctx.ip.*)});
|
||||
try writer.print(" = {},", .{field.default_value.fmt(ctx.ip.*)});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -788,6 +788,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const is_type_val = interpreter.ip.indexToKey(value.index).typeOf() == .type_type;
|
||||
|
||||
return TypeWithHandle{
|
||||
.type = .{
|
||||
@ -795,7 +796,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan
|
||||
.interpreter = interpreter,
|
||||
.value = value,
|
||||
} },
|
||||
.is_type_val = value.ty == InternPool.Index.type_type,
|
||||
.is_type_val = is_type_val,
|
||||
},
|
||||
.handle = node_handle.handle,
|
||||
};
|
||||
|
@ -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,35 +455,20 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
} 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});
|
||||
}
|
||||
}
|
||||
if (!expected.eql(actual_key)) {
|
||||
std.debug.print("expected `{}`, found `{}`\n", .{ expected.fmt(ip), actual_key.fmt(ip) });
|
||||
return error.TestExpectedEqual;
|
||||
}
|
||||
} else {
|
||||
std.debug.print("expected `{}`, found null\n", .{expected.fmt(ip)});
|
||||
return error.TestExpectedEqual;
|
||||
}
|
||||
}
|
||||
|
||||
fn interpretReportErrors(
|
||||
interpreter: *ComptimeInterpreter,
|
||||
|
Loading…
Reference in New Issue
Block a user