fix(ctx): 方法体编译失败路径也复位「当前方法」上下文,修 self 被按内层特化类解析

现象:编译 ExprGenFstr.translate_joined_str 时 self.Gen 被按 list[cfc4ca8da95a30e1.AST*] 解析 → 属性在类中找不到(连带错误)。

链路:parts.get(pi) 触发泛型特化按需补全(HandlesExprCall._try_complete_generic_specialization)→ 内层 list[...] 方法发射时 _EmitFunctionLlvm 先置 _CurrentCpythonObjectClass = 内层类(HandlesFunctions:1978)→ 方法体编译失败只 re-raise,末尾的类名/var_struct_class 复位被跳过 → 上层调用点**吞掉**该异常继续编译外层方法,于是外层 self.X 全按内层类解析、后续指令也会误插进内层函数的块。

两处修复:① _try_complete_generic_specialization 的 except 里同时复位 Gen.builder/func 与 _CurrentCpythonObjectClass/Gen._current_class_name(该点会吞异常,必须自己收尾);② _EmitFunctionLlvm 的失败路径在 re-raise 前复位 Gen._current_class_name / _CurrentCpythonObjectClass / var_struct_class / global_struct_class(对所有吞异常的调用点都成立)。
This commit is contained in:
TermiNexus committed 2026-09-28 16:52:03 +08:00
1 parent 96d8e13c5a
commit b3f72d675b
2 files changed
+28

No files matched your search

+16
View File
@@ -2894,6 +2894,18 @@ class ExprCallHandle(BaseHandle):
# 不预先标记:_specialize_generic_class 内部缓存逻辑会检测
# cached_declare_only=True 且 declare_only=False,自动补发方法体
# 并更新缓存为 (spec_name, False)。预注册机制(L117)防止递归。
#
# 上下文快照:本函数**吞掉**特化异常、让外层方法继续编译,故必须在失败时把
# 编译器上下文复位——`_EmitFunctionLlvm` 的类名/构建器恢复只在**成功路径**
# 执行(失败路径直接 re-raise),异常穿过多层后 `Gen.builder/func` 与
# `_CurrentCpythonObjectClass` 会停在内层特化方法的中间状态:外层方法随后
# 的 `self.X` 被按内层类解析(ExprGenFstr.vp 的 `self.Gen` 被按
# `list[cfc4ca8da95a30e1.AST*]` 解析 → "属性 'Gen' 在类中找不到"的连带错误),
# 且后续指令会误插进内层函数的块。
_saved_builder: object = Gen.builder
_saved_func: object = Gen.func
_saved_cpython_class: str | None = getattr(self.Trans, '_CurrentCpythonObjectClass', None)
_saved_class_name: object = getattr(Gen, '_current_class_name', None)
try:
self.Trans.ClassHandler._specialize_generic_class(
base_name, cached_type_args, Gen,
@@ -2902,6 +2914,10 @@ class ExprCallHandle(BaseHandle):
except Exception:
# 失败时恢复 declare_only 标记,避免后续调用跳过补发
Gen._generic_class_specializations[spec_key] = (cached_name, True)
Gen.builder = _saved_builder
Gen.func = _saved_func
self.Trans._CurrentCpythonObjectClass = _saved_cpython_class
Gen._current_class_name = _saved_class_name
def _compute_field_byte_offset(self, Gen: LlvmGeneratorMixin, ClassName: str, field_name: str) -> int | None:
"""计算字段在 struct 中的字节偏移(按 LLVM 布局含对齐)。
+12
View File
@@ -2086,6 +2086,18 @@ class FunctionHandle(BaseHandle):
Gen.builder.ret_void()
except Exception:
pass
# 失败路径也要复位「当前方法」上下文(成功路径的复位在函数末尾,异常
# 直接 re-raise 会跳过它)。上层有吞掉异常的调用点(如泛型特化按需补全
# _try_complete_generic_specialization、类按需实例化),它们让外层方法
# 继续编译;若此时 _CurrentCpythonObjectClass / var_struct_class 仍指向
# 内层特化类,外层方法的 `self.X` 会被按内层类解析(典型现象:
# ExprGenFstr.translate_joined_str 的 `self.Gen` 被按
# `list[cfc4ca8da95a30e1.AST*]` 解析 → "属性 'Gen' 在类中找不到")。
Gen._current_class_name = _saved_class_name
self.Trans._CurrentCpythonObjectClass = saved_cpython_class
Gen.var_struct_class = saved_var_struct_class
if hasattr(Gen, 'global_struct_class'):
Gen.global_struct_class = saved_global_struct_class
raise
if not Gen.builder.block.is_terminated:
if 'naked' in fn_attrs: