Files
ViperC/App/lib/core/Gen/VarGenMixin.vp
T
TermiNexus 89995dffde refactor(gen): 去除 Handles 委托层——链式化 + GenTypes/GenApi 迁移
- Gen 内部 1742 处 HandlesX.func() 改 self.Gen.Y.method() 链式调用
- 数据类/常量迁移 GenTypes.vp(10 类 + CLASS_META_*/META_*/SEQ_KIND_*/FIELD_MAX 等)
- 管线/保留文件经 GenApi.vp 模块级入口(48 函数带类型注解,从 Gen 方法签名自动生成)
- 删除 26 个纯委托 Handles 文件,仅留 5 个实体文件(Base/Translator/Expr/ExprCall/ExprCallTable)
- 修复字段分裂:TypeGenErr CurrentFile/TraceCount 链式化;CallGenBase TempDir/Cexport 真源化
2026-08-21 06:32:40 +08:00

405 lines
19 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 t, c
from stdint import *
import llvmlite
import memhub
import string
# ============================================================
# VarGenMixin - 变量/作用域符号表 Mixin(P6 对象化,wiki/14-oo-refactor.md)
#
# 从 HandlesVar.vp 迁移:模块级函数(init_vars/find_var/find_var_entry/
# lookup_var_entry/set_var_annot_class_name/set_var_ptr_element/add_var/
# _alloca_at_entry/get_or_create_var/_create_scope/init_symbol_table/
# enter_scope/exit_scope/define_var/define_module_var/lookup_var/
# lookup_current/lookup_module_var/is_module_scope/get_or_create_sym)
# → 本类方法。
#
# 迁移原则(与 P1-P5 一致,行为零变化):
# 1. 方法保留原函数签名(含 symtab/vars_ptr 等指针首参——self 是宿主
# Generator,这些函数操作数据结构指针,self 可能少用,但为风格
# 统一仍方法化)
# 2. 函数体原样搬入,本模块内部函数调用改为 self.xxx(...);
# 跨模块调用保留模块前缀(本模块无跨模块调用)
# 3. HandlesVar.vp 原函数保留为"一行委托":本地模块级单例 _g_gen
# (BuildPipeline/main 注入)cast 成 Generator 类型后调本类方法
# 4. VarEntry/Scope/SymbolTable 数据结构类保留在 HandlesVar.vp
# (本类方法签名/函数体经 HandlesVar 前缀引用)
#
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整,无 vtable 开销)。
# ============================================================
# ============================================================
# 常量(与 HandlesVar 保持一致)
# ============================================================
# 作用域类型常量
SCOPE_MODULE: t.CDefine = 0
SCOPE_FUNCTION: t.CDefine = 1
SCOPE_BLOCK: t.CDefine = 2
# 每个作用域最大变量数
# 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: Generator.Generator | t.CPtr
def init_vars(self, pool: memhub.MemBuddy | t.CPtr) -> GenTypes.VarEntry | t.CPtr:
"""分配并清零变量表数组"""
size: t.CSizeT = 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: t.CSizeT = GenTypes.VarEntry.__sizeof__()
for i in range(var_count):
entry_addr: t.CUInt64T = t.CUInt64T(vars_ptr) + i * entry_size
entry: GenTypes.VarEntry | t.CPtr = (GenTypes.VarEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry.Name is not None and entry.Used != 0:
if 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: t.CSizeT = GenTypes.VarEntry.__sizeof__()
for i in range(var_count):
entry_addr: t.CUInt64T = t.CUInt64T(vars_ptr) + i * entry_size
entry: GenTypes.VarEntry | t.CPtr = (GenTypes.VarEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
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 — 设置变量的原始类型注解类名
#
# 在函数参数定义后调用,存储原始类型注解的类名。
# 方法调用检测时,当 alloca 类型是 Ptr(i8)(联合类型简化),
# 通过 AnnotClassName 查找实际结构体类型。
# ============================================================
def set_var_annot_class_name(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str,
class_name: str) -> int:
"""设置变量的原始类型注解类名,返回 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
entry.AnnotClassName = class_name
return 0
# ============================================================
# set_var_ptr_element — 标记变量为"指针到 str/bytes"类型
#
# 注解为 bytes|t.CPtr 或 str|t.CPtr 的变量,其 alloca 类型是 i8*,
# 但下标访问应按 8 字节步长(i8** 语义),而非 1 字节步长(i8* 语义)。
# 此标志在 HandlesAnnAssign 中根据注解形式设置。
# ============================================================
def set_var_ptr_element(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str) -> int:
"""标记变量为 ptr_element 类型,返回 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
entry.IsPtrElement = 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: t.CSizeT = GenTypes.VarEntry.__sizeof__()
entry_addr: t.CUInt64T = t.CUInt64T(vars_ptr) + var_count * entry_size
entry: GenTypes.VarEntry | t.CPtr = (GenTypes.VarEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
entry.Name = name
entry.Alloca = alloca
entry.Used = 1
return 0
# ============================================================
# _alloca_at_entry - 在函数入口块生成 alloca(确保支配性)
#
# LLVM IR 要求指令支配所有使用点。如果 alloca 在条件分支内生成,
# 但在其他分支使用,会违反支配性(Instruction does not dominate all uses)。
# 标准做法:所有 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: memhub.MemBuddy | t.CPtr,
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: memhub.MemBuddy | t.CPtr,
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: memhub.MemBuddy | t.CPtr) -> 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: memhub.MemBuddy | t.CPtr = 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 t.CUInt64T(symtab.Current) == t.CUInt64T(symtab.Root):
return 1
return 0
# ============================================================
# get_or_create_sym — 查找或创建变量(在当前作用域)
# ============================================================
def get_or_create_sym(self, symtab: GenTypes.SymbolTable | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
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
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 已创建但未入符号表,后续读取该变量
# 报"未定义名称"(NewAstTest main() 289 个局部变量 > MAX_VARS=256
# 的 NameError 根因),误导排查。变量表超限暴露上游规模问题。
self.Gen.TypeGenErr.fatal_error(
None,
f"变量 '{name}' 注册失败(作用域变量数超过上限 MAX_VARS)",
"InternalCompilerError")
return alloca