Add typed trait requirements and enforce actual capability results

This commit is contained in:
2026-10-22 14:36:57 +08:00
parent e26e227dd0
commit 100bf637f9
16 changed files with 799 additions and 107 deletions
+2 -2
View File
@@ -5,9 +5,9 @@
| 文件 | 职责 |
| --- | --- |
| [lib.rs](lib.rs) | VM 类型导出、objects 类型兼容重导出(含 ReadAccess/CallSignature/CallType/CallEffect)、宿主调用与字节码 rustdoc 示例 |
| [vm.rs](vm.rs) | Vm 拥有 ObjectStore 与错误位置记录;对象操作和宿主 new_class 和显式Trait注册/关系查询显式委托给 objects;函数/字段检查统一开关、统一参数区间查询与函数类型配置 |
| [vm.rs](vm.rs) | Vm 拥有 ObjectStore 与错误位置记录;对象操作和宿主 new_class 和显式/类型化Trait注册/关系查询显式委托给 objects;函数/字段检查统一开关、统一参数区间查询与函数类型配置 |
| [frame.rs](frame.rs) | EndorFrame、栈条目、源码与原生委托返回契约执行根、执行位置元数据、ExecutionLimits |
| [eval.rs](eval.rs) | execute/call、只读调用与依赖结果、参数绑定、原生实现前数量/类型/效果检查和委托返回检查、可选注解/动态覆写兼容检查、源码类字段环境绑定、帧切换、取指分派、MakeFunction、结果根转移与失败清理 |
| [eval.rs](eval.rs) | execute/call、只读调用与依赖结果、参数绑定、原生实现前数量/类型/效果检查和原生/类型化Trait委托返回检查、可选注解/动态覆写兼容检查、源码类字段环境绑定、帧切换、取指分派、MakeFunction、结果根转移与失败清理 |
## 常见修改入口
+45 -21
View File
@@ -179,14 +179,14 @@ impl Vm {
Value::Object(receiver) => Some(*receiver),
_ => None,
});
if self.source_type_checks
&& let Some(receiver) = receiver
{
let typed_method = if let Some(receiver) = receiver {
self.objects
.check_method_override_call(function, receiver)?;
}
.check_method_contract_call(function, receiver, self.source_type_checks)?
} else {
false
};
let mut return_type = None;
if self.source_type_checks
if (self.source_type_checks || typed_method)
&& let Some(signature) = executable.source_signature()
{
for (index, expected_name) in signature.parameters.iter().enumerate() {
@@ -234,6 +234,14 @@ impl Vm {
executable,
};
let initialized = (|| -> Result<(), VmError> {
if let Some(receiver) = receiver {
for (name, expected) in self.objects.method_trait_returns(function, receiver)? {
self.push_native_return(
&mut frame.native_returns,
NativeReturn { name, expected },
)?;
}
}
let defaults = data.defaults().iter().skip(arguments.len() - required);
for (slot, value) in frame
.localsplus
@@ -277,11 +285,12 @@ impl Vm {
for argument in arguments {
self.validate_value(argument)?;
}
if self.source_type_checks
&& let Some(Value::Object(receiver)) = arguments.first()
{
self.objects
.check_method_override_call(function, *receiver)?;
if let Some(Value::Object(receiver)) = arguments.first() {
self.objects.check_method_contract_call(
function,
*receiver,
self.source_type_checks,
)?;
}
if let Some(arity) = self.objects.call_arity(function)?
&& !arity.accepts(arguments.len())
@@ -326,7 +335,9 @@ impl Vm {
.is::<EndorFunctionObject>()
{
let mut frame = self.prepare_frame(function, &arguments)?;
let mut local = std::mem::take(&mut frame.native_returns);
frame.native_returns = std::mem::take(&mut returns);
frame.native_returns.append(&mut local);
return Ok(PreparedCall::Frame(frame));
}
if *executed >= limits.max_instructions {
@@ -340,17 +351,12 @@ impl Vm {
name: self.object(function)?.payload().kind_name().into(),
expected: signature.returns,
};
let mut retained = Vec::new();
for reference in check.expected.references() {
if let Err(error) = self.retain(reference) {
for reference in retained {
self.release(reference)?;
}
return Err(error);
}
retained.push(reference);
self.push_native_return(&mut returns, check)?;
}
if let Some(Value::Object(receiver)) = arguments.first() {
for (name, expected) in self.objects.method_trait_returns(function, *receiver)? {
self.push_native_return(&mut returns, NativeReturn { name, expected })?;
}
returns.push(check);
}
match self.invoke_native(function, &arguments)? {
NativeResult::Return(value) => {
@@ -389,6 +395,24 @@ impl Vm {
result
}
fn push_native_return(
&mut self,
returns: &mut Vec<NativeReturn>,
check: NativeReturn,
) -> Result<(), VmError> {
let mut retained = Vec::new();
for reference in check.expected.references() {
if let Err(error) = self.retain(reference) {
for reference in retained {
self.release(reference)?;
}
return Err(error);
}
retained.push(reference);
}
returns.push(check);
Ok(())
}
fn clear_native_returns(&mut self, returns: Vec<NativeReturn>) {
for check in returns {
for reference in check.expected.references() {
+1 -1
View File
@@ -47,7 +47,7 @@ pub use frame::{EndorFrame, ExecutionLimits, ExecutionLocation};
pub use objects::ObjectStore;
pub use objects::{
AnnotationType, ClassField, EndorTraitObject, EndorTypeObject, MethodRequirement, RootTypes,
TypeConstructor,
TypeConstructor, TypedMethodRequirement,
};
pub use objects::{
CallArity, CallEffect, CallSignature, CallType, ChangeBatch, ChangeKey, CodeLayout, CompareOp,
+7
View File
@@ -40,6 +40,13 @@ impl Vm {
) -> Result<ObjectRef, VmError> {
self.objects.new_trait(name, requirements)
}
pub fn new_typed_trait(
&mut self,
name: impl Into<String>,
requirements: Vec<crate::TypedMethodRequirement>,
) -> Result<ObjectRef, VmError> {
self.objects.new_typed_trait(name, requirements)
}
pub fn implement_trait(
&mut self,
definition: ObjectRef,
+1
View File
@@ -8,6 +8,7 @@
| [builtins.rs](builtins.rs) | 无损 Number、Unicode 字符串、Dict 顺序与键、Cell、函数环境/共享代码、异常数据 |
| [execution.rs](execution.rs) | wordcode 与校验、控制流、参数/默认值、嵌套帧、字节码函数创建/默认值/环境捕获、闭包、返回值存活、失败清理、限额 |
| [types.rs](types.rs) | 名义身份、继承与实例关系、类型边保活/循环回收、只读类型元数据与外来句柄、显式Trait身份/继承/参数约束及后代更新原子性;统一函数分类、原生数量元数据/副作用前拒绝/能力更新/默认参数与返回类型错误根清理;仅远端执行 |
| [typed_traits.rs](typed_traits.rs) | 类型化Trait登记、原子拒绝、源码检查关闭时的显式约束、宽参数调用、类型强边GC、体后实际能力结果检查与临时验证集合恢复;仅远端执行 |
| [method_contracts.rs](method_contracts.rs) | 源码/原生方法覆写参数逆变/返回协变、联合参数、效果、祖先替换原子性、动态名称重绑定/前向名称与体前拒绝/恢复及清理;仅远端执行 |
| [native_context.rs](native_context.rs) | 原生作用域根、分配失败清理、原生签名/联合与可空类型/效果和委托返回根、绑定方法元数据、委托、只读结果分配/写入拒绝/依赖/校验与普通读取区分及失败恢复、弱引用过期及有界变更;仅远端执行 |
+365
View File
@@ -0,0 +1,365 @@
//! Remote-only coverage of explicit typed capability contracts.
use endor_core::{
AnnotationType as T, CallEffect, CallSignature, CodeLayout, ContextNativeFunction, EndorCode,
Instruction, NativeCallable, NativeContext, NativeResult, ObjectRef, Opcode, SourceSignature,
TypeConstructor, TypedMethodRequirement, Value, Vm, VmError,
};
fn capability(
vm: &mut Vm,
name: &str,
parameters: Vec<T>,
returns: T,
effect: CallEffect,
) -> ObjectRef {
vm.new_typed_trait(
name,
vec![TypedMethodRequirement {
name: "check".into(),
signature: CallSignature::exact(parameters, returns, effect),
}],
)
.unwrap()
}
fn source(
vm: &mut Vm,
globals: ObjectRef,
owner: &str,
types: &[&str],
returns: &str,
value: Value,
) -> ObjectRef {
let code = EndorCode::from_instructions(
&[
Instruction::new(Opcode::LoadConst, 0),
Instruction::new(Opcode::StoreGlobal, 0),
Instruction::new(Opcode::LoadConst, 1),
Instruction::simple(Opcode::ReturnValue),
],
vec![Value::Number(42.into()), value],
CodeLayout {
parameters: (0..types.len()).map(|i| format!("p{i}")).collect(),
names: vec!["ran".into()],
..CodeLayout::default()
},
)
.unwrap()
.with_source_signature(SourceSignature {
parameters: types.iter().map(|s| (*s).into()).collect(),
return_type: returns.into(),
method_owner: Some(owner.into()),
})
.unwrap();
let code = vm.new_code(code).unwrap();
let function = vm
.new_function("check", code, globals, vec![], vec![])
.unwrap();
vm.release(code).unwrap();
function
}
#[derive(Debug)]
struct Native(CallSignature);
impl NativeCallable for Native {
fn signature(&self) -> Option<CallSignature> {
Some(self.0.clone())
}
fn invoke(&self, _: &mut NativeContext<'_>, _: &[Value]) -> Result<NativeResult, VmError> {
Ok(NativeResult::Return(Value::Number(7.into())))
}
}
#[test]
fn typed_registration_is_atomic_and_wider_native_inputs_remain_callable() {
let mut vm = Vm::new().unwrap();
let roots = vm.bootstrap_types().unwrap();
let definition = vm
.new_type("Model", roots.object, TypeConstructor::Record)
.unwrap();
let contract = capability(
&mut vm,
"Numeric",
vec![T::Any, T::Number],
T::Number,
CallEffect::ReadOnly,
);
for (argument, effect) in [
(T::String, CallEffect::ReadOnly),
(T::Any, CallEffect::Mutating),
] {
let function = vm
.allocate(ContextNativeFunction::new(
"bad",
Native(CallSignature::exact(
vec![T::Nominal(definition), argument],
T::Number,
effect,
)),
vec![],
))
.unwrap();
vm.define_method(definition, "check", function).unwrap();
let version = vm.change_version(definition).unwrap();
assert!(matches!(
vm.implement_trait(definition, contract),
Err(VmError::TraitViolation { .. })
));
assert_eq!(vm.change_version(definition).unwrap(), version);
assert!(!vm.implements_trait(definition, contract).unwrap());
vm.release(function).unwrap();
}
let function = vm
.allocate(ContextNativeFunction::new(
"good",
Native(CallSignature::exact(
vec![T::Nominal(definition), T::Any],
T::Number,
CallEffect::ReadOnly,
)),
vec![],
))
.unwrap();
vm.define_method(definition, "check", function).unwrap();
vm.implement_trait(definition, contract).unwrap();
let receiver = vm.new_object().unwrap();
vm.attach_type(receiver, definition).unwrap();
assert_eq!(
vm.call(
function,
&[
Value::Object(receiver),
Value::Text("extra supported input".into())
]
)
.unwrap(),
Value::Number(7.into())
);
for object in [
receiver,
function,
definition,
contract,
roots.object,
roots.type_type,
] {
vm.release(object).unwrap();
}
vm.collect_cycles();
assert_eq!(vm.object_count(), 0);
}
#[test]
fn typed_source_methods_enforce_their_annotations_even_when_global_checks_are_off() {
let mut vm = Vm::new().unwrap();
let roots = vm.bootstrap_types().unwrap();
let definition = vm
.new_type("Model", roots.object, TypeConstructor::Record)
.unwrap();
let globals = vm.new_dict().unwrap();
let function = source(
&mut vm,
globals,
"Model",
&["Model", "Number"],
"Number",
Value::Bool(true),
);
vm.define_method(definition, "check", function).unwrap();
let receiver = vm.new_object().unwrap();
vm.attach_type(receiver, definition).unwrap();
// Existing untyped declarations retain the default-off compatibility mode.
assert_eq!(
vm.call(function, &[Value::Object(receiver), Value::Bool(false)])
.unwrap(),
Value::Bool(true)
);
let contract = capability(
&mut vm,
"Numeric",
vec![T::Any, T::Number],
T::Number,
CallEffect::ReadOnly,
);
vm.implement_trait(definition, contract).unwrap();
vm.dict_set(globals, Value::Text("ran".into()), Value::Number(0.into()))
.unwrap();
assert!(matches!(
vm.call(function, &[Value::Object(receiver), Value::Bool(false)]),
Err(VmError::TypeMismatch { .. })
));
assert_eq!(
vm.dict_get(globals, &Value::Text("ran".into())).unwrap(),
Some(Value::Number(0.into()))
);
assert!(
matches!(vm.call(function, &[Value::Object(receiver), Value::Number(1.into())]), Err(VmError::TypeMismatch { position, .. }) if position=="return")
);
assert_eq!(
vm.dict_get(globals, &Value::Text("ran".into())).unwrap(),
Some(Value::Number(42.into()))
);
for object in [
receiver,
function,
definition,
contract,
globals,
roots.object,
roots.type_type,
] {
vm.release(object).unwrap();
}
vm.collect_cycles();
assert_eq!(vm.object_count(), 0);
}
#[test]
fn typed_trait_definitions_trace_their_signature_types_without_hidden_roots() {
let mut vm = Vm::new().unwrap();
let roots = vm.bootstrap_types().unwrap();
let definition = vm
.new_type("Value", roots.object, TypeConstructor::Record)
.unwrap();
let weak = vm.downgrade(definition).unwrap();
let contract = capability(
&mut vm,
"ValueContract",
vec![T::Any, T::Nominal(definition)],
T::Nominal(definition),
CallEffect::ReadOnly,
);
vm.release(definition).unwrap();
let retained = vm
.upgrade(weak)
.unwrap()
.expect("signature retains its type");
vm.release(retained).unwrap();
vm.release(contract).unwrap();
vm.collect_cycles();
assert!(vm.upgrade(weak).unwrap().is_none());
for object in [roots.object, roots.type_type] {
vm.release(object).unwrap();
}
vm.collect_cycles();
assert_eq!(vm.object_count(), 0);
}
#[derive(Debug)]
struct RebindThenReturn {
signature: CallSignature,
globals: ObjectRef,
other: ObjectRef,
result: ObjectRef,
}
impl NativeCallable for RebindThenReturn {
fn signature(&self) -> Option<CallSignature> {
Some(self.signature.clone())
}
fn invoke(&self, cx: &mut NativeContext<'_>, _: &[Value]) -> Result<NativeResult, VmError> {
cx.dict_set(
self.globals,
Value::Text("Current".into()),
Value::Object(self.other),
)?;
Ok(NativeResult::Return(Value::Object(self.result)))
}
}
#[test]
fn actual_trait_returns_revalidate_capabilities_after_body_side_effects_and_restore_guards() {
let mut vm = Vm::new().unwrap();
let roots = vm.bootstrap_types().unwrap();
let result_base = vm
.new_type("Result", roots.object, TypeConstructor::Record)
.unwrap();
let result_child = vm
.new_type("ResultChild", result_base, TypeConstructor::Record)
.unwrap();
let other = vm
.new_type("Other", roots.object, TypeConstructor::Record)
.unwrap();
let model = vm
.new_type("Model", roots.object, TypeConstructor::Record)
.unwrap();
let globals = vm.new_dict().unwrap();
vm.dict_set(
globals,
Value::Text("Current".into()),
Value::Object(result_child),
)
.unwrap();
let read = source(&mut vm, globals, "Model", &["Model"], "Current", Value::Nil);
vm.define_method(model, "check", read).unwrap();
let feature = capability(
&mut vm,
"Feature",
vec![T::Any],
T::Nominal(result_base),
CallEffect::External,
);
vm.implement_trait(model, feature).unwrap();
let value = vm.new_object().unwrap();
vm.attach_type(value, model).unwrap();
let actor = vm
.new_type("Actor", roots.object, TypeConstructor::Record)
.unwrap();
let contract = capability(
&mut vm,
"Producer",
vec![T::Any],
T::Capability(feature),
CallEffect::External,
);
let function = vm
.allocate(ContextNativeFunction::new(
"produce",
RebindThenReturn {
signature: CallSignature::exact(
vec![T::Nominal(actor)],
T::Nominal(model),
CallEffect::Mutating,
),
globals,
other,
result: value,
},
vec![globals, other, value],
))
.unwrap();
vm.define_method(actor, "check", function).unwrap();
vm.implement_trait(actor, contract).unwrap();
let receiver = vm.new_object().unwrap();
vm.attach_type(receiver, actor).unwrap();
assert!(matches!(
vm.call(function, &[Value::Object(receiver)]),
Err(VmError::TraitViolation { .. })
));
// Repeated failures must not leave a recursive-validation guard marked active.
for _ in 0..2 {
assert!(vm.implements_trait(model, feature).is_err());
}
vm.dict_set(
globals,
Value::Text("Current".into()),
Value::Object(result_child),
)
.unwrap();
assert!(vm.implements_trait(model, feature).unwrap());
for object in [
receiver,
function,
actor,
contract,
value,
read,
model,
feature,
globals,
other,
result_child,
result_base,
roots.object,
roots.type_type,
] {
vm.release(object).unwrap();
}
vm.collect_cycles();
assert_eq!(vm.object_count(), 0);
}
+4 -4
View File
@@ -4,16 +4,16 @@
| 文件 | 职责 |
| --- | --- |
| [lib.rs](lib.rs) | 包边界、对象类型/ObjectStore 公开导出(含Trait/MethodRequirement/SourceSignature/AnnotationType/CallArity/CallSignature/CallType/CallEffect/ReadAccess)与独立存储 rustdoc 示例 |
| [lib.rs](lib.rs) | 包边界、对象类型/ObjectStore 公开导出(含Trait/MethodRequirement/TypedMethodRequirement/SourceSignature/AnnotationType/CallArity/CallSignature/CallType/CallEffect/ReadAccess)与独立存储 rustdoc 示例 |
| [object.rs](object.rs) | Value、CompareOp、ObjectPayload(含显式函数标记)、ObjectHeader、EndorObject、Record、可发布数量元数据的 NativeFunction |
| [types.rs](types.rs) | 托管名义类型、显式 Object/Type 引导、继承查询、受管方法表/接收者绑定、类字段、显式Trait定义/声明/方法参数约束、原生构造委托、统一源码/原生/绑定方法参数区间查询与能力验证、声明环境托管边、字段构造/写入检查与标记为校验读取的注解解析/匹配 |
| [contracts.rs](contracts.rs) | 方法参数逆变/返回协变、名义/能力/联合包含关系、祖先方法更新的后代检查、动态注解调用前复查 |
| [types.rs](types.rs) | 托管名义类型、显式 Object/Type 引导、继承查询、受管方法表/接收者绑定、类字段、显式/类型化Trait定义、强类型签名边与声明约束、原生构造委托、统一源码/原生/绑定方法参数区间查询与能力验证、声明环境托管边、字段构造/写入检查与标记为校验读取的注解解析/匹配 |
| [contracts.rs](contracts.rs) | 方法参数逆变/返回协变、名义/能力/联合包含关系、祖先方法更新的后代检查、动态注解调用前复查、类型化能力替换/返回约束与临时递归验证集合 |
| [native.rs](native.rs) | NativeContext、CallArity/CallSignature 类型与效果元数据、调用前检查、签名类型强边、上下文原生函数、作用域语言函数工厂、显式能力声明入口、函数标记、根与非递归委托调用 |
| [changes.rs](changes.rs) | 合并变更批次、变更键与有界通知额度 |
| [number.rs](number.rs) | Integer/Decimal/Rational/BinaryFloat/Complex 的无损表示和装箱载荷 |
| [builtins.rs](builtins.rs) | String、Dict、Cell、Function、BaseException 载荷与 trace 边、语言函数标记 |
| [heap.rs](heap.rs) | 私有 Heap、不可伪造 ObjectRef、稳定 Box、引用事务、迭代释放、循环回收、后代契约更新的私有借用枚举、同类型附加幂等、只读变更屏障/全调用及嵌套子调用读取跟踪(区分注解校验与内容读取)及私有边界测试 |
| [store.rs](store.rs) | ObjectStore、空堆创建、字段检查配置/属性写入屏障、详细读取区域结果、对象操作、retain/release 和回收入口、弱 Function 类型配置与分配分类 |
| [store.rs](store.rs) | ObjectStore、空堆创建、字段检查配置/属性写入屏障、详细读取区域结果、逐线程临时能力检查集合、对象操作、retain/release 和回收入口、弱 Function 类型配置与分配分类 |
| [store_builtins.rs](store_builtins.rs) | 载荷校验、代码/函数工厂、Dict/Cell 操作及键语义 |
| [instruction.rs](instruction.rs) | Opcode、Instruction、wordcode 编解码、MakeFunction/比较参数与跳转读取接口 |
| [code.rs](code.rs) | CodeLayout、EndorCode/EndorCodeObject、可选源码签名、共享不可变代码和操作数/控制流/栈验证 |
+223 -58
View File
@@ -36,6 +36,26 @@ struct Patch<'a> {
function: ObjectRef,
}
struct TraitCheckGuard<'a> {
store: &'a ObjectStore,
thread: std::thread::ThreadId,
key: (ObjectRef, ObjectRef),
}
impl Drop for TraitCheckGuard<'_> {
fn drop(&mut self) {
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);
}
}
}
impl CallType {
/// Whether every value admitted by provided is admitted by self.
/// Nominal inheritance and explicit capabilities participate; no structural
@@ -70,6 +90,31 @@ impl CallType {
}
impl ObjectStore {
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()
.expect("trait checker poisoned")
.entry(thread)
.or_default()
.insert(key)
{
return Ok(());
}
let _guard = TraitCheckGuard {
store: self,
thread,
key,
};
check()
}
fn method_contract(
&self,
function: ObjectRef,
@@ -182,59 +227,7 @@ impl ObjectStore {
let derived = self
.method_contract(function, owner)?
.ok_or_else(|| invalid("override lacks type metadata"))?;
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"))?
.accepts_type(self, &receiver)?
{
return Err(invalid("override receiver rejects its declaring 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"))?;
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"))?;
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) {
let compatible = match expected {
CallEffect::ReadOnly => actual == CallEffect::ReadOnly,
CallEffect::Mutating => actual != CallEffect::External,
CallEffect::External => true,
};
if !compatible {
return Err(invalid("override expands native effects"));
}
}
self.check_substitution(owner, &derived, &base, invalid)?;
Ok(())
})();
match checked {
@@ -242,6 +235,104 @@ impl ObjectStore {
result => result,
}
}
fn check_substitution(
&self,
owner: ObjectRef,
derived: &Contract,
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"))?
.accepts_type(self, &receiver)?
{
return Err(invalid("override receiver rejects its declaring type"));
}
if !base
.parameter(0)
.ok_or_else(|| invalid("required signature lacks receiver"))?
.accepts_type(self, &receiver)?
{
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"))?;
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"))?;
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) {
let compatible = match expected {
CallEffect::ReadOnly => actual == CallEffect::ReadOnly,
CallEffect::Mutating => actual != CallEffect::External,
CallEffect::External => true,
};
if !compatible {
return Err(invalid("override expands native effects"));
}
}
Ok(())
}
pub(super) fn validate_typed_requirement(
&self,
definition: ObjectRef,
capability: ObjectRef,
name: &str,
function: ObjectRef,
expected: &CallSignature,
defer_unknown: bool,
) -> Result<(), VmError> {
let invalid = |reason| VmError::TraitViolation {
capability: self
.payload::<super::EndorTraitObject>(capability, "Trait")
.expect("checked trait")
.name
.clone(),
method: name.into(),
reason,
};
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)
})();
match checked {
Err(VmError::UnknownAnnotation(_)) if defer_unknown => Ok(()),
result => result,
}
}
pub(super) fn validate_class_overrides(&self, definition: ObjectRef) -> Result<(), VmError> {
if self.field_type_checks {
for (name, function) in &self.payload::<EndorTypeObject>(definition, "Type")?.methods {
@@ -270,23 +361,97 @@ impl ObjectStore {
}
Ok(())
}
/// Revalidate dynamic annotation names before a source method body executes.
/// Ordinary functions whose first argument is an object are not methods.
/// Explicit override validation, including any typed capabilities.
pub fn check_method_override_call(
&self,
function: ObjectRef,
receiver: ObjectRef,
) -> Result<(), VmError> {
let mut owner = self.type_of(receiver)?;
self.check_method_contract_call(function, receiver, true)
.map(|_| ())
}
/// Typed capabilities are explicit opt-ins and always checked. Ordinary
/// override checking remains configurable by the VM's source-check flag.
pub fn check_method_contract_call(
&self,
function: ObjectRef,
receiver: ObjectRef,
overrides: bool,
) -> Result<bool, VmError> {
let mut typed = false;
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 {
if *candidate != function {
continue;
}
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)
{
actual.unwrap()
} else {
definition
};
for capability in self.inherited_traits(effective)? {
if let Some(expected) = self
.payload::<super::EndorTraitObject>(capability, "Trait")?
.signatures
.get(name)
{
typed = true;
self.validate_typed_requirement(
effective, capability, name, function, expected, false,
)?;
}
}
}
owner = data.base;
}
Ok(())
Ok(typed)
}
/// Borrowed expected results for all typed capabilities of this effective
/// method. The evaluator retains definitions before entering the body.
pub fn method_trait_returns(
&self,
function: ObjectRef,
receiver: ObjectRef,
) -> Result<Vec<(String, CallType)>, VmError> {
let actual = self.type_of(receiver)?;
let mut owner = actual;
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)
{
actual.unwrap()
} 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),
expected.returns.clone(),
));
}
}
}
owner = data.base;
}
Ok(returns)
}
}
+1 -1
View File
@@ -49,5 +49,5 @@ pub use object::{
pub use store::ObjectStore;
pub use types::{
AnnotationType, ClassField, EndorTraitObject, EndorTypeObject, MethodRequirement, RootTypes,
TypeConstructor,
TypeConstructor, TypedMethodRequirement,
};
+16
View File
@@ -375,6 +375,12 @@ impl<'a> NativeContext<'a> {
if let Some(signature) = &signature {
signature.check_arguments(store, &name, arguments)?;
}
let trait_returns = if let Some(Value::Object(receiver)) = arguments.first() {
store.check_method_contract_call(function, *receiver, false)?;
store.method_trait_returns(function, *receiver)?
} else {
Vec::new()
};
let mut context = Self {
store,
roots: Vec::new(),
@@ -386,6 +392,11 @@ impl<'a> NativeContext<'a> {
context.protect(&Value::Object(reference))?;
}
}
for (_, expected) in &trait_returns {
for reference in expected.references() {
context.protect(&Value::Object(reference))?;
}
}
for argument in arguments {
context.protect(argument)?;
}
@@ -393,6 +404,11 @@ impl<'a> NativeContext<'a> {
if let (Some(signature), NativeResult::Return(value)) = (&signature, &result) {
signature.check_return(context.store(), &name, value)?;
}
if let NativeResult::Return(value) = &result {
for (name, expected) in &trait_returns {
expected.check(context.store(), name, "return".into(), value)?;
}
}
let transferred_from = context.roots.len();
match &result {
NativeResult::Return(value) => context.protect(value)?,
+7
View File
@@ -12,6 +12,12 @@ pub struct ObjectStore {
pub(crate) heap: Heap,
function_type: Option<WeakObjectRef>,
pub(crate) field_type_checks: bool,
pub(crate) active_trait_checks: std::sync::Mutex<
std::collections::HashMap<
std::thread::ThreadId,
std::collections::BTreeSet<(ObjectRef, ObjectRef)>,
>,
>,
}
impl ObjectStore {
@@ -151,6 +157,7 @@ impl ObjectStore {
heap: Heap::new()?,
function_type: None,
field_type_checks: false,
active_trait_checks: std::sync::Mutex::new(std::collections::HashMap::new()),
})
}
+104 -14
View File
@@ -634,16 +634,23 @@ impl NativeCallable for CallBoundMethod {
}
/// Minimal explicit capability contract. Arity excludes the bound receiver.
/// Parameter/return types, default implementations and syntax are host concerns.
/// Use TypedMethodRequirement for parameter/return/effect contracts.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MethodRequirement {
pub name: String,
pub parameters: usize,
}
/// Typed capability requirement, including the explicit receiver slot.
#[derive(Debug, Clone)]
pub struct TypedMethodRequirement {
pub name: String,
pub signature: crate::CallSignature,
}
#[derive(Debug)]
pub struct EndorTraitObject {
name: String,
requirements: Vec<MethodRequirement>,
signatures: BTreeMap<String, crate::CallSignature>,
}
impl EndorTraitObject {
pub fn name(&self) -> &str {
@@ -652,8 +659,18 @@ impl EndorTraitObject {
pub fn requirements(&self) -> &[MethodRequirement] {
&self.requirements
}
pub fn signature(&self, name: &str) -> Option<&crate::CallSignature> {
self.signatures.get(name)
}
}
impl ObjectPayload for EndorTraitObject {
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
for signature in self.signatures.values() {
for reference in signature.references() {
visit(reference);
}
}
}
fn reference_assignment_only(&self) -> bool {
true
}
@@ -690,7 +707,49 @@ impl ObjectStore {
));
}
}
self.allocate(EndorTraitObject { name, requirements })
self.allocate(EndorTraitObject {
name,
requirements,
signatures: BTreeMap::new(),
})
}
pub fn new_typed_trait(
&mut self,
name: impl Into<String>,
methods: Vec<TypedMethodRequirement>,
) -> Result<ObjectRef, VmError> {
let name = name.into();
if name.is_empty() {
return Err(VmError::InvalidArgument("trait name must not be empty"));
}
let mut signatures = BTreeMap::new();
let mut requirements = Vec::new();
for method in methods {
method.signature.validate(self)?;
let arity = method.signature.arity;
if method.name.is_empty()
|| method.name == "type"
|| arity.required == 0
|| arity.required != arity.maximum
|| arity.maximum == usize::MAX
|| signatures.contains_key(&method.name)
{
return Err(VmError::InvalidArgument(
"invalid typed trait canonical signature",
));
}
requirements.push(MethodRequirement {
name: method.name.clone(),
parameters: arity.maximum - 1,
});
signatures.insert(method.name, method.signature);
}
self.allocate(EndorTraitObject {
name,
requirements,
signatures,
})
}
fn inherited_traits(&self, definition: ObjectRef) -> Result<Vec<ObjectRef>, VmError> {
@@ -708,6 +767,8 @@ impl ObjectStore {
capability: ObjectRef,
requirement: &MethodRequirement,
function: Option<ObjectRef>,
definition: ObjectRef,
defer_unknown: bool,
) -> Result<(), VmError> {
let invalid = |reason| VmError::TraitViolation {
capability: self
@@ -727,22 +788,45 @@ impl ObjectStore {
"positional parameter count does not match (including self)",
));
}
Ok(())
}
fn validate_trait(&self, definition: ObjectRef, capability: ObjectRef) -> Result<(), VmError> {
let contract = self.payload::<EndorTraitObject>(capability, "Trait")?;
for requirement in &contract.requirements {
self.validate_requirement(
if let Some(expected) = self
.payload::<EndorTraitObject>(capability, "Trait")?
.signatures
.get(&requirement.name)
{
self.validate_typed_requirement(
definition,
capability,
requirement,
self.lookup_method(definition, &requirement.name)?,
&requirement.name,
function,
expected,
defer_unknown,
)?;
}
Ok(())
}
fn validate_trait(
&self,
definition: ObjectRef,
capability: ObjectRef,
defer_unknown: bool,
) -> Result<(), VmError> {
self.with_trait_check(definition, capability, || {
let contract = self.payload::<EndorTraitObject>(capability, "Trait")?;
for requirement in &contract.requirements {
self.validate_requirement(
capability,
requirement,
self.lookup_method(definition, &requirement.name)?,
definition,
defer_unknown,
)?;
}
Ok(())
})
}
fn validate_inherited_traits(&self, definition: ObjectRef) -> Result<(), VmError> {
for capability in self.inherited_traits(definition)? {
self.validate_trait(definition, capability)?;
self.validate_trait(definition, capability, true)?;
}
Ok(())
}
@@ -793,7 +877,13 @@ impl ObjectStore {
.requirements
{
if requirement.name == name {
self.validate_requirement(capability, requirement, Some(function))?;
self.validate_requirement(
capability,
requirement,
Some(function),
descendant,
true,
)?;
}
}
}
@@ -807,7 +897,7 @@ impl ObjectStore {
) -> Result<(), VmError> {
self.payload::<EndorTraitObject>(capability, "Trait")?;
for descendant in self.descendant_types(definition)? {
self.validate_trait(descendant, capability)?;
self.validate_trait(descendant, capability, false)?;
}
let mut updated = self.payload::<EndorTypeObject>(definition, "Type")?.clone();
if !updated.traits.contains(&capability) {
@@ -824,7 +914,7 @@ impl ObjectStore {
self.payload::<EndorTraitObject>(capability, "Trait")?;
let declared = self.inherited_traits(definition)?.contains(&capability);
if declared {
self.validate_trait(definition, capability)?;
self.validate_trait(definition, capability, false)?;
}
Ok(declared)
}
+1 -1
View File
@@ -6,7 +6,7 @@
| 解释执行、资源修订与引擎升级 | [执行与版本](architecture/execution-and-versions.md) | frontend/compiler/core |
| EndorObject、句柄、默认操作 | [对象模型](runtime/objects.md) | objects/src |
| 原生上下文、只读调用、子调用读取依赖及校验分类、统一参数数量/类型/联合/效果、委托返回与变更通知 | [原生调用](runtime/native-calls.md) | objects/src/native.rs、changes.rs、core/src/eval.rs |
| 托管类型、宿主类注册、原生分配继承、字段存储、显式能力、函数分类、声明字段/源码注解与方法覆写检查 | [名义类型/方法覆写](runtime/types.md) | objects/src/types.rs、contracts.rs |
| 托管类型、宿主类注册、原生分配继承、字段存储、显式/类型化能力、函数分类、声明字段/源码注解与方法覆写检查 | [名义类型/方法覆写](runtime/types.md) | objects/src/types.rs、contracts.rs |
| Number、String、Dict、Cell、Function | [基本对象](runtime/builtins.md) | objects/src |
| 源码读取与来源 | [Reader](runtime/reader.md) | frontend/src/reader.rs |
| 词法、语法、裸赋值和诊断 | [Frontend](runtime/frontend.md) | frontend/src/lexer.peg/grammar.peg |
+1 -1
View File
@@ -2,7 +2,7 @@
| 文档 | 内容 |
| --- | --- |
| [types.md](types.md) | 托管 Object/Type、名义继承、方法覆写方差/动态检查、声明字段/函数注解检查、构造与类型边生命周期 |
| [types.md](types.md) | 托管 Object/Type、名义继承、方法覆写/类型化Trait/动态检查、声明字段/函数注解检查、构造与类型边生命周期 |
| [objects.md](objects.md) | 对象、句柄与默认操作 |
| [native-calls.md](native-calls.md) | 原生上下文、读取分类、统一参数数量/类型/联合/效果、非递归委托返回、弱引用、托管赋值与变更批次 |
| [builtins.md](builtins.md) | Number、String、Dict、Cell、Function、异常数据 |
+2 -2
View File
@@ -76,7 +76,7 @@ take_changes 按对象与变更键合并同一批写入,返回最终版本。
`ObjectStore::call_arity(function)`/`Vm::call_arity(function)` 统一查询源码与原生函数。源码从真实形参和默认值计算区间;绑定方法沿实际函数扣除每层接收者,最大值不足时报错,最小值饱和到零。查询返回无托管引用的复制数据,不增加 GC 根。旧 positional_parameter_count 保留“源码总形参数量减绑定接收者”的兼容语义,不用于新的原生回调分派。通用 Type 构造器的参数组合由宿主决定,返回 None;NativeTyped 内部委托和宿主实际构造仍各自检查。
显式 Trait 声明、源码派生及方法表更新使用统一区间检查“规范参数数量加 self”是否被接受;缺少元数据的原生方法不能再登记为能力实现。可选参数允许实现接受规范调用数量之外的额外形式。更新仍先检查后代再修改,无隐藏类型根。类型注解兼容性、方差、参数名称及效果检查尚未接入此结构。
显式 Trait 声明、源码派生及方法表更新使用统一区间检查“规范参数数量加 self”是否被接受;缺少元数据的原生方法不能再登记为能力实现。可选参数允许实现接受规范调用数量之外的额外形式。更新仍先检查后代再修改,无隐藏类型根。旧 new_trait 保留数量规则;new_typed_trait 的参数/返回类型、方差及已知效果检查见类型主题,参数名称不构成调用契约。
Tessera 为原生方法、回调和内部视觉 continuation 发布数量元数据,事件监听要求接受零参数或一个 Event;两者都接受时优先传 Event。绑定计算和 Task 回调要求接受零参数,登记前拒绝无元数据/不兼容函数;普通旧原生调用的兼容性不等于允许其参加这些严格回调契约。
@@ -102,7 +102,7 @@ NativeResult::Invoke 仍是尾委托,不递归进入 Rust/VM。VM 为每层已
CallEffect::ReadOnly 声明只读现存对象,可分配临时结果;Mutating 与 External 在 ObjectStore 只读作用域内、进入实现前拒绝。普通执行允许这些效果。ReadOnly 声明不绕过已有写屏障,也不证明可信 Rust 不会进行隐藏 I/O;未知旧原生函数仍沿原调用/宿主检查,不能把 None 理解为纯函数。这不是静态效果推断、完整 I/O 沙箱或对阻塞原生代码的抢占。
Tessera 的 measure/arrange/paint/hitTest、Point 几何查询、Window、Block/Container/Page、Mount、Task、Event、Subscription 方法和 ObjectList.at 发布签名。Page.open 的 Window | Unit、Page.window 的 Window | Unit 返回,以及 Block.on 的 Bool | String 可选阶段有准确元数据;Mount 的父/子/同级/Page 弱查询为可空结果。Mount.bounds 现在返回带 Bounds 名义类型的现有几何记录,可直接组合到 Point.inside 或视觉方法。滚动位置/字典等通用记录仍声明 Object,不冒充新语言类型;任务 owner 的具体资源资格仍按原域检查。构造器和其他管理 API 的签名覆盖仍未全部接入。已接入的方法覆写方差与动态名称规则见类型主题。能力注册目前继续检查参数数量,不声称已经比较签名类型或效果。
Tessera 的 measure/arrange/paint/hitTest、Point 几何查询、Window、Block/Container/Page、Mount、Task、Event、Subscription 方法和 ObjectList.at 发布签名。Page.open 的 Window | Unit、Page.window 的 Window | Unit 返回,以及 Block.on 的 Bool | String 可选阶段有准确元数据;Mount 的父/子/同级/Page 弱查询为可空结果。Mount.bounds 现在返回带 Bounds 名义类型的现有几何记录,可直接组合到 Point.inside 或视觉方法。滚动位置/字典等通用记录仍声明 Object,不冒充新语言类型;任务 owner 的具体资源资格仍按原域检查。构造器和其他管理 API 的签名覆盖仍未全部接入。已接入的方法覆写方差与动态名称规则见类型主题。旧能力注册保留数量模式;类型化能力还检查类型/已知效果,并在实际返回和尾委托结果处保留能力返回约束。
回归源码 native_context.rs 覆盖参数副作用前拒绝、错误结果清理、跨原生/源码委托检查、单帧限制、名义签名边与绑定方法循环回收、只读效果拒绝、传统直接函数及重复参数和元数据错误。只编译,不本地执行。
+19 -2
View File
@@ -72,7 +72,7 @@ ObjectPayload 可选择实现 `attribute_target(store)`,把实例字段放在
implement_trait(Type, Trait)明确建立能力关系并校验必需方法。只有同名成员不算实现。implements_trait(Type, Trait)检查声明和继承;supports(instance, Trait)先查实际类型;未分类对象返回false,非法句柄或非定义参数报错。一个类型可显式实现多个能力,派生类继承关系。声明由Type的受管强边持有,查询和临时后代扫描不保活定义。
源码与原生方法通过统一 CallArity 验证是否接受规范数量加 self;无元数据的原生方法不能登记能力。数量区间包括源码默认参数与原生可选参数,详见[原生调用](native-calls.md#统一位置参数数量)。缺少方法、非可调用成员或参数数量错误以TraitViolation报告能力名、方法名和原因。本子集只验证数量接受性,不验证注解、关联类型、返回类型、效果或完整替换兼容性;没有默认实现和trait源码语法。
源码与原生方法通过统一 CallArity 验证是否接受规范数量加 self;无元数据的原生方法不能登记能力。数量区间包括源码默认参数与原生可选参数,详见[原生调用](native-calls.md#统一位置参数数量)。缺少方法、非可调用成员或参数数量错误以TraitViolation报告能力名、方法名和原因。旧 new_trait 只验证数量接受性;new_typed_trait 的类型契约见下文。关联类型、默认实现和 trait 源码语法尚未实现。
建立声明前检查已有后代;构造源码子类检查继承的所有能力。define_method更新前检查受影响后代的显式/继承契约,被后代自己的覆盖遮蔽的方法不受该祖先更新影响。失败保留原方法表和关系。内部扫描只枚举当前借用句柄,不引入全局强类型注册表。声明更新通过普通堆写屏障与trace进行,既有类型的只读作用域内变更会被拒绝。NativeContext也可使用同一implement_trait入口,仍受该屏障约束。
@@ -126,6 +126,23 @@ new_class 在发布之前校验本类覆写,失败释放候选定义。define_
未知前向注解在类建立和方法更新时暂缓,其他已知不兼容以 OverrideViolation 报类名、方法名和原因。启用 VM 类型检查时,源码和原生调用均在进入实现前检查实际接收者所属方法的当前替换契约;UnknownAnnotation 此时不能暂缓。普通函数的首参数恰好是对象,不会因此被视为方法。默认接收者也使用实际默认参数值参与复查。每次复查动态全局注解,所以 REPL 重绑定使旧覆写不兼容时,函数体执行前拒绝;恢复兼容绑定后可继续普通调用。不回滚先前已发生的其他输入副作用。
已知原生效果只允许缩小:ReadOnly 基类只能被 ReadOnly 原生覆写;Mutating 基类不允许 External 覆写;External 可接受三者。源码效果尚无静态元数据,不能声称已经证明其纯度;实际只读布局/绘制仍通过原有堆屏障与原生效果入口限制执行。未知旧基类类型签名仍是未证明的兼容边界,Trait 当前仍仅校验数量,尚未提供独立的类型/效果要求。
已知原生效果只允许缩小:ReadOnly 基类只能被 ReadOnly 原生覆写;Mutating 基类不允许 External 覆写;External 可接受三者。源码效果尚无静态元数据,不能声称已经证明其纯度;实际只读布局/绘制仍通过原有堆屏障与原生效果入口限制执行。未知旧基类类型签名仍是未证明的兼容边界,旧 new_trait 仍仅校验数量;new_typed_trait 提供独立类型与已知效果要求,见下文。
回归源码 core/tests/method_contracts.rs 覆盖合法参数扩大/返回缩小、错误参数/返回、祖先替换对已有覆写后代的原子拒绝、调用前动态重绑定拒绝和恢复、前向名称、原生联合与效果及根清理,仅编译,不在本地执行。
## 类型化 Trait
ObjectStore/Vm::new_typed_trait(name, Vec<TypedMethodRequirement>) 以每项 name 和 CallSignature 定义能力。保留旧 new_trait/MethodRequirement 的数量契约兼容性;两者都有名义身份和显式声明,不靠同名方法推断。类型化签名必须给出包含 self 的非零、有限、固定规范数量;实现仍可提供可选参数或更多调用形式。签名完整验证、重复/保留名字检查在分配前完成。EndorTraitObject::signature(name) 返回借用描述;所有名义/能力类型引用由 Trait 载荷 trace 追踪,定义不可变,没有隐藏全局强根。
显式 implement_trait 严格检查实际方法的参数逆变、返回协变、接收者和已知原生效果;失败不登记、不改变方法表/版本。所有已有后代也检查。继承的源码子类及方法更新使用同一规则,未知前向注解可暂缓,但显式登记/查询和实际调用不暂缓。旧无类型元数据的方法不能满足类型化要求。Trait 的 self 可声明 Object 或实际名义约束,实际接收者必须满足它;不会因为普通函数首参是对象而隐式声明能力。
对于实际接收者拥有类型化能力的方法,VM 在体前复查当前契约,并强制该源码方法自己的参数/返回注解,即使全局源码检查开关关闭。参数更宽的实现仍可以通过具体方法接受额外值;不会把实现收窄成 Trait 的规范参数集合,也不删除原生方便调用的省略参数形式。未携带相应能力的普通函数调用仍沿全局配置,不是对所有自由函数的静态类型系统。
除了实现自己的返回注解,所有匹配的类型化 Trait 返回约束也检查最终实际结果,覆盖源码/原生及尾委托链。执行帧/原生作用域保留这些定义的执行根,失败和预算清理均释放。方法体中修改其他类型的动态注解可能使返回对象的能力失效,不能仅依赖体前形式签名比较;最终 Capability 匹配会再次校验。错误返回不回滚已经发生的业务修改。
嵌套能力比较按线程/定义/能力保留临时验证集合,对已经在校验的同一名义能力关系按递归约束处理,避免无限递归;它不建立新声明关系。集合不持有 GC 根,RAII 在成功、错误和展开路径清理,不让失败验证成为后续成功缓存。没有长期签名缓存;动态名称每次仍从原环境解析。
源码效果仍未知,类型化 ReadOnly 要求不等于已经证明源码纯度。已知原生效果会比较,真正只读布局/绘制继续执行堆屏障;普通输入中源码副作用仍按现有运行模式处理。没有泛型/关联类型/效果推断或 Trait 源码声明语法。Tessera 的四个宿主能力使用类型化要求,其中 Presentable 允许普通展示构造副作用,不额外宣称纯函数。
core/tests/typed_traits.rs 覆盖登记失败原子性、参数扩大仍可直接调用、全局检查关闭时的显式能力实际参数/返回检查、签名类型保活/回收,以及体内动态改变返回对象能力后的最终拒绝和验证集合恢复,仅编译未本地执行。