diff --git a/core/src/main/java/org/lflang/generator/c/CGenerator.java b/core/src/main/java/org/lflang/generator/c/CGenerator.java index dd5e6feb97..1099b4e7ca 100644 --- a/core/src/main/java/org/lflang/generator/c/CGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CGenerator.java @@ -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 { @@ -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); diff --git a/core/src/main/java/org/lflang/generator/c/CPortGenerator.java b/core/src/main/java/org/lflang/generator/c/CPortGenerator.java index 6aafeaef5a..eafec3dce0 100644 --- a/core/src/main/java/org/lflang/generator/c/CPortGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CPortGenerator.java @@ -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"; } /** diff --git a/core/src/main/java/org/lflang/generator/c/CReactorHeaderFileGenerator.java b/core/src/main/java/org/lflang/generator/c/CReactorHeaderFileGenerator.java index 02e5d41f02..5deaa5fb7b 100644 --- a/core/src/main/java/org/lflang/generator/c/CReactorHeaderFileGenerator.java +++ b/core/src/main/java/org/lflang/generator/c/CReactorHeaderFileGenerator.java @@ -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() diff --git a/core/src/main/java/org/lflang/generator/c/TypeParameterizedReactor.java b/core/src/main/java/org/lflang/generator/c/TypeParameterizedReactor.java index f514141241..1c5ae863c0 100644 --- a/core/src/main/java/org/lflang/generator/c/TypeParameterizedReactor.java +++ b/core/src/main/java/org/lflang/generator/c/TypeParameterizedReactor.java @@ -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( @@ -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()); + } } diff --git a/test/C/src/generics/MultipleInstantiations.lf b/test/C/src/generics/MultipleInstantiations.lf new file mode 100644 index 0000000000..5f9e7abc97 --- /dev/null +++ b/test/C/src/generics/MultipleInstantiations.lf @@ -0,0 +1,21 @@ +target C + +reactor R(printf: string = "%s") { + input in: T + + reaction(in) {= + printf("%s", "Received "); + printf(self->printf, in->value); + printf("%s", ".\n"); + =} +} + +main reactor { + r1 = new R(printf="%d") + r2 = new R(printf="%d") + r3 = new R(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"); + =} +}