From c02e56e325c577acc3df5f5c7a892dc1dbe484f2 Mon Sep 17 00:00:00 2001 From: Lutz Wrage Date: Mon, 14 Sep 2026 15:11:50 -0400 Subject: [PATCH 1/2] Add regression test for issue #3226 Issue3226.aadl nests iteration over an array of arrays: the outer loop iterates the behavior variable of an array-of-arrays type, and the inner loop iterates the outer iterator. Both loops write their classifier, so the model does not depend on #3173. AS5506/3 Rev A D.6 gives the iterator the name scope of the construct, where it acts as a local variable, and admits an array data component reference as the iterated values, so the inner loop is legal and its iterator takes the element type of the outer one. Two assertions cover it: validation reports nothing, and the translated inner loop carries the outer iterator as its iterated values. The second one is needed because a loop with no iterated values at all would also validate and would say nothing about what the loop iterates. NonIterableValues.aadl is the other side of the same cast: an integer literal and a data port are the two shapes the shared D.7 integer value grammar accepts as iterated values and no strict ElementValues can represent. Each must be reported on the values as written rather than throw out of translation. All three assertions fail today. Translation casts the translated value to ElementValues, and IterativeVariableHolder, BehaviorIntegerLiteral, and DataPortHolder are not ElementValues, so the ClassCastException escapes into Xtext's validator dispatch and both models report the opaque 'Error executing EValidator' at line 0. The corpus goldens record that current behavior: the validated diagnostics projection holds that one message for each model and the resolved model is , while the plain-AADL diagnostics goldens are empty, which is what pins that neither fixture has an unrelated core-AADL error. BehaviorAnnexCharacterizationTest and BehaviorAnnexTranslationTest fail on the same exception until the fix lands. Co-Authored-By: Claude Opus 5 (1M context) --- ...te.ba.tests_models_issue3226_Issue3226.txt | 1 + ...sts_models_issue3226_NonIterableValues.txt | 1 + ...te.ba.tests_models_issue3226_Issue3226.txt | 0 ...sts_models_issue3226_NonIterableValues.txt | 0 ...te.ba.tests_models_issue3226_Issue3226.txt | 32 +++++ ...sts_models_issue3226_NonIterableValues.txt | 31 +++++ ...te.ba.tests_models_issue3226_Issue3226.txt | 2 + ...sts_models_issue3226_NonIterableValues.txt | 2 + ...te.ba.tests_models_issue3226_Issue3226.txt | 19 +++ ...sts_models_issue3226_NonIterableValues.txt | 19 +++ .../models/issue3226/.gitignore | 2 + .../models/issue3226/.project | 18 +++ .../models/issue3226/Issue3226.aadl | 65 +++++++++ .../models/issue3226/NonIterableValues.aadl | 54 ++++++++ .../src/org/osate/ba/tests/Issue3226Test.java | 129 ++++++++++++++++++ 15 files changed, 375 insertions(+) create mode 100644 ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_Issue3226.txt create mode 100644 ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_NonIterableValues.txt create mode 100644 ba/org.osate.ba.tests/expected/diagnostics/org.osate.ba.tests_models_issue3226_Issue3226.txt create mode 100644 ba/org.osate.ba.tests/expected/diagnostics/org.osate.ba.tests_models_issue3226_NonIterableValues.txt create mode 100644 ba/org.osate.ba.tests/expected/positions/org.osate.ba.tests_models_issue3226_Issue3226.txt create mode 100644 ba/org.osate.ba.tests/expected/positions/org.osate.ba.tests_models_issue3226_NonIterableValues.txt create mode 100644 ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_Issue3226.txt create mode 100644 ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_NonIterableValues.txt create mode 100644 ba/org.osate.ba.tests/expected/unparse/org.osate.ba.tests_models_issue3226_Issue3226.txt create mode 100644 ba/org.osate.ba.tests/expected/unparse/org.osate.ba.tests_models_issue3226_NonIterableValues.txt create mode 100644 ba/org.osate.ba.tests/models/issue3226/.gitignore create mode 100644 ba/org.osate.ba.tests/models/issue3226/.project create mode 100644 ba/org.osate.ba.tests/models/issue3226/Issue3226.aadl create mode 100644 ba/org.osate.ba.tests/models/issue3226/NonIterableValues.aadl create mode 100644 ba/org.osate.ba.tests/src/org/osate/ba/tests/Issue3226Test.java diff --git a/ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_Issue3226.txt b/ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_Issue3226.txt new file mode 100644 index 00000000000..b9ba48129e6 --- /dev/null +++ b/ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_Issue3226.txt @@ -0,0 +1 @@ +error | semantic | 0 | 0 | 0 | Error executing EValidator diff --git a/ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_NonIterableValues.txt b/ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_NonIterableValues.txt new file mode 100644 index 00000000000..b9ba48129e6 --- /dev/null +++ b/ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_NonIterableValues.txt @@ -0,0 +1 @@ +error | semantic | 0 | 0 | 0 | Error executing EValidator diff --git a/ba/org.osate.ba.tests/expected/diagnostics/org.osate.ba.tests_models_issue3226_Issue3226.txt b/ba/org.osate.ba.tests/expected/diagnostics/org.osate.ba.tests_models_issue3226_Issue3226.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ba/org.osate.ba.tests/expected/diagnostics/org.osate.ba.tests_models_issue3226_NonIterableValues.txt b/ba/org.osate.ba.tests/expected/diagnostics/org.osate.ba.tests_models_issue3226_NonIterableValues.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ba/org.osate.ba.tests/expected/positions/org.osate.ba.tests_models_issue3226_Issue3226.txt b/ba/org.osate.ba.tests/expected/positions/org.osate.ba.tests_models_issue3226_Issue3226.txt new file mode 100644 index 00000000000..de7585d8afd --- /dev/null +++ b/ba/org.osate.ba.tests/expected/positions/org.osate.ba.tests_models_issue3226_Issue3226.txt @@ -0,0 +1,32 @@ +annex[0] owner=Issue3226::worker.i + (AssignmentAction, , 2971, 21) + (BehaviorActionBlock, , 2844, 202) + (BehaviorAnnex, behavior_specification, 2629, 418) + (BehaviorState, done, 2766, 4) + (BehaviorState, start, 2731, 5) + (BehaviorStateGroup, , 2731, 22) + (BehaviorStateGroup, , 2766, 28) + (BehaviorTransition, , 2827, 220) + (BehaviorVariable, m, 2692, 1) + (BehaviorVariable, total, 2651, 5) + (BehaviorVariableGroup, , 2651, 28) + (BehaviorVariableGroup, , 2692, 11) + (BinaryExpression, , 2980, 12) + (ElementValues, , 2880, 1) + (ElementValues, , 2940, 3) + (ForStatement, , 2862, 170) + (ForStatement, , 2905, 109) + (Reference, , 2880, 1) + (Reference, , 2940, 3) + (Reference, , 2971, 5) + (Reference, , 2980, 4) + (Reference, , 2987, 5) + (ReferenceExpression, , 2880, 1) + (ReferenceExpression, , 2940, 3) + (ReferenceExpression, , 2980, 4) + (ReferenceExpression, , 2987, 5) + (ReferenceSegment, cell, 2980, 4) + (ReferenceSegment, m, 2880, 1) + (ReferenceSegment, row, 2940, 3) + (ReferenceSegment, total, 2971, 5) + (ReferenceSegment, total, 2987, 5) diff --git a/ba/org.osate.ba.tests/expected/positions/org.osate.ba.tests_models_issue3226_NonIterableValues.txt b/ba/org.osate.ba.tests/expected/positions/org.osate.ba.tests_models_issue3226_NonIterableValues.txt new file mode 100644 index 00000000000..1933548da79 --- /dev/null +++ b/ba/org.osate.ba.tests/expected/positions/org.osate.ba.tests_models_issue3226_NonIterableValues.txt @@ -0,0 +1,31 @@ +annex[0] owner=NonIterableValues::worker.i + (AssignmentAction, , 2662, 10) + (AssignmentAction, , 2771, 10) + (BehaviorActionBlock, , 2587, 226) + (BehaviorActionSequence, , 2605, 194) + (BehaviorAnnex, behavior_specification, 2396, 418) + (BehaviorIntegerLiteral, , 2637, 1) + (BehaviorState, done, 2509, 4) + (BehaviorState, start, 2474, 5) + (BehaviorStateGroup, , 2474, 22) + (BehaviorStateGroup, , 2509, 28) + (BehaviorTransition, , 2570, 244) + (BehaviorVariable, total, 2418, 5) + (BehaviorVariableGroup, , 2418, 28) + (ElementValues, , 2637, 1) + (ElementValues, , 2740, 7) + (ForStatement, , 2605, 85) + (ForStatement, , 2708, 91) + (Reference, , 2662, 5) + (Reference, , 2671, 1) + (Reference, , 2740, 7) + (Reference, , 2771, 5) + (Reference, , 2780, 1) + (ReferenceExpression, , 2671, 1) + (ReferenceExpression, , 2740, 7) + (ReferenceExpression, , 2780, 1) + (ReferenceSegment, i, 2671, 1) + (ReferenceSegment, j, 2780, 1) + (ReferenceSegment, reading, 2740, 7) + (ReferenceSegment, total, 2662, 5) + (ReferenceSegment, total, 2771, 5) diff --git a/ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_Issue3226.txt b/ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_Issue3226.txt new file mode 100644 index 00000000000..87c56fc72c4 --- /dev/null +++ b/ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_Issue3226.txt @@ -0,0 +1,2 @@ +annex[0] owner=Issue3226::worker.i + diff --git a/ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_NonIterableValues.txt b/ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_NonIterableValues.txt new file mode 100644 index 00000000000..bf3fdea43bb --- /dev/null +++ b/ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_NonIterableValues.txt @@ -0,0 +1,2 @@ +annex[0] owner=NonIterableValues::worker.i + diff --git a/ba/org.osate.ba.tests/expected/unparse/org.osate.ba.tests_models_issue3226_Issue3226.txt b/ba/org.osate.ba.tests/expected/unparse/org.osate.ba.tests_models_issue3226_Issue3226.txt new file mode 100644 index 00000000000..418421b311f --- /dev/null +++ b/ba/org.osate.ba.tests/expected/unparse/org.osate.ba.tests_models_issue3226_Issue3226.txt @@ -0,0 +1,19 @@ +===== annex[0] owner=Issue3226::worker.i ===== + + variables + total : Base_Types::Integer; + m : Matrix; + states + start : initial state; + done : complete final state; + transitions + start -[]-> done { + for (row : Row in m) { + for (cell : Base_Types::Integer in row) { + total := cell + total + } + } + }; +\s\s\s\s +----- Xtext reparse ----- +reparsed diff --git a/ba/org.osate.ba.tests/expected/unparse/org.osate.ba.tests_models_issue3226_NonIterableValues.txt b/ba/org.osate.ba.tests/expected/unparse/org.osate.ba.tests_models_issue3226_NonIterableValues.txt new file mode 100644 index 00000000000..ff10075ef12 --- /dev/null +++ b/ba/org.osate.ba.tests/expected/unparse/org.osate.ba.tests_models_issue3226_NonIterableValues.txt @@ -0,0 +1,19 @@ +===== annex[0] owner=NonIterableValues::worker.i ===== + + variables + total : Base_Types::Integer; + states + start : initial state; + done : complete final state; + transitions + start -[]-> done { + for (i : Base_Types::Integer in 5) { + total := i + }; + for (j : Base_Types::Integer in reading) { + total := j + } + }; +\s\s\s\s +----- Xtext reparse ----- +reparsed diff --git a/ba/org.osate.ba.tests/models/issue3226/.gitignore b/ba/org.osate.ba.tests/models/issue3226/.gitignore new file mode 100644 index 00000000000..afce51184c6 --- /dev/null +++ b/ba/org.osate.ba.tests/models/issue3226/.gitignore @@ -0,0 +1,2 @@ +/.aadlbin-gen/ +/instances/ diff --git a/ba/org.osate.ba.tests/models/issue3226/.project b/ba/org.osate.ba.tests/models/issue3226/.project new file mode 100644 index 00000000000..28778778790 --- /dev/null +++ b/ba/org.osate.ba.tests/models/issue3226/.project @@ -0,0 +1,18 @@ + + + issue3226 + + + + + + org.eclipse.xtext.ui.shared.xtextBuilder + + + + + + org.osate.core.aadlnature + org.eclipse.xtext.ui.shared.xtextNature + + diff --git a/ba/org.osate.ba.tests/models/issue3226/Issue3226.aadl b/ba/org.osate.ba.tests/models/issue3226/Issue3226.aadl new file mode 100644 index 00000000000..3b8ec7893e7 --- /dev/null +++ b/ba/org.osate.ba.tests/models/issue3226/Issue3226.aadl @@ -0,0 +1,65 @@ +-- Copyright (c) 2004-2026 Carnegie Mellon University and others. (see Contributors file). +-- All Rights Reserved. +-- +-- NO WARRANTY. ALL MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY +-- KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE +-- OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT +-- MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT. +-- +-- This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 +-- which is available at https://www.eclipse.org/legal/epl-2.0/ +-- SPDX-License-Identifier: EPL-2.0 +-- +-- Created, in part, with funding and support from the United States Government. (see Acknowledgments file). +-- +-- This program includes and/or can make use of certain third party source code, object code, documentation and other +-- files ("Third Party Software"). The Third Party Software that is used by this program is dependent upon your system +-- configuration. By using this program, You agree to comply with any and all relevant Third Party Software terms and +-- conditions contained in any such Third Party Software or separate license file distributed with such Third Party +-- Software. The parties who own the Third Party Software ("Third Party Licensors") are intended third party benefici- +-- aries to this license with respect to the terms applicable to their Third Party Software. Third Party Software li- +-- censes only apply to the Third Party Software and not any other portion of this program or this program as a whole. + +package Issue3226 +public + with Base_Types, Data_Model; + + data Row + properties + Data_Model::Data_Representation => Array; + Data_Model::Base_Type => (classifier (Base_Types::Integer)); + Data_Model::Dimension => (3); + end Row; + + data Matrix + properties + Data_Model::Data_Representation => Array; + Data_Model::Base_Type => (classifier (Row)); + Data_Model::Dimension => (2); + end Matrix; + + thread worker + end worker; + + -- D.6 gives the iterator of a for or forall the name scope of the construct, where it acts as a local variable, so + -- the iterated values of a nested loop can name the iterator of an enclosing one. The inner iterator then takes the + -- element type of the outer one, and 'total := cell + total' adds two integers. + thread implementation worker.i + annex behavior_specification {** + variables + total : Base_Types::Integer; + m : Matrix; + states + start : initial state; + done : complete final state; + transitions + start -[]-> done { + for (row : Row in m) { + for (cell : Base_Types::Integer in row) { + total := cell + total + } + } + }; + **}; + end worker.i; +end Issue3226; diff --git a/ba/org.osate.ba.tests/models/issue3226/NonIterableValues.aadl b/ba/org.osate.ba.tests/models/issue3226/NonIterableValues.aadl new file mode 100644 index 00000000000..8f71533a73c --- /dev/null +++ b/ba/org.osate.ba.tests/models/issue3226/NonIterableValues.aadl @@ -0,0 +1,54 @@ +-- Copyright (c) 2004-2026 Carnegie Mellon University and others. (see Contributors file). +-- All Rights Reserved. +-- +-- NO WARRANTY. ALL MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY +-- KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE +-- OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT +-- MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT. +-- +-- This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 +-- which is available at https://www.eclipse.org/legal/epl-2.0/ +-- SPDX-License-Identifier: EPL-2.0 +-- +-- Created, in part, with funding and support from the United States Government. (see Acknowledgments file). +-- +-- This program includes and/or can make use of certain third party source code, object code, documentation and other +-- files ("Third Party Software"). The Third Party Software that is used by this program is dependent upon your system +-- configuration. By using this program, You agree to comply with any and all relevant Third Party Software terms and +-- conditions contained in any such Third Party Software or separate license file distributed with such Third Party +-- Software. The parties who own the Third Party Software ("Third Party Licensors") are intended third party benefici- +-- aries to this license with respect to the terms applicable to their Third Party Software. Third Party Software li- +-- censes only apply to the Third Party Software and not any other portion of this program or this program as a whole. + +package NonIterableValues +public + with Base_Types; + + thread worker + features + reading : in data port Base_Types::Integer; + end worker; + + -- D.6 admits an integer range, an event data port, or an array data component reference as the iterated values of a + -- for or forall. The shared D.7 integer value grammar also accepts an integer literal, and a data port is not one of + -- the names a data component reference can start with, so the strict model has no iterated values to carry either + -- one. Both must be reported rather than translated. + thread implementation worker.i + annex behavior_specification {** + variables + total : Base_Types::Integer; + states + start : initial state; + done : complete final state; + transitions + start -[]-> done { + for (i : Base_Types::Integer in 5) { + total := i + }; + for (j : Base_Types::Integer in reading) { + total := j + } + }; + **}; + end worker.i; +end NonIterableValues; diff --git a/ba/org.osate.ba.tests/src/org/osate/ba/tests/Issue3226Test.java b/ba/org.osate.ba.tests/src/org/osate/ba/tests/Issue3226Test.java new file mode 100644 index 00000000000..218bbbf12e7 --- /dev/null +++ b/ba/org.osate.ba.tests/src/org/osate/ba/tests/Issue3226Test.java @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2004-2026 Carnegie Mellon University and others. (see Contributors file). + * All Rights Reserved. + * + * NO WARRANTY. ALL MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY + * KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE + * OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT + * MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * SPDX-License-Identifier: EPL-2.0 + * + * Created, in part, with funding and support from the United States Government. (see Acknowledgments file). + * + * This program includes and/or can make use of certain third party source code, object code, documentation and other + * files ("Third Party Software"). The Third Party Software that is used by this program is dependent upon your system + * configuration. By using this program, You agree to comply with any and all relevant Third Party Software terms and + * conditions contained in any such Third Party Software or separate license file distributed with such Third Party + * Software. The parties who own the Third Party Software ("Third Party Licensors") are intended third party benefici- + * aries to this license with respect to the terms applicable to their Third Party Software. Third Party Software li- + * censes only apply to the Third Party Software and not any other portion of this program or this program as a whole. + */ +package org.osate.ba.tests; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import org.eclipse.xtext.diagnostics.Severity; +import org.eclipse.xtext.nodemodel.util.NodeModelUtils; +import org.eclipse.xtext.testing.InjectWith; +import org.eclipse.xtext.testing.XtextRunner; +import org.eclipse.xtext.testing.validation.ValidationTestHelper; +import org.eclipse.xtext.validation.Issue; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.osate.aadl2.AadlPackage; +import org.osate.aadl2.DefaultAnnexSubclause; +import org.osate.aadl2.ThreadImplementation; +import org.osate.ba.aadlba.BehaviorVariableHolder; +import org.osate.ba.aadlba.ForOrForAllStatement; +import org.osate.ba.aadlba.IterativeVariableHolder; +import org.osate.testsupport.TestHelper; +import org.osate.xtext.aadl2.ba.util.BehaviorAnnexUtil; + +import com.google.inject.Inject; +import com.itemis.xtext.testing.XtextTest; + +/** + * AS5506/3 Rev A D.6 gives the iterator of a {@code for} or {@code forall} the name scope of the construct, where it + * acts as a local variable, and admits an array data component reference as the iterated values. A nested loop over an + * array of arrays therefore iterates the iterator of the enclosing loop, and the inner iterator takes the element type + * of the outer one. Verifies that such a loop is accepted and translated, and that the iterated values the strict model + * cannot carry at all are reported instead of throwing out of translation. + */ +@RunWith(XtextRunner.class) +@InjectWith(BehaviorAnnexInjectorProvider.class) +public class Issue3226Test extends XtextTest { + private static final String PATH = "org.osate.ba.tests/models/issue3226/"; + + @Inject + private TestHelper testHelper; + + @Inject + private ValidationTestHelper validationHelper; + + @Test + public void iteratingAnEnclosingIteratorIsAccepted() throws Exception { + validationHelper.assertNoIssues(testHelper.parseFile(PATH + "Issue3226.aadl")); + } + + /** + * The inner loop must reach the checkers with the outer iterator as its iterated values: an inner loop with no + * iterated values at all would also validate, and would say nothing about what the loop iterates. + */ + @Test + public void theInnerLoopIteratesTheOuterIterator() throws Exception { + var strict = BehaviorAnnexUtil.getStrictModel(defaultAnnex(testHelper.parseFile(PATH + "Issue3226.aadl"))); + var loops = new ArrayList(); + for (var contents = strict.eAllContents(); contents.hasNext();) { + if (contents.next() instanceof ForOrForAllStatement loop) { + loops.add(loop); + } + } + assertEquals(loops.toString(), 2, loops.size()); + var outer = loops.get(0); + var inner = loops.get(1); + assertEquals("row", outer.getIterativeVariable().getName()); + assertEquals("cell", inner.getIterativeVariable().getName()); + assertTrue(String.valueOf(outer.getIteratedValues()), + outer.getIteratedValues() instanceof BehaviorVariableHolder); + assertEquals("m", ((BehaviorVariableHolder) outer.getIteratedValues()).getBehaviorVariable().getName()); + assertTrue(String.valueOf(inner.getIteratedValues()), + inner.getIteratedValues() instanceof IterativeVariableHolder); + assertSame(outer.getIterativeVariable(), + ((IterativeVariableHolder) inner.getIteratedValues()).getIterativeVariable()); + } + + /** + * An integer literal and a data port are the two shapes the shared grammar accepts here and no strict + * {@code ElementValues} can represent, so each must be reported on the values as written. The loops are otherwise + * well formed, which is what makes the two reports the only ones. + */ + @Test + public void valuesThatAreNotIterableAreReported() throws Exception { + var root = testHelper.parseFile(PATH + "NonIterableValues.aadl"); + var source = NodeModelUtils.getNode(root).getRootNode().getText(); + // Xtext does not hand back the issues of one resource in the order the checkers report them. + var issues = validationHelper.validate(root).stream().sorted(Comparator.comparing(Issue::getOffset)).toList(); + assertEquals(issues.toString(), List.of("5", "reading"), issues.stream().map(issue -> { + var written = source.substring(issue.getOffset(), issue.getOffset() + issue.getLength()); + assertEquals(Severity.ERROR, issue.getSeverity()); + assertEquals("'" + written + "' cannot be iterated: a for or forall iterates an integer range, an event" + + " data port, a parameter, or an array data component reference", issue.getMessage()); + return written; + }).toList()); + } + + private static DefaultAnnexSubclause defaultAnnex(AadlPackage pkg) { + var implementation = (ThreadImplementation) pkg.getOwnedPublicSection().getOwnedClassifiers().stream() + .filter(ThreadImplementation.class::isInstance).findFirst().orElseThrow(); + return (DefaultAnnexSubclause) implementation.getOwnedAnnexSubclauses().getFirst(); + } +} From 4cdab7ff99676239c67912d8f17aa3e9f1659aae Mon Sep 17 00:00:00 2001 From: Lutz Wrage Date: Mon, 14 Sep 2026 15:29:28 -0400 Subject: [PATCH 2/2] Accept a loop that iterates an enclosing iterator AS5506/3 Rev A D.6 admits an array data component reference as the iterated values of a for or forall, and a data component reference starts with a local variable identifier. D.6 also gives the iterator of the construct the name scope of that construct, where it acts as a local variable, so an enclosing iterator is one of the names the iterated values may write. Nested iteration over an array of arrays is written that way: the inner loop iterates the outer iterator, and its own iterator takes the element type of that one. IterativeVariableHolder did not implement ElementValues, so it could not be the iterated values of a strict loop. Translation cast the translated value to ElementValues unconditionally, and DataHolder is an IntegerValueVariable, so the holder passed the inner cast and failed the outer one. The ClassCastException escaped into Xtext's validator dispatch and the user got 'Error executing EValidator' at line 0 with no strict model at all. Make IterativeVariableHolder an ElementValues in aadlba.ecore, with the regenerated model code, switch case, and child creation descriptor. The existing type check needs nothing further: the holder is a ValueVariable, so it already resolves through the value branch of getTypeHolder, which processes the Data Model array representation for a value in a loop and gives the iterated values the element type and dimension of the iterator they name. The same cast could fail for every other shape the grammar accepts there and no ElementValues represents: an integer literal, a property reference, and a name that resolves to a data port or a port value. Test the translated value instead of casting it, leave what it cannot represent out of the strict model, and report it from the validator on the values as written. The check gates the strict checkers, like the neighboring checks for constructs the strict model cannot carry, since a loop that iterates nothing is not a loop the checkers can say anything useful about. Fixes #3226 Co-Authored-By: Claude Opus 5 (1M context) --- ...iteratedValues_IterativeVariableHolder.gif | Bin 0 -> 223 bytes .../ForOrForAllStatementItemProvider.java | 3 ++ ...te.ba.tests_models_issue3226_Issue3226.txt | 1 - ...sts_models_issue3226_NonIterableValues.txt | 3 +- ...te.ba.tests_models_issue3226_Issue3226.txt | 25 ++++++++++++- ...sts_models_issue3226_NonIterableValues.txt | 28 ++++++++++++++- ba/org.osate.ba/model/aadlba.ecore | 2 +- .../ba/aadlba/IterativeVariableHolder.java | 2 +- .../ba/aadlba/impl/AadlBaPackageImpl.java | 1 + .../osate/ba/aadlba/util/AadlBaSwitch.java | 3 ++ .../DeclarativeToStrictTranslator.java | 6 +++- .../ba/validation/BehaviorAnnexValidator.java | 33 ++++++++++++++++++ 12 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 ba/org.osate.ba.edit/icons/full/ctool16/CreateForOrForAllStatement_iteratedValues_IterativeVariableHolder.gif diff --git a/ba/org.osate.ba.edit/icons/full/ctool16/CreateForOrForAllStatement_iteratedValues_IterativeVariableHolder.gif b/ba/org.osate.ba.edit/icons/full/ctool16/CreateForOrForAllStatement_iteratedValues_IterativeVariableHolder.gif new file mode 100644 index 0000000000000000000000000000000000000000..95a7dfdefa08aed9f82f5746225e78565661f7ae GIT binary patch literal 223 zcmZ?wbhEHb6krfwIKsg2|NsB{_xIb|$1^Z6eEt3P=$oVS@6WHiRQdmZ{QP+P|M%@1 z8XAzrcTHCVqOFsZ*LBG+ZIYT-DK@i2_}}06K=k+Xc_8}peDUwQ%|O#2K=CIFBZ#E~ zB0zRBu*N7T^rb}FsWHqvdw9x)t&{qj`zDom8hIDml|RP-FJ;6YL^= zT60dFbhBd6QM>Q$Vj&RJF#AQqheapbnHDCv$=_(Hxt*(Z`ek}PLrzUoYGYHjsv?6m E0I`*2_5c6? literal 0 HcmV?d00001 diff --git a/ba/org.osate.ba.edit/src/org/osate/ba/aadlba/provider/ForOrForAllStatementItemProvider.java b/ba/org.osate.ba.edit/src/org/osate/ba/aadlba/provider/ForOrForAllStatementItemProvider.java index 55e17887add..ea173ecf92d 100644 --- a/ba/org.osate.ba.edit/src/org/osate/ba/aadlba/provider/ForOrForAllStatementItemProvider.java +++ b/ba/org.osate.ba.edit/src/org/osate/ba/aadlba/provider/ForOrForAllStatementItemProvider.java @@ -194,6 +194,9 @@ protected void collectNewChildDescriptors(Collection newChildDescriptors newChildDescriptors.add(createChildParameter(AadlBaPackage.Literals.FOR_OR_FOR_ALL_STATEMENT__ITERATED_VALUES, AadlBaFactory.eINSTANCE.createIntegerRange())); + newChildDescriptors.add(createChildParameter(AadlBaPackage.Literals.FOR_OR_FOR_ALL_STATEMENT__ITERATED_VALUES, + AadlBaFactory.eINSTANCE.createIterativeVariableHolder())); + newChildDescriptors.add(createChildParameter(AadlBaPackage.Literals.FOR_OR_FOR_ALL_STATEMENT__ITERATED_VALUES, AadlBaFactory.eINSTANCE.createParameterHolder())); diff --git a/ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_Issue3226.txt b/ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_Issue3226.txt index b9ba48129e6..e69de29bb2d 100644 --- a/ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_Issue3226.txt +++ b/ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_Issue3226.txt @@ -1 +0,0 @@ -error | semantic | 0 | 0 | 0 | Error executing EValidator diff --git a/ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_NonIterableValues.txt b/ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_NonIterableValues.txt index b9ba48129e6..a22a6938553 100644 --- a/ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_NonIterableValues.txt +++ b/ba/org.osate.ba.tests/expected/diagnostics-validated/org.osate.ba.tests_models_issue3226_NonIterableValues.txt @@ -1 +1,2 @@ -error | semantic | 0 | 0 | 0 | Error executing EValidator +error | semantic | 45 | 49 | 1 | '5' cannot be iterated: a for or forall iterates an integer range, an event data port, a parameter, or an array data component reference +error | semantic | 48 | 49 | 7 | 'reading' cannot be iterated: a for or forall iterates an integer range, an event data port, a parameter, or an array data component reference diff --git a/ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_Issue3226.txt b/ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_Issue3226.txt index 87c56fc72c4..572f090acdf 100644 --- a/ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_Issue3226.txt +++ b/ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_Issue3226.txt @@ -1,2 +1,25 @@ annex[0] owner=Issue3226::worker.i - + parsedAnnexSubclause : BehaviorAnnex name=behavior_specification + variables[0] : BehaviorVariable name=total + variables[1] : BehaviorVariable name=m + states[0] : BehaviorState name=start + states[1] : BehaviorState name=done + transitions[0] : BehaviorTransition + actions[0] : BehaviorActionBlock + content : ForOrForAllStatement + behaviorActions : ForOrForAllStatement + behaviorActions : AssignmentAction + target : BehaviorVariableHolder element=total [BehaviorVariable] + valueExpression : ValueExpression + relations[0] : Relation + firstExpression : SimpleExpression + terms[0] : Term + factors[0] : Factor + firstValue : IterativeVariableHolder element=cell [IterativeVariable] + terms[1] : Term + factors[0] : Factor + firstValue : BehaviorVariableHolder element=total [BehaviorVariable] + iteratedValues : IterativeVariableHolder element=row [IterativeVariable] + iterativeVariable : IterativeVariable name=cell + iteratedValues : BehaviorVariableHolder element=m [BehaviorVariable] + iterativeVariable : IterativeVariable name=row diff --git a/ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_NonIterableValues.txt b/ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_NonIterableValues.txt index bf3fdea43bb..12b7846297c 100644 --- a/ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_NonIterableValues.txt +++ b/ba/org.osate.ba.tests/expected/resolved-model/org.osate.ba.tests_models_issue3226_NonIterableValues.txt @@ -1,2 +1,28 @@ annex[0] owner=NonIterableValues::worker.i - + parsedAnnexSubclause : BehaviorAnnex name=behavior_specification + variables[0] : BehaviorVariable name=total + states[0] : BehaviorState name=start + states[1] : BehaviorState name=done + transitions[0] : BehaviorTransition + actions[0] : BehaviorActionBlock + content : BehaviorActionSequence + actions[0] : ForOrForAllStatement + behaviorActions : AssignmentAction + target : BehaviorVariableHolder element=total [BehaviorVariable] + valueExpression : ValueExpression + relations[0] : Relation + firstExpression : SimpleExpression + terms[0] : Term + factors[0] : Factor + firstValue : IterativeVariableHolder element=i [IterativeVariable] + iterativeVariable : IterativeVariable name=i + actions[1] : ForOrForAllStatement + behaviorActions : AssignmentAction + target : BehaviorVariableHolder element=total [BehaviorVariable] + valueExpression : ValueExpression + relations[0] : Relation + firstExpression : SimpleExpression + terms[0] : Term + factors[0] : Factor + firstValue : IterativeVariableHolder element=j [IterativeVariable] + iterativeVariable : IterativeVariable name=j diff --git a/ba/org.osate.ba/model/aadlba.ecore b/ba/org.osate.ba/model/aadlba.ecore index 773ae971a49..183b5e8d965 100644 --- a/ba/org.osate.ba/model/aadlba.ecore +++ b/ba/org.osate.ba/model/aadlba.ecore @@ -503,7 +503,7 @@ - +
diff --git a/ba/org.osate.ba/src/org/osate/ba/aadlba/IterativeVariableHolder.java b/ba/org.osate.ba/src/org/osate/ba/aadlba/IterativeVariableHolder.java index ae674be19cc..68dd55cdec8 100644 --- a/ba/org.osate.ba/src/org/osate/ba/aadlba/IterativeVariableHolder.java +++ b/ba/org.osate.ba/src/org/osate/ba/aadlba/IterativeVariableHolder.java @@ -29,7 +29,7 @@ * @model * @generated */ -public interface IterativeVariableHolder extends DataHolder { +public interface IterativeVariableHolder extends DataHolder, ElementValues { /** * * diff --git a/ba/org.osate.ba/src/org/osate/ba/aadlba/impl/AadlBaPackageImpl.java b/ba/org.osate.ba/src/org/osate/ba/aadlba/impl/AadlBaPackageImpl.java index 4277e48d0e7..1b48779e4f1 100644 --- a/ba/org.osate.ba/src/org/osate/ba/aadlba/impl/AadlBaPackageImpl.java +++ b/ba/org.osate.ba/src/org/osate/ba/aadlba/impl/AadlBaPackageImpl.java @@ -4028,6 +4028,7 @@ public void initializePackageContents() { integerValueVariableEClass.getESuperTypes().add(getValueVariable()); iterativeVariableEClass.getESuperTypes().add(getBehaviorNamedElement()); iterativeVariableHolderEClass.getESuperTypes().add(getDataHolder()); + iterativeVariableHolderEClass.getESuperTypes().add(getElementValues()); literalEClass.getESuperTypes().add(getValueConstant()); literalEClass.getESuperTypes().add(getIntegerValueConstant()); lockActionEClass.getESuperTypes().add(getSharedDataAction()); diff --git a/ba/org.osate.ba/src/org/osate/ba/aadlba/util/AadlBaSwitch.java b/ba/org.osate.ba/src/org/osate/ba/aadlba/util/AadlBaSwitch.java index 2c43f220434..d074f6b0fe8 100644 --- a/ba/org.osate.ba/src/org/osate/ba/aadlba/util/AadlBaSwitch.java +++ b/ba/org.osate.ba/src/org/osate/ba/aadlba/util/AadlBaSwitch.java @@ -1851,6 +1851,9 @@ protected T doSwitch(int classifierID, EObject theEObject) { if (result == null) { result = caseDataHolder(iterativeVariableHolder); } + if (result == null) { + result = caseElementValues(iterativeVariableHolder); + } if (result == null) { result = caseElementHolder(iterativeVariableHolder); } diff --git a/ba/org.osate.xtext.aadl2.ba/src/org/osate/xtext/aadl2/ba/translation/DeclarativeToStrictTranslator.java b/ba/org.osate.xtext.aadl2.ba/src/org/osate/xtext/aadl2/ba/translation/DeclarativeToStrictTranslator.java index f41149df64f..71b9c97d79e 100644 --- a/ba/org.osate.xtext.aadl2.ba/src/org/osate/xtext/aadl2/ba/translation/DeclarativeToStrictTranslator.java +++ b/ba/org.osate.xtext.aadl2.ba/src/org/osate/xtext/aadl2/ba/translation/DeclarativeToStrictTranslator.java @@ -729,7 +729,11 @@ private org.osate.ba.aadlba.ForOrForAllStatement toForStatement(final ForStateme private ElementValues toElementValues(final org.osate.xtext.aadl2.ba.behaviorAnnex.ElementValues values) { if (values.getUpper() == null) { - return (ElementValues) toIntegerValue(values.getLower()); + // D.6 admits an integer range, an event data port, or an array data component reference here, and the + // shared D.7 integer value grammar accepts more than that: an integer literal, a property reference, + // and names that resolve to something no strict ElementValues can carry. Leave those out of the strict + // model, the way the rest of the translator does, and let BehaviorAnnexValidator report them. + return toIntegerValue(values.getLower()) instanceof ElementValues iterated ? iterated : null; } final var result = trace(FACTORY.createIntegerRange(), values); result.setLowerIntegerValue(toIntegerValue(values.getLower())); diff --git a/ba/org.osate.xtext.aadl2.ba/src/org/osate/xtext/aadl2/ba/validation/BehaviorAnnexValidator.java b/ba/org.osate.xtext.aadl2.ba/src/org/osate/xtext/aadl2/ba/validation/BehaviorAnnexValidator.java index a280ea224f5..bf55d67304b 100644 --- a/ba/org.osate.xtext.aadl2.ba/src/org/osate/xtext/aadl2/ba/validation/BehaviorAnnexValidator.java +++ b/ba/org.osate.xtext.aadl2.ba/src/org/osate/xtext/aadl2/ba/validation/BehaviorAnnexValidator.java @@ -54,6 +54,7 @@ import org.osate.aadl2.modelsupport.errorreporting.AnalysisErrorReporterManager; import org.osate.aadl2.parsesupport.ParseUtil; import org.osate.annexsupport.ParseResultHolder; +import org.osate.ba.aadlba.ForOrForAllStatement; import org.osate.ba.aadlba.PropertySetPropertyReference; import org.osate.ba.analyzers.AadlBaRulesCheckersDriver; import org.osate.ba.analyzers.AadlBaTypeChecker; @@ -90,6 +91,7 @@ public final class BehaviorAnnexValidator extends AbstractBehaviorAnnexValidator public static final String TIMEOUT_RESET_PORT = "org.osate.xtext.aadl2.ba.timeoutResetPort"; public static final String TIMEOUT_RESET_PORT_TIME = "org.osate.xtext.aadl2.ba.timeoutResetPortTime"; public static final String ITERATIVE_VARIABLE_TARGET = "org.osate.xtext.aadl2.ba.iterativeVariableTarget"; + public static final String ITERATED_VALUES = "org.osate.xtext.aadl2.ba.iteratedValues"; public static final String ARRAY_SIZE = "org.osate.xtext.aadl2.ba.arraySize"; public static final String PROPERTY_REFERENCE_VALUE = "org.osate.xtext.aadl2.ba.propertyReferenceValue"; public static final String MODE_REFINEMENT = "org.osate.xtext.aadl2.ba.modeRefinement"; @@ -125,6 +127,7 @@ public void checkBehaviorAnnex(final BehaviorAnnex source) { representable &= checkInternalConditionPorts(source, translation); representable &= checkTimeoutResetPorts(source, translation); representable &= checkIteratorTargets(source, translation); + representable &= checkIteratedValues(source, translation); // The strict model does carry a property reference that denotes no value, so this one is not a gate: the strict // checkers keep their model and whatever else they have to say about it. checkPropertyReferenceValues(translation); @@ -333,6 +336,36 @@ private boolean checkIteratorTargets(final BehaviorAnnex source, final Translati return accepted; } + /** + * The AS5506/3 Rev A D.6 element_values production admits an integer range, an event data port name, or an array + * data component reference, and a data component reference starts with a data subcomponent, a data access feature, + * a local variable, or a data access feature prototype. D.6 gives the iterator of a for or forall the name scope of + * the construct, where it acts as a local variable, so an enclosing iterator is one of those names as well. The + * iterated values share the D.7 integer value grammar with the rest of the annex, which also accepts an integer + * literal, a property reference, and names that denote none of the above, and the strict model has no element values + * to carry any of them: translation drops them, and the loop would otherwise reach the strict checkers iterating + * nothing at all. Report the values as written, since an integer range never fails here and everything else is one + * expression. + * + * @return {@code true} when every loop in the annex iterates values the strict model can carry + */ + private boolean checkIteratedValues(final BehaviorAnnex source, final TranslationResult translation) { + var accepted = true; + for (var contents = source.eAllContents(); contents.hasNext();) { + if (!(contents.next() instanceof ForStatement loop) || loop.getValues() == null + || !(translation.getStrict(loop) instanceof ForOrForAllStatement strict) + || strict.getIteratedValues() != null) { + continue; + } + error("'" + NodeModelUtils.getTokenText(NodeModelUtils.findActualNodeFor(loop.getValues())) + + "' cannot be iterated: a for or forall iterates an integer range, an event data port, a parameter," + + " or an array data component reference", loop.getValues(), null, + ValidationMessageAcceptor.INSIGNIFICANT_INDEX, ITERATED_VALUES); + accepted = false; + } + return accepted; + } + /** The target an action writes, which the assignment action and the dequeue action spell the same way. */ private static Reference writtenTarget(final EObject action) { if (action instanceof AssignmentAction assignment) {