Skip to content

Commit 4e6837f

Browse files
committed
Convert MethodDefinitions to Java
1 parent 557f356 commit 4e6837f

File tree

17 files changed

+445
-324
lines changed

17 files changed

+445
-324
lines changed

engine/runtime-compiler/src/main/java/org/enso/compiler/MetadataInteropHelpers.java

+7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.enso.compiler;
22

33
import org.enso.compiler.core.IR;
4+
import org.enso.compiler.core.ir.MetadataStorage.MetadataPair;
45
import org.enso.compiler.core.ir.ProcessingPass;
56
import org.enso.compiler.pass.IRPass;
67
import scala.Option;
@@ -39,5 +40,11 @@ public static <T> T getMetadata(IR ir, IRPass pass, Class<T> expectedType) {
3940
return metadataOrNull;
4041
}
4142

43+
public static <T extends ProcessingPass> void updateMetadata(
44+
IR ir, T pass, ProcessingPass.Metadata metadata) {
45+
var metaPair = new MetadataPair<>(pass, metadata);
46+
ir.passData().update(metaPair);
47+
}
48+
4249
private MetadataInteropHelpers() {}
4350
}

engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/PassPersistance.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import org.enso.compiler.pass.resolve.IgnoredBindings;
1717
import org.enso.compiler.pass.resolve.IgnoredBindings$;
1818
import org.enso.compiler.pass.resolve.MethodCalls$;
19-
import org.enso.compiler.pass.resolve.MethodDefinitions$;
19+
import org.enso.compiler.pass.resolve.MethodDefinitions;
2020
import org.enso.compiler.pass.resolve.ModuleAnnotations;
2121
import org.enso.compiler.pass.resolve.ModuleAnnotations$;
2222
import org.enso.compiler.pass.resolve.Patterns$;
@@ -54,7 +54,7 @@
5454
@Persistable(clazz = ModuleAnnotations$.class, id = 1212)
5555
@Persistable(clazz = GatherDiagnostics$.class, id = 1213)
5656
@Persistable(clazz = MethodCalls$.class, id = 1214)
57-
@Persistable(clazz = MethodDefinitions$.class, id = 1215)
57+
@Persistable(clazz = MethodDefinitions.class, id = 1215)
5858
@Persistable(clazz = GenericAnnotations$.class, id = 1216)
5959
@Persistable(clazz = ExpressionAnnotations$.class, id = 1217)
6060
@Persistable(clazz = FullyQualifiedNames$.class, id = 1218)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
package org.enso.compiler.pass.resolve;
2+
3+
import java.util.ArrayList;
4+
import org.enso.compiler.MetadataInteropHelpers;
5+
import org.enso.compiler.context.InlineContext;
6+
import org.enso.compiler.context.ModuleContext;
7+
import org.enso.compiler.core.CompilerError;
8+
import org.enso.compiler.core.IR;
9+
import org.enso.compiler.core.ir.DefinitionArgument;
10+
import org.enso.compiler.core.ir.Expression;
11+
import org.enso.compiler.core.ir.Function;
12+
import org.enso.compiler.core.ir.MetadataStorage;
13+
import org.enso.compiler.core.ir.Module;
14+
import org.enso.compiler.core.ir.Name;
15+
import org.enso.compiler.core.ir.expression.errors.Conversion;
16+
import org.enso.compiler.core.ir.expression.errors.Conversion.UnsupportedSourceType$;
17+
import org.enso.compiler.core.ir.module.scope.Definition;
18+
import org.enso.compiler.core.ir.module.scope.definition.Method;
19+
import org.enso.compiler.data.BindingsMap;
20+
import org.enso.compiler.data.BindingsMap.Resolution;
21+
import org.enso.compiler.data.BindingsMap.ResolvedConstructor;
22+
import org.enso.compiler.data.BindingsMap.ResolvedConversionMethod;
23+
import org.enso.compiler.data.BindingsMap.ResolvedExtensionMethod;
24+
import org.enso.compiler.data.BindingsMap.ResolvedModule;
25+
import org.enso.compiler.data.BindingsMap.ResolvedModuleMethod;
26+
import org.enso.compiler.data.BindingsMap.ResolvedPolyglotField;
27+
import org.enso.compiler.data.BindingsMap.ResolvedPolyglotSymbol;
28+
import org.enso.compiler.data.BindingsMap.ResolvedType;
29+
import org.enso.compiler.data.BindingsMap.Type;
30+
import org.enso.compiler.pass.IRPass;
31+
import org.enso.compiler.pass.IRProcessingPass;
32+
import org.enso.compiler.pass.analyse.BindingAnalysis$;
33+
import org.enso.compiler.pass.desugar.ComplexType$;
34+
import org.enso.compiler.pass.desugar.FunctionBinding$;
35+
import org.enso.compiler.pass.desugar.GenerateMethodBodies$;
36+
import scala.Option;
37+
import scala.collection.immutable.List;
38+
import scala.collection.immutable.Seq;
39+
import scala.jdk.javaapi.CollectionConverters;
40+
41+
/**
42+
* Resolves the correct {@code self} argument type for method definitions and stores the resolution
43+
* in the method's metadata.
44+
*
45+
* <p>Metadata type is {@link BindingsMap.Resolution}
46+
*/
47+
public class MethodDefinitions implements IRPass {
48+
public static final MethodDefinitions INSTANCE = new MethodDefinitions();
49+
50+
@Override
51+
public Seq<IRProcessingPass> precursorPasses() {
52+
java.util.List<IRProcessingPass> passes =
53+
java.util.List.of(
54+
ComplexType$.MODULE$,
55+
FunctionBinding$.MODULE$,
56+
GenerateMethodBodies$.MODULE$,
57+
BindingAnalysis$.MODULE$);
58+
return CollectionConverters.asScala(passes).toList();
59+
}
60+
61+
@Override
62+
@SuppressWarnings("unchecked")
63+
public Seq<IRProcessingPass> invalidatedPasses() {
64+
Object obj = scala.collection.immutable.Nil$.MODULE$;
65+
return (scala.collection.immutable.List<IRProcessingPass>) obj;
66+
}
67+
68+
@Override
69+
public Module runModule(Module ir, ModuleContext moduleContext) {
70+
BindingsMap availableSymbolsMap =
71+
MetadataInteropHelpers.getMetadata(ir, BindingAnalysis$.MODULE$, BindingsMap.class);
72+
var newDefs =
73+
ir.bindings()
74+
.map(
75+
def -> {
76+
if (def instanceof Method method) {
77+
var methodRef = method.methodReference();
78+
Option<Name> resolvedTypeRef =
79+
methodRef
80+
.typePointer()
81+
.map(tp -> resolveType(tp, availableSymbolsMap))
82+
.orElse(null);
83+
var resolvedMethodRef = methodRef.copyWithTypePointer(resolvedTypeRef);
84+
85+
return switch (method) {
86+
case Method.Explicit explicitMethod -> {
87+
var isStatic = computeIsStatic(explicitMethod.body());
88+
var resolvedMethod =
89+
explicitMethod.copy(
90+
resolvedMethodRef,
91+
explicitMethod.body(),
92+
isStatic,
93+
explicitMethod.isPrivate(),
94+
explicitMethod.isStaticWrapperForInstanceMethod(),
95+
explicitMethod.location(),
96+
explicitMethod.passData(),
97+
explicitMethod.diagnostics(),
98+
explicitMethod.id());
99+
yield resolvedMethod;
100+
}
101+
case Method.Conversion conversionMethod -> {
102+
var sourceTypeExpr = conversionMethod.sourceTypeName();
103+
Name resolvedName =
104+
switch (sourceTypeExpr) {
105+
case Name name -> resolveType(name, availableSymbolsMap);
106+
default -> new Conversion(
107+
sourceTypeExpr,
108+
UnsupportedSourceType$.MODULE$,
109+
MetadataStorage.EMPTY);
110+
};
111+
var resolvedMethod =
112+
conversionMethod.copy(
113+
resolvedMethodRef,
114+
resolvedName,
115+
conversionMethod.body(),
116+
conversionMethod.location(),
117+
conversionMethod.passData(),
118+
conversionMethod.diagnostics(),
119+
conversionMethod.id());
120+
yield resolvedMethod;
121+
}
122+
default -> throw new CompilerError(
123+
"Unexpected method type in MethodDefinitions pass.");
124+
};
125+
} else {
126+
return def;
127+
}
128+
});
129+
130+
java.util.List<Definition> withStaticAliases = new ArrayList<>();
131+
for (var def : CollectionConverters.asJava(newDefs)) {
132+
withStaticAliases.add(def);
133+
if (def instanceof Method.Explicit method && !method.isStatic()) {
134+
var staticAlias = generateStaticAliasMethod(method);
135+
if (staticAlias != null) {
136+
withStaticAliases.add(staticAlias);
137+
}
138+
}
139+
}
140+
141+
return ir.copyWithBindings(CollectionConverters.asScala(withStaticAliases).toList());
142+
}
143+
144+
/**
145+
* Returns null if there is no suitable static alias method that can be generated for the given
146+
* {@code method}.
147+
*
148+
* @param method Non-static method from which a static alias method is generated.
149+
* @return Static alias method for the given {@code method} or null.
150+
*/
151+
private Method.Explicit generateStaticAliasMethod(Method.Explicit method) {
152+
assert !method.isStatic();
153+
var typePointer = method.methodReference().typePointer();
154+
if (typePointer.isEmpty()) {
155+
return null;
156+
}
157+
var resolution =
158+
MetadataInteropHelpers.getMetadataOrNull(typePointer.get(), this, Resolution.class);
159+
if (resolution == null) {
160+
return null;
161+
}
162+
if (resolution.target() instanceof ResolvedType resType
163+
&& canGenerateStaticWrappers(resType.tp())) {
164+
assert method.body() instanceof Function.Lambda;
165+
var dup = method.duplicate(true, true, true, false);
166+
// This is the self argument that will receive the `SelfType.type` value upon dispatch, it is
167+
// added to avoid modifying the dispatch mechanism.
168+
var syntheticModuleSelfArg =
169+
new DefinitionArgument.Specified(
170+
new Name.Self(null, true, MetadataStorage.EMPTY),
171+
Option.empty(),
172+
Option.empty(),
173+
false,
174+
null,
175+
MetadataStorage.EMPTY);
176+
var newBody =
177+
new Function.Lambda(
178+
// This is the synthetic Self argument that gets the static module
179+
list(syntheticModuleSelfArg),
180+
// Here we add the type ascription ensuring that the 'proper' self argument only
181+
// accepts _instances_ of the type (or triggers conversions)
182+
addTypeAscriptionToSelfArgument(dup.body()),
183+
null,
184+
true,
185+
MetadataStorage.EMPTY);
186+
// The actual `self` argument that is referenced inside of method body is the second one in
187+
// the lambda.
188+
// This is the argument that will hold the actual instance of the object we are calling on,
189+
// e.g. `My_Type.method instance`.
190+
// We add a type check to it to ensure only `instance` of `My_Type` can be passed to it.
191+
var staticMethod =
192+
dup.copy(
193+
method.methodReference(),
194+
newBody,
195+
true,
196+
method.isPrivate(),
197+
true,
198+
method.location(),
199+
method.passData(),
200+
method.diagnostics(),
201+
method.id());
202+
return staticMethod;
203+
}
204+
return null;
205+
}
206+
207+
private static Expression addTypeAscriptionToSelfArgument(Expression methodBody) {
208+
if (methodBody instanceof Function.Lambda lambda) {
209+
if (lambda.arguments().isEmpty()) {
210+
throw new CompilerError(
211+
"MethodDefinitions pass: expected at least one argument (self) in the method, but got"
212+
+ " none.");
213+
}
214+
var firstArg = lambda.arguments().head();
215+
if (firstArg instanceof DefinitionArgument.Specified selfArg
216+
&& selfArg.name() instanceof Name.Self) {
217+
var selfType = new Name.SelfType(selfArg.identifiedLocation(), MetadataStorage.EMPTY);
218+
var newSelfArg = selfArg.copyWithAscribedType(selfType);
219+
return lambdaWithNewSelfArg(lambda, newSelfArg);
220+
} else {
221+
throw new CompilerError(
222+
"MethodDefinitions pass: expected the first argument to be `self`, but got "
223+
+ firstArg);
224+
}
225+
} else {
226+
throw new CompilerError("Unexpected body type " + methodBody + " in MethodDefinitions pass.");
227+
}
228+
}
229+
230+
private static Function.Lambda lambdaWithNewSelfArg(
231+
Function.Lambda lambda, DefinitionArgument newSelfArg) {
232+
var args = CollectionConverters.asJava(lambda.arguments());
233+
assert !args.isEmpty();
234+
args.set(0, newSelfArg);
235+
var newArgs = CollectionConverters.asScala(args).toList();
236+
return lambda.copyWithArguments(newArgs);
237+
}
238+
239+
// Generate static wrappers for
240+
// 1. Types having at least one type constructor
241+
// 2. All builtin types except for Nothing. Nothing's eigentype is Nothing and not Nothing.type,
242+
// would lead to overriding conflicts.
243+
// TODO: Remove the hardcoded type once Enso's annotations can define parameters.
244+
private static boolean canGenerateStaticWrappers(Type tp) {
245+
return tp.members().nonEmpty() || (tp.builtinType() && !"Nothing".equals(tp.name()));
246+
}
247+
248+
private Name resolveType(Name typePointer, BindingsMap availableSymbolsMap) {
249+
if (typePointer instanceof Name.Qualified || typePointer instanceof Name.Literal) {
250+
var items =
251+
switch (typePointer) {
252+
case Name.Qualified qualName -> qualName.parts().map(Name::name);
253+
case Name.Literal lit -> list(lit.name());
254+
default -> throw new CompilerError("Impossible to reach");
255+
};
256+
257+
var resolvedItemsOpt = availableSymbolsMap.resolveQualifiedName(items);
258+
if (resolvedItemsOpt.isLeft()) {
259+
var err = resolvedItemsOpt.swap().toOption().get();
260+
return new org.enso.compiler.core.ir.expression.errors.Resolution(
261+
typePointer,
262+
new org.enso.compiler.core.ir.expression.errors.Resolution.ResolverError(err),
263+
MetadataStorage.EMPTY);
264+
}
265+
var resolvedItems = resolvedItemsOpt.toOption().get();
266+
assert resolvedItems.size() == 1 : "Expected a single resolution";
267+
switch (resolvedItems.head()) {
268+
case ResolvedConstructor ignored -> {
269+
return new org.enso.compiler.core.ir.expression.errors.Resolution(
270+
typePointer,
271+
new org.enso.compiler.core.ir.expression.errors.Resolution.UnexpectedConstructor(
272+
"a method definition target"),
273+
MetadataStorage.EMPTY);
274+
}
275+
case ResolvedModule resMod -> {
276+
MetadataInteropHelpers.updateMetadata(typePointer, this, new Resolution(resMod));
277+
return typePointer;
278+
}
279+
case ResolvedType resType -> {
280+
MetadataInteropHelpers.updateMetadata(typePointer, this, new Resolution(resType));
281+
return typePointer;
282+
}
283+
case ResolvedPolyglotSymbol ignored -> {
284+
return new org.enso.compiler.core.ir.expression.errors.Resolution(
285+
typePointer,
286+
new org.enso.compiler.core.ir.expression.errors.Resolution.UnexpectedPolyglot(
287+
"a method definition target"),
288+
MetadataStorage.EMPTY);
289+
}
290+
case ResolvedPolyglotField ignored -> {
291+
return new org.enso.compiler.core.ir.expression.errors.Resolution(
292+
typePointer,
293+
new org.enso.compiler.core.ir.expression.errors.Resolution.UnexpectedPolyglot(
294+
"a method definition target"),
295+
MetadataStorage.EMPTY);
296+
}
297+
case ResolvedModuleMethod ignored -> {
298+
return new org.enso.compiler.core.ir.expression.errors.Resolution(
299+
typePointer,
300+
new org.enso.compiler.core.ir.expression.errors.Resolution.UnexpectedMethod(
301+
"a method definition target"),
302+
MetadataStorage.EMPTY);
303+
}
304+
case ResolvedExtensionMethod ignored -> {
305+
return new org.enso.compiler.core.ir.expression.errors.Resolution(
306+
typePointer,
307+
new org.enso.compiler.core.ir.expression.errors.Resolution.UnexpectedMethod(
308+
"a static method definition target"),
309+
MetadataStorage.EMPTY);
310+
}
311+
case ResolvedConversionMethod ignored -> {
312+
return new org.enso.compiler.core.ir.expression.errors.Resolution(
313+
typePointer,
314+
new org.enso.compiler.core.ir.expression.errors.Resolution.UnexpectedMethod(
315+
"a conversion method definition target"),
316+
MetadataStorage.EMPTY);
317+
}
318+
default -> throw new IllegalStateException("Unexpected value: " + resolvedItems.head());
319+
}
320+
} else if (typePointer instanceof org.enso.compiler.core.ir.expression.errors.Resolution) {
321+
return typePointer;
322+
} else {
323+
throw new CompilerError("Unexpected kind of name for method reference");
324+
}
325+
}
326+
327+
private static <T> List<T> list(T item) {
328+
return CollectionConverters.asScala(java.util.List.of(item)).toList();
329+
}
330+
331+
@Override
332+
public Expression runExpression(Expression ir, InlineContext inlineContext) {
333+
return ir;
334+
}
335+
336+
private static boolean computeIsStatic(IR body) {
337+
return Method.Explicit$.MODULE$.computeIsStatic(body);
338+
}
339+
}

engine/runtime-compiler/src/main/scala/org/enso/compiler/Passes.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class Passes(config: CompilerConfig) {
4040

4141
val globalTypingPasses = new PassGroup(
4242
List(
43-
MethodDefinitions,
43+
MethodDefinitions.INSTANCE,
4444
SectionsToBinOp.INSTANCE,
4545
OperatorToFunction,
4646
LambdaShorthandToLambda,

engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -875,7 +875,7 @@ object BindingsMap {
875875
case method: ir.module.scope.definition.Method.Explicit =>
876876
method.methodReference.methodName.name == this.method.name && method.methodReference.typePointer
877877
.forall(
878-
_.getMetadata(MethodDefinitions)
878+
_.getMetadata(MethodDefinitions.INSTANCE)
879879
.contains(Resolution(ResolvedModule(module)))
880880
)
881881
case _ => false

0 commit comments

Comments
 (0)