fix(vipeye-internal): [SIZE]T 数组不要求源码 import t(元素成员可解析)

ViperOS 协程调度器里 `coroutines: [8]Coroutine` 字段在**没 import t** 的
文件中,`m.coroutines[prev_cid].rsp` 报「无法解析类型上的成员“rsp”访问」,
`.rsp` 拿不到语义 token(黄标、无法跳转)。

根因:`[SIZE]T` 脱糖出的合成 `t` 名(isViperBracketArraySynthetic)在
typeEvaluator 的 Name 分支里解析不到时直接降级为 Unknown,于是
`t.CArray[Coroutine, 8]` 整条塌成 Unknown → 字段类型 Unknown → 元素成员访问全断。

修法:该分支改为 `getViperImplicitModuleType(node, name) ?? Unknown`,
回退到 builtins 作用域里绑定的 `t` 模块(与 `T*` 的 rawptr 回退、
`__attribute__` 的隐式模块根同口径);仍取不到才静默 Unknown。

- viperBracketArrayAndConstPtr.test.ts:增至 7 用例(新增「无 import t 时
  [SIZE]StructT 仍解析元素类型、.rsp 得到 variable(3) 语义 token」)
- .gitignore:新增 __viper_bracket_array_no_import_t__/
- CHANGELOG:1.1.426 追加 1 条
This commit is contained in:
TermiNexus committed 2026-09-28 14:22:38 +08:00
1 parent f234e38701
commit b4808c2821
4 files changed
+76 -3

No files matched your search

+1
View File
@@ -156,6 +156,7 @@ packages/vipeye-internal/src/tests/__viper_const_ptr_index__/
packages/vipeye-internal/src/tests/__viper_const_ptr_iter__/
packages/vipeye-internal/src/tests/__viper_variadic_noimport__/
packages/vipeye-internal/src/tests/__viper_carray_element__/
packages/vipeye-internal/src/tests/__viper_bracket_array_no_import_t__/
packages/vipeye-internal/src/tests/__viper_export_extern__/
# perfCompare.py cached commit bundles
@@ -5396,9 +5396,13 @@ export function createTypeEvaluator(
} else if (node.d.isViperBracketArraySynthetic) {
// Viper 扩展:`[SIZE]T` 数组语法脱糖出的合成 `t` 名
// (见 parser._parseViperBracketArrayType)。源文件未 import t 时
// 该名无法解析,静默退化为 Unknown(不报"未定义 t"),
// 数组类型语义随之退化;正常 Viper 源码均带 import t。
type = UnknownType.create();
// 该名在词法作用域里解析不到,此时**回退到 builtins 作用域的同名模块
// 绑定**(与 `T*` 的 rawptr 回退、`__attribute__` 的隐式模块根同口径,
// 见 getViperImplicitModuleType):否则 `coroutines: [8]Coroutine`
// 这类数组字段注解会整体退化成 Unknown,其元素成员访问
// (`m.coroutines[i].rsp`)随之解析不出来。
// 仍取不到才静默退化为 Unknown(不报"未定义 t")。
type = getViperImplicitModuleType(node, name) ?? UnknownType.create();
} else if (node.d.isViperTypedef) {
// Viper 扩展:`typedef T1 = T2` 脱糖出的合成 `t` 名
// (见 parser._parseViperTypedefStatement)。该写法不要求源码
@@ -24,6 +24,10 @@
* ④ `[SIZE]StructT` / `t.CArray[StructT, N]` 的元素访问能解析出元素类型:
* `buf[i].field` 不再报「无法解析类型上的成员访问」,成员名可高亮
* (Viper 侧按 C 语义直接取 CArray 的第一个类型实参,不依赖 TypeVar 替换)。
* ⑤ `[SIZE]T` 脱糖出的合成 `t` 名**不要求源码 `import t`**:文件没 `import t` 时
* 回退到 builtins 作用域的 `t` 模块(与 `T*` 的 rawptr 回退同口径),否则
* `coroutines: [8]Coroutine` 会整条塌成 Unknown,`m.coroutines[i].rsp` 报
* 「无法解析类型上的成员“rsp”访问」且 `.rsp` 拿不到语义 token(黄标)。
*
* 运行:npx jest src/tests/viperBracketArrayAndConstPtr.test.ts --runInBand
*/
@@ -396,3 +400,66 @@ def touch():
require('fs').rmSync(testDir, { recursive: true, force: true });
}
});
/*
* ⑤ `[SIZE]T` 数组不要求源码 `import t`(ViperOS 协程调度器场景):
* class CoroutineManager:
* coroutines: [8]Coroutine
* m: CoroutineManager* = _coro_mgr_ptr
* m.coroutines[prev_cid].rsp
* 合成 `t` 名(见 parser._parseViperBracketArrayType)解析不到时原先直接降级为
* Unknown,导致 `coroutines` 字段类型整条塌成 Unknown → `m.coroutines[i].rsp`
* 报「无法解析类型上的成员“rsp”访问」,`.rsp` 也随之不高亮。修复后回退到
* builtins 作用域的 `t` 模块(与 `T*` 的 rawptr 回退同口径),无 import 也成立。
*/
test('无 import t 时 [SIZE]StructT 数组仍解析元素类型(.rsp 可高亮)', () => {
const code = `class Coroutine:
rsp: uint64
cid: int
class CoroutineManager:
current_cid: int
coroutines: [8]Coroutine
_coro_mgr_ptr: CoroutineManager* = None
def f() -> uint64:
m: CoroutineManager* = _coro_mgr_ptr
prev_cid: int = m.current_cid
return m.coroutines[prev_cid].rsp
`;
const { program, configOptions, uri, serviceProvider, testDir } = buildProgramForCode(
code,
'__viper_bracket_array_no_import_t__'
);
try {
const diagnostics = getDiagnostics(program, configOptions, uri);
console.log('=== 无 import t 的 [SIZE]T 元素访问诊断 ===');
diagnostics.forEach((d) => console.log(` L${d.line}:${d.col} [category=${d.category}] ${d.message}`));
const real = diagnostics.filter((d) => d.category === 0 || d.category === 1);
assert.strictEqual(
real.length,
0,
`无 import t 时数组元素成员访问不应报错:${JSON.stringify(real)}`
);
// `.rsp`(第 14 行,0-based 13)应拿到 variable(3) 语义 token —— 即「高亮」。
const tokensByLine = getSemanticTokens(program, uri);
const rspLine = tokensByLine.get(13) || [];
console.log('=== `m.coroutines[prev_cid].rsp` 所在行语义 token ===');
rspLine.forEach((t) => console.log(` [${t.tokenType}] ${t.text}`));
const rspToken = rspLine.find((t) => t.text === 'rsp');
assert(rspToken, `该行应有 rsp 的语义 token:${JSON.stringify(rspLine)}`);
assert.strictEqual(
rspToken!.tokenType,
3,
`rsp 应为 variable(3) 语义 token(未解析时是 undefined(11)),实际 ${rspToken!.tokenType}`
);
} finally {
program.dispose();
serviceProvider.dispose();
require('fs').rmSync(testDir, { recursive: true, force: true });
}
});
+1
View File
@@ -38,6 +38,7 @@
- `*args` is bound to the `va_list` **pointer** even when the file doesn't `import t`: the Viper var-arg rule used to look up `t.CPtr` through the source-visible `t` module only, so in a file like ViperOS `drivers/serial/uart/serial.vp` (no `import t`) it fell back to Python's `tuple` semantics and `viperlib.vsnprintf(&buf, 1024, fmt, args)` was reported as "无法将"tuple"类型的参数分配给函数"vsnprintf"中类型为"str"的参数"ap"". The lookup now falls back to the compiler built-in `rawptr` from `builtins.vpi` (= `t.CPtr`), the same mechanism `T*` annotations and `[SIZE]T` already use
- Indexing a C array now yields the **element type**, so member access on an element resolves: with `kbd_buf: [KBD_BUF_SIZE]usb_kbd_event`, `kbd_buf[i].modifier` used to report "无法解析类型上的成员"modifier"访问,对象可能未定义" and the member name got no semantic token at all (plain white). The CArray stub declares `class CArray(CType, Generic[T, N])` with `def __getitem__(self, index: int) -> T`, but Viper-side `TypeVar(...)` does not produce substitutable type variables (the class's type parameters end up as synthesized `__P0` / `__P1`), so the return type never became concrete. The Viper index path now reads the **first type argument** directly (C semantics: `arr[i]` has the element type), the same way the `CPtr[T]` branch reads `priv.typeArgs[0]`; this covers both `[SIZE]T` and the explicit `t.CArray[T, N]` spelling, and works for a union base too (`CArray[T, N] | t.CExport`)
- **`export` / `extern` on variable declarations** are now supported: `export gdt: [7]gdt_entry = []`, `export tss_buf: [104]uint8` and `extern ext_flag: int`. The variable form is told apart from the function form (`export def f()` / `export f(...)`) by the third token — `:` is a variable (type annotation), `(` / `[` is a function, the same criterion as TransPyC's `_is_export_extern_var`). They desugar exactly like TransPyC's `_parse_export_extern_var`: `export NAME: T [= v]` becomes `T | t.CExport` (bare-named definition, **no module SHA1 header**), `extern NAME: T` becomes `T | t.State` (= `CExtern | CExport`, declaration only). An **initializer is allowed for `export` but rejected for `extern`** — `extern a: int = 1` reports "extern 变量声明不能带初始值(符号定义在外部)", matching the compiler's fail-fast. Storage-class markers render as a prefix qualifier (`export CArray[gdt_entry, 7]`, `extern int`), and the marker is treated as a modifier everywhere else: in the `Struct | CPtr` fold (so `extern p: t.CPtr` stays a pointer instead of degrading to `CPtr[State]`), in the union index access, and as a CArray element access (so `gdt[i].access` still resolves to `uint8` and the zero-initializer `= []` is accepted)
- `[SIZE]T` array annotations no longer require the file to `import t`. The parser desugars `coroutines: [8]Coroutine` into a synthetic `t.CArray[Coroutine, 8]` member access; when that synthetic `t` could not be resolved through the lexical scope it silently degraded to `Unknown`, so the whole array annotation collapsed and `m.coroutines[i].rsp` reported "无法解析类型上的成员“rsp”访问" with `rsp` getting no semantic token at all (rendered unhighlighted, no jump-to-definition). The synthetic `t` now falls back to the `t` module bound in the built-in `builtins.vpi` scope — the same mechanism `T*` uses for `rawptr` and `__attribute__(...)` uses for its implicit module roots — so a file without `import t` still gets the real `CArray[Coroutine, Literal[8]]` and the element's member resolves
## 1.1.425