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

[PoC] Generate IR definitions with annotation processor #11267

Draft
wants to merge 94 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
6e33120
Add parser-dsl and parser-processor projects
Akirathan Oct 6, 2024
69f9247
Add JModule
Akirathan Oct 6, 2024
0e4ceb0
Fix module declaration - no compiler module dependency
Akirathan Oct 7, 2024
9891eb7
Add annotation processor testing.
Akirathan Oct 7, 2024
a3bf203
Rewrite some IR elements to IRNode annotated interfaces
Akirathan Oct 9, 2024
08b0571
IRProcessor does some sanity checks
Akirathan Oct 9, 2024
51796ba
IRNode does not have name parameter
Akirathan Oct 9, 2024
3ea7a90
IRProcessor generates source file for record
Akirathan Oct 9, 2024
f6bf733
Add imports and overriden IR methods to generated record
Akirathan Oct 10, 2024
1870ef2
Add field processing
Akirathan Oct 10, 2024
826245d
IRProcessor generates classes, not records
Akirathan Oct 14, 2024
fc89b3d
IRProcessor generates overrides for user-defined parameterless methods
Akirathan Oct 14, 2024
1fc0497
Add Field.isExpression method
Akirathan Oct 17, 2024
d2bb8c3
sbt picks up Scala case classes generated by Java annotation processor
Akirathan Oct 18, 2024
68a070b
IRProcessor generates Scala classes.
Akirathan Oct 18, 2024
cf08781
Revert "sbt picks up Scala case classes generated by Java annotation …
Akirathan Oct 21, 2024
f930e53
Revert "IRProcessor generates Scala classes."
Akirathan Oct 21, 2024
298c883
IRProcessor handles primitive fields
Akirathan Oct 21, 2024
bc1e9c9
Remove unused methods
Akirathan Oct 21, 2024
ef05c64
IRProcessor fails fast
Akirathan Oct 21, 2024
aa0a5b6
IRProcessor can collect fields from super interfaces
Akirathan Oct 21, 2024
c6def78
Fix collecting fields from super interfaces.
Akirathan Oct 22, 2024
8cea491
Prepare IRNodeProcessor for processing multiple nested interfaces
Akirathan Oct 22, 2024
f5cb331
IRNodeProcessor handles multiple nested interfaces
Akirathan Oct 22, 2024
2393e18
runtime-parser-processor does not depend on runtime-parser
Akirathan Oct 22, 2024
cf1d079
Move parser processor tests to a separate project
Akirathan Oct 22, 2024
f63bda1
runtime-parser-processor does not depend on runtime-parser
Akirathan Oct 22, 2024
158a4f1
runtime-parser depends on runtime-parser-processor
Akirathan Oct 22, 2024
1289374
IRProcessor adds package
Akirathan Oct 25, 2024
73a9d31
ParserDependenciesTest ensures that IRNodeProcessor is on class path
Akirathan Oct 25, 2024
be4396c
runtime-parser-processor module provides annotation processor service
Akirathan Oct 25, 2024
3665232
Fix getSimpleTypeName in ReferenceField
Akirathan Oct 25, 2024
260d11d
No nested @IRNode annotation
Akirathan Oct 25, 2024
df95a4e
JModule extends IR
Akirathan Oct 25, 2024
7918fd2
IRNodeProcessor handles parametrized List child
Akirathan Oct 25, 2024
9ae8e3f
runtime-parser-processor-tests runs with enabled assertions
Akirathan Oct 25, 2024
cf264cf
Test IRNode with List of children
Akirathan Oct 25, 2024
0f21885
All IR interfaces use scala list, not java.util.List
Akirathan Oct 25, 2024
18b2d10
Do not process already overriden methods
Akirathan Oct 25, 2024
26239a3
Enable IRProcessor in runtime-parser
Akirathan Oct 29, 2024
9c3a580
Ensure that only fields from current interface and its super interfac…
Akirathan Oct 29, 2024
c6b1987
Implement passData, location and diagnostics methods
Akirathan Oct 30, 2024
399ca43
Override duplicate method
Akirathan Oct 30, 2024
7a97c61
Fix duplicate method generation
Akirathan Oct 30, 2024
8acfa30
Move duplicate method generation to a separate class
Akirathan Oct 31, 2024
7988276
Duplicate method duplicates also non-child fields
Akirathan Oct 31, 2024
c84ebb7
Simplify tests
Akirathan Oct 31, 2024
8a778f0
During runtime-parser-processor-test compilation, IRNodeProcessor runs
Akirathan Oct 31, 2024
86d1b0e
docs
Akirathan Oct 31, 2024
8aa64bd
Add some IR interfaces to the testing code
Akirathan Oct 31, 2024
e91f09e
test generated list children
Akirathan Oct 31, 2024
dd34e5d
DuplicateMethodGenerator duplicates also meta fields
Akirathan Nov 1, 2024
2a2b6d3
Builder allows setting meta fields
Akirathan Nov 1, 2024
7def7b1
Add tests for duplicated metadata
Akirathan Nov 1, 2024
66aba89
Add runtime-parser-processor-tests to the enso aggregate
Akirathan Nov 1, 2024
f6b75ed
Add IRCopyMethod annotation
Akirathan Nov 1, 2024
9bccf1c
Iteration over super interfaces is handled only via SuperInterfaceVis…
Akirathan Nov 1, 2024
f027307
Simplify super interface iteration
Akirathan Nov 1, 2024
687d0e1
Builder has a copy constructor
Akirathan Nov 1, 2024
bd9b7e4
Implement CopyMethodGenerator
Akirathan Nov 1, 2024
98f4743
Fix all tests
Akirathan Nov 1, 2024
92d9004
Test copy method generation
Akirathan Nov 1, 2024
69728be
Do not try to override static or default methods
Akirathan Nov 1, 2024
7709597
docs
Akirathan Nov 1, 2024
7d012fe
Allow multiple copy methods
Akirathan Nov 4, 2024
cb06f03
CopyMethodGenerator generates copy method with correct order of param…
Akirathan Nov 4, 2024
c08c035
Test valid parameter names for copy method
Akirathan Nov 4, 2024
43ed045
More tests
Akirathan Nov 4, 2024
69258cf
Generate mapExpressions method
Akirathan Nov 5, 2024
f888cee
Add mock definition of JCallArgument
Akirathan Nov 5, 2024
57c5bff
Use qualified type names in mapExpressions method
Akirathan Nov 6, 2024
be2d374
Implement JBlank.create factory method
Akirathan Nov 6, 2024
b828603
JExpression overrides duplicate and mapExpressions
Akirathan Nov 6, 2024
814d031
Merge branch 'develop' into wip/akirathan/java-ir
Akirathan Dec 2, 2024
419dca0
Remove unused import
Akirathan Dec 2, 2024
c8cbc98
Move all the sample `JXyz` interfaces to tests
Akirathan Dec 2, 2024
fd6ad5b
Generate identifiedLocation method override
Akirathan Dec 2, 2024
e066374
Fix compilation after the move
Akirathan Dec 2, 2024
68389f4
Move sample `JXyz` interfaces to a dedicated test package
Akirathan Dec 2, 2024
1b7a120
Add sample definition of JEmpty
Akirathan Dec 2, 2024
aa345ed
Replace Scala Empty with Java Empty annotated with @IRNode
Akirathan Dec 2, 2024
fa26144
Refactor Utils.findMethod
Akirathan Dec 2, 2024
c4a669e
Fix test after merge of develop
Akirathan Dec 2, 2024
e4222f0
Fix TestIRProcessorInline after `J*` classes were moved to test dir
Akirathan Dec 2, 2024
4d5b214
Implement SetLocationMethodGenerator
Akirathan Dec 2, 2024
5ef1bde
fmt
Akirathan Dec 2, 2024
9b48ed4
Ensure only a single IR interface is extended
Akirathan Dec 2, 2024
5ac716a
Fix compilation after Refactoring Empty to an interface with @IRNode
Akirathan Dec 2, 2024
4a0ea14
Empty extends Expression
Akirathan Dec 2, 2024
9b5f1b2
Override equals and hashCode
Akirathan Dec 2, 2024
e294877
Explicitly list all the processor classes in javacOptions
Akirathan Dec 2, 2024
00ad5f6
Refactor field class to field package
Akirathan Dec 3, 2024
e6c7609
Refactor method generation classes to methodgen package
Akirathan Dec 3, 2024
c8b7e34
Introduce utils package
Akirathan Dec 3, 2024
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
63 changes: 62 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,9 @@ lazy val enso = (project in file("."))
`runtime-compiler`,
`runtime-integration-tests`,
`runtime-parser`,
`runtime-parser-dsl`,
`runtime-parser-processor`,
`runtime-parser-processor-tests`,
`runtime-language-arrow`,
`runtime-language-epb`,
`runtime-instrument-common`,
Expand Down Expand Up @@ -3213,14 +3216,72 @@ lazy val `runtime-parser` =
Compile / moduleDependencies ++= Seq(
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion
),
// Java compiler is not able to correctly find all the annotation processor, because
// one of the is on module-path. To overcome this, we explicitly list all of them here.
Compile / javacOptions ++= {
val processorClasses = Seq(
"org.enso.runtime.parser.processor.IRProcessor",
"org.enso.persist.impl.PersistableProcessor",
"org.netbeans.modules.openide.util.ServiceProviderProcessor",
"org.netbeans.modules.openide.util.NamedServiceProcessor"
).mkString(",")
Seq(
"-processor",
processorClasses
)
},
Compile / internalModuleDependencies := Seq(
(`syntax-rust-definition` / Compile / exportedModule).value,
(`persistance` / Compile / exportedModule).value
(`persistance` / Compile / exportedModule).value,
(`runtime-parser-dsl` / Compile / exportedModule).value,
(`runtime-parser-processor` / Compile / exportedModule).value
)
)
.dependsOn(`syntax-rust-definition`)
.dependsOn(`persistance`)
.dependsOn(`persistance-dsl` % "provided")
.dependsOn(`runtime-parser-dsl`)
.dependsOn(`runtime-parser-processor`)

