From 903b3bbf9da328d4e6bfefb4354af3b0e9b30aee Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Sun, 15 Nov 2026 19:11:16 +0800 Subject: [PATCH] docs: fill annotation audit gaps in object and type paths --- crates/core/tests/types.rs | 1 + crates/frontend/tests/reader.rs | 1 + crates/objects/src/code.rs | 1 + crates/objects/src/effects.rs | 2 ++ crates/objects/src/heap.rs | 4 ++++ crates/objects/src/instruction.rs | 1 + crates/objects/src/native.rs | 3 +++ crates/objects/src/number.rs | 13 +++++++++++++ crates/objects/src/store.rs | 4 ++++ crates/objects/src/store_builtins.rs | 2 ++ crates/objects/src/types.rs | 4 +++- crates/objects/tests/store.rs | 1 + 12 files changed, 36 insertions(+), 1 deletion(-) diff --git a/crates/core/tests/types.rs b/crates/core/tests/types.rs index 4655662..347b7b7 100644 --- a/crates/core/tests/types.rs +++ b/crates/core/tests/types.rs @@ -340,6 +340,7 @@ fn failed_nominal_return_releases_value_and_captured_type_roots() { let expected = vm .new_type("Expected", roots.object, TypeConstructor::Record) .unwrap(); + // 创建与 Expected 同父但身份不同的类型,返回其实例应触发名义返回约束失败。 let other = vm .new_type("Other", roots.object, TypeConstructor::Record) .unwrap(); diff --git a/crates/frontend/tests/reader.rs b/crates/frontend/tests/reader.rs index 3850cf8..37e5451 100644 --- a/crates/frontend/tests/reader.rs +++ b/crates/frontend/tests/reader.rs @@ -160,6 +160,7 @@ impl SourceFile { impl Drop for SourceFile { /// 退出文件测试作用域时清理文件,忽略已不存在等删除错误,避免 Drop 再次失败。 fn drop(&mut self) { + // 只尽力删除测试文件,显式忽略删除结果,不在析构中制造第二个错误。 let _ = std::fs::remove_file(&self.0); } } diff --git a/crates/objects/src/code.rs b/crates/objects/src/code.rs index 6ffaf0f..7c44c72 100644 --- a/crates/objects/src/code.rs +++ b/crates/objects/src/code.rs @@ -459,6 +459,7 @@ fn verify( let height = heights[index].unwrap(); // 逻辑操作码与完整 u32 参数;前缀已由解码器合并。 let Instruction { opcode, argument } = instruction.instruction; + // 完整 u32 操作数转成当前平台索引/数量,用于下面栈需求与布局计算。 let argument = argument as usize; // required 是最深访问需求,popped/pushed 是净变化来源;Copy/Swap 不弹出其深层项。 let (required, popped, pushed) = match opcode { diff --git a/crates/objects/src/effects.rs b/crates/objects/src/effects.rs index 2dafb7a..45f6c46 100644 --- a/crates/objects/src/effects.rs +++ b/crates/objects/src/effects.rs @@ -28,6 +28,7 @@ enum Fact { impl Fact { // 把实际值降为抽象身份事实,直接标量不保留其内容。 fn value(value: Value) -> Self { + // 对象保留借用身份,标量统一变成 Unknown;不把抽象事实当作实际求值结果。 match value { Value::Object(object) => Self::Object(object), _ => Self::Unknown, @@ -35,6 +36,7 @@ impl Fact { } // 只从普通对象事实提取身份,不把方法对当作对象。 fn object(self) -> Option { + // 仅 Object 事实可用于属性接收者或调用身份,Unknown/Null/Method 都不冒充普通对象。 match self { Self::Object(object) => Some(object), _ => None, diff --git a/crates/objects/src/heap.rs b/crates/objects/src/heap.rs index c10d265..0ffa60f 100644 --- a/crates/objects/src/heap.rs +++ b/crates/objects/src/heap.rs @@ -118,6 +118,7 @@ pub enum ReadDependency { impl ReadDependency { /// 缓存/读取分析使用者取得观察所属对象,不创建外部拥有根。 pub fn object(&self) -> ObjectRef { + // 三种依赖形状都提取其观察对象身份,不重新读取内容或改变依赖精度。 match self { Self::Object { object, .. } | Self::DictionaryKind { object } @@ -311,6 +312,7 @@ impl Heap { ))?; // 锁的取得顺序与结束路径一致:先分类区间,再精确依赖区间。 let mut regions = scope.regions.lock().expect("read tracker poisoned"); + // 与分类区间相同锁顺序取得精确依赖区间栈,令牌检查必须同时核对两者。 let mut precise = scope .dependency_regions .lock() @@ -564,6 +566,7 @@ impl Heap { let object = self.get_mut(reference)?; // 暂存旧边/旧载荷,等新状态与通知建立后再释放旧状态。 let old_edges = std::mem::replace(&mut object.payload_edges, edges); + // 旧载荷也要暂存到变更通知及旧引用边释放之后再析构,保持状态提交顺序。 let old_payload = std::mem::replace(&mut object.payload, Box::new(payload)); self.record_change(reference, ChangeKey::Contents); self.release_edges(old_edges); @@ -966,6 +969,7 @@ mod tests { let mut heap = Heap::new().unwrap(); // owner 接收属性边,target 模拟无法再增加强计数的对象。 let owner = heap.allocate(Box::new(Record)).unwrap(); + // 另建目标对象,随后将强计数推至上限,测试 retain 失败不能修改外部根或 owner 的边。 let target = heap.allocate(Box::new(Record)).unwrap(); // 仅改强计数,外部根仍为一;后面的断言检查失败是否意外改动 external_refs。 heap.get_mut(target).unwrap().header.strong_refs = usize::MAX; diff --git a/crates/objects/src/instruction.rs b/crates/objects/src/instruction.rs index 217d2ca..b46219d 100644 --- a/crates/objects/src/instruction.rs +++ b/crates/objects/src/instruction.rs @@ -171,6 +171,7 @@ impl Instruction { pub const fn compare(operation: CompareOp) -> Self { Self::new( Opcode::CompareOp, + // 将六种比较映射为稳定字节码编号,必须与 eval 的 CompareOp 解码一致。 match operation { CompareOp::Lt => 0, CompareOp::Le => 1, diff --git a/crates/objects/src/native.rs b/crates/objects/src/native.rs index f85e0cb..2c34e17 100644 --- a/crates/objects/src/native.rs +++ b/crates/objects/src/native.rs @@ -90,6 +90,7 @@ pub enum CallType { Union(Vec), } impl From for CallType { + /// 宿主用单个 AnnotationType 构建原生签名时由 From/Into 包成 Single;这里不新增类型边或校验堆身份。 fn from(value: crate::AnnotationType) -> Self { Self::Single(value) } @@ -479,6 +480,7 @@ impl<'a> NativeContext<'a> { context.protect(&Value::Object(function))?; // 可选完整签名存在时才校验/保护;缺失元数据保留既有调用协议。 if let Some(signature) = &signature { + // 逐项保护签名持有的类型/能力定义,使回调执行期间不依赖宿主另外保留根。 for reference in signature.references() { context.protect(&Value::Object(reference))?; } @@ -603,6 +605,7 @@ impl ObjectPayload for ContextNativeFunction { fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) { // 签名中的参数、可变参数和返回约束定义也属于载荷拥有边。 if let Some(signature) = &self.signature { + // 按签名实际引用报告每条拥有边,包括重复出现的定义,不因某个联合候选已匹配而省略。 for reference in signature.references() { visit(reference); } diff --git a/crates/objects/src/number.rs b/crates/objects/src/number.rs index 3d2bd80..5a76016 100644 --- a/crates/objects/src/number.rs +++ b/crates/objects/src/number.rs @@ -54,6 +54,7 @@ impl EndorInteger { pub fn to_i64(&self) -> Option { // 先读 u64 幅值以容纳 i64::MIN 的绝对值,再检查有符号范围。 let magnitude = self.digits.parse::().ok()?; + // 根据符号选择有符号范围检查,负数还需容纳 i64::MIN 的额外一单位幅值。 if self.negative { // 最小负数的幅值比最大正数多一,必须单独处理。 if magnitude == (i64::MAX as u64) + 1 { @@ -70,6 +71,7 @@ impl EndorInteger { impl fmt::Display for EndorInteger { /// Display/宿主诊断按当前表示输出,精确表示不经二进制浮点中转。 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // 仅负值输出前导减号,数字幅值随后原样写出,不通过浮点格式化。 if self.negative { f.write_str("-")?; } @@ -104,10 +106,13 @@ impl EndorDecimal { } // 分别记录已有小数点、数字数量与尾数是否全零。 let mut decimal_point = false; + // 统计尾数数字个数,允许前导零,但至少要有一个数字。 let mut digits = 0_usize; + // 初始假设尾数全零,每读到一个非零数字便保持为 false,用于零值语义。 let mut zero = true; // 按 ASCII 字节扫描,第二个点或其他字符都拒绝。 for byte in mantissa.bytes() { + // 数字更新计数及全零状态,首个点只登记位置,第二个点或其他字节明确失败。 match byte { b'.' if !decimal_point => decimal_point = true, b'0'..=b'9' => { @@ -209,6 +214,7 @@ pub enum EndorReal { impl EndorReal { /// 数值真值/分母检查使用的零判断,具体实现按表示处理。 pub fn is_zero(&self) -> bool { + // 按实际实数表示判断零;分数看分子,二进制浮点按其原有 f64 零语义判断。 match self { Self::Integer(value) => value.is_zero(), Self::Decimal(value) => value.is_zero(), @@ -221,6 +227,7 @@ impl EndorReal { impl fmt::Display for EndorReal { /// Display/宿主诊断按当前表示输出,精确表示不经二进制浮点中转。 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // 各精确表示委托自己的 Display;BinaryFloat 才按存储的浮点位模式显示。 match self { Self::Integer(value) => value.fmt(f), Self::Decimal(value) => value.fmt(f), @@ -275,6 +282,7 @@ impl EndorNumber { /// VM 算术/转换查询实数,Complex 即使虚部为零也返回 None。 pub fn as_real(&self) -> Option<&EndorReal> { + // 只有 Real 能借用实数部分;Complex 即使虚部为零也不在此自动降级。 match self { Self::Real(value) => Some(value), Self::Complex { .. } => None, @@ -283,6 +291,7 @@ impl EndorNumber { /// VM 快路径仅接受整数变体,不自动转小数或可整除分数。 pub fn as_i64(&self) -> Option { + // 仅整数实数尝试收窄为 i64,小数、分数、浮点和复数均不隐式转换。 match self { Self::Real(EndorReal::Integer(value)) => value.to_i64(), _ => None, @@ -291,6 +300,7 @@ impl EndorNumber { /// 数值真值/分母检查使用的零判断,具体实现按表示处理。 pub fn is_zero(&self) -> bool { + // 实数委托自身零判断;复数要求实部和虚部都为零。 match self { Self::Real(value) => value.is_zero(), Self::Complex { real, imaginary } => real.is_zero() && imaginary.is_zero(), @@ -315,6 +325,7 @@ impl From for EndorNumber { impl fmt::Display for EndorNumber { /// Display/宿主诊断按当前表示输出,精确表示不经二进制浮点中转。 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // 实数保持原表示,复数分别显示两个实数部分和显式虚数单位。 match self { Self::Real(value) => value.fmt(f), Self::Complex { real, imaginary } => write!(f, "({real} + ({imaginary})i)"), @@ -412,6 +423,7 @@ pub enum ArithmeticOp { impl ArithmeticOp { /// eval 解 BinaryArithmetic 的 0..3 运算编号,其他值返回 None。 pub fn from_argument(value: u32) -> Option { + // 只接受字节码定义的四个算术编号,其他值返回 None 交调用方拒绝。 match value { 0 => Some(Self::Add), 1 => Some(Self::Subtract), @@ -617,6 +629,7 @@ fn exact_real(value: &EndorReal) -> Result { s.parse() .map_err(|_| VmError::InvalidArgument("invalid exact number")) }; + // 不同实数表示转成运算用精确有理数;浮点必须有限,十进制另检查指数运算预算。 match value { EndorReal::Integer(n) => Ok(BigRational::from_integer(integer(&n.to_string())?)), EndorReal::Rational(n) => Ok(BigRational::new( diff --git a/crates/objects/src/store.rs b/crates/objects/src/store.rs index 5e078e4..2cec058 100644 --- a/crates/objects/src/store.rs +++ b/crates/objects/src/store.rs @@ -196,6 +196,7 @@ impl ObjectStore { } // 旧式宿主原地赋值在调用前检查写屏障;实现不得悄悄改变 trace 中的托管边。 self.heap.prepare_change(target)?; + // 调用宿主原地赋值协议,返回是否确实改变;只有 true 才发布 Contents 变更。 let changed = self .object(target)? .payload() @@ -295,7 +296,9 @@ impl ObjectStore { pub fn attribute_path(&self, reference: ObjectRef) -> Result, VmError> { // path 保存访问顺序,seen 检测转发环,current 是本轮需要验证并展开的对象。 let mut path = Vec::new(); + // 记录已经展开的对象身份,转发环在再次遇到同一对象时失败。 let mut seen = std::collections::BTreeSet::new(); + // 从原始门面开始逐跳追踪 attribute_target,最终返回整个存储路径。 let mut current = reference; // 每次至少加入一个对象;遇到无转发目标才正常结束,因此成功路径一定非空。 loop { @@ -364,6 +367,7 @@ impl ObjectStore { ) -> Result { // 借用双方载荷;这条底层协议路径不通过 object,因此不自动收集对象版本依赖。 let left_payload = self.heap.get(left)?.payload(); + // 借用右侧载荷,左侧不支持时才反向比较;此底层访问不自动登记内容依赖。 let right_payload = self.heap.get(right)?.payload(); // 左侧能处理就立即返回;Some(false) 也是已实现结果,不能继续尝试右侧。 if let Some(result) = left_payload.compare(right_payload, operation)? { diff --git a/crates/objects/src/store_builtins.rs b/crates/objects/src/store_builtins.rs index 13702ce..024f182 100644 --- a/crates/objects/src/store_builtins.rs +++ b/crates/objects/src/store_builtins.rs @@ -188,6 +188,7 @@ impl ObjectStore { self.key_hash(&key)?; // 对象值必须来自本堆且仍有效;标量无需额外句柄检查。 self.validate_value(&value)?; + // 先找相等键再决定覆盖或追加,保证保留首个键对象及插入顺序。 if let Some(index) = self.dict_position(&dict, &key)? { // 相等键只覆盖值,保留第一次插入的键对象及其顺序,不保存本次传入的替代键。 dict.entries[index].1 = value; @@ -241,6 +242,7 @@ impl ObjectStore { self.validate_value(value)?; // 直接值使用标准哈希器;对象委托各自实现,可由载荷拒绝不可哈希种类。 let mut hasher = DefaultHasher::new(); + // 标量按已定义类别散列;对象委托载荷哈希,尚无数值哈希协议时拒绝 Number。 match value { Value::Nil => "nil".hash(&mut hasher), Value::Bool(value) => value.hash(&mut hasher), diff --git a/crates/objects/src/types.rs b/crates/objects/src/types.rs index cbd58e0..cca867a 100644 --- a/crates/objects/src/types.rs +++ b/crates/objects/src/types.rs @@ -522,6 +522,7 @@ impl ObjectStore { let mut current = Some(type_object); // 基类固定于创建,逐层校验类型句柄而不是按名称检索。 while let Some(type_object) = current { + // 本层必须是有效 Type 载荷,随后只在该层方法表查找名称,未命中才沿固定基类继续。 let definition = self.payload::(type_object, "Type")?; // 找到方法即返回借用身份,不替调用方 retain。 if let Some(function) = definition.methods.get(name) { @@ -1075,10 +1076,11 @@ impl ObjectStore { /// 方法/能力更新前扫描存活类型,返回包含 base 自身的全部子类型借用身份。 fn descendant_types(&self, base: ObjectRef) -> Result, VmError> { self.payload::(base, "Type")?; - // 去重能力身份,不建立堆外拥有注册表或延长能力寿命。 + // 收集受更新影响的类型借用身份;不创建后代拥有索引,也不延长类型寿命。 let mut found = vec![]; // 扫描当前存活对象;只选类型载荷及其名义子类型。 for reference in self.heap.live_references() { + // 只收集仍存活的 Type 对象及 base 的名义后代,普通实例和无关类型不参与更新检查。 if self .object(reference)? .payload() diff --git a/crates/objects/tests/store.rs b/crates/objects/tests/store.rs index 7b57fda..83129be 100644 --- a/crates/objects/tests/store.rs +++ b/crates/objects/tests/store.rs @@ -133,6 +133,7 @@ fn read_only_scopes_block_existing_container_and_type_writes_but_allow_fresh_res let record = store.new_object().unwrap(); // 已有字典受同一只读屏障保护,不只保护普通属性。 let dict = store.new_dict().unwrap(); + // 进入只读前创建的 Cell 同样属于受保护堆对象,不能靠内部值更新绕过写屏障。 let cell = store .new_cell(Some(objects::Value::Number(1_i64.into()))) .unwrap();