From e63fc3405776aba5a9648637ac5ce26c59fba0de Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 27 Oct 2026 21:23:01 +0800 Subject: [PATCH] fix(codegen): reject out-of-range field defaults at declaration --- App/lib/core/Gen/StructGenMixin.vp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index 8daef55..323ccd9 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -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: