Files
ViperC/App/lib/core/Gen/VarGenMixin.vp
T
2026-09-16 16:06:48 +08:00

507 lines
25 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import lib.core.Gen.Type.TypeSpec as TSpec
import t, c
from stdint import *
import llvmlite
import memhub
import string
import stdlib
# ============================================================
# VarGenMixin - 变量/作用域符号表 Mixin
#
# 职责:变量注册/查找、作用域进出(模块/函数/match)与 alloca 创建。
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整,无 vtable 开销)。
# ============================================================
# ============================================================
# 常量(与 HandlesVar 保持一致)
# ============================================================
# 作用域类型常量
SCOPE_MODULE: t.CDefine = 0
SCOPE_FUNCTION: t.CDefine = 1
SCOPE_BLOCK: t.CDefine = 2
# match case 作用域(对齐 MatchGenMixin.SCOPE_MATCH;本地定义避免循环依赖)。
# 该作用域仅用于"变体字段绑定隔离",普通赋值语义上属于外层函数作用域,
# get_or_create_sym/_lookup_through_match_scope 会"看穿"它写回外层变量。
SCOPE_MATCH: t.CDefine = 6
# 每个作用域最大变量数
# 256 太小:NewAstTest main() 有 289 个局部变量(几十个测试用例),
# add_var 超限静默失败导致变量未注册,后续使用报"未定义名称"(NameError)。
MAX_VARS: t.CDefine = 1024
# ============================================================
# VarGenMixin - 变量/作用域符号表 Mixin
# ============================================================
@t.NoVTable
class VarGenMixin:
"""变量/作用域 Mixin(P6 方法化)"""
# 迁移自 HandlesVar 的模块级函数
# ============================================================
# init_vars — 分配并清零 VarEntry 数组
# ============================================================
Gen: rawptr[Generator.Generator]
def init_vars(self, pool: rawptr[memhub.MemBuddy]) -> GenTypes.VarEntry | t.CPtr:
"""分配并清零变量表数组"""
size: sizet = MAX_VARS * GenTypes.VarEntry.__sizeof__()
vars_ptr: GenTypes.VarEntry | t.CPtr = pool.alloc(size)
if vars_ptr is not None:
string.memset(vars_ptr, 0, size)
return vars_ptr
# ============================================================
# find_var — 在变量表中按名称查找
# ============================================================
def find_var(self, vars_ptr: GenTypes.VarEntry | t.CPtr,
var_count: int,
name: str) -> llvmlite.Value | t.CPtr:
"""在变量表中按名称查找"""
if name is None or vars_ptr is None:
return None
entry_size: sizet = GenTypes.VarEntry.__sizeof__()
for i in range(var_count):
entry_addr: uint64 = uint64(vars_ptr) + i * entry_size
entry: GenTypes.VarEntry | t.CPtr = (GenTypes.VarEntry | t.CPtr)(entry_addr)
if entry.Name is not None and entry.Used != 0:
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp
if entry.Name[0] == name[0] and string.strcmp(entry.Name, name) == 0:
return entry.Alloca
return None
# ============================================================
# find_var_entry — 在变量表中按名称查找,返回 VarEntry(含 AnnotTy)
# ============================================================
def find_var_entry(self, vars_ptr: GenTypes.VarEntry | t.CPtr,
var_count: int,
name: str) -> GenTypes.VarEntry | t.CPtr:
"""在变量表中按名称查找,返回 VarEntry 或 None"""
if name is None or vars_ptr is None:
return None
entry_size: sizet = GenTypes.VarEntry.__sizeof__()
for i in range(var_count):
entry_addr: uint64 = uint64(vars_ptr) + i * entry_size
entry: GenTypes.VarEntry | t.CPtr = (GenTypes.VarEntry | t.CPtr)(entry_addr)
if entry.Name is not None and entry.Used != 0:
if string.strcmp(entry.Name, name) == 0:
return entry
return None
# ============================================================
# lookup_var_entry — 从当前作用域逐级向上查找变量,返回 VarEntry
# ============================================================
def lookup_var_entry(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str) -> GenTypes.VarEntry | t.CPtr:
"""从当前作用域逐级向上查找变量,返回 VarEntry 或 None"""
if symtab is None or name is None:
return None
scope: GenTypes.Scope | t.CPtr = symtab.Current
while scope is not None:
result: GenTypes.VarEntry | t.CPtr = self.find_var_entry(scope.Vars, scope.VarCount, name)
if result is not None:
return result
scope = scope.Parent
return None
# ============================================================
# set_var_annot_class_name — 设置变量的原始类型注解类名
#
# 在函数参数定义后调用,存储原始类型注解的类名(Spec.SourceName)。
# 方法调用检测时,当 alloca 类型是 Ptr(i8)(联合类型简化),
# 通过 Spec.SourceName 查找实际结构体类型。
# ============================================================
def set_var_annot_class_name(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str,
class_name: str) -> int:
"""设置变量的原始类型注解类名(写入 Spec.SourceName),返回 0=成功 / 1=失败
[S4] 原 VarEntry.AnnotClassName 字段已删除,注解名统一承载在 Spec.SourceName:
- 整数符号性:SpecFromLLVMType 反推恒 TS_INT(LLVM iN 不分符号),
注解名携带真实无符号性(uint32/CUInt32T 等)→ 切 TS_INT→TS_UINT;
- 来源名:`X | t.CPtr` 简化为 Ptr(i8) 后 Spec 里没有类名,附上供
属性访问/方法分派恢复(消费点经 var_annot_class_name 读取)。
类型信息缺失(SpecFromLLVMType 不适用致 Spec 为 None)时建 TS_UNKNOWN
承载注解名 —— TS_UNKNOWN 不表态任何具体类型,等价于原来的"Spec 缺失"。
"""
if symtab is None or name is None:
return 1
entry: GenTypes.VarEntry | t.CPtr = self.lookup_var_entry(symtab, name)
if entry is None:
return 1
if class_name is None:
# 无注解名:不写入(VarEntry 槽位不复用,原值恒为 None,与清空等价)。
return 0
arena_sn: TSpec.TypeSpecArena | t.CPtr = self.Gen.TypeGenBase.ensure_spec_arena()
if arena_sn is None:
return 1
if entry.Spec is None:
entry.Spec = TSpec.NewTypeSpec(arena_sn, TSpec.TS_UNKNOWN, 0,
TSpec.TS_SIGN_UNKNOWN)
if entry.Spec is None:
return 1
sign: int = self.Gen.TypeGenCType.spec_sign_of_annot(class_name)
if sign != TSpec.TS_SIGN_UNKNOWN:
entry.Spec = TSpec.SpecWithSign(arena_sn, entry.Spec, sign)
entry.Spec = TSpec.SpecWithSource(entry.Spec, class_name)
return 0
# ============================================================
# var_annot_class_name - 变量条目注解类名的统一读取入口(S4 切读·类名块)
#
# 注解侧由 set_var_annot_class_name 经 SpecWithSource 写入 Spec.SourceName。
# 变量侧类名恢复读取点统一走此入口。
# ============================================================
def var_annot_class_name(self, entry: GenTypes.VarEntry | t.CPtr) -> str:
"""变量条目注解类名(Spec.SourceName);未设置返回 None"""
if entry is None:
return None
return TSpec.SpecSourceName(entry.Spec)
# set_var_ptr_element — 标记变量为「指针元素(ptr-element)」语义
# 注解为 bytes|t.CPtr / str|t.CPtr / T|t.CPtr / 64位标量|t.CPtr 的变量,其
# LLVM 类型退化为 i8*(Ptr(Int8)),但下标访问应按 8 字节槽步长(i8** 语义),
# 而非 1 字节步长(i8* 语义)。
# 该语义由 Spec.ElemBits 承载:上述四类在 LLVM 层都是 Ptr(Int8)(ElemBits=8,
# 无法区分),故注解侧把元素槽宽覆盖为 8 字节,消费点经 var_is_ptr_element 读取。
# 由 AnnAssign 按注解形式(_is_ptr_element_annotation)调用。
def set_var_ptr_element(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str) -> int:
"""标记变量为 ptr-element(Spec.ElemBits=64),返回 0=成功 / 1=失败"""
if symtab is None or name is None:
return 1
entry: GenTypes.VarEntry | t.CPtr = self.lookup_var_entry(symtab, name)
if entry is None:
return 1
# ptr-element 变量的 alloca 是 Ptr(Ptr(Int8)),其值类型 Ptr(Int8) ⇒ Spec 必为
# PtrLevel>=1。不满足即上游异常(类型缺失/退化),fail-fast 暴露 ——
# 若静默放过,读点 var_is_ptr_element 返回 0 → 下标按 1 字节误算(数据损坏)。
if entry.Spec is None or entry.Spec.PtrLevel < 1:
self.Gen.TypeGenErr.fatal_error(
None, f"变量 '{name}' 标记为 ptr-element,但其 Spec 不可承载指针语义",
"InternalCompilerError")
return 1
entry.Spec = TSpec.SpecWithElemBits(entry.Spec, 64)
return 0
# var_is_ptr_element - 变量是否「指针元素」语义(Spec.ElemBits 承载)
# 判据:PtrLevel>=1 且元素位宽为 8(LLVM 层退化后的 i8*)且 ElemBits==64
# (注解侧覆盖的 8 字节槽)。后两个条件缺一不可:真实 i64* 变量 ElemBits 也为 64
# 但 Elem.Bits=64,不命中;纯 str/bytes/char|t.CPtr 变量 ElemBits=8,同样不命中。
def var_is_ptr_element(self, entry: GenTypes.VarEntry | t.CPtr) -> int:
"""变量是否为 ptr-element 语义(1=是 / 0=否)"""
if entry is None or entry.Spec is None:
return 0
sp: TSpec.TypeSpec | t.CPtr = entry.Spec
if sp.PtrLevel < 1 or sp.Elem is None:
return 0
if sp.Elem.Bits != 8:
return 0
if sp.ElemBits == 64:
return 1
return 0
# ============================================================
# add_var — 添加变量到变量表
# ============================================================
def add_var(self, vars_ptr: GenTypes.VarEntry | t.CPtr,
var_count: int,
name: str,
alloca: llvmlite.Value | t.CPtr) -> int:
"""添加变量到变量表"""
if name is None or alloca is None or vars_ptr is None:
return 1
if var_count >= MAX_VARS:
return 1
entry_size: sizet = GenTypes.VarEntry.__sizeof__()
entry_addr: uint64 = uint64(vars_ptr) + var_count * entry_size
entry: GenTypes.VarEntry | t.CPtr = (GenTypes.VarEntry | t.CPtr)(entry_addr)
# 变量名必须深拷贝到 stdlib(跨 pool reset 存活):entry.Name 直接存 AST
# 节点 Name.id 指针时,该段内存被后续分配覆盖会使 find_var 的 strcmp 静默
# 失配 → Handle 阶段找不到 PreScan 已建的同名 alloca → 另建类型退化的
# alloca(随后 .get 等按错误元素类型分派)。深拷贝后符号表条目不再依赖
# AST tree 生命周期。
name_len: sizet = string.strlen(name)
name_copy: bytes = stdlib.malloc(name_len + 1)
if name_copy is None:
return 1
string.strcpy(name_copy, name)
entry.Name = name_copy
entry.Alloca = alloca
entry.Used = 1
# [S3 条目双写] 值类型 Spec(alloca.Ty 是 Ptr(值类型) ⇒ 取其 pointee)
entry.Spec = self._spec_of_var_alloca(alloca)
return 0
# ============================================================
# _spec_of_var_alloca - 由 alloca 的值类型构造 TypeSpec(S3 条目双写)
#
# alloca.Ty 为 Ptr(var_ty)(见 _alloca_at_entry / 全局变量 SSAValue 路径),
# 故取 pointee 才是变量的**值类型**。失败或类型不适用(Label/Metadata 等
# SpecFromLLVMType 不支持者)返回 None —— 后者是设计内的「不适用」,非兜底。
# 分配一律 PermPool 驻留 ⇒ 跨文件/跨 1b reset 安全。
# ============================================================
def _spec_of_var_alloca(self, alloca: llvmlite.Value | t.CPtr) -> TSpec.TypeSpec | t.CPtr:
"""由 alloca 的值类型构造 TypeSpec,不适用/失败返回 None"""
if alloca is None or alloca.Ty is None:
return None
ty: llvmlite.LLVMType | t.CPtr = alloca.Ty
if self.Gen.ExprGenBase.is_ptr_type(ty) != 0:
ty = ty.Pointee
arena: TSpec.TypeSpecArena | t.CPtr = self.Gen.TypeGenBase.ensure_spec_arena()
if arena is None:
return None
return TSpec.SpecFromLLVMType(arena, ty)
# _alloca_at_entry - 在函数入口块生成 alloca(确保支配性)
# LLVM IR 要求指令支配所有使用点,故所有 alloca 须在函数入口块生成。
# 实现:临时切换 builder.CurBlock 到入口块,生成 alloca 后恢复。
def _alloca_at_entry(self, builder: llvmlite.IRBuilder | t.CPtr,
ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""在函数入口块生成 alloca,返回 alloca 值
使用 llvmlite.build_alloca_at_entry 在入口块终止指令之前插入 alloca,
确保所有 alloca 在入口块,避免支配性违规。
"""
if builder is None or ty is None:
return None
return llvmlite.build_alloca_at_entry(builder, ty)
# get_or_create_var — 查找或创建变量 alloca(旧版兼容)
def get_or_create_var(self, pool: rawptr[memhub.MemBuddy],
builder: llvmlite.IRBuilder | t.CPtr,
vars_ptr: GenTypes.VarEntry | t.CPtr,
var_count: int,
name: str,
ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""查找或创建变量 alloca"""
existing: llvmlite.Value | t.CPtr = self.find_var(vars_ptr, var_count, name)
if existing is not None:
return existing
alloca: llvmlite.Value | t.CPtr = self._alloca_at_entry(builder, ty)
if alloca is None:
return None
if self.add_var(vars_ptr, var_count, name, alloca) != 0:
return None
return alloca
# ============================================================
# _create_scope — 创建新作用域节点(内部辅助函数)
# ============================================================
def _create_scope(self, pool: rawptr[memhub.MemBuddy],
parent: GenTypes.Scope | t.CPtr,
kind: int) -> GenTypes.Scope | t.CPtr:
"""创建并初始化作用域节点"""
scope: GenTypes.Scope | t.CPtr = pool.alloc(GenTypes.Scope.__sizeof__())
if scope is None:
return None
string.memset(scope, 0, GenTypes.Scope.__sizeof__())
scope.Parent = parent
scope.Vars = self.init_vars(pool)
scope.VarCount = 0
scope.Kind = kind
if scope.Vars is None:
return None
return scope
# ============================================================
# init_symbol_table — 创建符号表(含模块级根作用域)
# ============================================================
def init_symbol_table(self, pool: rawptr[memhub.MemBuddy]) -> GenTypes.SymbolTable | t.CPtr:
"""创建并初始化符号表,包含模块级根作用域"""
if pool is None:
return None
symtab: GenTypes.SymbolTable | t.CPtr = pool.alloc(GenTypes.SymbolTable.__sizeof__())
if symtab is None:
return None
string.memset(symtab, 0, GenTypes.SymbolTable.__sizeof__())
symtab.Pool = pool
# 创建根作用域(模块级)
root: GenTypes.Scope | t.CPtr = self._create_scope(pool, None, SCOPE_MODULE)
if root is None:
return None
symtab.Root = root
symtab.Current = root
return symtab
# ============================================================
# enter_scope — 进入新作用域
# ============================================================
def enter_scope(self, symtab: GenTypes.SymbolTable | t.CPtr,
kind: int) -> GenTypes.Scope | t.CPtr:
"""进入新作用域,返回新创建的作用域"""
if symtab is None:
return None
pool: rawptr[memhub.MemBuddy] = symtab.Pool
scope: GenTypes.Scope | t.CPtr = self._create_scope(pool, symtab.Current, kind)
if scope is None:
return None
symtab.Current = scope
return scope
# ============================================================
# exit_scope — 退出当前作用域
# ============================================================
def exit_scope(self, symtab: GenTypes.SymbolTable | t.CPtr):
"""退出当前作用域,恢复到父作用域"""
if symtab is not None and symtab.Current is not None:
symtab.Current = symtab.Current.Parent
# ============================================================
# define_var — 在当前作用域定义变量
# ============================================================
def define_var(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str,
alloca: llvmlite.Value | t.CPtr) -> int:
"""在当前作用域定义变量,返回 0 成功"""
if symtab is None or name is None or alloca is None:
return 1
scope: GenTypes.Scope | t.CPtr = symtab.Current
if scope is None:
return 1
ret: int = self.add_var(scope.Vars, scope.VarCount, name, alloca)
if ret == 0:
scope.VarCount = scope.VarCount + 1
return ret
# ============================================================
# define_module_var — 在模块作用域定义变量
# ============================================================
def define_module_var(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str,
alloca: llvmlite.Value | t.CPtr) -> int:
"""在模块作用域定义变量,返回 0 成功"""
if symtab is None or name is None or alloca is None:
return 1
scope: GenTypes.Scope | t.CPtr = symtab.Root
if scope is None:
return 1
ret: int = self.add_var(scope.Vars, scope.VarCount, name, alloca)
if ret == 0:
scope.VarCount = scope.VarCount + 1
return ret
# ============================================================
# lookup_var — 从当前作用域逐级向上查找变量
# ============================================================
def lookup_var(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str) -> llvmlite.Value | t.CPtr:
"""从当前作用域逐级向上查找变量,返回 alloca 或 None"""
if symtab is None or name is None:
return None
scope: GenTypes.Scope | t.CPtr = symtab.Current
while scope is not None:
result: llvmlite.Value | t.CPtr = self.find_var(
scope.Vars, scope.VarCount, name)
if result is not None:
return result
scope = scope.Parent
return None
# ============================================================
# lookup_current — 仅在当前作用域查找变量
# ============================================================
def lookup_current(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str) -> llvmlite.Value | t.CPtr:
"""仅在当前作用域查找变量"""
if symtab is None or name is None:
return None
scope: GenTypes.Scope | t.CPtr = symtab.Current
if scope is None:
return None
return self.find_var(scope.Vars, scope.VarCount, name)
# ============================================================
# lookup_module_var — 在模块作用域查找变量
# ============================================================
def lookup_module_var(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str) -> llvmlite.Value | t.CPtr:
"""在模块作用域查找变量"""
if symtab is None or name is None:
return None
scope: GenTypes.Scope | t.CPtr = symtab.Root
if scope is None:
return None
return self.find_var(scope.Vars, scope.VarCount, name)
# ============================================================
# is_module_scope - 判断当前是否在模块级作用域
#
# 用于区分模块级代码和函数内代码:
# - 模块级代码:AnnAssign 应修改模块级变量(Python 语义)
# - 函数内代码:无 global 声明时 AnnAssign 应创建局部变量(Python 语义)
# ============================================================
def is_module_scope(self, symtab: GenTypes.SymbolTable | t.CPtr) -> int:
"""判断当前作用域是否为模块级作用域(Current == Root)
Returns: 1=模块级作用域, 0=函数内作用域
"""
if symtab is None:
return 0
if symtab.Root is None or symtab.Current is None:
return 0
# 比较指针:Current 与 Root 相同则为模块级
if uint64(symtab.Current) == uint64(symtab.Root):
return 1
return 0
# _lookup_through_match_scope — 穿越 match case 作用域查找变量
# match case 作用域(SCOPE_MATCH)仅用于"变体字段绑定隔离"(case 绑定的模式
# 变量互不泄漏);case 体内对"外层普通变量"的赋值语义上应写回外层 alloca,
# 而非新建 case 局部槽——否则外层变量恒为初始值。
# 遍历规则:逐级向上查找;SCOPE_MATCH 边界可穿越(可命中外层 case 声明的变量),
# 遇到第一个非 match 作用域(函数/模块)后停止——不跨函数边界(闭包 nonlocal
# 语义由 is_nonlocal_name 单独处理)。
def _lookup_through_match_scope(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str) -> llvmlite.Value | t.CPtr:
"""从当前作用域向上,仅穿越 SCOPE_MATCH 作用域查找变量 alloca"""
if symtab is None or name is None:
return None
scope: GenTypes.Scope | t.CPtr = symtab.Current
while scope is not None:
result: llvmlite.Value | t.CPtr = self.find_var(
scope.Vars, scope.VarCount, name)
if result is not None:
return result
if scope.Kind == SCOPE_MATCH:
scope = scope.Parent
continue
return None
return None
# ============================================================
# get_or_create_sym — 查找或创建变量(在当前作用域)
# ============================================================
def get_or_create_sym(self, symtab: GenTypes.SymbolTable | t.CPtr,
pool: rawptr[memhub.MemBuddy],
builder: llvmlite.IRBuilder | t.CPtr,
name: str,
ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""查找或创建变量 alloca(在当前作用域)"""
existing: llvmlite.Value | t.CPtr = self.lookup_current(symtab, name)
if existing is not None:
return existing
# SCOPE_MATCH 隔离作用域对"赋值"必须透明(详见 _lookup_through_match_scope):
# case 体内对外层变量的赋值要写回外层 alloca。仅穿越 match 边界,
# 不跨函数/模块作用域(global/nonlocal 在 AssignGenMixin 提前拦截)。
existing = self._lookup_through_match_scope(symtab, name)
if existing is not None:
return existing
alloca: llvmlite.Value | t.CPtr = self._alloca_at_entry(builder, ty)
if alloca is None:
return None
if self.define_var(symtab, name, alloca) != 0:
# fail-fast: 变量注册失败(如超过 MAX_VARS)必须立即终止。
# 静默忽略返回值会使 alloca 已建但未入符号表,后续读取报"未定义名称"。
self.Gen.TypeGenErr.fatal_error(
None,
f"变量 '{name}' 注册失败(作用域变量数超过上限 MAX_VARS)",
"InternalCompilerError")
return alloca