Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

错误码参考

编译器的每条错误都带一个四位码,格式是 error[E0001]: ...。这个码是稳定接口:LSP 诊断的 code 字段就是它,code_description 的链接指向本文对应的锚点。语法错误不带码,只打印 error: expected ...。

本文收录当前实现定义的 80 个 E 码:78 个由 crates/ 下的检查器发出,E0400 由 riddlec 的过程宏驱动发出,E0200 目前没有任何发射点。每个条目给出含义、触发条件和一段可单独编译的最小程序,程序里的 // EXXXX 注释标出它会触发的码。除 E0053(要 riddlec --no-std 或写在标准库里)和 E0310(要在 Clue.toml 里关掉 GC)以外,示例都能直接用 riddlec <文件> 复现。

主题码
类型、调用与字面量E0001–E0012、E0043
名字、字段、方法与可见性E0013–E0015、E0050–E0052、E0054、E0064
trait、impl 与泛型E0020–E0030、E0032–E0037、E0044–E0045、E0047
内部属性、lang item 与保留名E0048–E0049、E0053
模式、控制流与 unsafeE0010、E0038–E0039、E0042、E0046、E0057–E0058、E0061–E0063、E0065–E0066
常量、类型布局与降级E0040、E0060、E0067、E0072、E0391
移动、初始化与析构E0031、E0041、E0055–E0056、E0059、E0100、E0305–E0308
借用、逃逸与 GCE0200、E0300–E0304、E0309–E0310
过程宏与内部码E0400、E0999

类型、调用与字面量

E0001 类型不匹配

let 初始化式、赋值、实参、返回值、字段初始化的类型与期望类型不一致时发出。二元运算两侧类型不同(例如 1 + "a")也走这个码,数组长度与期望长度不符同样是它。

fun main() {
    let n: i32 = "one";  // E0001
}

类型之间的隐式转换白名单很窄,只有数值字面量的待定类型、切片降级、trait object 提升等少数几种,见数据类型。 相关:E0002、E0012

E0002 分支类型无法合并

if 与 else、match 各臂、值位置上的无 else if,类型对不上时发出。数组重复表达式 [value; n] 的长度不是能折进 usize 的整数字面量时也是这个码。

fun main() {
    let c = true;
    let x = if c { 1 } else { "a" };  // E0002
}

[0; n] 里的 n 是变量时报 array repeat length must be an integer literal that fits usize。分支规则见控制流。 相关:E0001、E0003

E0003 运算符的操作数类型不对

算术要求数值,%、<<、>> 要求整数,位运算要求整数或 bool,排序比较要求两侧兼容的数值、char 或实现了 PartialOrd 的类型,一元 ! 要求 bool 或整数。

fun main() {
    let x = true + 1;  // E0003
}

相等比较走 PartialEq,缺实现时报 E0036。运算符清单见表达式与块。

E0004 调用了不是函数的值

fun main() {
    let x = 1;
    x();  // E0004
}

可调用值的形态见函数与匿名函数与迭代器。 相关:E0005

E0005 实参个数或形状不符

函数、方法、匿名函数、可调用字段的调用点实参数量不对时发出。泛型调用的类型实参无法从实参和期望返回类型推断出来时,也用这个码,原文是 cannot infer type argument(s) for ...。

fun f(a: i32) {}

fun main() {
    f();  // E0005
}

相关:E0004、E0032

E0006 未知字段

结构体字面量或字段访问用了定义里没有的字段名,枚举结构变体上的字段访问同样处理。

struct P { x: i32 }

fun main() {
    let p = P { x: 1 };
    let y = p.y;  // E0006
}

相关:E0007、E0054、结构体

E0007 结构体字面量缺字段

struct P { x: i32, y: i32 }

fun main() {
    let p = P { x: 1 };  // E0007
}

字段名写错是 E0006,不是缺字段。

E0008 解引用不是引用或裸指针的值

