Repository navigation
fix(runtime): bind a nested requirement usage's subject from the owning object - #1312
Merged
Merged
Conversation
…ng object A requirement usage a definition owns held an object of its definition when its body only restated the subject, so the usage's subject binding (subject s :>> s = sub;, subject s = sub;, a bare subject = sub;, a sibling part, a satisfy usage's by) never reached the object's shape: p1.rq.s read <undetermined> and p1.rq.result with it, while the requirement check on p1 evaluated the binding. The usage is now the type of the object it holds, the inherited subject takes its value from the bare subject member or the by operand, and a satisfy usage's result is its conditions' verdict with the by operand bound as its subject. A subject nothing binds stays <undetermined>. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…s sr row Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ment-subject-binding
…ment-subject-binding
…ment-subject-binding
…the library's reading A body that only defaults the subject (Cases::Case::obj) no longer retypes its holder, so an objective's subj and result read as before; a negated satisfy usage reads the requirement's verdict as its result while its standing is the negation; the sweep's sr probe is written as the grammar admits (by sub) and its rows re-recorded; spec clause numbers corrected to SysML v2 7.21.2/7.21.4, 8.3.21.11 and KerML 7.4.11. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ment-subject-binding
…text A subject's default (`subject s :>> s default sub;`, the library objective's `default Case::result`) applies when nothing else values it (KerML 1.0 §7.4.11) and is read against the object owning the case, a qualified name of that object's type as its member; an objective's result is its conditions'. Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ment-subject-binding
Contributor
Author
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
…quirement on its own features A satisfy usage's by operand is its subject's most specific value: a subject member it supersedes reads it instead of a value or default the satisfied requirement declares (SysML v2 §7.21.4), in the result read and in the shape. A nested requirement usage's conditions read its own object, so a feature of its own is read over the owner's of the same name, in the read and in the check on the owning object alike; only a subject written as default yields to an empty or undetermined source, one written with = is held and refused when empty. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ment-subject-binding
…e differential gate The gate's node values for a satisfy assertion read each reference as the assertion's conditions do, through ReadInSatisfaction, so the subject is the object the by operand names rather than the default its declaration states. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…trace Reaching the object a nested requirement usage holds is structure the verdict's shape fixes, not a value the check reads, so it does not stop the verdict from standing for every object of the shape. Co-Authored-By: jason.han <hanhuijun@gmail.com>
A subject member whose names the by operand did not bind keeps evaluating and holding its own value. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ment-subject-binding
This was referenced Oct 21, 2026
This was referenced Oct 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
A requirement usage a definition owns —
part def P1 { attribute sub : Integer = 3; requirement rq : R1 { subject s :>> s = sub; } }— held an object of its definition (R1) when its body only restated the subject:declaresFeaturescounted body members that are*ast.Usagenodes but not an*ast.SubjectMember, soCompositeTypeOffell back to the def. The object's shape was thenR1's, whosesbinds nothing, andp1.rq.sread<undetermined>withp1.rq.resultundetermined through it — while-validateonp1(which evaluates the usage's body bindings against the object throughmemberBindings) decided the requirement. The read and the check disagreed about the same object.The fix is in the shape/materialisation layer, not in the read path:
runtime/instance.godeclaresFeatures: a body restating the subject (named, redefining, or a baresubject = x;) declares a feature, so the usage is the type of the object it holds, as any body declaring a feature already made it.CompositeTypeOftreats a satisfy usage'sbythe same way, sinceby xis the subject's feature value (SysML.xtextSatisfactionSubjectMember).runtime/shape.goeffectiveFeature→ newroleRedefiningDefault: when neither the inherited subject nor anything it redefines by name binds a value, the value of a body member redefining it under no name of its own (subject = sub;, an implicit role redefinition) or of the satisfy usage'sbyoperand becomes the feature's value, evaluated in the scope it was written in.runtime/context.gomemberBindings+ newsubjectParameterNames: a satisfy usage'sbyoperand is bound under its subject's names before the body's members are read, unless an object is supplied from outside (the check path'sbyobject) — so theresultread of a satisfy usage sees the same subject the check does.runtime/return_parameters.gostatesConditions: a satisfy requirement usage is a requirement usage (SysML v2 §7.21.4), so itsresultis its conditions' verdict.runtime/satisfy.goCheckSatisfactionOn:satisfy requirement r : R by x;is checked against the conditionsRstates (newstatesAnyCondition) instead of being refused as satisfying no requirement;satisfiedByreads thebyoperand throughsemantics.SatisfyEndsOf.runtime/shape.gonewsatisfiedByValueandruntime/context.gomemberBindings: thebyoperand is the subject's most specific value, over a value or default the satisfied requirement's subject declares or inherits; only a subject writtendefault(valueIsDefault) yields to an empty or undetermined source.solve/differential_test.gonewNodeValues: the differential gate reads a satisfaction's references throughReadInSatisfaction, with the assertion's bindings, so its node values name thebyobject as the evaluator does (the gate now agrees on 131 elements, 0 disagreed; the production pin reader,SatisfactionReader, already read them so).runtime/return_parameters.goconditionResultValue+ newconditionSelf(used bycheckRequirementOnandaction_holds.goholds): a nested usage's subject and actor bindings are read against the owning object, its conditions against the object it holds — the usage's ownvover the owner'sv.Specification basis
SysML v2 §7.21.2 Requirements (the subject is a parameter the usage's body redefines and binds), §8.3.21.11 SubjectMembership (a bare⚠️ Approximate, naming the open subrequirement and no-
subject = x;implicitly redefines the definition's subject), §7.21.4 Satisfy Requirement Usages (bybinds the subject;isNegatedasserts the requirement false, §8.3.21.10), §7.22.2 Cases (an objective is a requirement usage whose subject defaults to the case's result); KerML 1.0 §7.4.11 Feature Values (a value in a nested feature's body is evaluated in that feature's context; a default applies when nothing else values the feature), §7.3.4.5 (the more specific declaration governs the object's type). Adds a row todocs/project/spec-compliance.md(byitems below).Behaviour changes
p1.rq.s→<undetermined>,p1.rq.result→<undetermined>forrequirement rq : R1 { subject s :>> s = sub; }inP1, at model level and on an instantiatedp1;-validate=p1heldp1.rq.s→3,p1.rq.result→true(false for a failing value), equal to-validate=p1subject s = sub;and the baresubject = sub;likewise undetermined3/truesatisfy requirement sat : RP by pa;nested in a definition:-validaterefused it ("no requirement to satisfy"),p1.sat.s/p1.sat.resultundeterminedRP's conditions;p1.sat.sis thepaobject,p1.sat.resultits verdict; same at package level (satisfy requirement satPkg : RP by p1.pa;)bybinds the subject; the declared requirement states its type's conditions)not satisfy requirement n : R by x;(nested or package-level):-validate/-satisfyrefused it;n.resultundeterminedn.resultisR's verdict (truewhenRholds) while the assertion's standing is violated — no inversion of the readisNegated: the requirement is asserted false); Constraints.sysmlnegatedConstraintChecks :> falseEvaluationsobject:Probe::R1; the library'sCases::Case::objholder byRequirements::RequirementCheck)object:Probe::P1::rq,object:UseCases::UseCase::obj), as a usage whose body declares any other feature already wasrequirement rqDefault : R1 { subject s :>> s default sub; }:p1.rqDefault.s/.result<undetermined>3/true— the default applies since nothing else valuessca2.obj.subjforcase ca2 : Ca2 { return rr : Integer = 7; }→<undetermined>(the library'ssubject subj default Case::result;read as the declaration)7: the default is evaluated in the owning case's context,Case::resultbeing that case's own result;ca2.obj.resultstaystrue(RequirementConstraintCheck::resultstates no condition onsubj); a use case'su1.obj.subj, its case stating no result, stays<undetermined>withu1.obj.resulttrueobj.resultwhose objective condition reads a subject that fails it (use case u3 : U3 { subject s = bad0; },w= −1):trueon develop, vacuously — the holder was typedRequirementCheckand the objective's condition never evaluatedfalse: the objective's condition is evaluated against the bound subjectRequirementCheck::resultsatisfy requirement sat : RG by bad;whereRGdeclaressubject s : PA default good;:-satisfyrefused it;sat.sreadgood(the inherited default)bad;sat.sisbad,sat.resultfalse— thebyoperand supersedes the subject's declared value or default, in the result read and in the object's shape alikebybinds the satisfy usage's subject); KerML 1.0 §7.4.11 (the redefining feature's value governs)requirement rq : RV { attribute extra = 1; }in a part withattribute v = -1(RVstatesv > 0on its ownv) —-validatereportedrqfails on the owner'sv;p.rq.result<undetermined>rqis undecided (no value for feature v) andp.rq.result<undetermined>;requirement rqOwnV : RV { attribute v :>> v = 5; }istruein bothsubject s :>> s = P1::optional;withoptional : Integer[0..1]empty and the subject declareddefault 1:-validaterefused it for its multiplicity, butp1.r.sread1multiplicity violation: 0 value(s) bound to a feature with multiplicity lower bound 1); only a subject writtendefaultyields to an empty or undetermined source=is the feature's value and must meet its multiplicity)subject s :>> s;, or a bodilessrequirement rq : R1;)<undetermined>at model level, uninitialized on an object, the check refuses it; nevertrueVerification usages were already right (
RunVerificationbinds the body's subject) and still agree:p1.ver.verdict=pass/failforverification ver : VD { subject s :>> s = sub; }.Measured fallout
docs/project/library-and-prefix-sweep-outcomes.txtre-recorded, 17 rows moved, all in the systems sweep'sProbemodel:by(Probe::p1.rq,Probe::p1.sr,new Probe::P1().rq/.sr, their.self, theobjectrows, and the twocheckedConstraintscollections listing them):object:Probe::R1→object:Probe::P1::rq/::sr— 10 rows.Probe::p1.rq.s:undetermined→object:Probe::P2— the subject now holds whatsubject s :>> s = sub#(1)binds — 1 row.Probe::p1.sr.s/.sr.resultfor the probesatisfy requirement sr : R1 by sub;(rewritten fromby sub#(1), a syntax error: SysML.xtextSatisfactionReferenceExpressionis aFeatureChainMember, and the pilot 0.62.0 validator refuses it too):undetermined/constant true→multiplicity violation: 2 value(s) bound to a feature with multiplicity upper bound 1, sincesub : P2[2]is bound tosubject s : P2. The row now pins that refusal (KerML 1.0 §7.4.11 binding, §7.3.4.4 multiplicity); before, thebynever reached a read and the result was vacuouslytrue— 2 rows.Probe::u1.obj,Probe::p1.uq.obj,Probe::p1.iu.obj,new Probe::U1().obj):object:Requirements::RequirementCheck→object:UseCases::UseCase::obj— 4 rows; theirobj.resultstaystrue(pinned in the fixture for a case, a use case, both nested in a part, and on an instantiated object).testdata/kernel_member_reads.golden(TestKernelLibraryMembersReadable): no rows moved.internal/exec/solveTestDifferentialConformanceCorpus): 131 agreed, 0 disagreed (129 before);satisfy_chain_unresolved'ssatisfy r by config.nopemoves from "without values" to "unreadable-read", since thebyoperand is now the subject's value and does not evaluate — no verdict either way.training_examples_expected.txtuntouched.Left open
result(§7.21.2,Requirements.sysmlsubrequirements :> constraints), and a satisfy stating nobyinside a part does not bind the containing part as its subject (§7.21.4); both pre-existing, named in the compliance row (verPkg.s) goes through the case-usage output path and is refused asunknown output; itsverdictreads correctly.How it was verified
On the branch head (fix commit, docs commit, review-fix commit, merges of
origin/developup to 188f222):go build ./...,go vet ./...— ok;gofmt -l .— empty;make lint(staticcheck + gosec) —✓ Lint passedgo test -count=1 ./internal/exec/runtime -run 'TestReturnParameter|TestExecutionConformance|TestNestedRequirementUsageShapeBindsItsSubject|TestRuntimeRobustnessNestedSubjectBinding|TestKernelLibraryMembersReadable|TestLibraryAndPrefixSweep'— ok (two conformance fixtures, sweep re-recorded: 13 rows, kernel golden unchanged)OPENSYSML_REQUIRE_TRAINING_CORPUS=1 OPENSYSML_REQUIRE_PILOT_CORPORA=1 OPENSYSML_REQUIRE_PILOT_LIBRARY_XMI=1 go test -count=1 ./...with the corpora fromscripts/download-training-examples.sh,scripts/download-pilot-corpora.sh,scripts/download-pilot-library-xmi.sh— 119 packages ok, exit 0 (tests/codegen,cmd/sysml,internal/checkincluded)python3 scripts/changelog.py check,python3 scripts/check-doc-ids.py— okp1.rq.s = 3,p1.rq.result = truewith-e, on-instantiate p1, and-instantiate p1 -validate=p1reportsrequirement rq holds;not satisfy requirement nsrHolds : R1 by one;readsnsrHolds.result = truewhile-validatereports it violated (Required condition evaluated to false: not s.w > 0);ca2.obj.subj<undetermined>,ca2.obj.result/u1.obj.result/h.uq.obj.result/new Holder().cq.obj.resulttrue;package OccurrencesTest { part def P1 { part pa; part pb; attribute v : ScalarValues::Integer = self.suboccurrences->SequenceFunctions::size(); } part p1 : P1; }readsp1.v = 2.On the head with the defaulted-subject rule (
ac4865890):go build ./...,go vet ./...,gofmt -l .empty,make lintpassed; TestReturnParameter*, TestExecutionConformance, TestLibraryAndPrefixSweep (re-recorded), TestKernelLibraryMembersReadable (no rows moved), TestNestedRequirementUsageShapeBindsItsSubject, TestRuntimeRobustnessNestedSubjectBinding, TestAnalysis*/TestObjective* green;go test ./...with the three corpora and their REQUIRE variables: 119 packages ok;python3 scripts/changelog.py checkandpython3 scripts/check-doc-ids.pyclean.On the merge head over
origin/developa270e66 (#1302, #1305, #1313, #1315–#1318, #1323, #1325; clean merge, no golden or fixture row moved by the merged work):go build ./...,go vet ./...,gofmt -l .empty,make lint; the runtime gates above;./internal/exec/... ./internal/check/... ./cmd/sysml/...with the three corpora and their REQUIRE variables;OccurrencesTestp1.v= 2 at model level and on an instantiated object; changelog and doc-id checks clean.On the head with the review fixes merged over
origin/develop43a831e (#1306):go build ./...,go vet ./...,gofmt -l .empty,make lint; the runtime gates above (fixture extended with the own-feature, empty-subject andby-over-default reads; sweep and kernel golden unchanged);./internal/exec/... ./internal/check/... ./cmd/sysml/...with the three corpora and their REQUIRE variables;OccurrencesTestp1.v= 2 at model level and on an instantiated object; changelog and doc-id checks clean.On the merge head over
origin/develope3ec3c2 (PRs #1299–#1304 merged in; no conflicts, no sweep or kernel golden row moved by the merged work):go build ./...,go vet ./...,gofmt -l .empty; the runtime gates above,./internal/exec/... ./internal/check/... ./cmd/sysml/...with the three corpora and their REQUIRE variables all ok;OccurrencesTestp1.v= 2; changelog and doc-id checks clean.Checklist
make testandmake lintpass locallychanges/unreleased/<slug>.<section>.md, not as an edit toCHANGELOG.mdmake docs-countsrun if a gate count moved (compliance rows need nothing: the census is counted at docs build)F4,K5) in the body, docs, or changelog