Skip to content

fix(semantics): a body expression owns the body Expression it refers to, and the body its parameters, members and result — read natively as the conversion writes them; the conversion states a body's ResultExpressionMembership as the FeatureMembership it is - #1823

Merged
HuiJun merged 18 commits into
developfrom
fix/body-expression-parts
Nov 1, 2026

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

What and why

The second of the three expression-parts parity follow-ups (after #1817, whose branch this one is built on — its diff reads as its own three commits once #1817 lands): a body expression.

For

attribute y = { in q : ScalarValues::Real; attribute k = 2; q * k };

the RDF/API-JSON writer (bodyExpression in internal/translate/export/rdf_expr.go) states the { … } as a FeatureReferenceExpression whose referent is the body Expression it owns through a FeatureMembership; the body owns its parameters and members through FeatureMemberships and its result through a ResultExpressionMembership, in source order; the reference owns its own out. The native read had the body's parameters and members as symbols in a body-local scope with no owner at all, no body Expression, no result membership, and no ids for any of them.

Now:

  • the semantics synthesize the body Expression (role ExpressionBody, metaclass Expression) as the reference's referent, owned through a FeatureMembership ahead of the ReturnParameterMembership; the body owns the symbols its scope declares (parameters and members, in the writer's order) and its result (role ExpressionBodyResult, ResultExpressionMembership); ownerOf a body parameter or member is the body, found through the declaration whose value holds it when nothing reflected the body yet (bodyDeclaredBy);
  • ownedElement/ownedFeature of the reference list the body ahead of its result, and of the body its parameters, members and result; parameter/directedFeature list parameters only (a body's result expression is a ResultExpressionMember, not a parameter);
  • ast.BodyDeclarations is the one source-order numbering the writer (bodyDeclarations), the id reader and the semantics share, so in%d/m%d slots agree by construction;
  • the id reader (internal/translate/export/element_ids.go) registers body, every parameter (by its own span — a body's parameters all share the body node) and member (with their value expressions, recursively), result and out as the writer mints them; queryexec.ElementIDOf reads a body parameter and its membership through that span;
  • one writer fix: bodyMembership states ownedFeatureMembership/ownedFeature/ownedMemberFeature for every FeatureMembership subclass it owns, so a body's ResultExpressionMembership (a FeatureMembership, KerML 8.3.4.1) is listed as the pilot lists it.

A referee call made on the way, kept as the writer has it: KerMLExpressions' BodyParameterMember returns ParameterMembership, but both notations override ExpressionBody with a type body (KerML.xtext FunctionBodyPart, SysML.xtext CalculationBody) whose in feature is a FeatureMember, i.e. a FeatureMembership — and the pilot writes that (switching to ParameterMembership flipped 32 matched body-parameter memberships to unmatched in every corpus root). The native read states FeatureMembership for a body parameter explicitly, with the citation. docs/reference/rdf-mapping.md records it.

Not in this PR: the chain Feature a dotted name owns (last follow-up). A body member written as a bare expression ahead of the result has no symbol and is not listed natively (the writer writes it at m%d).

Specification basis

KerML 1.0 §8.2.5.8.4 / KerMLExpressions BodyExpression, ExpressionBodyMember (a FeatureReferenceExpression owning the body Expression through a FeatureMembership), ResultExpressionMember (ResultExpressionMembership); KerML.xtext ExpressionBody : '{' FunctionBodyPart '}' and SysML.xtext ExpressionBody : CalculationBody for the parameters' FeatureMembership; KerML 8.3.4.1 ResultExpressionMembership specializes FeatureMembership. No spec-compliance.md row moves: the reflective read agreeing with the conversion, not a new rule.

How it was verified

  • internal/semantic/semantics — TestBodyExpressionOwnsItsBodyParametersAndResult: the reference's two memberships, referent, the body's three memberships and their metaclasses/members in order, owners of parameter, member and result, the result's operator and parameters, repeated-read identity; and on a fresh model, a reduce body's parameter finds its owner before anything reflected the body.
  • internal/translate/export — TestElementIDReaderSpellsABodyExpressionAsTheConversionWritesIt: body/result/out parts, the parameter by span (…_pin0, FeatureMembership), the member (…_pm1), its value, the result (…_presult) and the result's operand — all as the conversion writes them.
  • Five RDF goldens re-recorded (tests/export): the only movement is the ResultExpressionMembership now listed under ownedFeatureMembership/ownedFeature with ownedMemberFeature; the .golden.sysml round trips are unchanged.
  • go build ./..., go vet ./..., gofmt -l . (empty), go test -count=1 ./... (full suite, run after the last change), the corpus round trip with the corpora required.
  • api-json: pilot-apijson-diff -update then -check; elements unchanged, properties 1144182 / 27977 → 1144254 present-equal / 27934 present-different; exactly three rows moved, all to equal (Expression.ownedFeatureMembership, Expression.ownedFeature, ResultExpressionMembership.ownedMemberFeature), accounted for in docs/project/api-json-differential.md; go -C tools test ./referee/apijson/... ./oracle/baseline/... and make docs-counts green.

Checklist

  • make test and make lint pass locally
  • Tests added or updated for the change
  • Documentation extended where it already covers the surface (see CONTRIBUTING.md)
  • Changelog entry added as changes/unreleased/<slug>.<section>.md, not as an edit to CHANGELOG.md
  • baselines regenerated and make docs-counts run if a gate count moved (compliance rows need nothing: the census is counted at docs build)
  • No internal work-item labels (waves, slices, F4, K5) in the body, docs, or changelog

Link to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/18ad47adf36f44e9b6824a4472f9678d
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/18ad47adf36f44e9b6824a4472f9678d?variant=devin
Requested by: @NicolasRouquette

devin-ai-integration Bot and others added 15 commits November 1, 2026 04:01
…eReferenceExpression wrapper whose referent is the condition — the wrapper, its FeatureMembership and result read natively as the conversion writes them

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>
…trigger id registration (digest only)

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>
Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>
…to, and the body its parameters, members and result — read natively as the conversion writes them; the conversion owns a body parameter through a ParameterMembership

A `{ in q; attribute k = 2; q * k }` value is a FeatureReferenceExpression whose referent is the body Expression it owns through a FeatureMembership (KerMLExpressions BodyExpression). The body owns its parameters through ParameterMemberships (BodyParameterMember), its members and its result through a ResultExpressionMembership, in source order; each parameter and member answers the body as its owner. ast.BodyDeclarations is the one ordering the writer, the id reader and the semantics share. The conversion wrote a body parameter's membership as a FeatureMembership; it is a ParameterMembership, stated with ownedMemberParameter, parameter and input as the other parameters are, and every FeatureMembership subclass a body owns now states ownedFeatureMembership/ownedFeature.

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>
…ons override KerMLExpressions' BodyParameterMember with a type body, as the pilot writes; a body's ResultExpressionMembership is stated as the FeatureMembership it is

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>
…pression writer change — a body's ResultExpressionMembership stated as a FeatureMembership moves three rows to equal (1144254 / 27934)

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>
…y-chain-parts

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>

# Conflicts:
#	docs/project/api-json-differential-baseline.json
… merge (digest only)

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>
…on-parts

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>

# Conflicts:
#	docs/project/api-json-differential-baseline.json
… merge — the same three rows move to equal (1144254 / 27934)

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>
…apper's ownedElement/ownedFeature list the condition it owns

The trigger is result-bearing (expressionStructure writes its out), so the change-event branch of expressionPartsOf keeps parts.result and the id reader registers the trigger's out. expressionOwnedParts lists the wrapper's condition ahead of its result, as its FeatureMembership leads; parameter and directedFeature read expressionParameterParts, the parameters alone.

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>
… out registration (digest only)

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>
…on-parts

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>

# Conflicts:
#	docs/project/api-json-differential-baseline.json
…y's its parameters, members and result; a body's result expression is no parameter

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>
…tree — the same three rows move to equal (1144254 / 27934)

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review November 1, 2026 11:19
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 3 commits November 1, 2026 11:27
…sion, so a nested body finds its owner on a first read; the id reader descends a local member's own members

featureValueOf finds a body parameter's declaration by its span (the symbol shares the body node), so ValueExpression answers it and reflectBodiesUnder reaches a body nested in it. registerBodyDeclarations recurses into a local member's own members as encodeMember's local case mints them.

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>
…member id registration (digest only)

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>
…pression is a ResultExpressionMember, not the result parameter the conversion omits for a body

Co-Authored-By: Nicolas Rouquette <nicolas.f.rouquette@jpl.nasa.gov>
@HuiJun
HuiJun merged commit bb5809b into develop Nov 1, 2026
30 checks passed
@HuiJun
HuiJun deleted the fix/body-expression-parts branch November 1, 2026 13:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants