fix(parser): 类型注解位的 __attribute__(...) 只解析调用本身,不再吞掉后续 []T

原实现用 _parse_test 解析 __attribute__(...) —— 那是通用表达式解析器,解析完调用后
会继续吃 postfix trailer,把紧随的 C 风格前置数组 `[]T` 的 `[]` 当作**合法空下标**
吞掉(_parse_slice 显式支持空下标 T[],见其注释)。后果:ac_node 不再是 Call ⇒
描述符循环 break(markers 一个都没收集),且 _match_op(Lsqb) 此时看到的是元素类型
⇒ 数组分支被跳过 ⇒ 整个注解退化成裸元素类型,且**全程零报错**。

现场:TransPyV/App/main.vp:44
  V_TPV_VERSION: __attribute__(section(".PRVERSN")) []char = "0.0.5-dev.479"
被解析成 `char`:stub 记 `V_TPV_VERSION: char`、IR 出
`@g = global i8 getelementptr ...`(opt: got 'ptr' but expected 'i8')、段名 ".PRVERSN" 丢失。

改为:_advance 过 `__attribute__` 后用 _parse_call 解析 `( 实参表 )`,postfix 不再被
误吃,`[]char` 交回数组分支脱糖为 t.CArray[char, None] → i8*。
This commit is contained in:
TermiNexus committed 2026-10-02 17:29:58 +08:00
1 parent c27ebc1b69
commit d4234c75cc
1 file changed
+17 -1
+17 -1
View File
@@ -2631,7 +2631,23 @@ def _parse_type_ann(ps: rawptr[Parser]) -> rawptr[AST]:
# 与 const / volatile 同位置、同顺序自由、可混写;每个实参 desugar 成
# `t.attr.<name>`(带调用则保留实参)后并入同一 markers 列表,由
# _PrependDescriptorMarkers 贴到类型联合最左(与 TPC 文本展开同形)。
ac_node: rawptr[AST] = _parse_test(ps)
#
# ⚠ 只用 _parse_call 解析**这个调用本身**,绝不能用 _parse_test:后者是
# 通用表达式解析器,解析完 `__attribute__(...)` 会继续吃 postfix trailer,
# 把紧随的 C 风格前置数组 `[]T` 的 `[]` 当作**合法空下标**吞掉
# (_parse_slice 显式支持空下标 `T[]`,见其注释)——全程**零报错**,
# 但 ac_node 已不是 Call ⇒ 本循环 break、markers 一个都没收集,且
# `_match_op(Lsqb)` 此时看到的是元素类型 ⇒ 数组分支被跳过 ⇒ 注解整体
# 退化成裸元素类型。现场:main.vp:44
# V_TPV_VERSION: __attribute__(section(".PRVERSN")) []char = "…"
# 被解析成 `char`(stub 记 `V_TPV_VERSION: char`、IR 出
# `global i8 <getelementptr>` 非法、段名 ".PRVERSN" 丢失)。
mka_lineno: int = _cur_lineno(ps)
mka_col: int = _cur_col(ps)
mka_fn: rawptr[AST] = Name(ps.pool, "__attribute__", ASTCtx.Load)
_set_pos(mka_fn, mka_lineno, mka_col, 0, 0)
_advance(ps) # 消费 `__attribute__`
ac_node: rawptr[AST] = _parse_call(ps, mka_fn) # 只解析 `( 实参表 )`
if ac_node is None:
break
if ac_node.kind() != ASTKind.Call: