Repository navigation
fix(runtime): read a behavior's own features from its performance in body expressions - #1511
Conversation
…body expressions An attribute a state or action definition declares over its own occurrence features (size(entryAction), size(en), size(subperformances), a nested part) reads the performance's feature value, not the performer's same-named feature nor an object of the bare declaration. The state machine's evaluation contexts carry the machine's occurrence, so a tracking cell re-derives such an attribute after a feature it reads is written and in a held-image copy, which no longer answers 'no value for feature entryAction'. Co-Authored-By: jason.han <hanhuijun@gmail.com>
…nces counts by agreement with the part's read Co-Authored-By: jason.han <hanhuijun@gmail.com>
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
…formed from its performance A chain or assignment target starting at a part the body declares (fresh.n) reaches the part the performance holds, as a bare read of it does, instead of an object of the bare declaration. Co-Authored-By: jason.han <hanhuijun@gmail.com>
|
9831f6c extends the performance-first read to the two paths that bypass the bare-name lookup: a feature chain and a chained assignment target whose base is a usage the behavior declares ( |
What and why
An attribute a state or action definition declares over its own occurrence features —
size(entryAction),size(en),size(doAction),size(subperformances), a nestedpart fresh— read the wrong object, or nothing, whenever it was evaluated through the behavior's own evaluation context. A held-image copy of an exhibited machine withattribute n : Integer = entryAction->size();answeredno value for feature entryActionand its state data read{}; the same machine live failed the same way as soon as a feature the expression also reads was written (attribute total = size(entryAction) + offset;reads 2, thenassign offset := 2leavestotalunreadable); andsize(subperformances)orsize(enclosedPerformances)in a state definition counted the exhibiting part's performances (0) whilesm.subperformancesread directly held one.The root is in the evaluation of names, not in the restore order.
evalNameGeneralread a bare name from the frames, then from the performer (self) by name, then from the declaration's scope — never from the performance's own occurrence, which the state machine's cell contexts (ensureStateCells,stateAttributeContext, the step and trigger contexts) did not even carry. Live this was masked:initializeAttributesseeds a binding cell from the occurrence's already-materialized feature value, so the expression was never evaluated through the machine's context until something made the tracking cell derive again — a held-image copy (tracking values restore unmaterialized and re-derive lazily, as documented) or a written dependency.The fix is general and names no entry, do or exit action:
StateExecutor.newEvalContextbuilds every machine evaluation context with the machine's occurrence besidethisOccurrence, as the action performances'evalContextForalready did.EvalContext.performanceFeatureValuereads a name that resolves to a feature the performance's type holds from the performance occurrence, after the frames and before the performer's same-named feature. A feature a frame holds without a value (an unsupplied parameter) stays the frame's to answer, sobind noting.n = levelinside a performed action still reads the action's emptylevel, not the part's.selfFeatureValueis the same read onself(featureValueOn).fresh.n,assign fresh.n := 5inside the performedgo), and a qualified read of one (SM::entryAction), start from the performance's own part too (evalFeatureChain,chainRoot,evalNameGeneral's qualified path), so a barefresh,fresh.nand a write tofresh.nname one object; before,fresh.nread and wrote an object of the bare declaration (#4 fresh owner=#0) beside the performance's.No change to
held_image_behavior.go: the restore path was re-deriving correctly through a context that could not see the occurrence.Behaviour changes
T::pexhibitssm : SM, performsad : AD)sm.n = size(entryAction),sm.m = size(en),sm.d = size(doAction),sm.x = size(exitAction),sm.sa = size(subactions)livesm.sp = size(subperformances),sm.ep = size(enclosedPerformances)liveT::p.sm.subperformancesreads[#4]on both)StateDataWithError)no value for feature entryAction, data{}total = size(entryAction) + offsetafter a transition assignsoffset := 2(live)no value for feature entryActionad.sa = size(subactions)on an action def, live and in a copyad.sp,ad.epon an action def, liveno value for feature sp/epsubperformances; not touched here)size(triggerCount)-style reads of empty[0..1]features (incomingTransitionTrigger,accepted) in a copyCreateStateExecutor(SM):size(entryAction)OccurrencesTest::p1.v,new OccurrencesTest::P1().vfresh.nread, thenassign fresh.n := 5, thenfresh.nagain, inside the performed action#4 fresh owner=#0),go.freshuntouchedgo.fresh(#3 Widget owner=#2.fresh), no ownerless objectpart fresh/part sparenamed inside a performed action (occurrence_lifecycle_in_performed_action)#3 fresh owner=#0); the performance's owngo.freshslot stays<unknown>go.fresh(#3 Widget owner=#2.fresh); same ids, same valuesGolden and fixture movement
conformance/occurrence_lifecycle_in_performed_action.trace.golden: 4 lines,materialize: fresh #3/create: fresh #3/materialize: spare #5/create: spare #5→Widget #3/Widget #5(regenerated with-update-traces): the body'sfreshnow evaluates to the performance'sgo.freshfeature value, which the trace labels by type, instead of an object of the bare declaration labelled by its name. No value in the case moved.TestKernelLibraryMembersReadableWhileRunning,TestLibraryAndPrefixSweep, the corpora ratchets andtraining_examples_expected.txtunchanged).conformance/behavior_occurrence_attributesfails on develop atsubperformancesAgree/enclosedAgree(false) and inTestHeldImageRoundTrip(the copy differs from the graph imaged); passes here. The two Kernel-collection counts are pinned as agreement with the part'ssize(modes.subperformances)rather than as1, because fix(runtime): derive implicit specializations for every performance the run creates #1492 derives the collections from every member held (sm.subperformances= 3 there); the fixture passes on this branch merged with fix(runtime): derive implicit specializations for every performance the run creates #1492.Not in this PR
ad.sp/ad.ep:size(subperformances)declared on an action def read through the part failsno value for feature spon develop and here (instance-level derivation of a performed action's Kernel collections);size(subactions)works.in gauge : Integer[0..1]reads<undetermined>insize(gauge)on develop and here; the occurrence's slot is undetermined too, so such an object cannot be imaged (a undetermined the model does not determine).Specification basis
KerML 1.0 §7.4.11 Feature Values: a feature value's implicit binding connector has "the same featuring types as the declared feature", so
attribute n = size(entryAction)declared instate def SMbinds within eachSMperformance, between its ownnand its ownentryAction; KerML 1.0 §8.2.3.5 name resolution:entryAction,subperformanceswritten inSM's body resolve toSM's (inherited) members, never to the exhibiting part's same-named features. New row indocs/project/spec-compliance.mdafter the state entry/do/exit multiplicity row.How it was verified
conformance/behavior_occurrence_attributes(state def:entryAction,en,doAction,exitAction,subactionscounts,subperformances/enclosedPerformancesagreement with the part's read; action def:subactions), carried byTestExecutionConformance,TestExecutionConformanceUnderPoliciesandTestHeldImageRoundTripheld_image_behavior_attributes_test.go:TestHeldImageRederivesBehaviorOccurrenceAttributes: the copy'sStateDataWithErroris error-free and equal to the source's seven values; the copied performed action'ssubactionCountcell reads 2robustness_behavior_occurrence_attributes_test.go:TestRuntimeRobustnessBehaviorOccurrenceAttributes: an optional occurrence feature empty after restore reads 0 in source and copy; a tracking attribute re-derives to 3 after a transition writes a feature it reads, live and in a copy of the moved machine; a chained read and a chained write (fresh.n) inside a performed action name the performance's own part and leave no ownerless object; an attribute over a valueless feature is a typedErrNoValuenaming the feature, never a panicunsetguard is pinned by the existingconformance/performed_action_binding_end_names_parameter,TestRuntimeRobustnessBindingEndNamesandTestRuntimeRobustnessStepParameter, which fail without itTestHeldImageRoundTrip, wall time, best of three): develop 1.54 s / 1.57 s / 1.69 s, branch 1.54 s / 1.59 s / 1.70 s (the branch suite carries one fixture more), unchanged within noisego build ./...,go vet ./...,gofmt -l .empty,make lint(staticcheck + gosec) clean,go test ./internal/exec/runtime(targetedTestExecutionConformance|TestHeldImage|TestKernelLibraryMembersReadableWhileRunning|TestRuntimeRobustnessincluded),go test ./...with the training, pilot, spec-example and pilot-library-XMI corpora downloaded andOPENSYSML_REQUIRE_TRAINING_CORPUS=1 OPENSYSML_REQUIRE_PILOT_CORPORA=1 OPENSYSML_REQUIRE_SPEC_EXAMPLES=1 OPENSYSML_REQUIRE_PILOT_LIBRARY_XMI=1set: all packagesok(cmd/sysml294 s,tests/codegen325 s,tests/corpus45 s,tests/identity,internal/exec/runtime179 s)python3 scripts/changelog.py check,python3 scripts/check-doc-ids.py,make docs-countscurrentorigin/develop, fix(runtime): derive implicit specializations for every performance the run creates #1492fix/implicit-specialization-runtime-objectsand test(runtime): pin a state's entry, do and exit actions as one performance each #1507test/state-action-default-performances: no conflicts; the new fixture and tests pass on the tree merged with fix(runtime): derive implicit specializations for every performance the run creates #1492Checklist
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 changelogLink to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/56a7606a30964a9c969dbbd909b120ef
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/56a7606a30964a9c969dbbd909b120ef?variant=devin
Requested by: @HuiJun