From 19728fd15d2e4bbcab37faed7e3953439d35d291 Mon Sep 17 00:00:00 2001 From: Manoel Brunnen Date: Sat, 7 Nov 2026 23:30:39 +0100 Subject: [PATCH] Count inherited return parameters in the result-parameter rules KerML 8.3.4.7.3 and 8.3.4.7.4 count the owned and the inherited return parameters, so `function G :> F, H;` with a return in each general violates validateFunctionResultParameterMembership. Only the most specific owners count, because an owned return parameter redefines those of the general types. Owners in the library are left out: the checks do not read the implied specializations to library bases, and without them two library bases look unrelated. The opensysml baselines gain the new finding on `calc c : C1, C2;`, which inherits a return parameter from each type. --- CHANGELOG.md | 6 ++++ crates/sysmlv2-model/src/check.rs | 30 ++++++++++++++++ crates/sysmlv2-model/src/check/structural.rs | 34 +++++++++++++++---- .../fixtures/opensysml/baseline-library.json | 7 ++++ .../tests/fixtures/opensysml/baseline.json | 7 ++++ 5 files changed, 78 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d370d5e..1f79fdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Fixed + +- `validateFunctionResultParameterMembership` and `validateExpressionResultParameterMembership` count the inherited return parameters too (KerML 8.3.4.7.3, 8.3.4.7.4: "owned or inherited"), so `function G :> F, H;` is reported where `F` and `H` each own one. An owned return parameter redefines those of the general types (`checkFeatureResultRedefinition`), so only the most specific owners count; owners in the library are left out, because the checks do not read the implied specializations that link a type to its library base. + ## v0.11.3 ### Added diff --git a/crates/sysmlv2-model/src/check.rs b/crates/sysmlv2-model/src/check.rs index da39904..4453c2f 100644 --- a/crates/sysmlv2-model/src/check.rs +++ b/crates/sysmlv2-model/src/check.rs @@ -1168,6 +1168,36 @@ mod tests { assert_eq!(shown(&on_threads), shown(&in_line)); } + /// KerML 8.3.4.7 validateFunctionResultParameterMembership and + /// validateExpressionResultParameterMembership count the owned and + /// the inherited return parameters. An owned one redefines those of + /// the general types, so only the most specific owners count. + #[test] + fn inherited_return_parameters_count() { + let mut model = Model::new(); + model.add_source( + "user.kerml", + "package P { + function F { return r; } + function H { return s; } + function G :> F, H; + function K :> F, H { return t; } + function M :> F; + function N :> M, F; + function T { return a; return b; } + expr X { return x; } + expr Y { return y; } + expr Z :> X, Y; + }", + ); + let found: Vec = findings(&model) + .into_iter() + .map(|(_, m)| m) + .filter(|m| m.contains("return parameter")) + .collect(); + assert_eq!(found.len(), 3, "{found:?}"); + } + #[test] fn semantic_checks_walk_tables_without_copying_them() { // Fresh build: library rows and user rows share one storage. diff --git a/crates/sysmlv2-model/src/check/structural.rs b/crates/sysmlv2-model/src/check/structural.rs index 3750e77..1caf9d9 100644 --- a/crates/sysmlv2-model/src/check/structural.rs +++ b/crates/sysmlv2-model/src/check/structural.rs @@ -475,14 +475,36 @@ pub(super) fn validate(r: &ResolvedModel, model: &Model, g: &Facts) -> Vec<(usiz "Only one owned or inherited result expression is allowed", ); } - let returns = g.members[e] + // An owned return parameter redefines those of the general + // types, so only the most specific owners in the closure count. + // The facts hold no implied specializations, which link a type + // to its library base, so owners in the library are left out. + let owned_returns = |t: usize| { + g.members[t] + .iter() + .filter(|&&m| { + b.elements[m] + .owning_relationship + .is_some_and(|r| b.elements[r].ty == "ReturnParameterMembership") + }) + .count() + }; + let owners: Vec<(usize, usize)> = g + .closure(e) + .into_iter() + .filter(|&t| t >= b.lib_boundary) + .map(|t| (t, owned_returns(t))) + .filter(|&(_, n)| n > 0) + .collect(); + let returns: usize = owners .iter() - .filter(|&&m| { - b.elements[m] - .owning_relationship - .is_some_and(|r| b.elements[r].ty == "ReturnParameterMembership") + .filter(|&&(t, _)| { + !owners + .iter() + .any(|&(u, _)| u != t && g.closure_shared(u).contains(&t)) }) - .count(); + .map(|&(_, n)| n) + .sum(); if returns > 1 { report( e, diff --git a/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline-library.json b/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline-library.json index b0e256d..32c07fc 100644 --- a/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline-library.json +++ b/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline-library.json @@ -3856,6 +3856,13 @@ "stage": "semantics", "start": 234 }, + { + "end": 235, + "message": "Only one return parameter is allowed [validateExpressionResultParameterMembership]", + "severity": "Error", + "stage": "semantics", + "start": 234 + }, { "end": 244, "message": "CalculationUsage must have exactly one non-redundant type; found 2", diff --git a/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline.json b/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline.json index e5e723b..a8a4964 100644 --- a/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline.json +++ b/crates/sysmlv2-parser/tests/fixtures/opensysml/baseline.json @@ -4059,6 +4059,13 @@ "stage": "semantics", "start": 234 }, + { + "end": 235, + "message": "Only one return parameter is allowed [validateExpressionResultParameterMembership]", + "severity": "Error", + "stage": "semantics", + "start": 234 + }, { "end": 244, "message": "CalculationUsage must have exactly one non-redundant type; found 2",