comptime VM: flip Type to .type_value; migrate the .any refs that mean a Type value
type_resolver "Type" -> .type_value; const_type result + emitConstType now a bare 8-byte i64 handle (not a 16-byte Any box). Migrated every .any ref meaning "a Type value", leaving real boxed-Any refs: - "Any holds a Type" meta-marker tag .any -> .type_value at all 4 consumers (reflectArgTypeId, reflectTypeId, the comptime type_tag-as-struct path, resolveTypeCategoryTags "type"). - reflection-builtin return types (type_of/declare/define) -> .type_value; runtime type_of(any) reads the tag as a .type_value (no re-box). - expr_typer: a bare type-name expr is .type_value (backtick is_raw exempt). - reflectionArgIsType accepts .type_value OR .any (a reflection arg can be a bare Type or a boxed Any). - comptime switch_br accepts a .type_tag discriminant (type-category match). - a bare function name in a Type slot -> const_type(its function type), not a func-ref (fixes a JIT crash); old string-box kept only for genuine Any params. - field-not-found diagnostic + formatTypeName render .type_value as "Type". Fixed 3 unit tests asserting the old .any behavior. 697/0 both gates (gate ON bails cleanly to legacy since the VM doesn't model Type values yet) + 494 unit tests. 24 snapshots regenerated (22 .ir const_type shape; 2 .stderr Any->Type).
This commit is contained in:
@@ -1690,7 +1690,7 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
// self-reference resolves); the interp's `declare` returns that slot.
|
||||
const name_ref = self.lowerExpr(c.args[0]);
|
||||
const args_owned = self.alloc.dupe(Ref, &.{name_ref}) catch return Ref.none;
|
||||
return self.builder.callBuiltin(.declare, args_owned, .any);
|
||||
return self.builder.callBuiltin(.declare, args_owned, .type_value);
|
||||
}
|
||||
if (std.mem.eql(u8, name, "define")) {
|
||||
// Comptime type-construction primitive: complete a declare()'d slot
|
||||
@@ -1711,7 +1711,7 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
const args_owned = self.alloc.dupe(Ref, &.{ handle_ref, info_ref }) catch return Ref.none;
|
||||
// define returns the (now-completed) handle as a `Type` value, so the
|
||||
// one-shot constructor form chains: `T :: define(declare(), info)`.
|
||||
return self.builder.callBuiltin(.define, args_owned, .any);
|
||||
return self.builder.callBuiltin(.define, args_owned, .type_value);
|
||||
}
|
||||
if (std.mem.eql(u8, name, "type_info")) {
|
||||
// Comptime reflection-into-data: reflect a type INTO a `TypeInfo`
|
||||
@@ -1935,16 +1935,15 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
} }, .any);
|
||||
}
|
||||
if (std.mem.eql(u8, name, "type_of")) {
|
||||
// type_of(val) — produce a Type value (.any-typed aggregate).
|
||||
// type_of(val) — produce a Type value (`.type_value`, a bare i64 handle).
|
||||
if (c.args.len < 1) return self.builder.constType(.void);
|
||||
const arg_ty = self.inferExprType(c.args[0]);
|
||||
if (arg_ty == .any) {
|
||||
// Runtime: extract tag, rebuild Any with `{.any, tag}` so
|
||||
// the returned value carries Type semantics (tag field
|
||||
// says ".any" → the value field holds the type id).
|
||||
// Runtime: the held value's type is the Any's tag (field 0). Read it
|
||||
// out AS the 8-byte `.type_value` handle — type_of of a runtime Any
|
||||
// names the held value's type, so the tag IS the type id.
|
||||
const val = self.lowerExpr(c.args[0]);
|
||||
const tag_val = self.builder.structGet(val, 0, .i64);
|
||||
return self.builder.boxAny(tag_val, .any);
|
||||
return self.builder.structGet(val, 0, .type_value);
|
||||
} else {
|
||||
return self.builder.constType(arg_ty);
|
||||
}
|
||||
@@ -1988,13 +1987,17 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
/// Strict `$T: Type` classification shared by the 7 type-introspection
|
||||
/// builtins. An argument denotes a type iff it is a spelled /
|
||||
/// compile-time type or generic type parameter (the `isStaticTypeArg`
|
||||
/// shapes), or a runtime `Type` value — which is `.any`-typed at
|
||||
/// shapes), or a runtime `Type` value — which is `.type_value`-typed at
|
||||
/// runtime (`type_of(x)`, a `[]Type` element `list[i]`, a `Type`-typed
|
||||
/// local / field / param). Any other expression — a value of type
|
||||
/// i64 / f64 / bool / a struct — is NOT a type.
|
||||
pub fn reflectionArgIsType(self: *Lowering, arg: *const Node) bool {
|
||||
if (self.isStaticTypeArg(arg)) return true;
|
||||
return self.inferExprType(arg) == .any;
|
||||
// Either a bare `Type` value (`.type_value`) or an `Any` that may hold a Type
|
||||
// — the boxed reflection path (`case type: type_name(val)` where `val: Any`,
|
||||
// the runtime tag deciding). Both are valid reflection arguments.
|
||||
const ty = self.inferExprType(arg);
|
||||
return ty == .type_value or ty == .any;
|
||||
}
|
||||
|
||||
/// Guard for the type-introspection builtins (`size_of`, `align_of`,
|
||||
|
||||
@@ -1881,8 +1881,12 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
d.addFmt(.err, node.span, "'{s}' is not visible; #import the module that declares it", .{eff_fn_name});
|
||||
break :blk self.emitError(eff_fn_name, node.span);
|
||||
}
|
||||
// Type-as-value: if target is Any (Type variable), produce a type name string
|
||||
if (self.target_type == .any) {
|
||||
// Type-as-value: a bare function name in a `Type` (`.type_value`)
|
||||
// slot is its FUNCTION TYPE — `const_type(() -> R)` — so it prints
|
||||
// / reflects as the real function type, not a func-ref. For a
|
||||
// genuine `Any` param the old behavior is kept (a formatted
|
||||
// type-name string boxed as Any).
|
||||
if (self.target_type == .any or self.target_type == .type_value) {
|
||||
const fd_any: ?*const ast.FnDecl = self.program_index.fn_ast_map.get(eff_fn_name) orelse fd_blk: {
|
||||
switch (self.selectPlainCallableAuthor(id.name, self.current_source_file.?)) {
|
||||
.func => |sf| break :fd_blk sf.decl,
|
||||
@@ -1890,6 +1894,13 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
}
|
||||
};
|
||||
if (fd_any) |fd| {
|
||||
if (self.target_type == .type_value) {
|
||||
var param_ids = std.ArrayList(TypeId).empty;
|
||||
defer param_ids.deinit(self.alloc);
|
||||
for (fd.params) |p| param_ids.append(self.alloc, self.resolveParamType(&p)) catch {};
|
||||
const fn_tid = self.module.types.functionType(param_ids.items, self.resolveReturnType(fd));
|
||||
break :blk self.builder.constType(fn_tid);
|
||||
}
|
||||
const fn_type_str = self.formatFnTypeString(fd);
|
||||
const sid = self.module.types.internString(fn_type_str);
|
||||
const str = self.builder.constString(sid);
|
||||
|
||||
@@ -438,6 +438,7 @@ pub fn formatTypeName(self: *Lowering, ty: TypeId) []const u8 {
|
||||
if (ty == .void) return "void";
|
||||
if (ty == .string) return "string";
|
||||
if (ty == .any) return "Any";
|
||||
if (ty == .type_value) return "Type";
|
||||
if (ty == .usize) return "usize";
|
||||
if (ty == .isize) return "isize";
|
||||
|
||||
@@ -665,7 +666,9 @@ pub fn resolveTypeCategoryTags(self: *Lowering, name: []const u8) []const u64 {
|
||||
return tags.items;
|
||||
}
|
||||
if (std.mem.eql(u8, name, "type") or std.mem.eql(u8, name, "Type")) {
|
||||
tags.append(self.alloc, TypeId.any.index()) catch {};
|
||||
// A Type value's runtime tag is `.type_value` (was `.any` when Type and
|
||||
// Any shared a TypeId) — so `case type:` matches an Any holding a Type.
|
||||
tags.append(self.alloc, TypeId.type_value.index()) catch {};
|
||||
return tags.items;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user