fix(expr): 整型与 None 比较统一按 == 0 / != 0 处理

- HandlesExprOps.py:is / is not / == / != 遇整型与 None 比较时直接与 0 比较,
  避免把 i8 当作空指针 load;检查置于类型对齐与指针分支之前
- README.md:同步引用 wiki 第 12 章(12-compilation-system.md)
This commit is contained in:
TermiNexus committed 2026-10-02 22:07:06 +08:00
1 parent 5eef845e5b
commit 0efdc9e111
2 files changed
+19 -11

No files matched your search

+2 -2
View File
@@ -43,7 +43,7 @@ Viper 语言通过两个核心模块提供类型和操作支持:
## 快速上手
1. **环境**:Python 3.12+、LLVM(llc/clang)、Windows(当前主平台)
2. **构建项目**:在项目目录运行 `python ../Projectrans.py build`(详见 [wiki/12-project.md](wiki/12-project.md))
2. **构建项目**:在项目目录运行 `python ../Projectrans.py build`(详见 [wiki/12-compilation-system.md](wiki/12-compilation-system.md))
3. **示例**:参考 [Test/](Test/) 下 30+ 测试项目(AstTest/ZlibTest/NoVTableTest/LLvmLiteTest 等)
4. **写第一个程序**:
@@ -72,5 +72,5 @@ def main() -> CInt | CExport:
| [09-exceptions.md](wiki/09-exceptions.md) | 异常处理 |
| [10-imports.md](wiki/10-imports.md) | 模块与导入系统(存根生成、SHA1 命名空间与导入) |
| [11-builtins.md](wiki/11-builtins.md) | 内置函数与运算符 |
| [12-project.md](wiki/12-project.md) | 项目配置与构建(增量编译、SHA1 映射) |
| [12-compilation-system.md](wiki/12-compilation-system.md) | 编译系统(流水线、工程配置 `project.vpj`、`viperbuild` 元编程) |
| [13-bootstrapping.md](wiki/13-bootstrapping.md) | 自举可行性分析(当前进展、剩余工作、路径规划) |
+17 -9
View File
@@ -397,19 +397,27 @@ class ExprOpsHandle(BaseHandle):
}
result: bool = cmp_map.get(ComparatorSymbol, False)
return ir.Constant(ir.IntType(1), 1 if result else 0)
# is / is not: 值类型(IntType)与 None(空指针常量)比较
# 值类型(如 i8/i32)永远不可能为 None,因此:
# - 值类型 is None → 恒为 false (i1 0)
# - 值类型 is not None → 恒为 true (i1 1)
# 此检查必须在类型对齐之前,否则类型对齐会对 i8* null 执行 load(空指针解引用)导致崩溃
if ComparatorSymbol in ('is', 'is not'):
# is / is not / == / != : 整数与 None(空指针常量)比较
# 整数上的 None 隐式表示 0 ⇒ `x is None` / `x == None` 等价 `x == 0`,
# `x is not None` / `x != None` 等价 `x != 0`(与 TPV 的 ExprGenOps 统一)
# 此检查必须在类型对齐之前,否则类型对齐会对 i8* null 执行 load(空指针解引用)导致崩溃;
# 也必须在下方"指针 vs 整型"特例分支之前,否则 i8 会命中 `Gen._load(null)`
if ComparatorSymbol in ('is', 'is not', '==', '!='):
LeftIsNullPtr: bool = isinstance(LeftVal, ir.Constant) and LeftVal.constant is None and isinstance(LeftVal.type, ir.PointerType)
RightIsNullPtr: bool = isinstance(RightVal, ir.Constant) and RightVal.constant is None and isinstance(RightVal.type, ir.PointerType)
LeftIsValueType: bool = isinstance(LeftVal.type, ir.IntType)
RightIsValueType: bool = isinstance(RightVal.type, ir.IntType)
if (LeftIsValueType and RightIsNullPtr) or (RightIsValueType and LeftIsNullPtr):
IsNotResult: int = 1 if ComparatorSymbol == 'is not' else 0
return ir.Constant(ir.IntType(1), IsNotResult)
IsNoneNegated: bool = ComparatorSymbol in ('is not', '!=')
if LeftIsValueType and RightIsNullPtr:
IsNoneCmpL: ir.Value = Gen.builder.icmp_signed('==', LeftVal, ir.Constant(LeftVal.type, 0), name="is_none_int")
if IsNoneNegated:
IsNoneCmpL = Gen.builder.not_(IsNoneCmpL, name="is_not_result")
return IsNoneCmpL
if RightIsValueType and LeftIsNullPtr:
IsNoneCmpR: ir.Value = Gen.builder.icmp_signed('==', RightVal, ir.Constant(RightVal.type, 0), name="is_none_int")
if IsNoneNegated:
IsNoneCmpR = Gen.builder.not_(IsNoneCmpR, name="is_not_result")
return IsNoneCmpR
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
if LeftVal.type.width < RightVal.type.width:
# 如果宽类型常量值超出窄类型有符号范围,则必须用zext