Represent nullable and union native contracts with traced alternatives

This commit is contained in:
2026-10-22 14:00:48 +08:00
parent 680caf81e3
commit 6b4cce1fee
12 changed files with 229 additions and 81 deletions
+91 -49
View File
@@ -59,14 +59,91 @@ pub enum CallEffect {
External,
}
/// Resolved native value contract. A union is flat, nonempty and preserves
/// all definition edges even when an earlier alternative already matches.
#[derive(Debug, Clone)]
pub enum CallType {
Single(crate::AnnotationType),
Union(Vec<crate::AnnotationType>),
}
impl From<crate::AnnotationType> for CallType {
fn from(value: crate::AnnotationType) -> Self {
Self::Single(value)
}
}
impl CallType {
pub fn nullable(value: crate::AnnotationType) -> Self {
Self::Union(vec![value, crate::AnnotationType::Unit])
}
fn alternatives(&self) -> &[crate::AnnotationType] {
match self {
Self::Single(value) => std::slice::from_ref(value),
Self::Union(values) => values,
}
}
pub fn references(&self) -> impl Iterator<Item = ObjectRef> + '_ {
self.alternatives()
.iter()
.copied()
.filter_map(crate::AnnotationType::reference)
}
pub fn is_any(&self) -> bool {
self.alternatives()
.iter()
.any(|value| matches!(value, crate::AnnotationType::Any))
}
pub fn validate(&self, store: &ObjectStore) -> Result<(), VmError> {
if self.alternatives().is_empty() {
return Err(VmError::InvalidArgument("empty native union type"));
}
for value in self.alternatives() {
match *value {
crate::AnnotationType::Nominal(reference) => {
store.payload::<crate::EndorTypeObject>(reference, "Type")?;
}
crate::AnnotationType::Capability(reference) => {
store.payload::<crate::EndorTraitObject>(reference, "Trait")?;
}
_ => {}
}
}
Ok(())
}
pub fn check(
&self,
store: &ObjectStore,
name: &str,
position: String,
value: &Value,
) -> Result<(), VmError> {
self.validate(store)?;
for expected in self.alternatives() {
if store.matches_annotation(value, *expected)? {
return Ok(());
}
}
let names = self
.alternatives()
.iter()
.map(|expected| store.annotation_type_name(*expected))
.collect::<Result<Vec<_>, _>>()?;
Err(VmError::TypeMismatch {
function: name.into(),
position,
expected: names.join(" | "),
actual: store.value_type_name(value)?,
})
}
}
/// Immutable native contract, including an explicit receiver where applicable.
/// Missing fixed slots are covered by variadic; optionality comes from arity.
#[derive(Debug, Clone)]
pub struct CallSignature {
pub arity: CallArity,
pub parameters: Vec<crate::AnnotationType>,
pub variadic: Option<crate::AnnotationType>,
pub returns: crate::AnnotationType,
pub parameters: Vec<CallType>,
pub variadic: Option<CallType>,
pub returns: CallType,
pub effect: CallEffect,
}
impl CallSignature {
@@ -77,19 +154,18 @@ impl CallSignature {
) -> Self {
Self {
arity: CallArity::exact(parameters.len()),
parameters,
parameters: parameters.into_iter().map(CallType::from).collect(),
variadic: None,
returns,
returns: returns.into(),
effect,
}
}
pub fn references(&self) -> impl Iterator<Item = ObjectRef> + '_ {
self.parameters
.iter()
.copied()
.chain(self.variadic)
.chain([self.returns])
.filter_map(crate::AnnotationType::reference)
.chain(self.variadic.as_ref())
.chain([&self.returns])
.flat_map(CallType::references)
}
pub fn validate(&self, store: &ObjectStore) -> Result<(), VmError> {
self.arity.validate()?;
@@ -101,19 +177,10 @@ impl CallSignature {
for expected in self
.parameters
.iter()
.copied()
.chain(self.variadic)
.chain([self.returns])
.chain(self.variadic.as_ref())
.chain([&self.returns])
{
match expected {
crate::AnnotationType::Nominal(reference) => {
store.payload::<crate::EndorTypeObject>(reference, "Type")?;
}
crate::AnnotationType::Capability(reference) => {
store.payload::<crate::EndorTraitObject>(reference, "Trait")?;
}
_ => {}
}
expected.validate(store)?;
}
Ok(())
}
@@ -134,16 +201,9 @@ impl CallSignature {
let expected = self
.parameters
.get(index)
.copied()
.or(self.variadic)
.or(self.variadic.as_ref())
.ok_or(VmError::InvalidArgument("incomplete native signature"))?;
self.check_value(
store,
name,
format!("parameter {}", index + 1),
value,
expected,
)?;
expected.check(store, name, format!("parameter {}", index + 1), value)?;
}
Ok(())
}
@@ -153,25 +213,7 @@ impl CallSignature {
name: &str,
value: &Value,
) -> Result<(), VmError> {
self.check_value(store, name, "return".into(), value, self.returns)
}
fn check_value(
&self,
store: &ObjectStore,
name: &str,
position: String,
value: &Value,
expected: crate::AnnotationType,
) -> Result<(), VmError> {
if !store.matches_annotation(value, expected)? {
return Err(VmError::TypeMismatch {
function: name.into(),
position,
expected: store.annotation_type_name(expected)?,
actual: store.value_type_name(value)?,
});
}
Ok(())
self.returns.check(store, name, "return".into(), value)
}
}