最后核对:2026-10-04(全库逐份翻新复核——资产定性未变:假想清单仍未接防线,G1b 真实失败路径通道由 CORE_ASSET_VERDICT 台账承载(原
native/tests/CORE_ASSET_VERDICT_FAILURES.md随 2026-10-05 删区退役,史实见 tagrust-oracle-freeze与《核心资产重构裁定》),本清单维持候选料源定位。前一沿革 2026-09-13 资产定性补记)⚠️ 资产定性(2026-09-13 补记):本清单是人工整理的假想清单——预期诊断由作者写定, 尚未接入任何自动化测试防线(
native/中无测试引用本文件;见核心资产重构裁定.md§T5 / 缺口 G1)。 片段的预期输出以实际运行vitro_cli run -为准(MoonBit 侧亦可用moonbit/cmd/run端到端验证);"真实失败路径"语料库由裁定 G1(2026-09-13 起拆分为 G1a 真实通道 / G1b 合成靶料)另行建设,台账见native/tests/CORE_ASSET_VERDICT_FAILURES.md——本清单可作为 G1b/防线 6 的候选料源(每条需按 G1b 红线标注来源、先红后绿验证)。以下代码片段可直接用
vitro_cli run -验证(从标准输入读取源码,无需前端),用于测试编译器诊断、错误提示和自动修复能力。 每个片段包含:错误代码 + 预期问题 + 正确写法。
1. 语法类错误
1.1 缺少分号
#include <stdio.h>
int main() {
int a = 10
printf("%d\n", a);
return 0;
}
预期诊断:Parser 错误,指出第 3 行缺少 ;。
正确写法:int a = 10;
1.2 括号不匹配
#include <stdio.h>
int main() {
int arr[3] = {1, 2, 3;
printf("%d\n", arr[0]);
return 0;
}
预期诊断:Parser 错误,初始化列表 } 不匹配。
正确写法:int arr[3] = {1, 2, 3};
1.3 字符串引号未闭合
#include <stdio.h>
int main() {
char s[10] = "hello;
printf("%s\n", s);
return 0;
}
预期诊断:Lexer 错误,字符串引号未闭合。
正确写法:char s[10] = "hello";
2. 声明与类型错误
2.1 变量未声明就使用
#include <stdio.h>
int main() {
x = 5;
printf("%d\n", x);
return 0;
}
预期诊断:TypeChecker 错误,x 未声明。
正确写法:先 int x; 再使用。
2.2 重复定义变量
#include <stdio.h>
int main() {
int a = 1;
int a = 2;
printf("%d\n", a);
return 0;
}
预期诊断:TypeChecker 错误,同一作用域内重复定义 a。
正确写法:去掉重复声明,或直接赋值 a = 2;。
2.3 隐式类型转换警告(赋值截断)
#include <stdio.h>
int main() {
int a = 3.14;
printf("%d\n", a);
return 0;
}
预期诊断:TypeChecker 警告,double 赋值给 int 会截断小数。
正确写法:int a = (int)3.14; 或 double a = 3.14;
2.4 数组大小不是常量
#include <stdio.h>
int main() {
int n = 5;
int arr[n];
arr[0] = 1;
printf("%d\n", arr[0]);
return 0;
}
预期诊断:TypeChecker/Parser 错误(若当前子集不支持 VLA)。
正确写法:int arr[5]; 或 #define N 5 后 int arr[N];
3. 数组与指针常见错误
3.1 数组越界访问
#include <stdio.h>
int main() {
int arr[3] = {10, 20, 30};
printf("%d\n", arr[5]);
return 0;
}
预期行为:VM 运行期越界,可能读到脏数据或报错(取决于 VM 边界检查)。
正确写法:确保索引在 0 ~ 2 范围内。
3.2 数组名当指针用但试图整体赋值
#include <stdio.h>
int main() {
int a[3] = {1, 2, 3};
int b[3];
b = a;
printf("%d\n", b[0]);
return 0;
}
预期诊断:TypeChecker 错误,数组不可赋值。
正确写法:逐个拷贝 b[0] = a[0]; b[1] = a[1]; b[2] = a[2];
3.3 未初始化的指针解引用
#include <stdio.h>
int main() {
int *p;
*p = 10;
printf("%d\n", *p);
return 0;
}
预期行为:VM 运行期访问非法地址,可能崩溃或报错。
正确写法:int x; int *p = &x; *p = 10;
3.4 指针类型不匹配
#include <stdio.h>
int main() {
int x = 10;
float *p = &x;
printf("%f\n", *p);
return 0;
}
预期诊断:TypeChecker 警告/错误,float* 与 int* 不兼容。
正确写法:int *p = &x;
4. 函数相关错误
4.1 函数未声明先调用
#include <stdio.h>
int main() {
int r = add(3, 4);
printf("%d\n", r);
return 0;
}
int add(int a, int b) {
return a + b;
}
预期诊断:TypeChecker 错误(若当前子集要求前向声明),或默认 int 隐式声明警告。
正确写法:在 main 前加 int add(int, int); 原型声明。
4.2 实参与形参类型不匹配
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int main() {
int r = add(3.5, 2);
printf("%d\n", r);
return 0;
}
预期诊断:TypeChecker 警告,double 传入 int 形参会截断。
正确写法:add((int)3.5, 2) 或修改函数参数类型。
4.3 非 void 函数缺少 return
#include <stdio.h>
int add(int a, int b) {
int c = a + b;
}
int main() {
printf("%d\n", add(1, 2));
return 0;
}
预期诊断:TypeChecker 警告/错误,非 void 函数缺少 return。
正确写法:return c;
4.4 递归缺少终止条件(栈溢出/死循环)
#include <stdio.h>
int fact(int n) {
return n * fact(n - 1);
}
int main() {
printf("%d\n", fact(5));
return 0;
}
预期行为:VM 无限递归,step_count 溢出或调用栈溢出。
正确写法:添加终止条件 if (n <= 1) return 1;
5. 字符串与 scanf 错误
5.1 scanf 忘记取地址符 &
#include <stdio.h>
int main() {
int a;
scanf("%d", a);
printf("%d\n", a);
return 0;
}
预期诊断:TypeChecker 警告,scanf 参数应为 int* 但传入 int。
正确写法:scanf("%d", &a);
5.2 字符串用 scanf %s 读取到未分配空间的指针
#include <stdio.h>
int main() {
char *s;
scanf("%s", s);
printf("%s\n", s);
return 0;
}
预期行为:VM 运行期写入非法地址,崩溃或报错。
正确写法:char s[100]; scanf("%s", s);
5.3 strcpy 目标数组太小
#include <stdio.h>
#include <string.h>
int main() {
char s[5];
strcpy(s, "hello world");
printf("%s\n", s);
return 0;
}
预期行为:VM 运行期缓冲区溢出,破坏相邻内存。
正确写法:char s[20]; strcpy(s, "hello world");
6. 运算符与逻辑错误
6.1 赋值号 = 与比较号 == 混淆
#include <stdio.h>
int main() {
int a = 5;
if (a = 0) {
printf("yes\n");
} else {
printf("no\n");
}
return 0;
}
预期行为:编译通过但逻辑错误,输出 no(因为 a = 0 结果为 0)。
正确写法:if (a == 0)
6.2 死循环:for 条件恒真
#include <stdio.h>
int main() {
for (int i = 0; i >= 0; i++) {
printf("%d\n", i);
}
return 0;
}
预期行为:VM step_count 溢出终止。
正确写法:设置合理的终止条件 i < 10。
6.3 整数除以零
#include <stdio.h>
int main() {
int a = 5;
int b = 0;
printf("%d\n", a / b);
return 0;
}
预期行为:VM 运行期除以零错误。
正确写法:判断 if (b != 0) 再除。
6.4 位运算符误用(优先级陷阱)
#include <stdio.h>
int main() {
int a = 5;
int b = 3;
if (a & b == 1) {
printf("yes\n");
}
return 0;
}
预期行为:== 优先级高于 &,实际为 a & (b == 1)。
正确写法:if ((a & b) == 1)
7. 复合结构与 typedef 错误
7.1 struct 成员访问用错运算符
#include <stdio.h>
struct Point {
int x;
int y;
};
int main() {
struct Point p;
p->x = 10;
printf("%d\n", p->x);
return 0;
}
预期诊断:TypeChecker 错误,p 是结构体变量不是指针,不能用 ->。
正确写法:p.x = 10;
7.2 typedef 后忘记用新类型名
#include <stdio.h>
typedef int Integer;
int main() {
Integer a = 5;
int b = a;
printf("%d\n", b);
return 0;
}
(此段本身正确,可用于验证 typedef 正常。错误版本如下)
#include <stdio.h>
typedef int Integer;
int main() {
Intger a = 5; /* 拼写错误 */
printf("%d\n", a);
return 0;
}
预期诊断:TypeChecker 错误,Intger 未定义。
正确写法:Integer a = 5;
7.3 union 成员写入后读取另一个成员
#include <stdio.h>
union Data {
int i;
float f;
};
int main() {
union Data d;
d.i = 42;
printf("%f\n", d.f); /* 错误:写入 i 后按 f 读取,值不可预期 */
return 0;
}
预期诊断:教学提示 union 成员共享内存,写入一个成员后读取另一个成员是未定义行为(可关联 E3051 类型安全提示)。
正确写法:通过同一个成员读取,或明确使用 struct 替代 union。
8. 综合调试挑战
8.1 多重错误混合(适合测试诊断列表)
#include <stdio.h>
int main() {
int a = 10
int b;
b = a + c;
scanf("%d", a);
for (int i = 0; i < 5; i++)
printf("%d ", i)
return 0;
}
预期诊断(应同时报告):
- 第 4 行缺少
; - 第 6 行
c未声明 - 第 7 行
scanf参数类型不匹配(应为int*) - 第 9 行
printf后缺少; - 第 10 行
for循环体后的语句受for控制,缩进歧义(可选警告)
8.2 段错误三件套
#include <stdio.h>
int main() {
int *p;
*p = 1; /* 野指针写 */
int arr[2];
arr[100] = 2; /* 数组越界 */
char *s;
strcpy(s, "x"); /* 野指针字符串操作 */
return 0;
}
预期行为:VM 运行期多次越界/非法访问,应被边界检查捕获。
测试建议
| 测试维度 | 推荐用例 |
|---|---|
| Parser 死循环防护 | 1.2、8.1(故意留语法错误) |
| TypeChecker 错误收集 | 2.1、4.1、8.1(多错误同时存在) |
| VM 边界检查 | 3.1、3.3、5.3、8.2 |
| 自动修复建议 | 1.1、2.1、4.1、5.1、6.1、7.1 |
| 安全加固验证 | 4.4(step_count)、6.2(死循环)、6.3(除零) |
9. C++ 常见错误(Stage 0~6)
以下用例针对 Vitro 已支持的 C++ 子集编写,用于验证 C++ 诊断知识卡片(E4100~E4104)。 若某段代码当前子集无法编译,预期诊断中会有说明。
9.1 内存泄漏:new 后未 delete
#include <stdio.h>
class Box {
public:
int* data;
Box() { data = new int(42); }
// 缺少析构函数:data 指向的内存不会被释放
};
int main() {
Box* b = new Box();
printf("%d\n", *b->data);
delete b; // 只释放了 Box 本身,data 指向的 int 泄漏
return 0;
}
预期诊断:运行时可能报告内存泄漏(E4100)。
正确写法:在 ~Box() 中添加 delete data;,或使用 unique_ptr<int>。
9.2 悬垂引用:返回局部变量引用
#include <stdio.h>
int& bad_ref() {
int x = 42;
return x; // 返回局部变量的引用
}
int main() {
int& r = bad_ref();
printf("%d\n", r); // 使用悬垂引用
return 0;
}
预期诊断:TypeChecker 可能报错(E4101),或运行时产生不可预测的值。
正确写法:返回值对象 int bad_ref(),或确保返回的变量生命周期足够长。
9.3 对象切片:派生类赋值给基类值对象
#include <stdio.h>
class Animal {
public:
int age;
Animal(int a) : age(a) {}
};
class Dog : public Animal {
public:
int breed;
Dog(int a, int b) : Animal(a), breed(b) {}
};
void print(Animal a) { // 按值传递:Dog 的 breed 被切掉
printf("age=%d\n", a.age);
}
int main() {
Dog d(3, 7);
print(d);
return 0;
}
预期诊断:可通过教学提示指出对象切片风险(E4102)。
正确写法:参数改为 const Animal& a 或 Animal* a。
9.4 unique_ptr 所有权混乱
#include <stdio.h>
template <typename T>
class unique_ptr {
T* ptr;
public:
unique_ptr(T* p = 0) : ptr(p) {}
~unique_ptr() { delete ptr; }
unique_ptr(unique_ptr&& other) : ptr(other.ptr) { other.ptr = 0; }
T* get() const { return ptr; }
};
int main() {
unique_ptr<int> a(new int(10));
unique_ptr<int> b = a; // 错误:unique_ptr 不可拷贝,当前简化版可能允许但会导致双重释放
printf("%d %d\n", *a.get(), *b.get());
return 0;
}
预期诊断:教学提示 unique_ptr 不可拷贝(E4103);当前简化版若允许拷贝可能在运行时触发双重释放检测。
正确写法:使用 unique_ptr<int> b = std::move(a);,且 move 后不再使用 a。
9.5 move 后继续使用源对象
#include <stdio.h>
template <typename T>
class unique_ptr {
T* ptr;
public:
unique_ptr(T* p = 0) : ptr(p) {}
~unique_ptr() { delete ptr; }
unique_ptr(unique_ptr&& other) : ptr(other.ptr) { other.ptr = 0; }
T* get() const { return ptr; }
};
unique_ptr<int> make() {
return unique_ptr<int>(new int(20));
}
int main() {
unique_ptr<int> a(new int(10));
unique_ptr<int> b = a; // 假设为 move 语义
printf("%d\n", *a.get()); // 错误:move 后继续使用源对象
return 0;
}
预期诊断:教学提示 move 后源对象处于未指定状态(E4104)。
正确写法:move 后将源对象视为无效,不再读取其值。
9.6 隐式浅拷贝导致双重释放
#include <stdio.h>
#include <stdlib.h>
class Buffer {
public:
int* data;
int len;
Buffer(int n) {
data = (int*)malloc(sizeof(int) * n);
len = n;
}
~Buffer() { free(data); }
};
void work(Buffer b) { /* 按值传递 */ }
int main() {
Buffer a(10);
work(a); // 默认拷贝构造只复制 data 指针
printf("%d\n", a.data[0]); // 此处 a.data 可能已被释放
return 0;
}
预期诊断:教学提示默认浅拷贝风险(E4105);运行时可能触发 Use-After-Free 或 Double-Free 检测。
正确写法:自定义拷贝构造/拷贝赋值实现深拷贝,或禁用拷贝(使用 unique_ptr 或移动语义)。
9.7 引用绑定到临时对象
#include <stdio.h>
int make() { return 42; }
int main() {
int& r = make(); // 错误:非 const 引用绑定到临时对象
printf("%d\n", r);
return 0;
}
预期诊断:TypeChecker 教学提示引用绑定到临时对象风险(E4106)。
正确写法:使用 int x = make(); 接收值,或 const int& r = make();(生命周期延长)。
测试建议
| 测试维度 | 推荐用例 |
|---|---|
| Parser 死循环防护 | 1.2、8.1(故意留语法错误) |
| TypeChecker 错误收集 | 2.1、4.1、8.1(多错误同时存在) |
| VM 边界检查 | 3.1、3.3、5.3、8.2 |
| 自动修复建议 | 1.1、2.1、4.1、5.1、6.1、7.1 |
| 安全加固验证 | 4.4(step_count)、6.2(死循环)、6.3(除零) |
| C++ 教学诊断 | 9.1~9.7(E4100~E4106 知识卡片) |