Files
ViperC/App/lib/core/Gen/VarGenMixin.vp
T
TermiNexus d99b6268de refactor(tpv): Gen 目录按前缀分组迁移子目录 + REnum.kind() 编译期转 __tag
- Gen/*.vp 按前缀分组迁入 Call/Class/Expr/Func/Stmt/Type 子目录,import 同步更新(BuildPipeline/Generator/StructGenMixin 等)

- CallGenDispatch: REnum.kind() 前置拦截 → bitcast i32* + load(读 __tag),杜绝方法查找兜底误绑 AST.kind(this 偏移语义错误)

- 留根:Assign/Async/Base/Imports/Match/Struct/Translator/Var 单文件前缀 + GenApi/GenTypes/Generator/__init__
2026-08-28 14:16:03 +08:00

437 lines
21 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)
# → 本类方法。
#
# @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: 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
# ============================================================
# _lookup_through_match_scope — 穿越 match case 作用域查找变量
#
# match case 作用域(SCOPE_MATCH)仅用于"变体字段绑定隔离"(case 绑定的
# 模式变量互不泄漏)。case 体内对"外层普通变量"的赋值(如 FuncGenDef
# 参数 match 的 param_ty2 = Ptr(...),或 translate_subscript 内层 case 的
# list_struct_name = lsn)语义上应写回外层 alloca,而非新建 case 局部槽
# ——否则外层变量恒为初始值。自举证据:gen1 编译出的 gen2 其 FuncGenDef
# 参数 match 把赋值写进 case 局部槽、build_alloca 读初始 i32_ty,导致
# 指针参数 alloca 退化为 i32 → FuncTest main.vp:32 `p[0] = p[0] + val`
# 的 translate_subscript 读 i32 非指针返回 None("赋值右侧为 None
# [kind=11]" 根因)。
# 遍历规则:逐级向上查找;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: 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
# 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 已创建但未入符号表,后续读取该变量
# 报"未定义名称"(NewAstTest main() 289 个局部变量 > MAX_VARS=256
# 的 NameError 根因),误导排查。变量表超限暴露上游规模问题。
self.Gen.TypeGenErr.fatal_error(
None,
f"变量 '{name}' 注册失败(作用域变量数超过上限 MAX_VARS)",
"InternalCompilerError")
return alloca