fix(type): 支持内建裸名函数指针构造子 callable[[...], R]

TransPyV 侧 TypeGenResolve._IsCallableSubscriptValue 已把 t.Callable[...] 与内建裸名
callable[...] / Callable[...] 视为逐点等价,TPC 侧只有模块限定形态能识别:

- TypeAnnotationResolver._from_node_subscript 的 Callable 分支要求 IsTModule(parts[0]),
  裸名落进泛型特化兜底未命中 → 空 CTypeInfo;改为裸名同判并共用同一构造体。
- DeclarationGenerator._get_type_str 的 Subscript 分支缺 callable 判定,落到末尾
  兜底 'i32';补判返回 'i8*'(与 Attribute 分支的 t.Callable 一致)。

现场:Repl.vp:307 ReplFragFn: callable[[rawptr], int] 被声明成 i32 全局,
Repl.vp:2727 ReplFragFn = fp_addr(i8*)触发「指针隐式转整数已禁止」。
This commit is contained in:
TermiNexus committed 2026-10-01 21:53:43 +08:00
1 parent f0e630457f
commit 85d68e5913
2 files changed
+21 -3

No files matched your search

+6
View File
@@ -1377,6 +1377,12 @@ class DeclarationGenerator:
if (IsPtrSubscriptNode(annotation, TCAliasMap=self._t_c_imported_names)
or (ValueIsCPtr and isinstance(annotation.slice, (ast.Attribute, ast.Name, ast.Subscript)))):
return self._ptr_subscript_type_str(annotation)
# 内建裸名函数指针构造子 callable[...] / Callable[...]:与 Attribute 分支的
# t.Callable[...] 逐点等价(同为 i8*)。缺此分支时落到下方 Name 泛型特化 /
# 兜底返回 'i32'(现场 Repl.vp:307 `ReplFragFn: callable[[rawptr], int]`
# 声明成 i32 全局 ⇒ 赋 i8* 触发「指针隐式转整数」)。
if isinstance(annotation.value, ast.Name) and (annotation.value.id == 'callable' or annotation.value.id == 'Callable'):
return 'i8*'
base: str = self._get_type_str(annotation.value)
if base == 'i8*' and isinstance(annotation.slice, ast.Constant):
return f'[{self._get_const_int(annotation.slice)} x i8]'
+15 -3
View File
@@ -954,15 +954,27 @@ class TypeAnnotationResolver:
if ElemInfo.IsPtr and not ElemInfo.ArrayDims:
Result.PtrCount = max(Result.PtrCount, 1)
return Result
# 函数指针构造子的两种书写形态逐点等价(对齐 TPV
# TypeGenResolve._IsCallableSubscriptValue):
# t.Callable[...] / c.Callable[...] → 模块限定 Attribute
# callable[...] / Callable[...] → 内建裸名(无需 import)
# 裸名形态此前落进下方泛型特化兜底(ResolveGenericClassSpecName)未命中 ⇒
# 空 CTypeInfo ⇒ 槽位类型退化 i32(现场 Repl.vp:307
# `ReplFragFn: callable[[rawptr], int]`,赋 i8* 时被「指针隐式转整数」拦下)。
parts: list[str] = []
IsBareCallable: bool = False
if isinstance(base, ast.Attribute):
parts: list[str] = []
current: ast.AST = base
while isinstance(current, ast.Attribute):
parts.insert(0, current.attr)
current = current.value
if isinstance(current, ast.Name):
parts.insert(0, current.id)
if parts and IsTModule(parts[0], SymbolTable) and parts[-1] == t.CPtr.__name__:
elif isinstance(base, ast.Name) and (base.id == 'callable' or base.id == 'Callable'):
# 裸名只在注解位置当函数指针构造子(非类型位置仍是普通标识符)
IsBareCallable = True
if isinstance(base, ast.Attribute) or IsBareCallable:
if (not IsBareCallable) and parts and IsTModule(parts[0], SymbolTable) and parts[-1] == t.CPtr.__name__:
Result: CTypeInfo = CTypeInfo()
Result.BaseType = t.CVoid()
Result.PtrCount = 1
@@ -990,7 +1002,7 @@ class TypeAnnotationResolver:
if SliceInfo and SliceInfo.BaseType and not isinstance(SliceInfo.BaseType, t.CVoid):
Result.BaseType = SliceInfo.BaseType
return Result
if parts and IsTModule(parts[0], SymbolTable) and parts[-1] == 'Callable':
if IsBareCallable or (parts and IsTModule(parts[0], SymbolTable) and parts[-1] == 'Callable'):
Result = CTypeInfo()
Result.IsFuncPtr = True
slice_node = Node.slice