fun main() {
    let x = 1;
    let y = *x;  // E0008
}

帮助文本里还留着一句「只有数组能下标」,下标失败实际报 E0036。

E0009 结构体字面量或变体的形状不对

路径解析到的不是结构体、给元组变体写结构体字段、结构体字面量的类型实参个数不对,都报这个码。

enum E { A(i32) }

fun main() {
    let x = E::A { v: 1 };  // E0009
}

路径本身找不到是 E0050,变体不属于被匹配的枚举是 E0038。

E0011 字面量后缀或数值范围非法

词法层接受 i128、u128、f16、f128 后缀,类型系统里没有这些类型,写出来会得到「不支持」的提示;无法识别的后缀报未知后缀。字面量和常量值超出目标类型范围时也是这个码。usize/isize 的边界由选中的目标给出(--target、RIDDLE_TARGET 或 [build].target),i686-* 下 4294967296usize 越界,64 位目标下合法;运行编译器机器的宽度不参与。

fun main() {
    let x = 1i128;  // E0011
}

let x: u8 = 300; 报整数越界。42_i99 不属于这个码:_ 会另起一个 token,得到的是解析错误,而解析错误不带码。

E0012 as 的类型对不支持

支持的组合:整数之间、整数与浮点之间、整数转 bool、bool 与 char 转整数、u8 转 char、裸指针之间,以及 &T 转 *const T/*mut T(要求 T sized,目标可变时源也必须可变)。其余组合报这个码。

fun main() {
    let x = true as f64;  // E0012
}

(*const T, usize) 与 &[T]、&[u8] 与 &str 之间的布局转换需要 unsafe;*const T 转 *mut T 单独需要 unsafe,缺了报 E0046。指针与 FFI 见 FFI 与 C 后端。

E0043 不定长类型出现在需要定长的位置

str、[T]、dyn Trait 没有独立于借用或指针的布局,不能作为局部绑定、参数、返回值、字段,也不能作为元组、数组、结构体、枚举的类型实参。&T 和 *const T 的内层允许不定长。

struct S { s: str }  // E0043

字符串值用 &str,切片与 trait object 用 &[T]、&dyn Trait 或拥有形式。见数据类型。 相关:E0072

名字、字段、方法与可见性

E0013 方法不存在或不能动态派发

接收者上没有这个固有方法或 trait 方法时报 unknown method。dyn Trait 上同名多来源的歧义方法、以及不满足对象安全的方法(按值 self、泛型方法、参数或返回值里出现裸 Self)在被调用时也报这个码。

struct P {}

fun main() {
    let p = P {};
    p.missing();  // E0013
}

对象安全不是 trait 声明处的硬性检查:声明本身能通过,调用点才报错。

trait T { fun consume(self) -> i32; }

struct S {}

impl T for S { fun consume(self) -> i32 { 1 } }

fun f(x: &dyn T) -> i32 {
    x.consume()  // E0013
}

见 Trait与 impl 块。 相关:E0054

E0014 枚举变体字段不能写 mut

结构体字段上的 mut 表示经共享引用也能写;变体字段只能通过模式到达,写入无法穿过对枚举的共享引用,因此直接拒绝。

enum E { A { mut x: i32 } }  // E0014

fun main() {}

把字段放进结构体,或改用 &mut 接收者。见结构体。

E0015 函数声明缺少函数体

fun 声明以分号结束时没有函数体:MIR 降级不会为它生成任何函数,对它的调用只会在运行期以 “call to unknown function” 失败,因此在编译期拒绝。extern 块里的声明不受此限制。

fun helper() -> i32;  // E0015

fun main() -> i32 { helper() }

补上函数体,或把声明移入 unsafe extern "C" 块。

E0050 名字解析不到

作用域图在所有可见作用域里都找不到这个路径时发出。

fun main() {
    let x = missing;  // E0050
}

见模块、use 与包。 相关:E0051、E0052、E0064

E0051 空的 use 声明

use 树里没有任何可以暴露的名字,例如只写了 crate、super 这样的锚点。

use crate;  // E0051

E0052 glob 导入的目标找不到

use missing::*;  // E0052

普通路径导入失败报 E0050。

E0054 字段或方法不可见

结构体字段与固有方法默认私有,可见性要求同一个包,并且定义模块包含使用模块。trait impl 里的方法不做可见性过滤。

mod m {
    pub struct P { value: i32 }
}

fun read(p: m::P) -> i32 {
    p.value  // E0054
}

加 pub,或通过类型自己提供的公开方法访问。见模块、use 与包。

E0064 同一作用域里重名

函数、结构体、枚举、trait、常量、类型别名、模块共享当前作用域的声明名,重复定义时两个位置都会被标注。

fun f() {}

fun f() {}  // E0064

不同模块里的同名声明不冲突。相关:E0050

trait、impl 与泛型

E0020 trait 内重复方法,或 callable 签名不符

同一个 trait 里定义了两个同名方法时发出。impl Fn* for T 的 call 方法签名与 impl 头写的调用签名不一致时也用这个码。

trait T {
    fun f();
    fun f();  // E0020
}

两个 impl 方法签名不符是 E0028、E0029、E0030。

E0021 callable impl 缺 call

impl Fn(..) -> T for X 要求实现体提供 call。Fn 用 &self,FnMut 用 &mut self,FnOnce 用 self。

struct S {}

impl Fn(i32) -> i32 for S {}  // E0021

E0022 trait 内重复关联类型

trait T {
    type A;
    type A;  // E0022
}

dyn Trait<X = ..> 里对同一个关联类型写两次绑定也是这个码。相关:E0025、E0034

E0023 引用了未知 trait

impl 的 trait、impl Trait、泛型 bound 里写了找不到的 trait 名时发出。

struct S {}

impl Missing for S {}  // E0023

相关:E0044

E0024 impl 内重复方法

struct S {}

impl S {
    fun f() {}
    fun f() {}  // E0024
}

E0025 impl 内重复关联类型

struct S {}

impl S {
    type A = i32;
    type A = i64;  // E0025
}

E0026 impl 缺少必需方法

trait 里没有默认体的方法必须在 impl 里出现。

trait T { fun f(); }

struct S {}

impl T for S {}  // E0026

trait 方法有默认体时不报,关联类型看 E0027。

E0027 impl 缺少必需关联类型

trait 里没有默认值的类型别名必须在 impl 里给出。

trait T { type A; }

struct S {}

impl T for S {}  // E0027

E0028 impl 方法签名与 trait 声明不符

参数个数、泛型个数、unsafe 不一致都报这个码。

trait T { fun f(x: i32); }

struct S {}

impl T for S {
    fun f() {}  // E0028
}

参数类型不符是 E0029,返回类型不符是 E0030。

E0029 impl 方法参数类型不符

trait T { fun f(x: i32); }

struct S {}

impl T for S {
    fun f(x: bool) {}  // E0029
}

E0030 impl 方法返回类型不符

trait T { fun f() -> i32; }

struct S {}

impl T for S {
    fun f() -> bool { true }  // E0030
}

E0032 泛型实参个数不符

结构体、枚举、trait 的类型实参个数与声明不匹配时发出。trait 参数带默认值时,尾部的实参可以省略。

struct B<T> { v: T }

fun f(b: B<i32, i32>) {}  // E0032

相关:E0005、泛型

E0033 泛型递归让类型实参变大

泛型函数递归调用自己时,实际类型实参如果被包进了更大的类型,单态化会没有终点,编译器在调用图上找到这种环就拒绝。

struct Box<T> { v: T }

fun wrap<T>(v: T) {
    wrap(Box { v });  // E0033
}

类型实参相同的递归是允许的,见泛型。

E0034 类型标注非法

类型名找不到、dyn Trait 缺关联类型绑定、数组类型写反([3; i32])等都在这里报。未知类型名走这个码,不是 E0050。

fun main() {
    let x: Missing = 1;  // E0034
}

数组类型写反时另给一条 note:array types use [T; N]; write [i32; 3] instead。

E0035 trait bound 不满足

调用点、方法查找、for 迭代协议等位置要求某个类型实现 trait,而实际类型没有实现时发出。

trait M {}

struct B<T> where T: M { v: T }

struct P {}

fun main() {
    let b = B { v: P {} };  // E0035
}

见泛型、Trait。 相关:E0036

E0036 缺少运算符或索引所需的实现

相等比较缺 PartialEq、索引缺 Index/IndexMut、类型不能被下标类型索引,以及 impl 了某个 trait 却没有 impl 它的 supertrait,都报这个码。

struct P {}

fun main() {
    let x = P {} == P {};  // E0036
}

排序比较(<、>)两侧不兼容走 E0003。同点原始指针的 == 是内建按地址比较,不需要实现。

E0037 impl 的 where 子句违反 Paterson 条件

impl 的 where bound 必须严格小于被 impl 的类型,否则 trait 求解可能不终止。

trait F {}

struct V<T> { v: T }

impl<T> F for T where V<T>: F {}  // E0037

E0044 supertrait 未知或成环

trait C: Missing {}  // E0044

trait First: Second 与 trait Second: First 这样的环同样报这个码。impl 了子 trait 却没有 impl 父 trait 报 E0036。

E0045 类型推断不出来

匿名函数参数没有标注类型,且没有任何地方能约束它时发出。延迟绑定的 let 在首次赋值前也推不出类型,同样报这个码。

fun main() {
    let f = [x -> x];  // E0045
}

写成 [x: i32 -> x],或让调用点约束参数类型。见匿名函数与迭代器。 相关:E0067

E0047 impl 冲突或重叠

同一个 trait 的两个 impl header 覆盖到同一组类型时发出,第二条诊断会指向先出现的 impl。callable impl 缺调用签名、dyn Fn 缺签名也用这个码。

trait F {}

struct P {}

impl F for P {}

impl F for P {}  // E0047

impl Fn for S {} 报 impl Fn requires a callable signature。见 impl 块。 相关:E0048

内部属性、lang item 与保留名

E0048 违反孤儿规则

当前包只能实现自己定义的 trait,或为自己定义的类型实现外部 trait。判定顺序是先查 impl 重叠(E0047),再查本地性:&T 与 #[fundamental] 类型会传递本地性,本地类型之前不能出现未被覆盖的类型参数。Fn、FnMut、FnOnce 名字由编译器保留,重名声明也报这个码。

impl Clone for () {  // E0048
    fun clone(&self) -> () { () }
}

Clone 与 () 都来自标准库,所以这是外部 trait 加外部类型。见 Trait。

E0049 用户包使用了保留的内部属性

默认加载标准库时,#[lang = "..."] 与 #[fundamental] 只允许出现在随编译器附加的标准库中。来源检查优先于属性形状检查,所以用户包里的任何写法统一报这个码。

#[lang = "copy"]  // E0049
trait MyCopy {}

用 riddlec --no-std 时不附加标准库,参与编译的包可以自行定义 lang item,那时形状错误才由 E0053 接管。

E0053 lang item 或 #[fundamental] 的形状非法

lang 名未知、同一个 lang item 定义两次、一个 trait 标多个 [lang]、目标不是 trait 或枚举、缺字符串值、签名不符、Option 与 Result 的泛型个数不对,以及 #[fundamental] 用在非类型上或带了值,都报这个码。

// 需要 riddlec --no-std,或写在标准库自身里
#[lang = "unknown"]
trait F {}          // E0053: unknown lang item 'unknown'

#[lang = "copy"]
trait A {}

#[lang = "copy"]
trait B {}          // E0053: lang item 'copy' is defined more than once

在默认标准库模式下,同样的写法先被 E0049 拦下。

模式、控制流与 unsafe

E0010 模式元数或绑定修饰符位置不对

元组模式的元素个数与值不符、&pat 没有初值、在自动引用解构后的模式里再写 mut 或 &mut,都报这个码。

fun main() {
    let (x, y) = (1, 2, 3);  // E0010
}

模式形状与类型不符是 E0038,同一模式里重复绑定是 E0058。见枚举、模式与 match。

E0038 模式的形状或类型不符

解构元数不对、变体不属于被匹配的枚举、把非枚举值写成路径模式等,都报这个码。

enum L { S }

enum R { S }

fun f(x: L) -> i32 {
    match x {
        R::S => 1,  // E0038
        L::S => 0,
    }
}

E0039 match 不穷尽

至少有一个取值没有被任何无 guard 的臂覆盖。诊断会给出一个缺失模式,整数模式还会在 note 里列出未覆盖的区间;带 guard 的臂不计入穷尽性。

enum S { A, B }

fun f(s: S) -> i32 {
    match s {
        S::A => 1,  // E0039
    }
}

补上缺失的臂,或用 _ 覆盖剩余取值。

E0042 break 或 continue 不在循环里

fun f() {
    break;  // E0042
}

while、loop、for 的函数体、闭包体都不构成循环边界。见控制流。 相关:E0065

E0046 该操作需要 unsafe

解引用或索引裸指针、调用 unsafe fun 与不安全的外部函数、裸部件与切片的布局转换、*const T 转 *mut T,都必须在 unsafe 块里。

fun f(p: *const i32) -> i32 {
    *p  // E0046
}

unsafe 只支持块形式,fun f() { unsafe foo(); } 会得到语法错误。裸指针的有效性由程序员保证。见 FFI 与 C 后端。 相关:E0012

E0057 let 或 for 头里的模式可反驳

普通 let 与 for 头没有备选分支,模式必须覆盖该类型的每个取值。let-else 的 else 能发散时不受此限制。

fun main() {
    let o: Option<i32> = Option::Some(1);
    let Option::Some(v) = o;  // E0057
}

换成 let Option::Some(v) = o else { return; };,或改用 match。见枚举、模式与 match。 相关:E0038、E0066

E0058 同一模式里重复绑定

一个模式内的绑定名必须唯一。不同 let 语句或不同臂之间可以正常遮蔽同名变量。

fun main() {
    let (a, a) = (1, 2);  // E0058
}

E0061 ? 的操作数不是 Option 或 Result

? 只能展开标准库的 Option 与 Result,用在普通值或自定义枚举上报这个码。

fun f() -> i32 {
    let x = 1?;  // E0061
    x
}

见错误处理。 相关:E0062、E0063

E0062 ? 与函数返回类型不匹配

含 ? 的函数必须返回同一种枚举:操作数是 Result 就返回 Result,是 Option 就返回 Option。

fun f() -> i32 {
    let x = Option::Some(1)?;  // E0062
    x
}

E0063 ? 的错误类型转不过去

源错误类型到返回类型的错误类型,编译器先找 Into::into,再找 From::from;两条路都拿不到目标类型时报这个码。

fun g() -> Result<i32, i32> { Result::Err(1) }

fun f() -> Result<i32, bool> {
    let x = g()?;  // E0063
    Result::Ok(x)
}

E0065 带值的 break 出现在 loop 之外

只有 loop 能用带值的 break 交出结果,while 与 for 里只能写 break;。

fun f() {
    while true {
        break 1;  // E0065
    }
}

相关:E0042

E0066 let-else 的 else 块不发散

let-else 的 else 块必须离开当前控制流,正常落到绑定之后会报这个码。return、break、continue、panic! 或不会结束的 loop 都可以。

fun main() {
    let o: Option<i32> = Option::Some(1);
    let Option::Some(v) = o else { 0 };  // E0066
}

写成 else { return; } 就能通过。相关:E0057

常量、类型布局与降级

E0040 源码无法降级到 HIR

HIR 降级阶段遇到它解析不了的东西时发出,最常见的是超出 u64 的整数字面量。

fun main() {
    let x = 99999999999999999999;  // E0040
}

类型已经确定、只是超出目标类型范围的字面量报 E0011。

E0060 常量初始化式不是常量表达式,或常量成环

常量初始化式允许字面量、对其它常量的引用、非赋值的二元运算、无语句的块、元组/数组/结构体字面量、一元 -/+/!、字段访问、as、? 和下标。函数调用、闭包、if/while/loop/for/match/unsafe、带语句的块、赋值都不允许。

fun g() -> i32 { 0 }

const X: i32 = g();  // E0060

const A: i32 = B; 与 const B: i32 = A; 这样相互引用同样报这个码。常量值超出申明类型范围报 E0011。

E0067 推断出无限类型

统一两个类型时检出自我引用的替换环,继续下去会构造出无限大的类型。典型写法是把一个参数类型尚未确定的匿名函数传给它自己。

fun main() {
    let id = [value -> value];
    id(id);  // E0067
}

这段代码同时也会报 E0045,因为 value 的类型同样推不出来。给绑定加显式标注或改掉自应用即可。

E0072 递归类型大小无限

结构体或枚举的字段内联包含自己时发出,诊断会给该字段挂 recursive field 次标签。&T、*const T、*mut T、impl Trait、dyn Trait 和长度为 0 的 [T; 0] 都能打断环;[T; N](N 不为 0)和 [T] 不能。

struct N { next: N }  // E0072

标准库里没有 Arc/Rc,共享递归结构要靠 &T、裸指针、dyn Trait 或 Vector<T>(内部是 *mut T)。类型别名成环是 E0391。 相关:E0043

E0391 类型别名展开成环

type A = A;  // E0391

这是别名环,与 E0072 的布局环是两回事。

移动、初始化与析构

E0031 给不可变绑定赋值

左侧绑定没有 mut 却被重新赋值时发出。复合赋值的左值不可变、调用需要 &mut self 的方法但接收者绑定不可变、把需要修改捕获环境的匿名函数存进不可变绑定,也报这个码。

fun main() {
    let x = 1;
    x = 2;  // E0031
}

经共享引用写入是 E0309,两者不是一回事。见变量与可变性。

E0041 Copy impl 不合法

为类型实现 Copy 时,它的所有字段也必须 Copy,否则副本会与原件共享资源。无法枚举字段的类型(例如 &mut T)同样报这个码。

struct T { v: i32 }

struct W { v: T }

impl Copy for W {}  // E0041

字段全 Copy 的结构体也不会自动 Copy,必须显式 impl 或 [derive(Copy)]。同时实现 Drop 是 E0055。

E0055 为实现了 Drop 的类型实现 Copy

拥有析构逻辑的类型不能按位复制,否则多个副本会重复释放同一份资源。去掉其中一个 impl。

struct S {}

impl Drop for S {
    fun drop(&mut self) {}
}

impl Copy for S {}  // E0055

E0056 直接调用 Drop::drop

析构方法只能由编译器调用。要提前结束一个值,用标准库的 drop(value)。

struct S {}

impl Drop for S {
    fun drop(&mut self) {}
}

fun f(mut s: S) {
    s.drop();  // E0056
}

析构顺序与 drop glue 见移动语义。

E0059 读取未初始化的绑定

let x; 可以延迟初始化,但每条到达使用点的路径都必须先赋值;编译器合并 if、match 与循环的控制流,可能仍未初始化的读取就报这个码。

fun f() -> i32 {
    let x: i32;
    x  // E0059
}

E0100 使用已移动的值

值被移动后再次使用时报这个码。&T、*const T 是 Copy,&mut T 不是;结构体与枚举不会因为字段全 Copy 就自动 Copy。

struct P { x: i32 }

fun main() {
    let a = P { x: 1 };
    let b = a;
    let c = a;  // E0100
}

移动语义与部分移动见移动语义。 相关:E0304、E0305、E0308

E0305 从实现了 Drop 的类型里移出字段

实现了 Drop 的值必须整体保持有效,单独移出非 Copy 字段会跳过析构,因此被拒。

struct Guard {}

struct Owner { guard: Guard }

impl Drop for Owner {
    fun drop(&mut self) {}
}

fun main() {
    let owner = Owner { guard: Guard {} };
    let Owner { guard } = owner;  // E0305
}

字段是 Copy 时那是复制,不算移出。没有显式 Drop 的聚合类型可以部分移动,编译器用字段级 drop flag 避免重复析构。 相关:E0100、E0307

E0306 借用的存活期超过 Drop 拥有者

实现 Drop 的局部值在作用域结束时被确定性析构,指向它的引用不能作为返回值或捕获进逃逸的匿名函数活得更久。

struct Guard { value: i32 }

impl Drop for Guard {
    fun drop(&mut self) {}
}

fun leak() -> &i32 {
    let guard = Guard { value: 1 };
    &guard.value  // E0306
}

把所有权移出函数,或让引用只在拥有者作用域内使用。见引用、借用与逃逸。

E0307 在 match guard 里移动

guard 失败时还要继续尝试后面的臂,所以 guard 只能查看或借用非 Copy 的模式绑定,不能取得所有权;把 place 移进匿名函数同样被拒。

struct Token {}

enum MaybeToken { Some(Token), None }

fun consume(value: Token) -> bool { true }

fun main(value: MaybeToken) {
    match value {
        MaybeToken::Some(token) if consume(token) => {},  // E0307
        MaybeToken::Some(token) => {},
        MaybeToken::None => {},
    }
}

把移动放到选中臂的 body 里,或先借用再判断。 相关:E0304、E0305

E0308 从解引用里移出非 Copy 值

*reference 或显式 &pattern/&mut pattern 里的按值绑定会读取引用指向的 T;引用不拥有这个值,T 不是 Copy 时不能搬出来。

struct Token {}

fun main() {
    let token = Token {};
    let r = &token;
    let m = *r;  // E0308
}

保留引用并通过它访问,或只在确实允许按位复制时为类型实现 Copy。*reference = value 是写回原位置,不属于这个码。 相关:E0100

借用、逃逸与 GC

E0200 保留码

E0200 当前没有任何发射点。逃逸提升是静默的:逃逸分析的结果只用来决定 MIR 里用 alloca 还是堆分配,不会转成用户可见的诊断。

// 没有可复现的写法:源码中不存在这个码的发射点

E0300 要可变借用,已有共享借用

同一个 place 上已有共享借用且仍然活跃时,不能再创建可变借用。

fun main() {
    let mut x = 1;
    let r = &x;
    let m = &mut x;  // E0300
    println!("{} {}", *r, *m);
}

借用到持有它的绑定的最后一次使用为止,按源码位置计算,所以先用完再借是允许的:

fun main() {
    let mut x = 1;
    let r = &x;
    println!("{}", *r);
    let m = &mut x;
    *m = 2;
}

这个近似不是 CFG 活跃性分析,循环体会做不动点迭代,冲突只报一次。见引用、借用与逃逸。 相关:E0301、E0302、E0309

E0301 要共享借用,已有可变借用

fun main() {
    let mut x = 1;
    let m = &mut x;
    let r = &x;  // E0301
    println!("{} {}", *m, *r);
}

E0302 同一 place 同时两个可变借用

fun main() {
    let mut x = 1;
    let a = &mut x;
    let b = &mut x;  // E0302
    println!("{} {}", *a, *b);
}

不相交的结构体字段可以分别可变借用;已知常量下标可以分别可变借用,动态下标按重叠处理。方法返回的引用会保持接收者被借用。 相关:E0300

E0303 借用期间赋值

place 仍有活跃借用时不能给它赋值。

fun main() {
    let mut x = 1;
    let r = &x;
    x = 2;  // E0303
    println!("{}", *r);
}

相关:E0304、E0309

E0304 借用期间移动

place 仍有活跃借用时不能移动它,模式里的字段移动和移进匿名函数同样受限。

struct P { x: i32 }

fun main() {
    let p = P { x: 1 };
    let r = &p;
    let q = p;  // E0304
    println!("{} {}", r.x, q.x);
}

相关:E0303、E0100、E0307

E0309 通过共享引用写入

&T 只能读。字段访问和下标会隐式解引用,所以经共享引用写入与显式 *r = value 一样被拒;&self 接收者同理。

struct Counter { mut hits: i32, name: i32 }

impl Counter {
    fun bump(&self) {
        self.name = 1;  // E0309
    }
}

字段声明成 mut 就不受这条限制:经 &self 写 hits、或对 hits 调用需要 &mut self 的方法都可以。但 mut 字段上取 &mut 只允许出现在调用里(实参或接收者),绑定到名字、返回或存进其它聚合体会报同一个码。

struct Counter { mut hits: i32, name: i32 }

fun add_to(target: &mut i32) {
    *target += 1;
}

impl Counter {
    fun bump(&self) {
        self.hits += 1;
        add_to(&mut self.hits);
    }
}

fun main() {
    let counter = Counter { hits: 0, name: 0 };
    counter.bump();
}
struct Counter { mut hits: i32 }

impl Counter {
    fun bad(&self) {
        let target = &mut self.hits;  // E0309
        *target = 1;
    }
}

经裸指针写入不受此检查约束。见引用、借用与逃逸。 相关:E0300、E0303、E0031

E0310 无 GC 模式下引用逃出栈存储

Clue.toml 里设置 [runtime] gc = false 后,不会再靠 GC 堆延长局部值的存活时间;返回局部值或临时值的引用、按引用捕获并逃出当前栈帧都会被拒。默认配置下(GC 开启)同样的代码只做堆提升,不报错。

// Clue.toml
[runtime]
gc = false
// src/main.rid,用 clue check 复现
struct Data { value: i32 }

fun escaped() -> &Data {
    let value = Data { value: 42 };
    &value              // E0310
}

fun main() { escaped(); }

诊断文本是 reference to stack-owned value cannot escape when GC is disabled,help 提示返回或捕获拥有所有权的数据,note 说明从调用者传入的引用可以直接转发,因为那不会延长它指向值的存活期。gc = false 不能和 runtime.source 同时使用。见 Clue 构建器与引用、借用与逃逸。

过程宏与内部码

E0400 过程宏展开错误

riddlec 的过程宏驱动发出的错误,不在用户源码的 span 上。最常见的触发是调用了找不到的宏。

fun main() {
    unknown_macro!();  // E0400
}

内置函数式宏与派生宏清单见编写过程宏与形式化语法。

E0999 MIR 降级的兜底码

类型检查通过、但 MIR 的 as 降级找不到对应 CastOp 时发出,原文是 unsupported cast from X to Y。正常用户代码见不到它:出现这个码说明类型检查器的转换矩阵与 MIR 不一致,是编译器的 bug。

// 没有可复现的用户写法:这是内部兜底码

相关:E0012

编辑器里的码

LSP 诊断直接沿用上面的 E 码,source 固定为 riddle,code 是字符串形式的码。语言服务器无法从编辑器的增量变更重建缓冲区时,会为该文档发一条码为 LSP0001 的错误,消息是 this file is out of sync with the editor, so it was not analysed; reopen it or undo recent edits to restore diagnostics。见编辑器与 LSP。