fix(ir): reject non-type args to the 7 type-introspection builtins [F0.8]
size_of, align_of, field_count, type_name, type_eq, type_is_unsigned,
and is_flags silently reinterpreted a value argument as a type:
type_is_unsigned(6) read 6 as a TypeId index (types[6] = u8 -> true),
size_of(6)/size_of(true) sized its typeof (8), type_name(6) returned
types[6]'s name. Per Agra's ruling, all 7 now strictly require a type
(compile-time): a value argument is a compile error.
One shared guard (Lowering.reflectionTypeArgGuard, run at the top of
tryLowerReflectionCall) classifies each arg via reflectionArgIsType: a
spelled / compile-time type or generic type parameter (isStaticTypeArg),
or a runtime Type value (static type .any -- type_of(x), a []Type
element list[i], a Type-typed local/field/param) is accepted; anything
else is rejected with "<builtin> expects a type, got '<type>'". The
runtime path for type_name / type_is_unsigned is preserved (the {}
formatter calls type_is_unsigned(type_of(val)) at runtime). The 5
comptime-only builtins stay comptime-only (runtime reflection deferred).
Regression: examples/1144-diagnostics-reflection-builtin-needs-type.sx
(reject cases across all 7, exit 1). Unit test: reflectionArgIsType in
lower.test.zig. specs.md / readme.md document the strict type
requirement (and add the previously-undocumented align_of, type_eq,
type_is_unsigned). issues/0090 RESOLVED banner updated.
This commit is contained in:
@@ -1073,3 +1073,26 @@ test "lower: vectorLaneIndex maps swizzle components, colour aliases, rejects no
|
||||
try std.testing.expectEqual(@as(?u32, null), Lowering.vectorLaneIndex("len"));
|
||||
try std.testing.expectEqual(@as(?u32, null), Lowering.vectorLaneIndex(""));
|
||||
}
|
||||
|
||||
test "lower: reflectionArgIsType accepts spelled types, rejects plain values (issue 0090)" {
|
||||
const alloc = std.testing.allocator;
|
||||
var module = ir_mod.Module.init(alloc);
|
||||
defer module.deinit();
|
||||
var l = Lowering.init(&module);
|
||||
|
||||
const span = ast.Span{ .start = 0, .end = 0 };
|
||||
const ty_node = Node{ .span = span, .data = .{ .type_expr = .{ .name = "s64", .is_generic = false } } };
|
||||
const int_node = Node{ .span = span, .data = .{ .int_literal = .{ .value = 6 } } };
|
||||
const float_node = Node{ .span = span, .data = .{ .float_literal = .{ .value = 1.5 } } };
|
||||
const bool_node = Node{ .span = span, .data = .{ .bool_literal = .{ .value = true } } };
|
||||
|
||||
// A spelled type is a type → the introspection builtins accept it.
|
||||
try std.testing.expect(l.reflectionArgIsType(&ty_node));
|
||||
// Plain values are NOT types — these are exactly the arguments issue
|
||||
// 0090's strict `$T: Type` guard rejects, before a builtin could
|
||||
// reinterpret the value as a TypeId index (`type_is_unsigned(6)` → true)
|
||||
// or size its `typeof` (`size_of(true)` → 8).
|
||||
try std.testing.expect(!l.reflectionArgIsType(&int_node));
|
||||
try std.testing.expect(!l.reflectionArgIsType(&float_node));
|
||||
try std.testing.expect(!l.reflectionArgIsType(&bool_node));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user