fix(type): const 等限定联合不再被误判为 str*/bytes*

_is_ptr_element_annotation 原先只要注解联合里出现 str/bytes 就返回 1(按 i8** 处理,
8 字节元素步长),于是 const str(t.CConst | str,实际仍是 i8*)的下标取到的是指针。
现在要求联合必须带 CPtr 标记(真正的 str*/bytes*)才判定;模块级全局的同一处逻辑
(TranslatorGenMixin)同步收紧。新增 _annotation_has_cptr_marker 供两处复用。

复现:def F(p: str) -> int: / path: const str = p / ch: char = path[i]
      -> CastError 指针转整数(形参位置的 const str 一直正常,仅局部/全局错)
This commit is contained in:
TermiNexus committed 2026-10-07 14:35:25 +08:00
1 parent fb3d3787e6
commit 5993affd9c
2 files changed
+27 -4

No files matched your search

+24 -2
View File
@@ -517,6 +517,24 @@ novirtual class AssignGenMixin:
if self._annotation_contains_name(bop.right, name) != 0: return 1
return 0
def _annotation_has_cptr_marker(self, annot: ast.AST*) -> int:
"""Return 1 if the annotation union carries a CPtr marker (the star form `<base> | t.CPtr`)."""
if annot is None: return 0
k: int = annot.kind()
if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(annot)
if at.attr is not None and string.strcmp(at.attr, "CPtr") == 0: return 1
return 0
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(annot)
if nm.id is not None and string.strcmp(nm.id, "CPtr") == 0: return 1
return 0
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(annot)
if self._annotation_has_cptr_marker(bop.left) != 0: return 1
if self._annotation_has_cptr_marker(bop.right) != 0: return 1
return 0
# _is_ptr_element_annotation - check whether an annotation is bytes* or str*
# bytes*/str* => 8-byte element stride (i8**); char*/int8* => 1-byte stride.
def _is_ptr_element_annotation(self, annot: ast.AST*) -> int:
@@ -526,8 +544,12 @@ novirtual class AssignGenMixin:
if k != ast.ASTKind.BinOp: return 0
bop: ast.BinOp* = ast.BinOp*(annot)
if bop.op != ast.OpKind.BitOr: return 0
if self._annotation_contains_name(annot, "str") != 0: return 1
if self._annotation_contains_name(annot, "bytes") != 0: return 1
# str/bytes are already i8*; only the union WITH a pointer marker (str*/bytes*) is i8** and
# needs 8-byte element slots. A union that merely carries them (e.g. `const str`, which is
# t.CConst | str and stays i8*) must keep the 1-byte stride.
if self._annotation_has_cptr_marker(annot) != 0:
if self._annotation_contains_name(annot, "str") != 0: return 1
if self._annotation_contains_name(annot, "bytes") != 0: return 1
# Generic type param / nested union | t.CPtr: scalars use their own size, pointers use 8-byte slots
if self._is_generic_tp_or_nested(bop.left) != 0 or self._is_generic_tp_or_nested(bop.right) != 0:
gp_nm: str = None
+3 -2
View File
@@ -859,8 +859,9 @@ novirtual class TranslatorGenMixin:
if aa_ml.annotation.kind() == ast.ASTKind.BinOp:
bop_ml: ast.BinOp* = ast.BinOp*(aa_ml.annotation)
if bop_ml.op == ast.OpKind.BitOr:
if self.Gen.AssignGen._annotation_contains_name(aa_ml.annotation, "str") != 0 or self.Gen.AssignGen._annotation_contains_name(aa_ml.annotation, "bytes") != 0:
self.Gen.VarGen.set_var_ptr_element(trans.SymTab, var_name)
if self.Gen.AssignGen._annotation_has_cptr_marker(aa_ml.annotation) != 0:
if self.Gen.AssignGen._annotation_contains_name(aa_ml.annotation, "str") != 0 or self.Gen.AssignGen._annotation_contains_name(aa_ml.annotation, "bytes") != 0:
self.Gen.VarGen.set_var_ptr_element(trans.SymTab, var_name)
return 1
return 0