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

Fix for naming collision when generic reactor is instantiated with different parameters #1864

Merged
merged 4 commits into from
Jun 25, 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
45 changes: 28 additions & 17 deletions core/src/main/java/org/lflang/generator/c/CGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,19 @@ private void generateReactorDefinitions() throws IOException {
// do not generate code for reactors that are not instantiated
}

private record TypeParameterizedReactorWithDecl(TypeParameterizedReactor tpr, ReactorDecl decl) {}
private record TypeParameterizedReactorWithDecl(TypeParameterizedReactor tpr, ReactorDecl decl) {
@Override
public boolean equals(Object obj) {
// This is equivalence modulo decl
return obj == this
|| obj instanceof TypeParameterizedReactorWithDecl tprd && tprd.tpr.equals(this.tpr);
}

@Override
public int hashCode() {
return tpr.hashCode();
}
}

/** Generate user-visible header files for all reactors instantiated. */
private void generateHeaders() throws IOException {
Expand All @@ -826,23 +838,22 @@ private void generateHeaders() throws IOException {
it ->
new TypeParameterizedReactorWithDecl(
new TypeParameterizedReactor(it, rr), it.getReactorClass()))
.collect(Collectors.toSet())
.distinct()
.forEach(
it -> {
ASTUtils.allPorts(it.tpr.reactor())
.forEach(
p ->
builder.pr(
CPortGenerator.generateAuxiliaryStruct(
it.tpr,
p,
getTarget(),
messageReporter,
types,
new CodeBuilder(),
true,
it.decl())));
});
it ->
ASTUtils.allPorts(it.tpr.reactor())
.forEach(
p ->
builder.pr(
CPortGenerator.generateAuxiliaryStruct(
it.tpr,
p,
getTarget(),
messageReporter,
types,
new CodeBuilder(),
true,
it.decl()))));
}
},
this::generateTopLevelPreambles);
Expand Down
7 changes: 4 additions & 3 deletions core/src/main/java/org/lflang/generator/c/CPortGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,15 @@ public static String generateAuxiliaryStruct(
code.unindent();
var name =
decl != null
? localPortName(decl, port.getName())
? localPortName(tpr, decl, port.getName())
: variableStructType(port, tpr, userFacing);
code.pr("} " + name + ";");
return code.toString();
}

public static String localPortName(ReactorDecl decl, String portName) {
return decl.getName().toLowerCase() + "_" + portName + "_t";
public static String localPortName(
TypeParameterizedReactor tpr, ReactorDecl decl, String portName) {
return decl.getName().toLowerCase() + tpr.argsString() + "_" + portName + "_t";
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,10 @@ String getType(boolean userFacing) {
var typeName =
container == null
? CGenerator.variableStructType(tv, r, userFacing)
: CPortGenerator.localPortName(container.getReactorClass(), getName());
: CPortGenerator.localPortName(
new TypeParameterizedReactor(container, r),
container.getReactorClass(),
getName());
var isMultiport =
ASTUtils.isMultiport(
ASTUtils.allPorts(r.reactor()).stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ public String getName() {
+ typeArgs.values().stream().map(ASTUtils::toOriginalText).collect(Collectors.joining("_"));
}

/** Return a string representation of the type args of this. */
public String argsString() {
return typeArgs.values().stream()
.map(ASTUtils::toOriginalText)
.collect(Collectors.joining("_"));
}

/** #define type names as concrete types. */
public void doDefines(CodeBuilder b) {
typeArgs.forEach(
Expand Down Expand Up @@ -156,17 +163,48 @@ public String uniqueName() {

@Override
public int hashCode() {
return reactor.hashCode() * 31 + typeArgs.hashCode();
return reactor.hashCode() * 31
+ typeArgs.entrySet().stream()
.mapToInt(it -> it.getKey().hashCode() ^ typeHash(it.getValue()))
.sum();
}

@Override
public boolean equals(Object obj) {
return obj instanceof TypeParameterizedReactor other
&& reactor.equals(other.reactor)
&& typeArgs.equals(other.typeArgs);
&& typeArgs.entrySet().stream()
.allMatch(it -> typeEquals(other.typeArgs.get(it.getKey()), it.getValue()));
}

public Reactor reactor() {
return reactor;
}

// We operate on the ECore model rather than an internal IR, so hashcode and equals are provided
// here instead of as methods.
private static int typeHash(Type t) {
var sum = t.getStars() == null ? 0 : t.getStars().stream().toList().hashCode();
sum = 31 * sum + (t.getCode() == null ? 0 : Objects.hashCode(t.getCode().getBody()));
sum = 31 * sum + Objects.hashCode(t.getId());
sum = 31 * sum + Objects.hashCode(t.getArraySpec());
sum = 2 * sum + (t.isTime() ? 1 : 0);
sum = 31 * sum + (t.getTypeArgs() == null ? 0 : t.getTypeArgs().stream().toList().hashCode());
return sum;
}

private static boolean typeEquals(Type t, Type tt) {
return t.getStars() == null
? tt.getStars() == null
: t.getStars().stream().toList().equals(tt.getStars().stream().toList())
&& t.getCode() == null
? tt.getCode() == null
: Objects.equals(t.getCode().getBody(), tt.getCode().getBody())
&& Objects.equals(t.getId(), tt.getId())
&& Objects.equals(t.getArraySpec(), tt.getArraySpec())
&& t.isTime() == tt.isTime()
&& t.getTypeArgs() == null
? tt.getTypeArgs() == null
: t.getTypeArgs().stream().toList().equals(tt.getTypeArgs().stream().toList());
}
}
21 changes: 21 additions & 0 deletions test/C/src/generics/MultipleInstantiations.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
target C

reactor R<T>(printf: string = "%s") {
input in: T

reaction(in) {=
printf("%s", "Received ");
printf(self->printf, in->value);
printf("%s", ".\n");
=}
}

main reactor {
r1 = new R<int>(printf="%d")
r2 = new R<int>(printf="%d")
r3 = new R<string>(printf="%s")

reaction(startup) -> r1.in, r2.in, r3.in {=
lf_set(r1.in, 1); lf_set(r2.in, 2); lf_set(r3.in, "test");
=}
}