From 23cc75e4445f5c8bd42237c4f06a4e3b08239e0f Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sun, 23 Aug 2026 13:16:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(vipeye-internal):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=86=85=E7=BD=AE=20double=20=E7=B1=BB=E5=9E=8B=EF=BC=88?= =?UTF-8?q?=E5=A4=8D=E5=88=BB=20float=EF=BC=8C64=20=E4=BD=8D=E6=B5=AE?= =?UTF-8?q?=E7=82=B9=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - typeshed builtins.vpi 增加 double 类定义(复刻 float 结构) - 内置 float<->double 按 C 浮点规则互转,int->double 无损 - 内置 double 与 t.CDouble 双向隐式转换 - viperGenericTypeVarCall 12/12 测试通过 --- .../src/analyzer/typeEvaluator.ts | 31 +++++++- .../src/tests/viperGenericTypeVarCall.test.ts | 68 ++++++++++++++++++ .../typeshed-fallback/stdlib/builtins.vpi | 71 +++++++++++++++++++ 3 files changed, 167 insertions(+), 3 deletions(-) diff --git a/packages/vipeye-internal/src/analyzer/typeEvaluator.ts b/packages/vipeye-internal/src/analyzer/typeEvaluator.ts index 406f597f..dc2e7630 100644 --- a/packages/vipeye-internal/src/analyzer/typeEvaluator.ts +++ b/packages/vipeye-internal/src/analyzer/typeEvaluator.ts @@ -26592,6 +26592,30 @@ export function createTypeEvaluator( return true; } + // Viper 扩展:内置 float ↔ double 隐式转换。 + // Viper 中内置 double(64 位浮点,定义复刻 float)与内置 float 按 C 浮点 + // 隐式转换规则互转:float → double 无损(32 位 → 64 位), + // double → float 允许降级(64 位 → 32 位)。 + if ( + isClassInstance(destType) && ClassType.isBuiltIn(destType, 'double') && + isClassInstance(srcType) && ClassType.isBuiltIn(srcType, 'float') + ) { + return true; + } + if ( + isClassInstance(destType) && ClassType.isBuiltIn(destType, 'float') && + isClassInstance(srcType) && ClassType.isBuiltIn(srcType, 'double') + ) { + return true; + } + // 整数 → double(整数位宽 32/64 均 <= 64 位浮点,无损)。 + if ( + isClassInstance(destType) && ClassType.isBuiltIn(destType, 'double') && + isClassInstance(srcType) && ClassType.isBuiltIn(srcType, 'int') + ) { + return true; + } + // Viper 扩展:CEnum 子类 → int 隐式转换语义。 // C 语言中 enum 值可以隐式转换为 int(如 int x = FR_OK)。 // Viper 的 CEnum 子类(如 FRESULT)的成员(如 FRESULT.FR_OK) @@ -26607,19 +26631,20 @@ export function createTypeEvaluator( // Viper 中所有 C 类型(CInt, CFloat, CChar 等)都是 CType 的子类, // 在底层等价于 Python 内置类型(int, float, str 等)。 // 允许 `k: t.CInt = 1`(int → CInt)、`k: t.CFloat = 1.0`(float → CFloat)等赋值。 + // double 为 Viper 内置 64 位浮点,可赋给 t.CDouble。 if ( isClassInstance(destType) && isCTypeSubclassType(destType) && - isClassInstance(srcType) && ClassType.isBuiltIn(srcType, ['int', 'float', 'str', 'bool', 'bytes']) + isClassInstance(srcType) && ClassType.isBuiltIn(srcType, ['int', 'float', 'double', 'str', 'bool', 'bytes']) ) { return true; } // Viper 扩展:CType 子类 -> Python 内置类型隐式转换(反向)。 // CInt/CFloat/CChar 等 CType 子类在底层等价于 Python 内置类型, - // 可隐式转换回 int/float/str/bool/bytes。 + // 可隐式转换回 int/float/str/bool/bytes(含 t.CDouble → 内置 double)。 // 覆盖场景:CInt 值传给 int 形参(如 def __init__(self, w: int) 接收 CInt 实参)。 if ( - isClassInstance(destType) && ClassType.isBuiltIn(destType, ['int', 'float', 'str', 'bool', 'bytes']) && + isClassInstance(destType) && ClassType.isBuiltIn(destType, ['int', 'float', 'double', 'str', 'bool', 'bytes']) && isClassInstance(srcType) && isCTypeSubclassType(srcType) ) { return true; diff --git a/packages/vipeye-internal/src/tests/viperGenericTypeVarCall.test.ts b/packages/vipeye-internal/src/tests/viperGenericTypeVarCall.test.ts index c3bceef7..887ca608 100644 --- a/packages/vipeye-internal/src/tests/viperGenericTypeVarCall.test.ts +++ b/packages/vipeye-internal/src/tests/viperGenericTypeVarCall.test.ts @@ -876,3 +876,71 @@ def use(): } serviceProvider.dispose(); }); + +test('Viper 内置 double 类型(复刻 float,64 位)可用', () => { + const tempFile = new RealTempFile(); + const serviceProvider = createServiceProvider(createFromRealFileSystem(tempFile), new NullConsole(), tempFile); + const projectRoot = UriEx.file(TRANSPYC_INCLUDES); + const configOptions = new ConfigOptions(projectRoot); + configOptions.internalTestMode = true; + configOptions.initializeTypeCheckingMode('standard'); + + const execEnv = new ExecutionEnvironment( + 'python', + projectRoot, + configOptions.diagnosticRuleSet, + configOptions.defaultPythonVersion, + configOptions.defaultPythonPlatform, + [projectRoot], + /* skipNativeLibraries */ false, + /* defaultDialect */ 'viper' + ); + configOptions.executionEnvironments = [execEnv]; + + const code = `import t + +def test_double(): + # float 字面量 → double(32 位 → 64 位,无损) + d: double = 1.5 + # double 运算返回 double + e: double = d * 2.0 + # double 构造 + f: double = double(3) + # double + int → double(整数位宽 <= 64 位浮点,无损) + g: double = d + 1 + # 内置 double → t.CDouble + h: t.CDouble = d +`; + const testDir = path.join(__dirname, '__viper_double_test__'); + const fs = require('fs'); + fs.mkdirSync(testDir, { recursive: true }); + const testFile = path.join(testDir, 'double_type.vp'); + fs.writeFileSync(testFile, code, 'utf8'); + + try { + const importResolver = new ImportResolver(serviceProvider, configOptions, new FullAccessHost(serviceProvider)); + const program = new Program(importResolver, configOptions, serviceProvider); + const uri = UriEx.file(testFile); + program.setTrackedFiles([uri]); + while (program.analyze()) { + // 循环 + } + + const diagnostics = getDiagnostics(program, configOptions, uri); + console.log('=== double_type.vp diagnostics ==='); + diagnostics.forEach((d) => console.log(` ${d}`)); + + // 期望没有任何错误级诊断:double 类型、运算、构造及 t.CDouble 转换均可用。 + const errors = diagnostics.filter((d) => d.startsWith('[category=0]')); + assert.strictEqual( + errors.length, + 0, + `unexpected error diagnostics: ${JSON.stringify(diagnostics)}` + ); + + program.dispose(); + } finally { + fs.rmSync(testDir, { recursive: true, force: true }); + } + serviceProvider.dispose(); +}); diff --git a/packages/vipeye-internal/typeshed-fallback/stdlib/builtins.vpi b/packages/vipeye-internal/typeshed-fallback/stdlib/builtins.vpi index 7379d31f..d3ba7920 100644 --- a/packages/vipeye-internal/typeshed-fallback/stdlib/builtins.vpi +++ b/packages/vipeye-internal/typeshed-fallback/stdlib/builtins.vpi @@ -434,6 +434,77 @@ class float: @classmethod def from_number(cls, number: float | SupportsIndex | SupportsFloat, /) -> Self: ... +@disjoint_base +class double: + """double 类型(64 位浮点,定义复刻 float)。""" + + def __new__(cls, x: ConvertibleToFloat = 0, /) -> Self: ... + def as_integer_ratio(self) -> tuple[int, int]: ... + def hex(self) -> str: ... + def is_integer(self) -> bool: ... + @classmethod + def fromhex(cls, string: str, /) -> Self: ... + @property + def real(self) -> double: ... + @property + def imag(self) -> double: ... + def conjugate(self) -> double: ... + def __add__(self, value: double, /) -> double: ... + def __sub__(self, value: double, /) -> double: ... + def __mul__(self, value: double, /) -> double: ... + def __floordiv__(self, value: double, /) -> double: ... + def __truediv__(self, value: double, /) -> double: ... + def __mod__(self, value: double, /) -> double: ... + def __divmod__(self, value: double, /) -> tuple[double, double]: ... + + @overload + def __pow__(self, value: int, mod: None = None, /) -> double: ... + @overload + def __pow__(self, value: double, mod: None = None, /) -> Any: ... + + def __radd__(self, value: double, /) -> double: ... + def __rsub__(self, value: double, /) -> double: ... + def __rmul__(self, value: double, /) -> double: ... + def __rfloordiv__(self, value: double, /) -> double: ... + def __rtruediv__(self, value: double, /) -> double: ... + def __rmod__(self, value: double, /) -> double: ... + def __rdivmod__(self, value: double, /) -> tuple[double, double]: ... + + @overload + def __rpow__(self, value: _PositiveInteger, mod: None = None, /) -> double: ... + @overload + def __rpow__(self, value: _NegativeInteger, mod: None = None, /) -> complex: ... + @overload + def __rpow__(self, value: double, mod: None = None, /) -> Any: ... + + def __getnewargs__(self) -> tuple[double]: ... + def __trunc__(self) -> int: ... + def __ceil__(self) -> int: ... + def __floor__(self) -> int: ... + + @overload + def __round__(self, ndigits: None = None, /) -> int: ... + @overload + def __round__(self, ndigits: SupportsIndex, /) -> double: ... + + def __eq__(self, value: object, /) -> bool: ... + def __ne__(self, value: object, /) -> bool: ... + def __lt__(self, value: double, /) -> bool: ... + def __le__(self, value: double, /) -> bool: ... + def __gt__(self, value: double, /) -> bool: ... + def __ge__(self, value: double, /) -> bool: ... + def __neg__(self) -> double: ... + def __pos__(self) -> double: ... + def __int__(self) -> int: ... + def __float__(self) -> double: ... + def __abs__(self) -> double: ... + def __hash__(self) -> int: ... + def __bool__(self) -> bool: ... + def __format__(self, format_spec: str, /) -> str: ... + if sys.version_info >= (3, 14): + @classmethod + def from_number(cls, number: float | SupportsIndex | SupportsFloat, /) -> Self: ... + @disjoint_base class complex: # Python doesn't currently accept SupportsComplex for the second argument