lazy val `runtime-parser-dsl` =
(project in file("engine/runtime-parser-dsl"))
.enablePlugins(JPMSPlugin)
.settings(
frgaalJavaCompilerSetting
)

lazy val `runtime-parser-processor-tests` =
(project in file("engine/runtime-parser-processor-tests"))
.settings(
inConfig(Compile)(truffleRunOptionsSettings),
frgaalJavaCompilerSetting,
commands += WithDebugCommand.withDebug,
annotationProcSetting,
Compile / javacOptions ++= Seq(
"-processor",
"org.enso.runtime.parser.processor.IRProcessor"
),
Test / fork := true,
libraryDependencies ++= Seq(
"junit" % "junit" % junitVersion % Test,
"com.github.sbt" % "junit-interface" % junitIfVersion % Test,
"org.hamcrest" % "hamcrest-all" % hamcrestVersion % Test,
"com.google.testing.compile" % "compile-testing" % "0.21.0" % Test
)
)
.dependsOn(`runtime-parser-processor`)
.dependsOn(`runtime-parser`)

lazy val `runtime-parser-processor` =
(project in file("engine/runtime-parser-processor"))
.enablePlugins(JPMSPlugin)
.settings(
frgaalJavaCompilerSetting,
Compile / internalModuleDependencies := Seq(
(`runtime-parser-dsl` / Compile / exportedModule).value
)
)
.dependsOn(`runtime-parser-dsl`)

