docs(objects): explain method substitution contracts

This commit is contained in:
2026-11-15 17:35:00 +08:00
parent 58ec874dc0
commit c05d60cbc9
+114
View File
@@ -1,3 +1,6 @@
//! Store 类型/能力方法的替换约束;types 子模块可读取方法表,无需向宿主公开可变内部布局。
//! 参数要求逆变、返回要求协变;检查只观察当前定义,拟议更新通过 Patch 预演,不先修改再回滚。
//! UnknownAnnotation 仅在显式 defer_unknown 时暂缓,调用时检查使用完整解析路径。
//! Method substitution rules. This is a child of types so validation can inspect
//! method tables without exporting mutable definition internals.
use super::{AnnotationType, EndorTypeObject};
@@ -7,18 +10,25 @@ use crate::{
};
struct Contract {
/// 可接受的位置参数区间,包括接收者。
arity: CallArity,
/// 固定位置参数约束。
parameters: Vec<CallType>,
/// 固定部分之后的统一参数约束。
variadic: Option<CallType>,
/// 实现返回值必须落入父契约或能力允许范围。
returns: CallType,
/// 已知整体效果,None 表示不能证明,不等于 ReadOnly。
effect: Option<CallEffect>,
}
impl Contract {
/// 签名替换检查按位置选固定参数,超出固定部分回退可变参数约束。
fn parameter(&self, index: usize) -> Option<&CallType> {
self.parameters.get(index).or(self.variadic.as_ref())
}
}
impl From<CallSignature> for Contract {
/// 原生签名转内部契约,Delegated 不是已知整次调用效果,转换为 None。
fn from(value: CallSignature) -> Self {
Self {
arity: value.arity,
@@ -35,25 +45,35 @@ impl From<CallSignature> for Contract {
}
#[derive(Clone, Copy)]
struct Patch<'a> {
/// 拟议更新所属的类型身份,借用。
owner: ObjectRef,
/// 拟议更新的方法名,仅精确匹配此名。
name: &'a str,
/// 拟议的新函数身份,不由 Patch 保留根。
function: ObjectRef,
}
struct TraitCheckGuard<'a> {
/// 借用递归校验状态表。
store: &'a ObjectStore,
/// 创建守卫时的线程身份。
thread: std::thread::ThreadId,
/// 当前定义/能力对,Drop 时移除。
key: (ObjectRef, ObjectRef),
}
impl Drop for TraitCheckGuard<'_> {
/// 递归校验 RAII 清理,正常/报错/展开路径都移除本线程当前定义与能力对。
fn drop(&mut self) {
// 取得线程校验表的锁,守卫只会在成功插入 key 后创建。
let mut checks = self
.store
.active_trait_checks
.lock()
.expect("trait checker poisoned");
// 只删除当前递归对,不影响同线程其他活跃校验。
let active = checks.get_mut(&self.thread).expect("active trait check");
active.remove(&self.key);
// 线程没有剩余递归检查时移除空项。
if active.is_empty() {
checks.remove(&self.thread);
}
@@ -64,12 +84,18 @@ impl CallType {
/// Whether every value admitted by provided is admitted by self.
/// Nominal inheritance and explicit capabilities participate; no structural
/// or callable-signature inference is implied by this relation.
/// 替换校验的值集合包含关系:provided 的每项都必须被 self 的至少一项接纳。
pub fn accepts_type(&self, store: &ObjectStore, provided: &Self) -> Result<bool, VmError> {
// 先验证联合全部定义边,再做包含/匹配判断,不能让无效分支藏在后面。
self.validate(store)?;
provided.validate(store)?;
// provided 是待被接纳的值集合,必须逐个候选分支都被覆盖。
for actual in provided.alternatives() {
// 每个 provided 分支单独寻找一个可接受它的 self 分支。
let mut accepted = false;
// self 联合只需一个分支能接纳当前 actual,不要求所有分支都匹配。
for expected in self.alternatives() {
// Any 接受所有值;名义继承和显式能力参与,不推断结构相似或可调用签名。
accepted = match (*expected, *actual) {
(AnnotationType::Any, _) => true,
(AnnotationType::Nominal(base), AnnotationType::Nominal(child)) => {
@@ -81,10 +107,12 @@ impl CallType {
) => store.implements_trait(definition, capability)?,
(expected, actual) => expected == actual,
};
// 当前 actual 已找到覆盖分支即可结束内层循环。
if accepted {
break;
}
}
// 存在一个未被接纳的 provided 分支即整体不包含。
if !accepted {
return Ok(false);
}
@@ -94,14 +122,18 @@ impl CallType {
}
impl ObjectStore {
/// types::validate_trait 调用的递归守卫;同线程同定义/能力对重入时暂不重复展开。
pub(super) fn with_trait_check(
&self,
definition: ObjectRef,
capability: ObjectRef,
check: impl FnOnce() -> Result<(), VmError>,
) -> Result<(), VmError> {
// 递归重入按线程隔离,避免把其他线程的检查误作当前递归。
let thread = std::thread::current().id();
// 重入身份包含类型定义与能力,不仅是单个类型。
let key = (definition, capability);
// 同线程同定义/能力对已经在检查时返回,避免递归契约无限展开。
if !self
.active_trait_checks
.lock()
@@ -112,6 +144,7 @@ impl ObjectStore {
{
return Ok(());
}
// 锁已释放,守卫负责退出时清除 key;执行 check 时不持全局校验锁。
let _guard = TraitCheckGuard {
store: self,
thread,
@@ -119,14 +152,17 @@ impl ObjectStore {
};
check()
}
/// 替换检查读取原生签名或解析源码签名,缺失元数据返回 None,不猜测类型。
fn method_contract(
&self,
function: ObjectRef,
owner: ObjectRef,
) -> Result<Option<Contract>, VmError> {
// 优先使用已解析原生签名,其引用有效性由 Store 的签名入口校验。
if let Some(native) = self.native_signature(function)? {
return Ok(Some(native.into()));
}
// 既非原生完整签名又非源码函数时无法取得契约。
let Some(data) = self
.object(function)?
.payload()
@@ -135,15 +171,23 @@ impl ObjectStore {
else {
return Ok(None);
};
// 源码类型声明在函数指向的代码对象内,不从函数名字猜测。
let code = self.payload::<EndorCodeObject>(data.code(), "code")?.code();
// 源码没有声明就返回 None,调用方决定缺失元数据是否允许。
let Some(signature) = code.source_signature() else {
return Ok(None);
};
// 解析源码 owner 名称时考虑继承中的真实声明,避免只查全局同名对象。
let resolve = |name: &str| {
// 仅源码声明的 owner 名称走特殊继承解析,其余名称回退常规注解解析。
if signature.method_owner.as_deref() == Some(name) {
// 从当前声明 owner 向基类查找,不扫描任意后代。
let mut current = Some(owner);
// 逐层读取不可变类型定义,必要时确认父层方法身份仍是当前函数。
while let Some(definition) = current {
// 读取类型载荷并验证句柄,后续方法表与基类访问都基于该借用。
let data = self.payload::<EndorTypeObject>(definition, "Type")?;
// 名称相同还需属于当前 owner 或确实在父方法表持有该函数。
if data.name == name
&& (definition == owner
|| data
@@ -158,6 +202,7 @@ impl ObjectStore {
}
self.annotation_type(function, name, None)
};
// 逐个解析全部参数注解,首个错误即传播,不返回部分契约。
let parameters = signature
.parameters
.iter()
@@ -173,12 +218,14 @@ impl ObjectStore {
effect: self.call_effect(function)?,
}))
}
/// 查类型自身方法,若 Patch 精确匹配该类型/名称则读取拟议替代函数。
fn own_method(
&self,
owner: ObjectRef,
name: &str,
patch: Option<Patch<'_>>,
) -> Result<Option<ObjectRef>, VmError> {
// 只在 owner 和 name 都相同的查询位置替换视图,不修改真实方法表。
if let Some(patch) = patch
&& patch.owner == owner
&& patch.name == name
@@ -191,13 +238,16 @@ impl ObjectStore {
.get(name)
.copied())
}
/// 沿单继承链寻找最近方法及声明 owner,支持拟议 Patch 的查找视图。
fn inherited_method(
&self,
mut owner: Option<ObjectRef>,
name: &str,
patch: Option<Patch<'_>>,
) -> Result<Option<(ObjectRef, ObjectRef)>, VmError> {
// 向基类遍历;不假定候选函数一定是接收者当前生效的方法。
while let Some(definition) = owner {
// 最靠近当前类型的声明遮蔽更远的同名父方法。
if let Some(function) = self.own_method(definition, name, patch)? {
return Ok(Some((definition, function)));
}
@@ -205,6 +255,7 @@ impl ObjectStore {
}
Ok(None)
}
/// 检查当前方法替换最近父方法是否可代换;父方法无类型元数据时保持兼容。
fn check_override(
&self,
owner: ObjectRef,
@@ -213,32 +264,40 @@ impl ObjectStore {
patch: Option<Patch<'_>>,
defer_unknown: bool,
) -> Result<(), VmError> {
// 检查 owner 类型载荷,诊断使用其声明名称。
let definition = self.payload::<EndorTypeObject>(owner, "Type")?;
// 没有同名父方法即不是 override,无需普通替换检查。
let Some((base_owner, base_function)) =
self.inherited_method(definition.base, name, patch)?
else {
return Ok(());
};
// 闭包收集解析/校验结果,外层仅在允许时暂缓 UnknownAnnotation。
let checked = (|| {
// 父方法没有完整元数据时不新增更严格的旧接口约束。
let Some(base) = self.method_contract(base_function, base_owner)? else {
return Ok(());
};
// 按当前调用用途构造带类型/能力与方法定位的错误,不改变验证逻辑。
let invalid = |reason| VmError::OverrideViolation {
definition: definition.name.clone(),
method: name.into(),
reason,
};
// 父契约存在时,派生实现也必须提供完整可检查的类型元数据。
let derived = self
.method_contract(function, owner)?
.ok_or_else(|| invalid("override lacks type metadata"))?;
self.check_substitution(owner, &derived, &base, invalid)?;
Ok(())
})();
// 只有 UnknownAnnotation 可在 defer_unknown 时暂缓,其他失败始终传播。
match checked {
Err(VmError::UnknownAnnotation(_)) if defer_unknown => Ok(()),
result => result,
}
}
/// 父方法/能力签名的公共替换算法;所有不兼容原因交给调用方构造具体错误。
fn check_substitution(
&self,
owner: ObjectRef,
@@ -246,12 +305,15 @@ impl ObjectStore {
base: &Contract,
invalid: impl Fn(&'static str) -> VmError,
) -> Result<(), VmError> {
// 实现不得要求更多必填参数,也不得缩小最多可接收参数数量。
if derived.arity.required > base.arity.required
|| derived.arity.maximum < base.arity.maximum
{
return Err(invalid("override narrows accepted argument counts"));
}
// 接收者按实现声明类型核对,不能只对比两份签名的第零项文本。
let receiver = CallType::from(AnnotationType::Nominal(owner));
// 派生签名必须接受它自己声明类型的实例作为接收者。
if !derived
.parameter(0)
.ok_or_else(|| invalid("override lacks receiver"))?
@@ -259,6 +321,7 @@ impl ObjectStore {
{
return Err(invalid("override receiver rejects its declaring type"));
}
// 父/能力签名必须接纳实现类型,返回值则必须落在父约束范围。
if !base
.parameter(0)
.ok_or_else(|| invalid("required signature lacks receiver"))?
@@ -266,48 +329,62 @@ impl ObjectStore {
{
return Err(invalid("required receiver rejects implementing type"));
}
// 检查双方固定参数覆盖到的最大位置,但不超过父方法允许上界。
let fixed = base
.parameters
.len()
.max(derived.parameters.len())
.min(base.arity.maximum);
// 跳过已单独核对的接收者,第一个普通参数起检查逆变。
for index in 1..fixed {
// 调用方按父契约可传的类型集合,实现必须全部接纳。
let expected = base
.parameter(index)
.ok_or_else(|| invalid("incomplete base parameter metadata"))?;
// 实现的实际参数/方法契约必须存在,不能通过缺失元数据绕过检查。
let actual = derived
.parameter(index)
.ok_or_else(|| invalid("incomplete override parameter metadata"))?;
// 逆变方向是 actual 接纳 expected,反过来会错误允许缩窄参数。
if !actual.accepts_type(self, expected)? {
return Err(invalid("override narrows a parameter type"));
}
}
// 父契约还有固定段之后的位置时,用该位置的尾部约束核对其余参数。
if base.arity.maximum > fixed {
// 调用方按父契约可传的类型集合,实现必须全部接纳。
let expected = base
.parameter(fixed)
.ok_or_else(|| invalid("incomplete base variadic metadata"))?;
// 实现的实际参数/方法契约必须存在,不能通过缺失元数据绕过检查。
let actual = derived
.parameter(fixed)
.ok_or_else(|| invalid("incomplete override variadic metadata"))?;
// 逆变方向是 actual 接纳 expected,反过来会错误允许缩窄参数。
if !actual.accepts_type(self, expected)? {
return Err(invalid("override narrows a variadic parameter type"));
}
}
// 父/能力签名必须接纳实现类型,返回值则必须落在父约束范围。
if !base.returns.accepts_type(self, &derived.returns)? {
return Err(invalid("override widens the return type"));
}
// 仅双方效果都已知时比较;未知效果不在此处强行推断。
if let (Some(expected), Some(actual)) = (base.effect, derived.effect) {
// 只读只能替换为只读;Mutating 不接受 External;External/Delegated 不再收紧。
let compatible = match expected {
CallEffect::ReadOnly => actual == CallEffect::ReadOnly,
CallEffect::Mutating => actual != CallEffect::External,
CallEffect::External | CallEffect::Delegated => true,
};
// 效果扩大报替换错误,不靠实际执行检测。
if !compatible {
return Err(invalid("override expands known effects"));
}
}
Ok(())
}
/// types 能力验证把实现方法与声明签名比较,报 TraitViolation 而非普通覆盖错误。
pub(super) fn validate_typed_requirement(
&self,
definition: ObjectRef,
@@ -317,6 +394,7 @@ impl ObjectStore {
expected: &CallSignature,
defer_unknown: bool,
) -> Result<(), VmError> {
// 按当前调用用途构造带类型/能力与方法定位的错误,不改变验证逻辑。
let invalid = |reason| VmError::TraitViolation {
capability: self
.payload::<super::EndorTraitObject>(capability, "Trait")
@@ -326,38 +404,49 @@ impl ObjectStore {
method: name.into(),
reason,
};
// 闭包收集解析/校验结果,外层仅在允许时暂缓 UnknownAnnotation。
let checked = (|| {
// 实现的实际参数/方法契约必须存在,不能通过缺失元数据绕过检查。
let actual = self
.method_contract(function, definition)?
.ok_or_else(|| invalid("required member lacks type metadata"))?;
self.check_substitution(definition, &actual, &expected.clone().into(), invalid)
})();
// 只有 UnknownAnnotation 可在 defer_unknown 时暂缓,其他失败始终传播。
match checked {
Err(VmError::UnknownAnnotation(_)) if defer_unknown => Ok(()),
result => result,
}
}
/// 类型构造时按开关校验自身方法覆盖,允许暂缓尚未解析的注解。
pub(super) fn validate_class_overrides(&self, definition: ObjectRef) -> Result<(), VmError> {
// 类定义/更新阶段普通 override 检查受配置开关控制。
if self.field_type_checks {
// 逐个检查当前类型自身方法,不把继承方法当作自身声明。
for (name, function) in &self.payload::<EndorTypeObject>(definition, "Type")?.methods {
self.check_override(definition, name, *function, None, true)?;
}
}
Ok(())
}
/// set_method 提交前,以 Patch 校验受影响后代,避免先改表再发现错误。
pub(super) fn validate_override_update(
&self,
owner: ObjectRef,
name: &str,
function: ObjectRef,
) -> Result<(), VmError> {
// 类定义/更新阶段普通 override 检查受配置开关控制。
if self.field_type_checks {
// 拟议 owner/name/function 只作为查询视图,验证完成前不提交。
let patch = Some(Patch {
owner,
name,
function,
});
// 父方法变化可能破坏已有子方法的替换关系,所以检查全部后代。
for descendant in self.descendant_types(owner)? {
// 最靠近当前类型的声明遮蔽更远的同名父方法。
if let Some(function) = self.own_method(descendant, name, patch)? {
self.check_override(descendant, name, function, patch, true)?;
}
@@ -366,6 +455,7 @@ impl ObjectStore {
Ok(())
}
/// Explicit override validation, including any typed capabilities.
/// VM/宿主显式启用普通覆盖检查,同时校验接收者的类型化能力约束。
pub fn check_method_override_call(
&self,
function: ObjectRef,
@@ -376,25 +466,35 @@ impl ObjectStore {
}
/// Typed capabilities are explicit opt-ins and always checked. Ordinary
/// override checking remains configurable by the VM's source-check flag.
/// eval 原生调用前核对实际方法;能力约束始终检查,普通覆盖由 overrides 控制。
pub fn check_method_contract_call(
&self,
function: ObjectRef,
receiver: ObjectRef,
overrides: bool,
) -> Result<bool, VmError> {
// 返回值表示是否命中类型化能力约束,不代表普通覆盖检查是否执行。
let mut typed = false;
// 接收者的有效类型可能经 type_target 转发;无类型时不会枚举名义方法。
let actual = self.type_of(receiver)?;
// 从实际类型开始向基类枚举可能声明当前函数的方法名。
let mut owner = actual;
// 向基类遍历;不假定候选函数一定是接收者当前生效的方法。
while let Some(definition) = owner {
// 读取类型载荷并验证句柄,后续方法表与基类访问都基于该借用。
let data = self.payload::<EndorTypeObject>(definition, "Type")?;
// 同一函数可出现在多个方法名下,各自可能承担不同能力契约。
for (name, candidate) in &data.methods {
// 这里只处理正在调用的函数身份,其余方法不检查。
if *candidate != function {
continue;
}
self.check_typed_instance_storage(receiver)?;
// 普通 override 检查由调用方选择,能力约束不受该开关豁免。
if overrides {
self.check_override(definition, name, function, None, false)?;
}
// 实际方法查找仍选中该函数时按实际类型能力检查,否则按当前声明层处理。
let effective = if self
.lookup_method(actual.expect("method receiver has a type"), name)?
== Some(function)
@@ -403,7 +503,9 @@ impl ObjectStore {
} else {
definition
};
// 有效类型继承的全部能力都参与,不能只检查第一个匹配能力。
for capability in self.inherited_traits(effective)? {
// 只有该能力声明当前方法的完整类型签名时才有约束可检查。
if let Some(expected) = self
.payload::<super::EndorTraitObject>(capability, "Trait")?
.signatures
@@ -423,20 +525,29 @@ impl ObjectStore {
/// Borrowed expected results for all typed capabilities of this effective
/// method. The evaluator retains definitions before entering the body.
/// eval/NativeContext 收集有效方法的全部能力返回约束;返回类型句柄仍为借用。
pub fn method_trait_returns(
&self,
function: ObjectRef,
receiver: ObjectRef,
) -> Result<Vec<(String, CallType)>, VmError> {
// 接收者的有效类型可能经 type_target 转发;无类型时不会枚举名义方法。
let actual = self.type_of(receiver)?;
// 从实际类型开始向基类枚举可能声明当前函数的方法名。
let mut owner = actual;
// 累计所有能力返回约束,clone 不保留其中托管定义根。
let mut returns = Vec::new();
// 向基类遍历;不假定候选函数一定是接收者当前生效的方法。
while let Some(definition) = owner {
// 读取类型载荷并验证句柄,后续方法表与基类访问都基于该借用。
let data = self.payload::<EndorTypeObject>(definition, "Type")?;
// 同一函数可出现在多个方法名下,各自可能承担不同能力契约。
for (name, candidate) in &data.methods {
// 这里只处理正在调用的函数身份,其余方法不检查。
if *candidate != function {
continue;
}
// 实际方法查找仍选中该函数时按实际类型能力检查,否则按当前声明层处理。
let effective = if self
.lookup_method(actual.expect("method receiver has a type"), name)?
== Some(function)
@@ -445,8 +556,11 @@ impl ObjectStore {
} else {
definition
};
// 有效类型继承的全部能力都参与,不能只检查第一个匹配能力。
for capability in self.inherited_traits(effective)? {
// 读取能力签名表,名称用于返回错误的定位。
let contract = self.payload::<super::EndorTraitObject>(capability, "Trait")?;
// 只有该能力声明当前方法的完整类型签名时才有约束可检查。
if let Some(expected) = contract.signatures.get(name) {
returns.push((
format!("{}.{}", contract.name, name),