Add explicit managed traits and inherited method arity contracts

This commit is contained in:
2026-10-21 21:34:46 +08:00
parent b7daabdaad
commit eb60212428
16 changed files with 454 additions and 16 deletions
+5 -5
View File
@@ -4,19 +4,19 @@
| 文件 | 职责 |
| --- | --- |
| [lib.rs](lib.rs) | 包边界、对象类型/ObjectStore 公开导出与独立存储 rustdoc 示例 |
| [lib.rs](lib.rs) | 包边界、对象类型/ObjectStore 公开导出(含Trait/MethodRequirement)与独立存储 rustdoc 示例 |
| [object.rs](object.rs) | Value、CompareOp、ObjectPayload、ObjectHeader、EndorObject、Record、NativeFunction |
| [types.rs](types.rs) | 托管名义类型、显式 Object/Type 引导、继承查询、受管方法表/接收者绑定、类字段与原生构造委托 |
| [native.rs](native.rs) | NativeContext、上下文原生函数、作用域语言函数工厂、根与非递归委托调用 |
| [types.rs](types.rs) | 托管名义类型、显式 Object/Type 引导、继承查询、受管方法表/接收者绑定、类字段、显式Trait定义/声明/方法参数约束、原生构造委托 |
| [native.rs](native.rs) | NativeContext、上下文原生函数、作用域语言函数工厂、显式能力声明入口、根与非递归委托调用 |
| [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、引用事务、迭代释放、循环回收、只读变更屏障/全调用及嵌套子调用读取跟踪及私有边界测试 |
| [heap.rs](heap.rs) | 私有 Heap、不可伪造 ObjectRef、稳定 Box、引用事务、迭代释放、循环回收、后代契约更新的私有借用枚举、只读变更屏障/全调用及嵌套子调用读取跟踪及私有边界测试 |
| [store.rs](store.rs) | ObjectStore、空堆创建、对象操作、retain/release 和回收入口 |
| [store_builtins.rs](store_builtins.rs) | 载荷校验、代码/函数工厂、Dict/Cell 操作及键语义 |
| [instruction.rs](instruction.rs) | Opcode、Instruction、wordcode 编解码、MakeFunction/比较参数与跳转读取接口 |
| [code.rs](code.rs) | CodeLayout、EndorCode/EndorCodeObject、共享不可变代码和操作数/控制流/栈验证 |
| [error.rs](error.rs) | 对象、代码与执行共用的 VmError,避免反向依赖 core |
| [error.rs](error.rs) | 对象、代码与执行共用的 VmError(含能力/方法定位的TraitViolation),避免反向依赖 core |
对象操作变更同步 [对象模型](../../../docs/runtime/objects.md),内置载荷同步 [基本对象](../../../docs/runtime/builtins.md)。新增操作码需要同时修改本包 instruction.rs/code.rs 和 core 的 eval.rs 分派,见 [字节码 VM](../../../docs/runtime/bytecode.md)。
+10
View File
@@ -13,6 +13,11 @@ pub enum VmError {
UnsupportedOperation(&'static str),
InvalidArgument(&'static str),
ObjectKindMismatch(&'static str),
TraitViolation {
capability: String,
method: String,
reason: &'static str,
},
EmptyCell,
InvalidBytecode {
offset: usize,
@@ -43,6 +48,11 @@ impl fmt::Display for VmError {
}
Self::InvalidArgument(message) => f.write_str(message),
Self::ObjectKindMismatch(expected) => write!(f, "expected {expected} object"),
Self::TraitViolation {
capability,
method,
reason,
} => write!(f, "trait {capability} method {method}: {reason}"),
Self::EmptyCell => f.write_str("cell is empty"),
Self::InvalidBytecode { offset, reason } => {
write!(f, "invalid bytecode at code unit {offset}: {reason}")
+10
View File
@@ -69,6 +69,16 @@ impl Heap {
})
}
pub(crate) fn live_references(&self) -> impl Iterator<Item = ObjectRef> + '_ {
self.slots.iter().enumerate().filter_map(|(index, slot)| {
slot.object.as_ref().map(|_| ObjectRef {
heap_id: self.id,
index,
generation: slot.generation,
})
})
}
pub(crate) fn begin_read_only(&mut self) -> usize {
let protected = self
.slots
+3 -1
View File
@@ -44,4 +44,6 @@ pub use object::{
ObjectHeader, ObjectPayload, Record, Value,
};
pub use store::ObjectStore;
pub use types::{ClassField, EndorTypeObject, RootTypes, TypeConstructor};
pub use types::{
ClassField, EndorTraitObject, EndorTypeObject, MethodRequirement, RootTypes, TypeConstructor,
};
+8
View File
@@ -44,6 +44,14 @@ impl<'a> NativeContext<'a> {
self.store.attach_type(object, type_object)
}
pub fn implement_trait(
&mut self,
definition: ObjectRef,
capability: ObjectRef,
) -> Result<(), VmError> {
self.store.implement_trait(definition, capability)
}
pub fn store(&self) -> &ObjectStore {
self.store
}
+10
View File
@@ -77,6 +77,16 @@ impl ObjectStore {
"replace immutable type definition",
));
}
if self
.object(reference)?
.payload()
.as_any()
.is::<crate::EndorTraitObject>()
{
return Err(VmError::UnsupportedOperation(
"replace immutable trait definition",
));
}
self.validate_builtin(&payload)?;
self.heap.replace_payload(reference, payload)
}
+225 -2
View File
@@ -28,13 +28,14 @@ pub struct ClassField {
/// Nominal definition with immutable identity/base and a managed method table.
/// Its base, constructor and metatype are traced
/// edges; no registry outside the heap keeps unused definitions alive.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct EndorTypeObject {
name: String,
base: Option<ObjectRef>,
constructor: TypeConstructor,
methods: BTreeMap<String, ObjectRef>,
fields: Vec<ClassField>,
traits: Vec<ObjectRef>,
}
impl EndorTypeObject {
@@ -68,6 +69,9 @@ impl ObjectPayload for EndorTypeObject {
{
visit(function);
}
for capability in &self.traits {
visit(*capability);
}
for method in self.methods.values() {
visit(*method);
}
@@ -145,6 +149,7 @@ impl ObjectStore {
constructor: TypeConstructor::Record,
methods: BTreeMap::new(),
fields: Vec::new(),
traits: Vec::new(),
})?;
let mut allocated_type = None;
let result = (|| {
@@ -154,6 +159,7 @@ impl ObjectStore {
constructor: TypeConstructor::Abstract,
methods: BTreeMap::new(),
fields: Vec::new(),
traits: Vec::new(),
})?;
allocated_type = Some(type_type);
self.attach_type(object, type_type)?;
@@ -194,6 +200,7 @@ impl ObjectStore {
constructor,
methods: BTreeMap::new(),
fields,
traits: Vec::new(),
})?;
if let Err(error) = self.attach_type(definition, metatype) {
self.release(definition)?;
@@ -269,6 +276,7 @@ impl ObjectStore {
));
}
let mut methods = definition.methods.clone();
self.validate_method_update(type_object, &name, function)?;
methods.insert(name, function);
let updated = EndorTypeObject {
name: definition.name.clone(),
@@ -276,6 +284,7 @@ impl ObjectStore {
constructor: definition.constructor,
methods,
fields: definition.fields.clone(),
traits: definition.traits.clone(),
};
self.heap.replace_payload(type_object, updated)
}
@@ -331,8 +340,12 @@ impl ObjectStore {
constructor,
methods,
fields,
traits: Vec::new(),
})?;
if let Err(error) = self.attach_type(class, metatype) {
if let Err(error) = self
.validate_inherited_traits(class)
.and_then(|()| self.attach_type(class, metatype))
{
self.release(class)?;
return Err(error);
}
@@ -417,6 +430,11 @@ impl ObjectStore {
{
return Ok(AttributeLookup::Value(Value::Object(type_object)));
}
if name == "name"
&& let Some(capability) = object.payload().as_any().downcast_ref::<EndorTraitObject>()
{
return Ok(AttributeLookup::Value(Value::Text(capability.name.clone())));
}
if let Some(definition) = object.payload().as_any().downcast_ref::<EndorTypeObject>() {
let metadata = match name {
"name" => Some(Value::Text(definition.name.clone())),
@@ -503,3 +521,208 @@ impl NativeCallable for CallBoundMethod {
})
}
}
/// Minimal explicit capability contract. Arity excludes the bound receiver.
/// Parameter/return types, default implementations and syntax are host concerns.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MethodRequirement {
pub name: String,
pub parameters: usize,
}
#[derive(Debug)]
pub struct EndorTraitObject {
name: String,
requirements: Vec<MethodRequirement>,
}
impl EndorTraitObject {
pub fn name(&self) -> &str {
&self.name
}
pub fn requirements(&self) -> &[MethodRequirement] {
&self.requirements
}
}
impl ObjectPayload for EndorTraitObject {
fn reference_assignment_only(&self) -> bool {
true
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn kind_name(&self) -> &'static str {
"trait"
}
fn repr(&self, _: ObjectRef) -> Result<String, VmError> {
Ok(format!("<trait {}>", self.name))
}
}
impl ObjectStore {
pub fn new_trait(
&mut self,
name: impl Into<String>,
requirements: Vec<MethodRequirement>,
) -> Result<ObjectRef, VmError> {
let name = name.into();
let mut names = std::collections::BTreeSet::new();
if name.is_empty() {
return Err(VmError::InvalidArgument("trait name must not be empty"));
}
for requirement in &requirements {
if requirement.name.is_empty()
|| requirement.name == "type"
|| requirement.parameters == usize::MAX
|| !names.insert(&requirement.name)
{
return Err(VmError::InvalidArgument(
"invalid or duplicate trait requirement",
));
}
}
self.allocate(EndorTraitObject { name, requirements })
}
fn inherited_traits(&self, definition: ObjectRef) -> Result<Vec<ObjectRef>, VmError> {
let mut found = std::collections::BTreeSet::new();
let mut current = Some(definition);
while let Some(definition) = current {
let definition = self.payload::<EndorTypeObject>(definition, "Type")?;
found.extend(definition.traits.iter().copied());
current = definition.base;
}
Ok(found.into_iter().collect())
}
fn validate_requirement(
&self,
capability: ObjectRef,
requirement: &MethodRequirement,
function: Option<ObjectRef>,
) -> Result<(), VmError> {
let invalid = |reason| VmError::TraitViolation {
capability: self
.payload::<EndorTraitObject>(capability, "Trait")
.expect("validated trait")
.name
.clone(),
method: requirement.name.clone(),
reason,
};
let function = function.ok_or_else(|| invalid("required method is missing"))?;
if let Some(count) = self.positional_parameter_count(function)? {
if count != requirement.parameters + 1 {
return Err(invalid(
"positional parameter count does not match (including self)",
));
}
} else if self.object(function)?.payload().native_callable().is_none() {
return Err(invalid("required member is not callable"));
}
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(
capability,
requirement,
self.lookup_method(definition, &requirement.name)?,
)?;
}
Ok(())
}
fn validate_inherited_traits(&self, definition: ObjectRef) -> Result<(), VmError> {
for capability in self.inherited_traits(definition)? {
self.validate_trait(definition, capability)?;
}
Ok(())
}
// Enumerate borrowed heap handles only while validating a definition update;
// no global type registry or owning descendant index is introduced.
fn descendant_types(&self, base: ObjectRef) -> Result<Vec<ObjectRef>, VmError> {
self.payload::<EndorTypeObject>(base, "Type")?;
let mut found = vec![];
for reference in self.heap.live_references() {
if self
.object(reference)?
.payload()
.as_any()
.is::<EndorTypeObject>()
&& self.is_subtype(reference, base)?
{
found.push(reference);
}
}
Ok(found)
}
fn validate_method_update(
&self,
changed: ObjectRef,
name: &str,
function: ObjectRef,
) -> Result<(), VmError> {
for descendant in self.descendant_types(changed)? {
let mut current = Some(descendant);
let mut affected = false;
while let Some(definition) = current {
if definition == changed {
affected = true;
break;
}
let definition = self.payload::<EndorTypeObject>(definition, "Type")?;
if definition.methods.contains_key(name) {
break;
}
current = definition.base;
}
if !affected {
continue;
}
for capability in self.inherited_traits(descendant)? {
for requirement in &self
.payload::<EndorTraitObject>(capability, "Trait")?
.requirements
{
if requirement.name == name {
self.validate_requirement(capability, requirement, Some(function))?;
}
}
}
}
Ok(())
}
pub fn implement_trait(
&mut self,
definition: ObjectRef,
capability: ObjectRef,
) -> Result<(), VmError> {
self.payload::<EndorTraitObject>(capability, "Trait")?;
for descendant in self.descendant_types(definition)? {
self.validate_trait(descendant, capability)?;
}
let mut updated = self.payload::<EndorTypeObject>(definition, "Type")?.clone();
if !updated.traits.contains(&capability) {
updated.traits.push(capability);
self.heap.replace_payload(definition, updated)?;
}
Ok(())
}
pub fn implements_trait(
&self,
definition: ObjectRef,
capability: ObjectRef,
) -> Result<bool, VmError> {
self.payload::<EndorTraitObject>(capability, "Trait")?;
let declared = self.inherited_traits(definition)?.contains(&capability);
if declared {
self.validate_trait(definition, capability)?;
}
Ok(declared)
}
pub fn supports(&self, object: ObjectRef, capability: ObjectRef) -> Result<bool, VmError> {
self.payload::<EndorTraitObject>(capability, "Trait")?;
match self.type_of(object)? {
Some(definition) => self.implements_trait(definition, capability),
None => Ok(false),
}
}
}