From 988ba922f4feaf1ab101230f3c4356f0aef616bb Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 22 Sep 2026 21:28:50 +0800 Subject: [PATCH] =?UTF-8?q?refactor(vipeye-internal):=20str/bytes=20?= =?UTF-8?q?=E8=BF=AD=E4=BB=A3=E5=85=83=E7=B4=A0=E7=B1=BB=E5=9E=8B=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E4=B8=BA=20char?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 接上次下标统一:`for ch in s` 的迭代元素,以及 str/bytes 作为 Iterable/Iterator 实参 时的泛型推断(enumerate/zip/map/filter/reversed/sorted/list() 等)也由 t.CChar 别名 int 改为内置 char。至此 `s[i]`、`*s`、迭代三种取字符方式元素类型一致。 删除已无引用的 getViperCCharType 辅助函数。 回归用例:viperEnumerate.test.ts 断言改为 enumerate[char] / ch 为 char。 版本号提升至 1.1.422 并更新 CHANGELOG。 --- packages/vipeye-internal/package-lock.json | 4 +- packages/vipeye-internal/package.json | 2 +- .../src/analyzer/typeEvaluator.ts | 73 +++---------------- .../src/tests/viperEnumerate.test.ts | 14 ++-- packages/vipeye/package-lock.json | 4 +- packages/vipeye/package.json | 2 +- packages/vscode-vipeye/CHANGELOG.md | 6 +- packages/vscode-vipeye/package-lock.json | 4 +- packages/vscode-vipeye/package.json | 2 +- 9 files changed, 30 insertions(+), 81 deletions(-) diff --git a/packages/vipeye-internal/package-lock.json b/packages/vipeye-internal/package-lock.json index 2f48c444..3ab9960b 100644 --- a/packages/vipeye-internal/package-lock.json +++ b/packages/vipeye-internal/package-lock.json @@ -1,12 +1,12 @@ { "name": "vipeye-internal", - "version": "1.1.421", + "version": "1.1.422", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vipeye-internal", - "version": "1.1.421", + "version": "1.1.422", "license": "MIT", "dependencies": { "@yarnpkg/fslib": "2.10.4", diff --git a/packages/vipeye-internal/package.json b/packages/vipeye-internal/package.json index 27cf6a7a..155c332a 100644 --- a/packages/vipeye-internal/package.json +++ b/packages/vipeye-internal/package.json @@ -2,7 +2,7 @@ "name": "vipeye-internal", "displayName": "vipeye", "description": "Type checker for the Viper language", - "version": "1.1.421", + "version": "1.1.422", "license": "MIT", "private": true, "files": [ diff --git a/packages/vipeye-internal/src/analyzer/typeEvaluator.ts b/packages/vipeye-internal/src/analyzer/typeEvaluator.ts index 4322824e..30ac5d44 100644 --- a/packages/vipeye-internal/src/analyzer/typeEvaluator.ts +++ b/packages/vipeye-internal/src/analyzer/typeEvaluator.ts @@ -3291,15 +3291,11 @@ export function createTypeEvaluator( } return UnknownType.create(isIncomplete); } - // Viper 扩展:str/bytes 类型迭代返回 t.CChar(字符)。 - // Viper 语义:str/bytes = i8* 指针(const char*),迭代返回单个字符 CChar。 - // 使用 getViperCCharType 而非裸 int,保持与 s[i] 下标访问一致(t.CChar 显示)。 + // Viper 扩展:str/bytes 类型迭代返回 char(i8 字符)。 + // Viper 语义:str/bytes = i8* 指针(const char*),迭代返回单个字符。 + // 与下标访问 s[i]、解引用 *s 保持同一元素类型 char。 if (ClassType.isBuiltIn(subtype, ['str', 'bytes'])) { - const ccharType = getViperCCharType(errorNode); - if (ccharType) { - return ccharType; - } - return getBuiltInObject(errorNode, 'int'); + return getBuiltInObject(errorNode, 'char'); } // 检查联合类型中是否包含 CPtr const typeContainsCPtr = findSubtype(type, (s) => @@ -8728,57 +8724,6 @@ export function createTypeEvaluator( return getViperREnumVariantFieldInfo(renumType, memberName)?.type; } - // Viper 扩展:构造带 t.CChar 类型别名信息的 int 类型(表示 C 的 char 类型)。 - // Viper 中 str/bytes 是 i8* 指针,下标访问/迭代/字符运算返回 t.CChar(int 别名)。 - // 优先从 t 模块查找 CChar 类型别名以保留显示信息;找不到时回退为 - // 构造带 typeAliasInfo 的 int 类型。查找失败返回 undefined。 - function getViperCCharType(node: ExpressionNode): Type | undefined { - const tSymbolWithScope = lookUpSymbolRecursive(node, 't', /* honorCodeFlow */ true); - if (tSymbolWithScope) { - const tEffectiveType = getEffectiveTypeOfSymbolForUsage( - tSymbolWithScope.symbol, - /* usageNode */ undefined, - /* useLastDecl */ true - ).type; - if (tEffectiveType && isModule(tEffectiveType)) { - const ccharSymbol = ModuleType.getField(tEffectiveType, 'CChar'); - if (ccharSymbol) { - // 优先使用声明类型(保留 typeAliasInfo) - const ccharDeclType = getDeclaredTypeOfSymbol(ccharSymbol)?.type; - if (ccharDeclType) { - return convertToInstance(ccharDeclType); - } - // 回退到有效类型 - const ccharType = getEffectiveTypeOfSymbolForUsage( - ccharSymbol, - /* usageNode */ undefined, - /* useLastDecl */ true - ).type; - if (ccharType) { - return convertToInstance(ccharType); - } - } - } - } - // 回退:构造带 CChar 别名信息的 int 类型 - const fileInfo = AnalyzerNodeInfo.getFileInfo(node); - const intType = getBuiltInObject(node, 'int'); - const ccharAliasInfo: TypeAliasInfo = { - shared: { - name: 'CChar', - fullName: 't.CChar', - moduleName: 't', - fileUri: fileInfo.fileUri, - typeVarScopeId: '', - isTypeAliasType: false, - typeParams: undefined, - computedVariance: undefined, - }, - typeArgs: undefined, - }; - return TypeBase.cloneForTypeAlias(intType, ccharAliasInfo); - } - function getTypeOfIndexedObjectOrClass( node: IndexNode, baseType: ClassType, @@ -14195,8 +14140,8 @@ export function createTypeEvaluator( if (expectedType && requiresSpecialization(expectedType)) { // Viper 扩展:str/bytes 实参传给可迭代协议参数(Iterable[_T]/Iterator[_T] 等) // 时,泛型参数 _T 应从 str.__iter__ 的声明返回类型(Iterator[str])推断。 - // 但 Viper 中 str 是 i8* 指针,迭代元素应为 t.CChar 而非 str。 - // 因此将 argType 替换为 Iterator[CChar] 实例,使协议匹配把 _T 绑定为 CChar, + // 但 Viper 中 str 是 i8* 指针,迭代元素应为 char 而非 str。 + // 因此将 argType 替换为 Iterator[char] 实例,使协议匹配把 _T 绑定为 char, // 修复 `for i, ch in enumerate(src)` 中 ch 被推断为 str 的问题 // (同样覆盖 zip/map/filter/reversed/sorted/list() 等 Iterable 参数场景)。 let argTypeForConstraint = argType; @@ -14205,11 +14150,11 @@ export function createTypeEvaluator( isClassInstance(argType) && ClassType.isBuiltIn(argType, ['str', 'bytes']) ) { - const ccharType = getViperCCharType(argParam.errorNode); + const charType = getBuiltInObject(argParam.errorNode, 'char'); const iteratorClass = getTypingType(argParam.errorNode, 'Iterator'); - if (ccharType && iteratorClass && isInstantiableClass(iteratorClass)) { + if (iteratorClass && isInstantiableClass(iteratorClass)) { argTypeForConstraint = ClassType.cloneAsInstance( - ClassType.specialize(iteratorClass, [ccharType]) + ClassType.specialize(iteratorClass, [charType]) ); } } diff --git a/packages/vipeye-internal/src/tests/viperEnumerate.test.ts b/packages/vipeye-internal/src/tests/viperEnumerate.test.ts index b10796f9..2b51c92b 100644 --- a/packages/vipeye-internal/src/tests/viperEnumerate.test.ts +++ b/packages/vipeye-internal/src/tests/viperEnumerate.test.ts @@ -2,8 +2,8 @@ * viperEnumerate.test.ts * * 验证 Viper 方言下内置 enumerate 的语义: - * - enumerate(str) 调用类型为 enumerate[t.CChar](_T 按 for 原迭代逻辑绑定元素类型) - * - `for i, ch in enumerate(str)` 中 i 为 t.CSizeT(size_t 索引)、ch 为迭代元素 t.CChar + * - enumerate(str) 调用类型为 enumerate[char](_T 按 for 原迭代逻辑绑定元素类型) + * - `for i, ch in enumerate(str)` 中 i 为 t.CSizeT(size_t 索引)、ch 为迭代元素 char * - 与 Python 声明(Iterable[_T] 协议、tuple[int, _T])的差异由 Viper 特判覆盖 * * 运行:npx jest src/tests/viperEnumerate.test.ts --runInBand @@ -104,7 +104,7 @@ function collectForTargets( return { iterableTypes, targets }; } -test('for i, ch in enumerate(kw_arg):i 为 t.CSizeT、ch 为 t.CChar', () => { +test('for i, ch in enumerate(kw_arg):i 为 t.CSizeT、ch 为 char', () => { const source = `import t def f(kw_arg: str) -> int: @@ -116,16 +116,16 @@ def f(kw_arg: str) -> int: const { iterableTypes, targets } = collectForTargets(program, parseResults!); - // 1. enumerate 调用类型:元素类型按 for 原迭代逻辑(str → t.CChar) - assert(iterableTypes.some((t) => t === 'enumerate[CChar]'), `实际: ${JSON.stringify(iterableTypes)}`); + // 1. enumerate 调用类型:元素类型按 for 原迭代逻辑(str → char) + assert(iterableTypes.some((t) => t === 'enumerate[char]'), `实际: ${JSON.stringify(iterableTypes)}`); // 2. 索引 i 为 t.CSizeT(size_t) const iTypes = targets.get('i'); assert(iTypes && iTypes.some((t) => t === 'CSizeT'), `i 应为 CSizeT,实际: ${JSON.stringify(iTypes)}`); - // 3. 元素 ch 为 t.CChar + // 3. 元素 ch 为 char(str/bytes 迭代元素,与 s[i] / *s 统一) const chTypes = targets.get('ch'); - assert(chTypes && chTypes.some((t) => t === 'CChar'), `ch 应为 CChar,实际: ${JSON.stringify(chTypes)}`); + assert(chTypes && chTypes.some((t) => t === 'char'), `ch 应为 char,实际: ${JSON.stringify(chTypes)}`); program.dispose(); serviceProvider.dispose(); diff --git a/packages/vipeye/package-lock.json b/packages/vipeye/package-lock.json index f0f65db4..d9e97baf 100644 --- a/packages/vipeye/package-lock.json +++ b/packages/vipeye/package-lock.json @@ -1,12 +1,12 @@ { "name": "vipeye", - "version": "1.1.421", + "version": "1.1.422", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vipeye", - "version": "1.1.421", + "version": "1.1.422", "license": "MIT", "bin": { "vipeye": "index.js", diff --git a/packages/vipeye/package.json b/packages/vipeye/package.json index c5b6b46a..cfaae5fc 100644 --- a/packages/vipeye/package.json +++ b/packages/vipeye/package.json @@ -2,7 +2,7 @@ "name": "vipeye", "displayName": "Vipeye", "description": "Type checker for the Viper language", - "version": "1.1.421", + "version": "1.1.422", "license": "MIT", "author": { "name": "TermiNexus" diff --git a/packages/vscode-vipeye/CHANGELOG.md b/packages/vscode-vipeye/CHANGELOG.md index f2356282..ce5dd82b 100644 --- a/packages/vscode-vipeye/CHANGELOG.md +++ b/packages/vscode-vipeye/CHANGELOG.md @@ -1,8 +1,12 @@ # Change Log +## 1.1.422 + +- `str` / `bytes` iteration now yields `char` as well: `for ch in s` and `Iterable` / `Iterator` argument inference (`enumerate`, `zip`, `map`, `filter`, `reversed`, `sorted`, `list()` …) bind the element type to `char`, matching subscript `s[i]` and dereference `*s`. The old `t.CChar`-aliased `int` construction is removed + ## 1.1.421 -- `s[i]` on `str` / `bytes` now yields `char` (`i8`) instead of `t.CChar`, matching the dereference result of `*s` (the two remain implicitly convertible). Covers both the plain subscript path and the type-set subscript path (e.g. `CConst | str | CPtr` style annotations); iteration (`for ch in s`) and `Iterable` argument inference still produce `t.CChar` +- `s[i]` on `str` / `bytes` now yields `char` (`i8`) instead of `t.CChar`, matching the dereference result of `*s` (the two remain implicitly convertible). Covers both the plain subscript path and the type-set subscript path (e.g. `CConst | str | CPtr` style annotations). Iteration and `Iterable` inference were unified in 1.1.422 ## 1.1.420 diff --git a/packages/vscode-vipeye/package-lock.json b/packages/vscode-vipeye/package-lock.json index e4121e3f..9c0aa666 100644 --- a/packages/vscode-vipeye/package-lock.json +++ b/packages/vscode-vipeye/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-vipeye", - "version": "1.1.421", + "version": "1.1.422", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-vipeye", - "version": "1.1.421", + "version": "1.1.422", "license": "MIT", "dependencies": { "mermaid": "^11.12.0", diff --git a/packages/vscode-vipeye/package.json b/packages/vscode-vipeye/package.json index 477ad657..ed0c6a88 100644 --- a/packages/vscode-vipeye/package.json +++ b/packages/vscode-vipeye/package.json @@ -2,7 +2,7 @@ "name": "vipeye", "displayName": "Vipeye", "description": "VS Code language support for Viper (LSP client)", - "version": "1.1.421", + "version": "1.1.422", "private": true, "license": "MIT", "author": {