docs(wiki): 补 T*(x) 强转歧义约束、isinstance 与 FakeDuck 章节

This commit is contained in:
TermiNexus committed 2026-10-01 22:36:58 +08:00
1 parent a1501740de
commit ebd339e79a
3 files changed
+96

No files matched your search

+19
View File
@@ -686,6 +686,25 @@ q: void* = x # void* ↔ 任意 T* 双向隐式可转,
对于比较、四则运算,可以不使用强制类型转换而依靠隐式类型转换;但严谨数学计算推荐显式强转,避免判断出错。此外,若将一个不同类型的右值赋值到左值,则会隐式将右值转换为左值的类型。
### `T*(x)` 指针强转
C 风格强转可直接写 `T*(x)`——等价 `(T*)(x)`、`(T*)x`、`(T | t.CPtr)(x)`(四者产出**同一 AST、同一 IR 路径**):
```python
node: Node* = Node*(child)
p: int* = int*(raw)
data: uint8* = uint8*(s)
```
**歧义与约束**(`a * (b)` 是合法乘法,所以判定必须是纯语法的):
1. `T` 必须是**类型原子**(Name / Attribute / Subscript);
2. `T` 与 `*` 串、`*` 串与 `(` 之间**必须紧贴**(无空白 / 无换行)。
⇒ `T*(x)` 中的 `T` 必须是**类型名**(class 原型、typedef、`uint8` 等类型原子);`T` 若解析不出类型,会在类型解析阶段 fail-fast。而 `a * (b)`、`n * (m - 1)`、`(i + 1) * (j)`(有空白,或前驱不是类型原子)仍按**乘法**解析。
生成的 IR 与其它强转同路径:指针↔指针 `bitcast`、整数→指针 `inttoptr`、指针→整数 `ptrtoint`、整数↔整数 `zext/sext/trunc`。
## 宏定义常量
使用 `t.CDefine` 定义编译时常量(新写法用软关键字 `define X = v` / `define X: T = v`)。`t.CDefine` 不是数据类型,而是编译时元指令——标记为 `t.CDefine` 的常量在 LLVM IR 中不生成任何全局变量,而是在引用处直接内联展开。详见 [01-overview.md 中 t.CDefine 深度解析](01-overview.md#tcdefine-深度解析)。
+73
View File
@@ -669,6 +669,79 @@ del obj # 期望:调用 obj.__del__() 或 obj.__delete__()(尚未实
del arr[idx] # 调用 arr.__delitem__(idx)
```
## `isinstance`
`isinstance(obj, Cls)` 不是关键字,而是编译器在调用点按函数名识别的内建(返回 `i1`)。
```python
if isinstance(obj, Base):
...
if isinstance(obj, (A, B)): # 元组:任一命中即为真
...
```
语义:
- **`virtual` 类(带 vtable)**:运行时读对象头部 vtable 指针,与「目标类**及其全部已知子类**的 vtable 地址集合」逐一比较,相等即真 ⇒ 正确覆盖多态子类(这也是运行期唯一能拿到实际类的途径);
- **`novirtual` 类 / 未知类(无 vtable)**:回退**编译期静态 MRO 判定**(据 obj 的静态类型)——可能保守判假。
第二参数支持:裸类名 / 跨模块 `mod.Cls`(只取短名)/ 元组 `(A, B)`。**不支持** REnum 变体、`t.CEnum` 等。
限制:已知子类集合上限 64、元组上限 15;Viper 侧类型推断失败即 fail-fast(`TypeError: isinstance() 参数类型推断失败`)。
> 注意:胖节点 AST 的节点分派用的是**虚方法 `kind()`**(返回 `ASTKind`),不是 `isinstance`;文档中提到的 `vtype` 字段目前**不存在**(属迁移规划)。
## FakeDuck(伪鸭子类型)
FakeDuck 让**内建类型 `str`**(LLVM 层就是 `i8*`)也能像对象一样调用方法、读写字段——通过**扩展类**实现,**不是**关键字 / 装饰器 / 基类。
### 启用方式(命名约定 + import)
- 扩展类必须命名为 `_str`(字符串扩展方法)或 `_Type`(通用基类),定义在被 `import` 的模块中;
- 使用者 `import _fakeduck`(提供 `_str` / `_Type`);`with` 自动注入还需 `import _withcontent`。
内置扩展类见 [includes/_fakeduck.vp](../includes/_fakeduck.vp):`_Type.type_id()`;`_str` 提供 `upper` / `lower` / `capitalize` / `swapcase` / `title` / `casefold` / `isupper` / `islower` / `isalpha` / `isdigit` / `isalnum` / `isspace` / `isascii` / `istitle` / `split` 等。
### 扩展方法调用
```python
import _fakeduck
a: str = "Hello"
tid: int = a.type_id() # 解析到扩展类 _Type.type_id
u: str = a.upper() # 解析到扩展类 _str.upper
```
分派是**回退**:先走普通结构体方法,全部落空后才尝试 FakeDuck;接收者注解是 `list[...]` 时**跳过**。方法名在 `_Type` / `_str` 中查找。
### 影子字段与 with 自动注入
编译期为每个**裸 `str`** 变量额外开一个影子结构体 `<变量名>__meta__`,用来存 per-instance 字段(`__data__`、`__mbuddy__`):
```python
a.__mbuddy__ = bd # 语法糖,等价 a.set_mbuddy(bd)
d: str = a.__data__ # 读影子字段
```
`with` 上下文可**自动注入**字段:提供方类用类体注解 `__provides__ = ['字段名']` 声明,消费方扩展类用 `__requires__ = ['字段名']` 声明:
```python
with memhub.MemBuddy(arena, 65536) as bd:
s: str = "WithTest" # __mbuddy__ 自动注入,无需手动赋值
u2: str = s.upper()
```
另有 `__require_must__`:表示该字段必须由某个外层 `with` 提供,否则编译期 fail-fast。
### 限制
- 影子结构体**只对裸 `str` 注解创建**(`bytes` / `char*` 不创建);`_bytes` 扩展类**未实现**。
- `len()` / 下标 `[]` / `for` 迭代等**不走 FakeDuck**,而是 `list[T]` 的 dunder / 内建分派。
- `with` 注入依赖运行时 provider 栈([includes/_withcontent.vp](../includes/_withcontent.vp)),未 import `_withcontent` 时跳过注入。
真实用例:[Test/FakeDuckTest](../Test/FakeDuckTest)。
## Dunder 方法完整参考
### 已实现自动调度的 Dunder 方法
+4
View File
@@ -69,6 +69,10 @@ for i in range(0, 10, 2): pass # for (int i = 0; i < 10; i += 2)
函数指针类型构造子:`callable` / `Callable` / `t.Callable` / `c.Callable` 在类型域等价,统一为 `i8*`(带签名形态额外登记返回类型)。详见 [02-type-system.md 的数组指针与函数指针](02-type-system.md#数组指针与函数指针)。
### isinstance
`isinstance(obj, Cls)` / `isinstance(obj, (A, B))`:编译器内建(返回 `i1`)——对带 vtable 的对象做**运行时子类判定**,无 vtable 时回退编译期静态判定。详见 [06-oop.md 的 isinstance 章节](06-oop.md#isinstance)。
## 运算符
### 算术运算符