Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validator rules to check if target supports federation or inheritance #1726

Merged
merged 8 commits into from
May 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -137,15 +137,15 @@ public void disallowReactorCalledPreamble() throws Exception {

Model model_error_1 = parser.parse("""
target Cpp;
main reactor Preamble {
reactor Preamble {
}
""");

Model model_error_2 = parser.parse("""
target Cpp;
reactor Preamble {
}
main reactor Main {
main reactor {
}
""");

Expand Down Expand Up @@ -281,7 +281,7 @@ public void disallowUnderscoreStates() throws Exception {
public void disallowUnderscoreReactorDef() throws Exception {
String testCase = """
target TypeScript;
main reactor __Foo {
reactor __Foo {
}
""";
validator.assertError(parseWithoutError(testCase), LfPackage.eINSTANCE.getReactor(), null,
Expand Down Expand Up @@ -812,6 +812,41 @@ public void testPreambleVisibility() throws Exception {
}
}

@Test
public void testInheritanceSupport() throws Exception {
for (Target target : Target.values()) {
Model model = parseWithoutError("""
target %s
reactor A{}
reactor B extends A{}
""".formatted(target));

if (target.supportsInheritance()) {
validator.assertNoIssues(model);
} else {
validator.assertError(model, LfPackage.eINSTANCE.getReactor(), null,
"The " + target.getDisplayName() + " target does not support reactor inheritance.");
}
}
}

@Test
public void testFederationSupport() throws Exception {
for (Target target : Target.values()) {
Model model = parseWithoutError("""
target %s
federated reactor {}
""".formatted(target));

if (target.supportsFederated()) {
validator.assertNoIssues(model);
} else {
validator.assertError(model, LfPackage.eINSTANCE.getReactor(), null,
"The " + target.getDisplayName() + " target does not support federated execution.");
}
}
}


/**
* Tests for state and parameter declarations, including native lists.
Expand Down
20 changes: 20 additions & 0 deletions org.lflang/src/org/lflang/Target.java
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,26 @@ public boolean isReservedIdent(String ident) {
return this.keywords.contains(ident);
}

/**
* Return true if the target supports federated execution.
*/
public boolean supportsFederated() {
return switch (this) {
case C, CCPP, Python, TS -> true;
default -> false;
};
}

/**
* Return true if the target supports reactor inheritance (extends keyword).
*/
public boolean supportsInheritance() {
return switch (this) {
case C, CCPP, Python -> true;
default -> false;
};
}

/**
* Return true if the target supports multiports and banks
* of reactors.
Expand Down
97 changes: 60 additions & 37 deletions org.lflang/src/org/lflang/validation/LFValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -852,69 +852,82 @@ public void checkReaction(Reaction reaction) {
// FIXME: improve error message.
}

@Check(CheckType.FAST)
public void checkReactor(Reactor reactor) throws IOException {
lhstrh marked this conversation as resolved.
Show resolved Hide resolved
Set<Reactor> superClasses = ASTUtils.superClasses(reactor);
if (superClasses == null) {
public void checkReactorName(String name) throws IOException {
// Check for illegal names.
checkName(name, Literals.REACTOR_DECL__NAME);

// C++ reactors may not be called 'preamble'
if (this.target == Target.CPP && name.equalsIgnoreCase("preamble")) {
error(
"Problem with superclasses: Either they form a cycle or are not defined",
Literals.REACTOR_DECL__NAME
"Reactor cannot be named '" + name + "'",
Literals.REACTOR_DECL__NAME
);
// Continue checks, but without any superclasses.
superClasses = new LinkedHashSet<>();
}
String name = FileUtil.nameWithoutExtension(reactor.eResource());
if (reactor.getName() == null) {
if (!reactor.isFederated() && !reactor.isMain()) {
error(
"Reactor must be named.",
Literals.REACTOR_DECL__NAME
);
// Prevent NPE in tests below.
return;
}
}
TreeIterator<EObject> iter = reactor.eResource().getAllContents();
}

@Check(CheckType.FAST)
public void checkReactor(Reactor reactor) throws IOException {
String fileName = FileUtil.nameWithoutExtension(reactor.eResource());

if (reactor.isFederated() || reactor.isMain()) {
if(reactor.getName() != null && !reactor.getName().equals(name)) {
// Make sure that if the name is given, it matches the expected name.
error(
"Name of main reactor must match the file name (or be omitted).",
Literals.REACTOR_DECL__NAME
);
}
// Do not allow multiple main/federated reactors.
TreeIterator<EObject> iter = reactor.eResource().getAllContents();
int nMain = countMainOrFederated(iter);
if (nMain > 1) {
EAttribute attribute = Literals.REACTOR__MAIN;
if (reactor.isFederated()) {
attribute = Literals.REACTOR__FEDERATED;
attribute = Literals.REACTOR__FEDERATED;
}
error(
"Multiple definitions of main or federated reactor.",
attribute
);
}

if(reactor.getName() != null && !reactor.getName().equals(fileName)) {
// Make sure that if the name is given, it matches the expected name.
error(
"Name of main reactor must match the file name (or be omitted).",
Literals.REACTOR_DECL__NAME
);
}

// check the reactor name indicated by the file name
// Skip this check if the file is named __synthetic0. This Name is used during testing,
// and we would get an unexpected error due to the '__' prefix otherwise.
if (!fileName.equals("__synthetic0")) {
checkReactorName(fileName);
}
} else {
// Not federated or main.
int nMain = countMainOrFederated(iter);
if (nMain > 0 && reactor.getName().equals(name)) {
if (reactor.getName() == null) {
error(
"Name conflict with main reactor.",
"Reactor must be named.",
Literals.REACTOR_DECL__NAME
);
} else {
checkReactorName(reactor.getName());

TreeIterator<EObject> iter = reactor.eResource().getAllContents();
int nMain = countMainOrFederated(iter);
if (nMain > 0 && reactor.getName().equals(fileName)) {
error(
"Name conflict with main reactor.",
Literals.REACTOR_DECL__NAME
);
}
}
}

// Check for illegal names.
checkName(reactor.getName(), Literals.REACTOR_DECL__NAME);

// C++ reactors may not be called 'preamble'
if (this.target == Target.CPP && reactor.getName().equalsIgnoreCase("preamble")) {
Set<Reactor> superClasses = ASTUtils.superClasses(reactor);
if (superClasses == null) {
error(
"Reactor cannot be named '" + reactor.getName() + "'",
Literals.REACTOR_DECL__NAME
"Problem with superclasses: Either they form a cycle or are not defined",
Literals.REACTOR_DECL__NAME
);
// Continue checks, but without any superclasses.
superClasses = new LinkedHashSet<>();
}

if (reactor.getHost() != null) {
Expand All @@ -933,6 +946,11 @@ public void checkReactor(Reactor reactor) throws IOException {
variables.addAll(reactor.getActions());
variables.addAll(reactor.getTimers());

if (!reactor.getSuperClasses().isEmpty() && !target.supportsInheritance()) {
error("The " + target.getDisplayName() + " target does not support reactor inheritance.",
Literals.REACTOR__SUPER_CLASSES);
}

// Perform checks on super classes.
for (Reactor superClass : superClasses) {
HashSet<Variable> conflicts = new HashSet<>();
Expand Down Expand Up @@ -969,6 +987,11 @@ public void checkReactor(Reactor reactor) throws IOException {
}

if (reactor.isFederated()) {
if (!target.supportsFederated()) {
error("The " + target.getDisplayName() + " target does not support federated execution.",
Literals.REACTOR__FEDERATED);
}

FedValidator.validateFederatedReactor(reactor, this.errorReporter);
}
}
Expand Down