lazy val `runtime-compiler` =
(project in file("engine/runtime-compiler"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,9 @@ case object LambdaConsolidate extends IRPass {
}

val shadower: IR =
mShadower.getOrElse(Empty(spec.identifiedLocation))
mShadower.getOrElse(
Empty.createFromLocation(spec.identifiedLocation)
)

spec.getDiagnostics.add(
warnings.Shadowed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ case object SuspendedArguments extends IRPass {
} else if (args.length > signatureSegments.length) {
val additionalSegments = signatureSegments ::: List.fill(
args.length - signatureSegments.length
)(Empty(identifiedLocation = null))
)(Empty.createEmpty())

args.zip(additionalSegments)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class DiagnosticStorageTest extends CompilerTest {
def mkDiagnostic(name: String): Diagnostic = {
warnings.Shadowed.FunctionParam(
name,
Empty(identifiedLocation = null),
Empty.createEmpty(),
identifiedLocation = null
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ class OperatorToFunctionTest extends MiniPassTest {
// === The Tests ============================================================
val opName =
Name.Literal("=:=", isMethod = true, null)
val left = Empty(null)
val right = Empty(null)
val rightArg = CallArgument.Specified(None, Empty(null), false, null)
val left = Empty.createEmpty()
val right = Empty.createEmpty()
val rightArg = CallArgument.Specified(None, Empty.createEmpty(), false, null)

val (operator, operatorFn) = genOprAndFn(opName, left, right)

Expand All @@ -96,11 +96,11 @@ class OperatorToFunctionTest extends MiniPassTest {
"Operators" should {
val opName =
Name.Literal("=:=", isMethod = true, identifiedLocation = null)
val left = Empty(identifiedLocation = null)
val right = Empty(identifiedLocation = null)
val left = Empty.createEmpty()
val right = Empty.createEmpty()
val rightArg = CallArgument.Specified(
None,
Empty(identifiedLocation = null),
Empty.createEmpty(),
false,
identifiedLocation = null
)
Expand Down
3 changes: 3 additions & 0 deletions engine/runtime-parser-dsl/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module org.enso.runtime.parser.dsl {
exports org.enso.runtime.parser.dsl;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.enso.runtime.parser.dsl;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Abstract methods annotated with this annotation will return the <emph>child</emph> of the current
* IR element (the current IR element is the interface annotated with {@link IRNode} that encloses
* this method). Children of IR elements form a tree. A child will be part of the methods traversing
* the tree, like {@code mapExpression} and {@code children}. The method must have no parameters and
* return a subtype of {@code org.enso.compiler.ir.IR}.
*/
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface IRChild {
/** If true, the child will always be non-null. Otherwise, it can be null. */
boolean required() default true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.enso.runtime.parser.dsl;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* An abstract method annotated with this annotation will have generated implementation in the
* generated class. The types and names of the parameters must correspond to any field (abstract
* parameterless methods in the interface hierarchy), or to any of the following:
*
* <ul>
* <li>{@code MetadataStorage passData}
* <li>{@code IdentifiedLocation location}
* <li>{@code UUID id}
* </ul>
*
* The order of the parameters is not important. Number of parameters must not exceed total number
* of fields and meta fields.
*
* <p>There can be more methods annotated with this annotation. All of them must follow the contract
* describe in this docs. For each of those methods, an implementation will be generated.
*
* <p>The name of the annotated method can be arbitrary, but the convention is to use the {@code
* copy} name.
*/
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface IRCopyMethod {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.enso.runtime.parser.dsl;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* An interface annotated with this annotation will be processed by the IR processor. The processor
* will generate a class that extends this interface. The generated class will have the same package
* as this interface, and its name will have the "Gen" suffix. The interface must be a subtype of
* {@code org.enso.compiler.ir.IR}. The interface can contain {@link IRChild} and {@link
* IRCopyMethod} annotated methods.
*
* <p>For every abstract parameterless method of the interface, there will be a field in the
* generated class.
*
* <p>The interface can contain arbitrary number of nested interfaces. In such case, the processor
* will generate nested static classes for all these nested interfaces.
*/
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface IRNode {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.enso.runtime.parser.processor.test.gen.ir;

import org.enso.compiler.core.IR;
import org.enso.runtime.parser.dsl.IRChild;
import org.enso.runtime.parser.dsl.IRCopyMethod;
import org.enso.runtime.parser.dsl.IRNode;

@IRNode
public interface CopyNameTestIR extends IR {
@IRChild
NameTestIR name();

/**
* Should generate implementation that will produce the exact same copy with a different name
* field.
*/
@IRCopyMethod
CopyNameTestIR copy(NameTestIR name);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.enso.runtime.parser.processor.test.gen.ir;

import org.enso.compiler.core.IR;
import org.enso.runtime.parser.dsl.IRChild;
import org.enso.runtime.parser.dsl.IRNode;
import scala.collection.immutable.List;

@IRNode
public interface ListTestIR extends IR {
@IRChild
List<NameTestIR> names();

@IRChild(required = false)
NameTestIR originalName();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.enso.runtime.parser.processor.test.gen.ir;

import org.enso.compiler.core.IR;
import org.enso.runtime.parser.dsl.IRNode;

@IRNode
public interface NameTestIR extends IR {

String name();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.enso.runtime.parser.processor.test.gen.ir;

import org.enso.compiler.core.IR;
import org.enso.runtime.parser.dsl.IRChild;
import org.enso.runtime.parser.dsl.IRNode;

@IRNode
public interface OptNameTestIR extends IR {
@IRChild(required = false)
OptNameTestIR originalName();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.enso.runtime.parser.processor.test.gen.ir.core;

import org.enso.compiler.core.IR;
import org.enso.compiler.core.ir.Expression;
import org.enso.compiler.core.ir.Name;
import org.enso.runtime.parser.dsl.IRChild;
import org.enso.runtime.parser.dsl.IRNode;

@IRNode
public interface JCallArgument extends IR {
@IRChild(required = false)
Name name();

@IRChild
Expression value();

interface JSpecified extends JCallArgument {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.enso.runtime.parser.processor.test.gen.ir.core;

import org.enso.compiler.core.IR;
import org.enso.runtime.parser.dsl.IRChild;
import org.enso.runtime.parser.dsl.IRNode;

@IRNode
public interface JDefinitionArgument extends IR {
@IRChild
JName name();

@IRChild(required = false)
JExpression ascribedType();

@IRChild(required = false)
JExpression defaultValue();

boolean suspended();

interface JSpecified extends JDefinitionArgument {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.enso.runtime.parser.processor.test.gen.ir.core;

import java.util.UUID;
import org.enso.compiler.core.IR;
import org.enso.compiler.core.ir.DiagnosticStorage;
import org.enso.compiler.core.ir.IdentifiedLocation;
import org.enso.compiler.core.ir.MetadataStorage;
import org.enso.runtime.parser.dsl.IRCopyMethod;
import org.enso.runtime.parser.dsl.IRNode;

@IRNode
public interface JEmpty extends IR {
static JEmptyGen.Builder builder() {
return JEmptyGen.builder();
}

static JEmpty createEmpty() {
return JEmptyGen.builder().build();
}

static JEmpty createFromLocation(IdentifiedLocation location) {
return JEmptyGen.builder().location(location).build();
}

static JEmpty createFromLocationAndPassData(
IdentifiedLocation location, MetadataStorage passData) {
return JEmptyGen.builder().location(location).passData(passData).build();
}

@IRCopyMethod
JEmpty copy(
IdentifiedLocation location,
MetadataStorage passData,
DiagnosticStorage diagnostics,
UUID id);
}
Loading
Loading