# 隐式转换与整数提升(Implicit Conversions & Integer Promotions) > 本文记录 Viper(现行编译器 **VPC** / `ViperC`)的隐式类型转换规则。 > **总原则:允许无损拓宽,禁止有损收窄**;一切禁止项 **fail-fast**(报错并终止),不做静默截断、不做兜底推断。 > > 主要实现位置: > - `ViperC/App/lib/core/Gen/Expr/ExprGenBase.vp` — `coerce_to_type`(统一转换入口) > - `ViperC/App/lib/core/Gen/Expr/ExprGenOps.vp` — 提升/符号判定(`should_zext_widen`、`_node_type_unsigned`、`translate_binop`、一元运算) > - `ViperC/App/lib/core/Gen/AssignGenMixin.vp` — `define_min_width`、复合赋值(`x op= y`) > - `ViperC/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp` — `range()` 的边界/循环变量位宽 --- ## 1. 整数:拓宽与收窄 ### 1.1 拓宽(低 → 高):允许 方向按**源操作数的符号性**决定: | 源 | 指令 | |---|---| | u 类(无符号) | `zext` | | i 类(有符号) | `sext` | | 未标注的 `i8` | 默认按无符号字节(`zext`);其余未标注按有符号(`sext`) | 判据:`ExprGenOps.should_zext_widen`(内部走 `_node_type_unsigned`)。 ### 1.2 收窄(高 → 低):禁止 报 `CastError: implicit narrowing is forbidden`。**仅两类豁免**: 1. **目标为 `i1`**:真值归一化 `x != 0`(不是位截断)。 2. **源是编译期常量**(`val.IsConst == 1`)且值落进目标位宽的**两种解释**区间 `[-(2^(b-1)), 2^b - 1]`。超界即便只是「刚好越界一点」也报 `OverflowError`。 ### 1.3 表达式的工作位宽(`translate_binop`) - `work_bits` = `max(上下文目标位宽(赋值/形参/返回,若已知), 两侧操作数位宽)`,下限 **32**(`int`)。 - 移位(`<<` `>>`)**不把右值(计数)宽度计入** `work_bits`。 - 对齐时: - **位运算 `& | ^`**:额外允许「**常量无损适配**」——能无损落进另一侧的常量按另一侧宽度参与。 - **算术 `+ - * / %`**:**不做**常量适配,按 C 语义把窄侧拓宽(窄位宽下会回绕,例如 `(32 + 1) * 8` 在 i8 中溢出成 8)。 - 移位:只提升**左操作数**到 `work_bits`;计数不参与类型提升,改为**静态范围检查**(`0 <= c < 左操作数位宽`,无法静态确定则留待运行期)。 ### 1.4 `define` / 整数字面量的物化宽度 - 显式注解 `define X: T = V`:按 `T` 的位宽;值放不下 → **报错**。 - 自动推导:`W_min` = `8 / 16 / 32 / 64` 中**最小可容纳宽度**(`AssignGenMixin.define_min_width`)。 - **负数按有符号解释**(必须留符号位); - **非负按无符号位模式**(上界 `2^w - 1`;例如 `0xFFFFFFFF` → 32 位位型)。 - 使用处宽度 `w_ctx`:`w_ctx >= W_min` ⇒ **就地按 `w_ctx` 物化**(无损);`w_ctx < W_min` ⇒ **报错**。 - **符号性**:非负 CDefine 常量按**无符号位模式**参与宽化与比较 (`define N = 50000` 是 16 位位型 `0xC350`,须 `zext` 成 50000,而非 `sext` 成 -15536)。 --- ## 2. 整数提升(Promotions) | 上下文 | 规则 | |---|---| | 二元算术 `+ - * / // %` | 仿 C 拓宽到 `work_bits`(见 §1.3) | | 二元位运算 `& \| ^` | 对齐到较宽侧;**仅常量**可无损适配到另一侧宽度 | | 移位 `<< >>` | **只提升左操作数**到 `work_bits`;计数只做静态范围检查 | | 比较 `== != < <= > >=` | 两侧对齐到较宽者;无符号用 `ult/ugt`,有符号用 `slt/sgt` | | **一元 `+x` / `-x` / `~x`** | **u 类不提升**(保持原位宽);**i 类提升到 i32**(防负数陷阱,如 `int8 -128` 取负会溢出,提升后 = 128);未标注按 i 类处理 | | `not x` | 结果为 `i1`,与操作数位宽无关 | > 一元运算内部的常量(`-x` 的 `0`、`~x` 的 `-1`)与**当前**操作数同宽, > 因此「u 类不提升」后 `~uint16` 是纯 16 位取反(结果仍是 `uint16`),`-uint8` 也在 `uint8` 内取反。 --- ## 3. 浮点 - `float → double`:允许;`double → float`:**禁止**(有损)。 - 整数 → 浮点:要求**浮点位宽 ≥ 整数位宽**(无损);否则禁止。 - 浮点 → 整数:**禁止**(有损)。 --- ## 4. 指针 - **级数必须相同**。 - pointee 兼容性(`_ptr_cast_reason`): - 任一侧是 `void*` / rawptr → 允许; - pointee 相同 → 允许; - 两侧都是**基本数值类型** → 按整数规则; - **结构体(非基本类型)只允许上转型**(源类派生自目标类);不相关结构体 / 向下转型 / 结构体↔基本类型 → **禁止**。 - 单级指针 ↔ `void*` 双向允许;**多级禁止**。 - **显式转换不受上述限制**。 --- ## 5. 值 ↔ 指针(聚合类型) - 非基本类型的 `Foo* → Foo`(指针 → 值)**禁止**,仅两处豁免: 1. **构造物化**(`Foo(...)` 直接产出聚合值); 2. **值语义槽拷贝**(源是 `x: Foo` 这类「槽自身持有聚合物、`Spec.PtrLevel == 0`」)。 - 聚合值 → 指针:在栈上溢出临时(VPC 不按值传聚合)。 - **裸类形参 ABI = `T*`**:VPC 里对象本身即指针,`def f(node: T)` 的形参按对象地址传入。 --- ## 6. 复合赋值 `x op= y` - `y` 的**有效宽度必须 ≤ `x` 的宽度**(变量按 LLVM 类型宽度;常量按 `W_min`)。 否则报 `augmented assignment operand too wide`,须显式收窄右值(如 `BYTE(...)` / `uint8(...)`)。 - 例:`u8 += 1` 合法;`u8 += 300` 报错;`buf[i] |= BYTE(UINT(1) << bit_pos)` 合法。 - **移位(`<<=` / `>>=`)豁免**:计数不是写回目标的值,改为静态范围检查。 - 右值显式有符号而左值无符号**不单独拦截**:拓宽方向由 §1.1 统一决定(i 类 `sext`、u 类 `zext`), 与二元形态 `x = x op y` 口径一致(如 `sizet += int` 里 `int` 先拓宽到 u64 再相加)。 --- ## 7. `range()` - 边界(`start` / `stop` / `step`)**统一提升到至少 i32**(u64 边界保持 i64,避免截断); - 循环变量(未声明时)与边界同口径:**≥ i32**(不再有 `u16→i16` / `u8→i8` 档); - 边界若是非负 CDefine 常量,按**无符号位模式**比较(`icmp ult`)。 --- ## 变更记录 - **2026-10-12:一元 `+x` / `-x` / `~x` 提升规则由「一律提升到 i32」改为按符号性分档**(见 §2)。 - 动机:`ncomp: uint16 = ~n`(`n: uint16`)此前会被提升成 i32,再赋回 `uint16` 触发「隐式窄化禁止」误报; 现在 u 类保持原位宽,位图/掩码写法能同宽运算,同时 i 类仍提升到 i32 守住有符号负数陷阱。 - 配套:`-x` 的 `0`、`~x` 的 `-1` 常量改为按**当前**操作数位宽物化(否则会出现 `xor i16, i32` 这类非法 IR)。 - **2026-10-12:复合赋值取消「右值有符号而左值无符号」的单独拦截**(见 §6)。 - 动机:`pos: sizet; pos += match_len`(`match_len: int`)此前被拦,而等价的 `pos = pos + match_len` 本就允许(`match_len` 在二元路径里拓宽到 u64)——同一个语义两种写法不该一错一对。