//! Method substitution rules. This is a child of types so validation can inspect //! method tables without exporting mutable definition internals. use super::{AnnotationType, EndorTypeObject}; use crate::{ CallArity, CallEffect, CallSignature, CallType, EndorCodeObject, EndorFunctionObject, ObjectRef, ObjectStore, VmError, }; struct Contract { arity: CallArity, parameters: Vec, variadic: Option, returns: CallType, effect: Option, } impl Contract { fn parameter(&self, index: usize) -> Option<&CallType> { self.parameters.get(index).or(self.variadic.as_ref()) } } impl From for Contract { fn from(value: CallSignature) -> Self { Self { arity: value.arity, parameters: value.parameters, variadic: value.variadic, returns: value.returns, effect: Some(value.effect), } } } #[derive(Clone, Copy)] struct Patch<'a> { owner: ObjectRef, name: &'a str, function: ObjectRef, } 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. pub fn accepts_type(&self, store: &ObjectStore, provided: &Self) -> Result { self.validate(store)?; provided.validate(store)?; for actual in provided.alternatives() { let mut accepted = false; for expected in self.alternatives() { accepted = match (*expected, *actual) { (AnnotationType::Any, _) => true, (AnnotationType::Nominal(base), AnnotationType::Nominal(child)) => { store.is_subtype(child, base)? } ( AnnotationType::Capability(capability), AnnotationType::Nominal(definition), ) => store.implements_trait(definition, capability)?, (expected, actual) => expected == actual, }; if accepted { break; } } if !accepted { return Ok(false); } } Ok(true) } } impl ObjectStore { fn method_contract( &self, function: ObjectRef, owner: ObjectRef, ) -> Result, VmError> { if let Some(native) = self.native_signature(function)? { return Ok(Some(native.into())); } let Some(data) = self .object(function)? .payload() .as_any() .downcast_ref::() else { return Ok(None); }; let code = self.payload::(data.code(), "code")?.code(); let Some(signature) = code.source_signature() else { return Ok(None); }; let resolve = |name: &str| { if signature.method_owner.as_deref() == Some(name) { let mut current = Some(owner); while let Some(definition) = current { let data = self.payload::(definition, "Type")?; if data.name == name && (definition == owner || data .methods .values() .any(|candidate| *candidate == function)) { return Ok(AnnotationType::Nominal(definition)); } current = data.base; } } self.annotation_type(function, name, None) }; let parameters = signature .parameters .iter() .map(|name| resolve(name).map(CallType::from)) .collect::, _>>()?; Ok(Some(Contract { arity: self .call_arity(function)? .ok_or(VmError::InvalidArgument("source method lacks arity"))?, parameters, variadic: None, returns: resolve(&signature.return_type)?.into(), effect: None, })) } fn own_method( &self, owner: ObjectRef, name: &str, patch: Option>, ) -> Result, VmError> { if let Some(patch) = patch && patch.owner == owner && patch.name == name { return Ok(Some(patch.function)); } Ok(self .payload::(owner, "Type")? .methods .get(name) .copied()) } fn inherited_method( &self, mut owner: Option, name: &str, patch: Option>, ) -> Result, VmError> { while let Some(definition) = owner { if let Some(function) = self.own_method(definition, name, patch)? { return Ok(Some((definition, function))); } owner = self.payload::(definition, "Type")?.base; } Ok(None) } fn check_override( &self, owner: ObjectRef, name: &str, function: ObjectRef, patch: Option>, defer_unknown: bool, ) -> Result<(), VmError> { let definition = self.payload::(owner, "Type")?; let Some((base_owner, base_function)) = self.inherited_method(definition.base, name, patch)? else { return Ok(()); }; 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"))?; 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")); } } Ok(()) })(); 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::(definition, "Type")?.methods { self.check_override(definition, name, *function, None, true)?; } } Ok(()) } pub(super) fn validate_override_update( &self, owner: ObjectRef, name: &str, function: ObjectRef, ) -> Result<(), VmError> { if self.field_type_checks { 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)?; } } } Ok(()) } /// Revalidate dynamic annotation names before a source method body executes. /// Ordinary functions whose first argument is an object are not methods. pub fn check_method_override_call( &self, function: ObjectRef, receiver: ObjectRef, ) -> Result<(), VmError> { let mut owner = self.type_of(receiver)?; while let Some(definition) = owner { let data = self.payload::(definition, "Type")?; for (name, candidate) in &data.methods { if *candidate == function { self.check_override(definition, name, function, None, false)?; } } owner = data.base; } Ok(()) } }