fix(codegen): reject out-of-range field defaults at declaration

This commit is contained in:
TermiNexus committed 2026-10-27 21:23:01 +08:00
1 parent 002a98f3f4
commit e63fc34057
1 file changed
+28
+28
View File
@@ -2478,6 +2478,34 @@ novirtual class StructGenMixin:
else:
fe.Ty = field_ty
fe.DefaultVal = default_val
# Field defaults are otherwise validated only when a constructor applies them, so an
# uninstantiated class/struct would silently keep an out-of-range literal
# (`class C: x: int8 = 1000`). Validate the integer-literal form at declaration time.
if default_val is not None:
dv_kind: int = default_val.kind()
dv_iv: int64 = 0
dv_is_int: int = 0
if dv_kind == ast.ASTKind.Constant:
dv_cn: ast.Constant* = ast.Constant*(default_val)
if dv_cn is not None and dv_cn.const_kind == ast.CONST_INT:
dv_iv = dv_cn.int_val
dv_is_int = 1
elif dv_kind == ast.ASTKind.UnaryOp:
dv_uo: ast.UnaryOp* = ast.UnaryOp*(default_val)
if dv_uo is not None and dv_uo.op == ast.OpKind.USub and dv_uo.operand is not None:
if dv_uo.operand.kind() == ast.ASTKind.Constant:
dv_cn2: ast.Constant* = ast.Constant*(dv_uo.operand)
if dv_cn2 is not None and dv_cn2.const_kind == ast.CONST_INT:
dv_iv = -dv_cn2.int_val
dv_is_int = 1
if dv_is_int != 0:
dv_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(field_ty)
# 64-bit constants are already carried as int64; 1<<63/1<<64 would overflow here.
if dv_bits == 8 or dv_bits == 16 or dv_bits == 32:
dv_lo: int64 = -(int64(1) << (dv_bits - 1))
dv_hi: int64 = (int64(1) << dv_bits) - 1
if dv_iv < dv_lo or dv_iv > dv_hi:
self.Gen.TypeGenErr.fatal_error(default_val, f"field '{field_name}' default value {dv_iv} does not fit its {dv_bits}-bit type [{dv_lo}, {dv_hi}]; use an explicit cast or widen the field type", "OverflowError")
# Instances are always PermPool-allocated (resident or a separate copy), so they naturally survive a 1b reset.
arena_f: TSpec.TypeSpecArena* = self.Gen.TypeGenBase.ensure_spec_arena()
if arena_f is not None: