WIP: float numeric-limit accessors (NL.2) — blocked on 0091 (nan != nan); examples/docs pending

This commit is contained in:
agra
2026-06-04 16:48:34 +03:00
parent b5a2535ab6
commit 2e9e4fe873
3 changed files with 81 additions and 21 deletions

View File

@@ -4904,33 +4904,45 @@ pub const Lowering = struct {
return self.lowerFieldAccessOnType(obj, obj_ty, fa.field, span);
}
/// Numeric-limit accessor intercept (`<IntType>.min` / `.max`), a sibling of
/// the `error.X` / `Struct.CONST` / pack-arity identifier-receiver intercepts
/// in `lowerFieldAccess`. Folds an integer type's `.min`/`.max` to a comptime
/// const of that type via the shared `TypeResolver` width logic (no second
/// width parser) + the existing `constInt` const path. Returns null when this
/// is not an integer-limit access, so the caller continues normal field
/// lowering. A `.min`/`.max` on a builtin NON-numeric receiver
/// (`bool`/`string`/`void`/`Any`/`noreturn`) is a clean diagnostic here (then
/// a placeholder, so lowering finishes and `hasErrors()` aborts the build); a
/// float receiver falls through (float limits are NL.2).
/// Numeric-limit accessor intercept (`<Type>.min`/`.max`/`.epsilon`/
/// `.min_positive`/`.true_min`/`.inf`/`.nan`), a sibling of the `error.X` /
/// `Struct.CONST` / pack-arity identifier-receiver intercepts in
/// `lowerFieldAccess`. Folds the limit to a comptime const of the queried
/// type via the shared `TypeResolver` logic (no second computor) + the
/// existing `constInt` / `constFloat` const paths:
/// - integer `.min`/`.max` → `constInt` (NL.1, via `integerLimitFor`);
/// - float `.min`/`.max`/`.epsilon`/`.min_positive`/`.true_min`/`.inf`/
/// `.nan` → `constFloat` (via `floatLimitFor`).
/// Returns null when the field is not a limit accessor, or the receiver is not
/// a builtin type (a user struct → ordinary field lowering reports
/// field-not-found). Two clean diagnostics (then a placeholder, so lowering
/// finishes and `hasErrors()` aborts the build):
/// - a FLOAT-only accessor on an integer type (`s32.epsilon`, `u8.inf`);
/// - any accessor on a builtin NON-numeric receiver
/// (`bool`/`string`/`void`/`Any`/`noreturn`).
fn lowerNumericLimit(self: *Lowering, fa: *const ast.FieldAccess, span: ast.Span) ?Ref {
const name = switch (fa.object.data) {
.identifier => |id| id.name,
.type_expr => |te| te.name,
else => return null,
};
if (!std.mem.eql(u8, fa.field, "min") and !std.mem.eql(u8, fa.field, "max")) return null;
if (!TypeResolver.isLimitField(fa.field)) return null;
const ty = TypeResolver.resolveBuiltinName(name, &self.module.types) orelse return null;
if (TypeResolver.integerLimitFor(name, fa.field)) |value| {
return self.builder.constInt(value, ty);
}
// A builtin receiver that is not an integer: floats are NL.2 (fall
// through), every other builtin (bool/string/void/Any/noreturn) has no
// numeric limit.
if (ty == .f32 or ty == .f64) return null;
if (TypeResolver.floatLimitFor(name, fa.field)) |value| {
return self.builder.constFloat(value, ty);
}
// The field is a limit accessor, but it does not apply to this type.
if (self.diagnostics) |d| {
d.addFmt(.err, span, "type '{s}' has no '.{s}' — numeric limits apply only to integer and float types", .{ name, fa.field });
if (TypeResolver.integerWidthSign(name) != null) {
// Integer receiver + a float-only accessor.
d.addFmt(.err, span, "type '{s}' has no '.{s}' — '.{s}' applies only to float types (f32/f64); integer types expose only '.min'/'.max'", .{ name, fa.field, fa.field });
} else {
// Non-numeric builtin receiver (bool/string/void/Any/noreturn).
d.addFmt(.err, span, "type '{s}' has no '.{s}' — numeric limits apply only to integer and float types", .{ name, fa.field });
}
}
return self.emitPlaceholder(fa.field);
}