当前工具链状态
Riddle 仍处于开发阶段。本页记录当前仓库已经实现并被测试覆盖的能力,避免把未来设计误当成可用功能。
编译流程
riddlec 可执行完整前端和基础后端流程:
- 词法分析和语法分析(
IncrementalParser提供局部重解析 API); - AST 包装;
- HIR 降级(含 E0040/E0050/E0051/E0052 诊断);
- 作用域图构建和名字解析(基于片段的增量作用域图,支持部分失效);
- 类型检查(含可复用的
IncrementalTypeChecker); - 逃逸分析(过程间不动点,决定局部值用栈分配还是 GC 堆分配);
- move checker(移动后使用、借用冲突、借用期间赋值/移动检查);
- HIR 到 MIR 降级(SSA 形式,Phi 节点,基本块,
Alloca/HeapAlloc分配指令); - C 后端代码生成。
命令行入口支持:
riddlec [--verbose] [--no-std] [--backend c] [--target <triple>] [--output <file>] <file>...
--backend c 会生成调用 rgc ABI 的 C 代码;如需可执行文件,使用本机 cc、gcc 或 clang 同时编译生成结果与发行包附带的 runtime.c 和 args_runtime.c(C 入口 main 会无条件初始化进程参数,因此 std::env 的 args() / args_os() 在任何包中调用都可用);生成代码的头部注释会给出完整链接命令。clue build 会自动完成这一步,不依赖 Boehm GC。多个输入文件会作为一个包合并编译,文件之间可以直接引用彼此的顶层条目。
riddle fmt 提供源码格式化和 --check 检查,LSP 格式化请求与该命令共享实现。
不指定后端时,riddlec 在完成 move/borrow 检查后停止;只有生成后端代码时才继续降级 MIR。
riddlec 会自动把 std/lib.rid 拼到用户源码后面,因此 std::marker::Copy、std::clone::Clone 和比较、运算 trait 不需要手动定义。
MIR 中间表示
MIR(Mid-level IR)是 SSA 形式的中间表示,位于类型检查和代码生成之间:
- SSA 基本块:每个函数体由基本块组成,块以
Terminator(Branch、CondBranch、Return、Unreachable)结束; - Phi 节点:
InstKind::Phi合并来自多个前驱块的值; - 分配指令:
Alloca(栈分配)和HeapAlloc(GC 堆分配),由逃逸分析结果驱动; - 内存操作:
Load、Store、FieldPtr(字段指针)、IndexPtr/CheckedIndexPtr(原始指针索引 / 安全数组与切片索引)、ExtractValue(提取聚合字段); - 值构造:
StructValue、SparseStructValue、ArrayValue、TupleValue;枚举使用稀疏初始化保证不同变体的 payload 槽位稳定; - 类型转换:
IntToInt、IntToChar、IntToFloat、FloatToInt、FloatToFloat、BoolToInt、IntToBool、IntToPtr、PtrToPtr; - 比较操作:
Cmp支持Eq、Neq、Lt、Gt、LtEq、GtEq。 - 函数值:可调用值统一为
{ call, env, drop },FunctionRef取得隐藏函数或命名函数适配器地址,CallIndirect传入环境后调用;未逃逸的捕获环境使用栈存储,只有越过当前栈帧的环境才提升到 GC 堆。
MIR 类型系统包含 FnPtr、Ptr、Struct、Enum、Tuple、Array、Slice、Str、Never、Void,并为定长类型提供 size_bytes() 布局估算;裸 Str 和 Slice 没有独立大小。
riddle-lsp
仓库包含 app/riddle-lsp,一个基于 tower-lsp 的 Language Server Protocol 实现:
- 完整的诊断流水线:解析错误、HIR 诊断、类型检查错误、move/escape 分析诊断全部通过 LSP 推送;
- 增量文本同步(
TextDocumentSyncKind::INCREMENTAL); - UTF-16 位置编码(正确处理多字节字符如 emoji);
- 多工作区管理与索引:发现每个工作区文件夹中的 Clue 项目,在内存中递归索引未打开文件的模块、类型成员、trait 方法、容器和可见性,并维护静态调用边和直接类型关系;文件或 manifest 变化只失效受影响的项目快照;
- 补全(
textDocument/completion):在 Clue 项目中加载模块和本地依赖,优先使用所有已打开文件的未保存内容;候选遵循词法作用域,包含参数、局部变量和模式绑定,并支持字段、实例方法、模块项、枚举变体、关联函数及导入别名;不可见的公开符号可生成独立use path;编辑完成自动导入,重名声明保留独立路径; - 悬停(
textDocument/hover):显示函数签名、字段与参数类型、局部表达式的推断类型,以及声明前的文档注释或同行//<尾随文档; - 签名帮助(
textDocument/signatureHelp):显示函数或方法签名、声明文档,并跟踪嵌套调用中的当前参数; - 声明、定义、类型定义与实现跳转(
textDocument/declaration、textDocument/definition、textDocument/typeDefinition、textDocument/implementation):支持局部绑定、模块项、字段、方法及跨文件符号,并把 trait 调用分别映射到 trait 声明和具体 impl; - 静态调用层级与类型层级:调用边覆盖编译器能够静态确定的自由函数、命名函数值、固有方法和 trait 方法声明;类型层级连接直接 supertrait、子 trait 及
impl Trait for Type的实现类型; - 项目级引用与重命名、文档高亮覆盖未打开模块和非文件 URI;文档符号按当前文档返回,工作区符号会合并已打开文档分析与项目
ProjectIndex中未打开文件的符号; - 文档格式化与基于语法块的代码折叠;
- Inlay Hint 同时提供推断的局部类型和可省略的调用参数名;
- Code Action 可为可变闭包绑定补
mut,也可把不安全操作包入unsafe块; - 语义 Token(
textDocument/semanticTokens/full),内置类型使用keyword,区分自由函数、方法、struct、enum 和 trait,关联函数使用method/static,标准库符号使用defaultLibrary,并包含函数、参数和方法declaration及可变局部变量declaration/mutable标记; - 诊断区分主标签和次要标签(related information),错误码可跳转到错误码手册,注释和修复建议分别以
note:/help:附加; - Clue 项目按原始文件 URI 发布诊断,包括未打开模块,并在重新分析后清理过期诊断;
- 诊断严重性层级:Error、Warning、Information、Hint;
- 文档变更会先合并短时间内的连续输入,再在后台运行诊断并协作式取消过期分析;未变化的文件和无关 Clue 项目直接复用诊断,变化的分析单元复用增量语法树、函数体和全局类型检查缓存,在声明、overlay、磁盘源码或 manifest 变化时保守失效;诊断在 move/borrow 检查后停止,不生成 MIR;UTF-16 位置通过行索引换算,语义 Token 使用包含未保存 overlay 的项目级 HIR,并按文档文本和分析修订缓存;
- 支持动态注册
.rid与Clue.toml文件监听,编辑器外部的源码、模块和 manifest 变更会触发项目缓存失效与重新诊断; - 仓库内提供 Helix、VS Code、Zed 和 IntelliJ IDEA 2026.1+ 的
.rid文件与riddle-lsp适配;
工作区中的 Clue 项目会建立内存索引。补全可通过独立的 use path; 编辑自动导入可达的公开符号;调用层级只包含编译器能够静态解析的目标,不推测函数指针、闭包或 Trait 的运行时分派。
安装和验证步骤见编辑器与 LSP。
当前语言特性
模块和名字解析
mod name { ... }内联模块;mod name;外部模块声明的语法;use path;、use path as alias;;use path::*;;use path::{a, b as c};;pub可见性,模块路径只导出 public 项;pub use重新导出;self、super、crate和::root风格路径;- 局部变量、参数、模块项、结构体、枚举变体、函数和 impl 方法的解析。
变量、函数和表达式
let绑定,默认不可变;let mut可变绑定;[x -> x + 1]与move [x -> x + 1]方括号匿名函数、参数推断和闭包捕获;支持参数类型标注、参数解构、零参形式与块体;泛型参数、where子句、返回类型标注与自递归绑定不再属于匿名函数,需要时用具名函数表达(旧的fun(x) { ... }匿名函数语法已移除,编译器会给出指向方括号形式的诊断);- 参数和返回位置的一般
impl Trait,以及带调用签名的impl Fn、impl FnMut、impl FnOnce; - 按用法推断共享、可变和值捕获,精确追踪静态字段和元组元素,并据此检查
Fn、FnMut、FnOnce调用能力; - 每个匿名函数表达式、命名函数项和泛型函数实例具有独立的静态类型;
- 显式类型标注;
- 顶层和
impl内的const声明(const NAME: Type = value;),初始化式会做类型、纯表达式和循环检查; - 模块和
impl内的有值type别名,以及 trait 中可省略默认值的关联类型; let支持延迟初始化,首次赋值不要求mut,并检查跨if、match、循环的 definite-initialization;未初始化读取报E0059,不可变绑定二次赋值报E0031;- 函数定义和函数声明;
- 泛型函数(类型参数和 const 参数从实参与期望返回类型推断,支持 Rust 风格函数、方法及
Type::<T>::function::<U>()显式参数、<T: Trait>bound、where子句,C backend 单态化); - 函数参数、返回类型、尾表达式和
return; - 块表达式;
- 结构体字段、元组数字字段(
.0、.1等)、函数调用和方法调用; - 数组字面量、数组重复表达式
[value; N]、数组与切片安全索引(越界终止并报告运行时错误);原始指针索引仍需unsafe且不做边界检查; - 结构体字面量和字段简写;
- 类型转换表达式
expr as Type;支持安全的u8 as char与&str到&[u8],(*const T, usize)/(*mut T, usize)到&[T]、&[u8]到&str的 DST 等布局转换仅允许在unsafe中使用; unsafe { ... }块表达式,以及原始指针解引用和索引的安全上下文检查;unsafe fun函数和直接调用检查;不安全函数项不会满足安全的Fn*bound;unsafe extern "C"导入块,块内默认不安全并支持safe fun显式安全声明;- 解引用
*expr。
运算符
- 算术:
+、-、*、/、%; - 比较:
==、!=、<、>、<=、>=; - 逻辑:
&&、||、!; - 位运算:
&、|、^、<<、>>; - 赋值:
=; - 复合赋值:
+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=; - 一元:
+、-、&、&mut、*、!。
C backend 对整数回绕、除零、最小值除以 -1、移位计数和浮点转整数使用确定性规则:整数算术按位宽回绕,错误除法终止,移位计数按位宽取模并对有符号右移使用算术语义,NaN 转整数为零且溢出值钳制到边界。
控制流和模式
if/else if/else表达式;if let 模式 = 表达式 { } else { },在 HIR 降级时脱糖为带_通配臂的match,绑定只在匹配成功的分支内可见;let 模式 = 表达式 else { ... };,允许可反驳模式,失败分支必须发散(E0066),成功后的绑定进入外层作用域;while循环;while let 模式 = 表达式 { },脱糖为loop内每次迭代重新求值的match,匹配失败时break;loop { }无限循环表达式,break 值;交出循环结果,所有break值类型合并为结果类型,无可达break时类型为!;for item in iterable循环,按IntoIterator/Iterator做类型检查,并在 MIR 中降成into_iter/next调用;循环头接受任意不可反驳模式(元组、结构体、通配符等),可反驳模式报告E0057;当前元素、迭代器和提前退出路径具有独立的析构作用域;- 泛型参数可以通过
IntoIterator<Item = ..., IntoIter = ...>bound 使用for,具体 impl 在单态化时解析; - 标准库
Range、固定长度数组[T; N]、共享切片&[T]、可变切片&mut [T]和&str可直接用于for,数组按值遍历且不要求元素类型为Copy,字符串迭代产出 Unicodechar; match表达式,以及枚举、布尔值、()、整数、元组和结构体的递归穷尽性检查;- 非穷尽整数匹配会报告未覆盖的连续值区间;
matchguard,guard 失败后继续检查后续 arm,且带 guard 的 arm 不计入静态穷尽性;_通配模式;- 标识符绑定模式;
- 字面量模式;
- 路径模式;
- 显式
&pattern/&mut pattern,支持嵌套引用模式且要求可变性精确匹配; - 元组模式;
- 结构体模式;
- 枚举 unit/tuple/struct 变体模式,payload 绑定会进入 guard 和 arm 表达式;
- 引用 match ergonomics:结构化模式自动解引用
&T/&mut T,内部绑定继承共享或可变引用模式;裸绑定保留整个引用,且不提供ref/ref mut语法。默认绑定模式变为引用后,内部不能再写mut binding或显式引用模式。
类型系统
- 整数:
i8、i16、i32、i64、isize、u8、u16、u32、u64、usize; - 浮点:
f32、f64; bool、char、()、!;str:不定长字符串类型,仅能作为引用、原始指针或impl的目标;&str:引用str的定长胖指针值;[T]:不定长切片类型,仅能位于引用或原始指针后;&[T]/&mut [T]:携带元素地址和长度的胖指针,可由对应可变性的数组引用自动转换;- 引用:
&T、&mut T; - 原始指针类型:
*const T、*mut T; - 元组类型和元组表达式,例如
(2, 3)与(2,); - 固定长度数组
[T; N]; - const generics,例如
struct Buffer<T, const N: usize> { data: [T; N] }; - 结构体;
- 枚举;
- 标准库
Option<T>和Result<T, E>; - 独立的匿名函数与命名函数项类型,以及静态
Fn/FnMut/FnOncebound; - 泛型函数、泛型结构体、泛型枚举、泛型 impl;
- 函数、trait、impl、结构体和枚举的泛型 bound:
<T: Trait>、<T: A + B>、where T: Trait; - 类型参数实例化;
- const 参数实例化,例如
Buffer<i32, 3>; - 无空格嵌套泛型类型参数,例如
Box<Box<i32>>和Box<Box<Box<i32>>>。
Trait 和 impl
trait定义;- 父 trait 声明、传递 bound、父方法查找、impl 前置依赖和继承环检查;
- trait 方法签名;
- trait 默认方法;impl 未覆写时使用默认体,显式覆写优先;
- 关联类型声明和默认关联类型;
impl Trait for Type;- 用户类型实现
Fn(参数...) -> 返回类型、FnMut和FnOnce,并静态调用其call方法; impl Type固有方法;self、&self、&mut self接收者;- 方法调用
value.method();方法查找失败时会回退为调用存储在字段里的可调用值(self.f(x),f为闭包、函数项或带Fn/FnMutbound 的泛型字段),impl 的 callable bound 由闭包签名结构化满足(Fn值可用于FnMut/FnOnce需求); - 关联函数路径调用
Type::function(...); Type::Assoc关联类型路径;- trait impl 合约检查:缺少方法、参数类型、返回类型和缺少关联类型会报错;
- 泛型 trait impl 模式匹配,例如
impl<T> std::marker::Copy for Box<T>; impl上的where子句,并检查 Paterson condition:约束必须严格小于被实现的类型;- 算术、取余、位运算、移位、一元负号、逻辑非和复合赋值可通过对应的
#[lang = "..."]trait 为用户类型分派; ==/!=检查PartialEq,有序比较检查PartialOrd;- 标准库
Iterator/IntoIterator协议,含std::ops::{Range, range}、数组IntoIterator和for遍历。
属性和标准库内置项
Riddle 支持 Rust 风格外部属性,可放置在多项位置:
#[item]
struct Item {
#[field]
value: i32,
}
fun id(#[param] value: #[ty] i32) -> i32 {
#[expr] value
}
match value {
#[arm] Pattern => result,
}
属性当前会进入 AST/HIR。编译器识别 #[lang = "..."],用于把 trait 标记为编译器内置项。默认加载标准库时,该属性仅允许随编译器附加的标准库使用,用户包中出现会触发 E0049;使用 --no-std 时,参与编译的包可以为自定义 core 定义 lang item,编译器仍会检查名称、目标、固定签名和重复注册。
Clue 支持 #[proc_macro_derive(Name, attributes(...))]、#[proc_macro_attribute] 和 #[proc_macro] 导出的 Riddle 过程宏。过程宏包由 [lib] proc-macro = true 标记并为宿主平台构建,也可以依赖并使用另一个过程宏包。宏可通过分组、别名、通配符或 pub use 导入独立的宏命名空间,混合 use 会保留普通名称;derive 只允许放在结构体或枚举上,Riddle 当前没有 union 条目。函数式宏使用 name!() 语法,可出现在表达式、条目、类型和模式位置。宏函数接收由 Group、Ident、Punct 和 Literal 组成的递归 TokenStream;输入、输出、诊断和 span 通过带版本的长度前缀结构化协议传递,输出 token 直接进入解析器。复制到输出的 token 会保留源位置,生成代码中的宏会继续展开,最大深度为 32。过程宏包内置 syn 和 quote!:syn 提供结构化 DeriveInput、Riddle 语法分类、Parse、ToTokens、Visit 与 Fold,quote! 支持插值、重复和等长向量配对。LSP 同步支持宏高亮、悬停、定义、引用、别名重命名和补全。
当前标准库会自动拼到用户源码后面,根部通过 prelude 重导出常用项,同时按 Rust 风格分模块定义:
prelude 只直接提供 Option、Result、String、Vector、Some、None、Ok、Err、Copy、Clone、Drop、drop、Default、Into、比较 trait 和迭代协议。集合、格式化 trait、具体迭代器、区间、解析、时间及底层输出函数需要从各自模块显式导入;标准宏命名空间隐式提供 Debug、Clone、Copy、Default、Hash、PartialEq、Eq、PartialOrd、Ord 派生,格式化与输出宏,以及 assert! / assert_eq! / assert_ne!、对应的 debug_assert* 宏、todo!、unimplemented! 和 unreachable!。
std::option::Option<T>,提供is_some、is_none、unwrap、unwrap_or、map、and_then和or;std::result::Result<T, E>,提供is_ok、is_err、unwrap、unwrap_or、map、and_then、ok和err;std::ffi::OsString无损保存平台字符串;std::env::args_os()在 Unix 保存原始参数字节,在 Windows 解析GetCommandLineW并以 WTF-8 保存 UTF-16,std::env::args()则严格转换为String,遇到非 Unicode 参数时 panic;print!/println!通过隐藏的标准库输出入口和std::fmt::{Debug, Display, Formatter, Result}支持字符串、布尔、字符、整数和浮点标量;格式化 trait 不在 prelude 中,底层输出入口不属于用户 API。Debug与Display都使用fmt(&self, formatter: &mut Formatter) -> Result,字符串和字符的Debug输出会添加引号并转义;标准派生支持结构体、泛型结构体以及 unit、tuple、named 三类枚举变体,当前包括Debug、Clone、Copy、Default、Hash、PartialEq、Eq、PartialOrd和Ord,并为泛型参数生成相应 bound;枚举Default要求恰好一个带#[default]的 unit 变体,排序派生按变体声明顺序和 payload 字典序工作;Copyimpl 会验证所有字段和 payload,比较派生仍需满足父 trait;Option、Result、String、Vector、HashMap、HashSet、TreeMap和TreeSet均通过Debug派生实现格式化;print!/println!支持空调用,format!要求字符串字面量并返回String,panic!()使用explicit panic,panic!(...)在终止前格式化消息;四个宏都支持字符串字面量、{}/{:?}/{0}位置参数 /{name}命名捕获、尾随逗号以及{{/}},并在编译期校验格式串;{}按从左到右的顺序消费参数,{0}可重复引用任意参数,{name}隐式捕获调用处的同名局部变量;宽度、对齐等其他格式说明符尚未实现;assert!、assert_eq!、assert_ne!及对应的debug_assert*宏复用panic!;比较断言只求值两侧一次并显示Debug值,自定义消息仅在失败路径求值。todo!、unimplemented!和unreachable!返回!并保留调用位置;当前所有构建都会执行 debug assertion;vec!宏支持三种形式:vec![a, b, c]构造Vector并逐个push(元素按值移动,支持尾随逗号与嵌套vec!),vec![elem; count]展开为Vector::from_elem(elem, count)(要求元素实现Clone,为每个槽位克隆),空vec![]展开为Vector::new()块并由上下文推断元素类型(无法推断时报告类型错误);Vector::from_elem是公开的标准库 API;std::string::String提供new、from_str、as_str、len、capacity、is_empty、push_str、push_char、clear、split、replace、to_ascii_uppercase和to_ascii_lowercase(split返回Vector<String>,空分隔符行为与find一致);同一模块按 Rust 风格为str提供len、is_empty、as_bytes、contains、find、starts_with、ends_with、slice、trim、split、replace、to_ascii_uppercase、to_ascii_lowercase和按 Unicodechar遍历的StrIter;std::vector::Vector<T>提供new、len、capacity、is_empty、push、pop、insert、remove、get、get_mut、swap、sort(要求T: PartialOrd,插入排序)、contains(要求T: PartialEq)、retain、clear、as_slice、读写下标和按值迭代;Vector<T>另提供from_iterator(把任意迭代器收集为向量)和from_elem(value, count)(要求T: Clone,vec![value; count]的底层实现);下标越界调用panic,缓冲区通过运行时rgc_realloc、rgc_free管理;Vector<T>会拒绝零大小元素并检查容量乘法溢出;同点原始指针支持==/!=按地址比较,p == 0usize as *const T可用于空指针检查;std::iter::{Iterator, IntoIterator};Iterator提供默认方法count、nth、fold、for_each、all、any、find、position,以及惰性的map/filter(通过闭包字段适配器实现,可链式组合并支持for遍历);std::iter另提供急切求值的map_into/filter_into(返回Vector)与适配器构造函数enumerate/take/zip/skip,以及min/max(返回Option<Item>,要求Item: PartialOrd);Iterator::collect可把任意迭代器收集为Vector<Self::Item>,Vector::from_iterator与之等价;DoubleEndedIterator提供next_back,切片迭代器SliceIter支持从尾部遍历;std::slice::{SliceIter, SliceIterMut},并为[T]提供长度、边界检查访问、原始指针访问和借用迭代;std::array中的按值、共享借用和可变借用数组迭代器;std::ops::{Range, range(start, end)};范围表达式a..b脱糖为range(a, b),a..=b脱糖为range_inclusive(a, b)(std::ops::RangeInclusive,含单元素与空区间语义);std::marker::Copy;std::clone::Clone;std::cmp::{Ordering, PartialEq, Eq, PartialOrd, Ord};std::ops下的算术、位运算、移位、复合赋值以及Index/IndexMuttrait,均有可调用的必需方法;这些 trait 由对应#[lang = "..."]标记,用户类型的下标操作静态分派到index/index_mut。std::default::Default为标量、Option<T>、String和Vector<T>提供默认值;Default::default()支持按期望类型静态选择 impl;std::convert::Into<T>是?错误传播使用的错误转换协议;std::convert::From<T>已提供,?在没有Intoimpl 时回退查找Fromimpl(Rust 风格错误链路),且?同样支持Option<T>操作数(在返回Option的函数中把None提前返回);std::hash::Hash通过共享借用为标量提供确定性的usize哈希值;std::collections::{TreeMap, TreeSet}使用红黑树,键要求实现Ord;std::collections::{HashMap, HashSet}使用开放寻址哈希表、线性探测和负载扩容,键要求实现Hash + Eq;四类集合都提供remove:HashMap 采用线性探测的后移删除(backward-shift deletion),TreeMap 采用带删除修复(delete fixup)的 CLRS 红黑树删除并压缩 arena 槽位;对应实现模块位于std::collections::{tree_map, tree_set, hash_map, hash_set};HashMap::get_or_insert(key, default)返回已有值或插入默认值后的可变引用;std::parse提供parse_i32/parse_i64/parse_u64/parse_usize(十进制、溢出安全)与parse_with_radix(2–36 进制);std::time::time_now转发到 Ctime,Duration::from_secs/from_millis与sleep转发到riddle_sleep_ms;std::fs::FsFile通过运行时提供的riddle_fs_*薄包装(避免与<stdio.h>原型冲突)访问 Cstdio:open/create/append/read/write/flush/read_to_string,Drop保证关闭句柄;std::fs::{read_to_string, write}提供整文件便捷读写;std::fs::{exists, metadata, read_dir}提供存在性检查、FileMetadata { size, is_file, is_dir }元数据查询和目录条目枚举(read_dir返回Vector<String>,跨平台由 Win32FindFirstFile/ POSIXdirent支撑);?可直接在这些Result<FsError>API 间传播;std::random提供random_u32/random_u64/random_bool/random_below,由riddle_random_u32/riddle_random_u64运行时垫片支撑(Windows 使用GetTickCount种子的 xorshift,POSIX 读取/dev/urandom);std::ptr场景下同点原始指针可用==/!=按地址比较,p == 0usize as *const T即空指针检查;
Default、Hash、标量格式化和基础集合/解析/时间 API 已经具备可执行行为;整数解析会拒绝空串、非法字符和超出目标范围的输入。
当前影响编译器语义的 lang trait 包括:
#[lang = "copy"]:被它标记的Copytrait 会被 move checker 用来决定用户类型是否按复制语义处理;#[lang = "drop"]:被它标记的Droptrait 提供确定性析构;Drop + Copy、直接调用析构方法和从显式Drop类型移出字段会被拒绝;#[lang = "add"]到#[lang = "shr"]:用户类型的算术、位运算和移位会分派到对应 trait 方法;标量 impl 的方法调用直接降为 MIR 运算;#[lang = "neg"]和#[lang = "not"]:用户类型的一元负号和逻辑非会分派到对应 trait 方法;标量 impl 的方法调用直接降为 MIR 运算;#[lang = "add_assign"]到#[lang = "shr_assign"]:用户类型的复合赋值会分派到对应 trait 方法;标量 impl 的方法调用直接降为 MIR 的读取、运算和写回;#[lang = "index"]和#[lang = "index_mut"]:非内建下标读取和可变位置分别静态分派到Index::index与IndexMut::index_mut;数组、切片和裸指针保留原有直接索引路径;#[lang = "partial_eq"]:用户类型的==/!=分派到PartialEq::eq/ne;#[lang = "partial_ord"]:用户类型的<、>、<=、>=分派到PartialOrd::lt、gt、le、ge。
Clone::clone、PartialEq::eq、PartialOrd::partial_cmp、Ord::cmp 和各运算 trait 方法可以直接调用。带受支持 lang 标记的标量运算方法不会生成 add__i64 一类 C 包装函数,而是生成原生 C 运算表达式。未标记的同名 trait 仍按普通方法编译;用户类型的运算符会调用对应 trait impl 或默认方法。
二元、复合赋值和比较 trait 支持 Rhs = Self 默认类型参数以及异构右操作数 impl;泛型约束中的运算符调用在单态化后静态选择具体 impl。赋值求值顺序与 Rust 一致:普通赋值和内建复合赋值先右后左,重载复合赋值先左后右。
所有权、移动和逃逸
- 值默认移动;
?接受Result<T, E>和Option<T>:成功分支继续当前函数;错误分支通过Into(无Intoimpl 时回退From)转换后返回外层Result,Option操作数则在返回Option的函数中把None提前返回;- 标量、共享引用、原始指针和命名函数项等内置 Copy 候选默认可复制;
&mut T与闭包值不可复制; Option<T>和Result<T, E>仅在所有 payload 类型实现Copy时实现Copy;- 用户类型可以通过实现
std::marker::Copy进入复制语义;编译器会验证结构体字段和所有枚举 payload,并在泛型场景中使用 impl bound; - move checker 检查移动后使用;
- 借用期间移动会报错;
- 方法和函数返回值会传播引用来源,包含
Option<&T>等泛型包装;元组和数组的来源按元素保留,模式解构不会让无关元素互相延长借用; - 引用参数支持自动重借用,局部借用可在最后一次使用后结束;
- 模式生成的字段重借用按投影分别追踪;子借用存活时冻结父可变引用,显式引用模式复制
Copy内容而不移动引用; - 字段访问本身不会移动整个结构体;
- 数组元素和结构体字段按值移动;
match解构按字段记录部分移动,未移动的兄弟字段仍可继续使用;- 引用逃逸分析通过过程间的“外泄参数 / 返回来源参数”摘要,决定局部值使用栈分配还是 GC 堆分配。
- 共享/可变闭包捕获会让对应局部获得稳定地址;静态字段和元组元素按投影独立捕获,动态索引与解引用在无法继续静态细分的位置停止;闭包未逃逸时使用栈存储,闭包越过当前栈帧时才提升到 GC 堆,且分配位置不会放宽移动和借用检查;
move [...]按值捕获所有使用到的外部位置;Copy值仍复制,按值捕获本身不会强制闭包成为FnOnce;- 非
Copy值捕获会在创建闭包时移动该值,FnOnce闭包调用后不可再次使用。 - 需要析构的局部、参数、模式绑定、迭代元素、聚合字段和闭包值使用 drop flag 防止移动后的重复析构;逃逸到 GC 堆只改变地址,仍在所有者结束时确定性运行
Drop。 - GC 运行时(
runtime.c)对栈执行保守式扫描,从rgc_init记录的栈底开始向上标记;堆对象记录在动态注册表中,精确指针查找走地址哈希表,内部指针标记按每次回收重建的地址有序索引二分查找,清扫只访问已注册槽位,回收阈值随存活集合增长(next = max(1 MiB, live * 2)),RGC_DEBUG_STATS=1会向 stderr 打印回收统计。运行时能处理引用位于寄存器或栈缝中的常见情况,但依赖编译器在标记期间把活引用保持在可扫描的内存中,且未逃逸值(栈上)不参与堆回收;公开的rgc_*ABI 保持不变。
字符串和 FFI
str是不定长类型,不能作为局部变量、参数、返回值或普通字段;&str是{ ptr, len }胖指针,字符串字面量的类型也是&str;- 字符串字面量支持
"..."、r"..."、r#"..."#和r###"..."###; extern "C"支持声明块和带函数体的导出定义;- C 导入中的
&str映射为const char*,调用点会复制并补齐 NUL,临时指针只在调用期间有效且输入不能含嵌入 NUL;显式#[c_export]包装函数也使用该参数 ABI,边界另一侧必须提供 NUL 终止的数据;需要保留长度时应显式传递指针和usize;带函数体的既有extern "C"定义和普通 Riddle 函数仍使用{ ptr, len }; - C backend 只会在实际调用 C 字符串导入时生成内部的 NUL 终止桥接 helper;除此之外不按函数名提供内置 C helper,所有
extern "C"声明都按普通外部符号生成; - 标准库通过
as_bytes().len()实现str::len,并用受限的同布局转换实现&str/&[u8]转换;String::as_str先借用Vector<u8>为&[u8],再通过普通标准库 unsafe 函数转换为&str,不使用函数 builtin,也不生成或链接 C helper; String以Vector<u8>持有 UTF-8 字节,支持追加、清空和借用为&str;存活的as_str()视图会阻止可能使其失效的可变操作。
后端状态
| 后端 | 状态 |
|---|---|
| C backend | CLI 可用:--backend c。输出使用 rgc 运行时 ABI;默认 provider 由 clue 选择,也支持自定义 provider |
C backend 实现统一的 Backend trait:compile(&mut self, module: &Module) -> Result<String, Self::Error>。
C backend 会把标量 std 运算 trait 的显式方法调用直接输出为带确定性溢出、除法和移位保护的 +、-、*、&、<< 等 C 表达式,不声明或定义对应的 primitive wrapper;用户类型的 trait 方法仍输出普通 C 函数。
工具状态
| 工具 | 状态 |
|---|---|
riddle fmt | 源码格式化 CLI,支持文件、标准输入、--check、缩进宽度和硬制表符;与 LSP 复用 formatter |
riddlec | 编译器 CLI,支持前端检查、MIR 降级和 C backend |
riddle-lsp | LSP 服务器,基于 tower-lsp,提供诊断、补全、悬停、签名帮助、符号导航、引用、重命名、格式化、Inlay Hint 和语义 Token,并识别过程宏命名空间 |
clue | 包管理器和项目构建器,支持项目、workspace、path/git/registry 依赖、锁文件、features、test/bench、打包发布与安装;二进制项目会保留 C 并生成本机可执行文件,库项目可生成 .rmeta、.rlib、静态库和动态库,过程宏依赖构建为宿主进程 |
当前限制
- 标量类型限于 C11 可移植表示:
i128、u128、f16、f128在词法上可写,但类型检查会拒绝并给出诊断,语义上不存在这些宽类型; - 进程参数
std::env::args()/args_os()需要链接args_runtime.c(见上文编译流程);C 入口main无条件调用riddle_args_init,因此参数在任意包中使用都可用; - 当前定位为单线程语言:线程 / 互斥锁 / 原子变量 /
async/await/ 网络尚未实现;开区间范围(a../..b)、范围模式(match中的a..=b)、循环标签、Rc/Arc/Cell/RefCell等智能指针与内部可变性也尚未实现;matchguard 目前只在match中提供,let解构与解构赋值已直接支持; - 数字解析不支持十六进制浮点;整数已支持
0x/0o/0b前缀与_分隔符; - 泛型目前偏向单态化,尚未覆盖完整 Rust 泛型能力;
riddlec的 C backend 只输出 C;clue build会严格使用CC,或自动选择能完成 C11 编译和链接的系统 C 编译器来生成本机可执行文件;- 逃逸分析会沿结构体、元组和数组字段传播引用来源;字段模式绑定可以单独提升到 GC 堆,只有根绑定或无法静态细分的访问才提升整个存储槽;
- TODO:数组
IntoIterator当前按索引顺序产出元素;若未来允许自定义数组迭代器乱序移出元素,需要先加入MaybeUninit/ManuallyDrop等价存储和逐槽存活状态,确保剩余元素只析构一次; - trait 方法支持对象安全的
&dyn Trait/&mut dyn Trait借用对象和拥有所有权的dyn Trait值;拥有值使用数据指针、方法表和类型专属 drop 槽位,并在 GC / no-GC runtime 下分别使用rgc_alloc/rgc_free或riddle_alloc/riddle_free;拥有对象可以重借用为&dyn Trait,父 trait 支持对象向上转型,泛型参数可在满足 trait bound 时转换为拥有对象,数组字面量会逐元素应用转换;跨父 trait 的同名方法拒绝为歧义,非对象安全方法会明确报告原因;dyn Fn、dyn FnMut和dyn FnOnce支持拥有值与借用值,并复用 callable ABI;仍不支持带泛型方法的动态对象或异构可调用值容器; - 匿名函数不支持泛型参数、返回类型标注或自递归绑定,需要时使用具名泛型函数;带泛型方法的动态对象或异构可调用值容器仍未支持;
- 这是开发中工具链,不保证语法和 ABI 稳定。