feat(vipeye-internal): 新增内置 double 类型(复刻 float,64 位浮点)

- typeshed builtins.vpi 增加 double 类定义(复刻 float 结构)
- 内置 float<->double 按 C 浮点规则互转,int->double 无损
- 内置 double 与 t.CDouble 双向隐式转换
- viperGenericTypeVarCall 12/12 测试通过
This commit is contained in:
TermiNexus committed 2026-08-23 13:16:46 +08:00
1 parent ac08b01baa
commit 23cc75e444
3 files changed
+167 -3

No files matched your search

@@ -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;
@@ -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();
});
@@ -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