diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/types/TypeInferenceSignatures.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/types/TypeInferenceSignatures.java index 6d4244ae55569..6727335680c9f 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/types/TypeInferenceSignatures.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/types/TypeInferenceSignatures.java @@ -1,5 +1,7 @@ package org.enso.compiler.pass.analyse.types; +import java.util.List; +import java.util.UUID; import org.enso.compiler.context.InlineContext; import org.enso.compiler.context.ModuleContext; import org.enso.compiler.core.ir.Expression; @@ -22,9 +24,6 @@ import scala.jdk.javaapi.CollectionConverters; import scala.jdk.javaapi.CollectionConverters$; -import java.util.List; -import java.util.UUID; - /** * A precursor pass that prepares the IR for type inference, run before the main propagation logic * runs in {@link TypeInferencePropagation}. @@ -77,6 +76,7 @@ public Seq precursorPasses() { } @Override + @SuppressWarnings("unchecked") public Seq invalidatedPasses() { return (Seq) Seq$.MODULE$.empty(); } @@ -90,9 +90,14 @@ public Module runModule(Module ir, ModuleContext moduleContext) { case Method.Explicit b -> { TypeRepresentation resolvedType = resolveTopLevelTypeSignature(b.body()); if (resolvedType != null) { - System.out.println("Resolved "+b.methodReference().showCode()+" to type "+resolvedType); + System.out.println( + "Resolved " + + b.methodReference().showCode() + + " to type " + + resolvedType); // TODO maybe different metadata class? - // TODO use this in TypeInferencePropagation if its own metadata is not yet available + // TODO use this in TypeInferencePropagation if its own metadata is not yet + // available ir.passData().update(INSTANCE, new InferredType(resolvedType)); } yield b; @@ -114,7 +119,7 @@ public Expression runExpression(Expression ir, InlineContext inlineContext) { private TypeRepresentation resolveTopLevelTypeSignature(Expression expression) { return switch (expression) { - // Combine argument types with ascribed type (if available) for a function type signature + // Combine argument types with ascribed type (if available) for a function type signature case Function.Lambda lambda -> { boolean hasAnyDefaults = lambda.arguments().find((arg) -> arg.defaultValue().isDefined()).isDefined(); @@ -141,11 +146,20 @@ private TypeRepresentation resolveTopLevelTypeSignature(Expression expression) { }); TypeRepresentation ascribedReturnType = typeResolver.findTypeAscription(lambda.body()); - TypeRepresentation returnType = ascribedReturnType != null ? ascribedReturnType : TypeRepresentation.UNKNOWN; - yield TypeRepresentation.buildFunction(CollectionConverters$.MODULE$.asJava(argTypesScala), returnType); + + if (ascribedReturnType == null && argTypesScala.isEmpty()) { + // If we did not infer return type NOR arity, we know nothing useful about this function, so we withdraw. + yield null; + } + + TypeRepresentation returnType = + ascribedReturnType != null ? ascribedReturnType : TypeRepresentation.UNKNOWN; + yield TypeRepresentation.buildFunction( + CollectionConverters$.MODULE$.asJava(argTypesScala), returnType); } - // Otherwise, we encountered a 0-argument method, so its type is just its return type (if its known). + // Otherwise, we encountered a 0-argument method, so its type is just its return type (if + // its known). default -> typeResolver.findTypeAscription(expression); }; } diff --git a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/types/TypePropagation.java b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/types/TypePropagation.java index dd9b50080160a..6d3903362d9bc 100644 --- a/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/types/TypePropagation.java +++ b/engine/runtime-compiler/src/main/java/org/enso/compiler/pass/analyse/types/TypePropagation.java @@ -1,7 +1,6 @@ package org.enso.compiler.pass.analyse.types; import static org.enso.compiler.MetadataInteropHelpers.getMetadata; -import static org.enso.compiler.MetadataInteropHelpers.getMetadataOrNull; import java.util.List; import org.enso.compiler.context.NameResolutionAlgorithm; @@ -19,8 +18,6 @@ import org.enso.compiler.pass.analyse.AliasAnalysis$; import org.enso.compiler.pass.analyse.alias.Graph; import org.enso.compiler.pass.analyse.alias.Info; -import org.enso.compiler.pass.resolve.TypeSignatures; -import org.enso.compiler.pass.resolve.TypeSignatures$; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scala.Option; diff --git a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Passes.scala b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Passes.scala index f457256d295cd..f07b9e2311e2c 100644 --- a/engine/runtime-compiler/src/main/scala/org/enso/compiler/Passes.scala +++ b/engine/runtime-compiler/src/main/scala/org/enso/compiler/Passes.scala @@ -3,25 +3,12 @@ package org.enso.compiler import org.enso.compiler.data.CompilerConfig import org.enso.compiler.pass.PassConfiguration._ import org.enso.compiler.pass.analyse._ -import org.enso.compiler.pass.analyse.types.TypeInferencePropagation +import org.enso.compiler.pass.analyse.types.{TypeInferencePropagation, TypeInferenceSignatures} import org.enso.compiler.pass.desugar._ -import org.enso.compiler.pass.lint.{ - ModuleNameConflicts, - NoSelfInStatic, - ShadowedPatternFields, - UnusedBindings -} -import org.enso.compiler.pass.optimise.{ - LambdaConsolidate, - UnreachableMatchBranches -} +import org.enso.compiler.pass.lint.{ModuleNameConflicts, NoSelfInStatic, ShadowedPatternFields, UnusedBindings} +import org.enso.compiler.pass.optimise.{LambdaConsolidate, UnreachableMatchBranches} import org.enso.compiler.pass.resolve._ -import org.enso.compiler.pass.{ - IRPass, - PassConfiguration, - PassGroup, - PassManager -} +import org.enso.compiler.pass.{IRPass, PassConfiguration, PassGroup, PassManager} class Passes( config: CompilerConfig, @@ -100,6 +87,7 @@ class Passes( GenericAnnotations ) ++ (if (config.staticTypeInferenceEnabled) { List( + TypeInferenceSignatures.INSTANCE, TypeInferencePropagation.INSTANCE ) } else List()) diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/CompilerTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/CompilerTest.java index cbdb4f4ca62b1..032bd94fd6186 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/CompilerTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/CompilerTest.java @@ -1,5 +1,8 @@ package org.enso.compiler; +import static org.junit.Assert.assertTrue; + +import java.util.List; import org.enso.compiler.context.FreshNameSupply; import org.enso.compiler.context.ModuleContext; import org.enso.compiler.core.IR; @@ -18,10 +21,6 @@ import scala.collection.immutable.Seq$; import scala.jdk.javaapi.CollectionConverters; -import java.util.List; - -import static org.junit.Assert.assertTrue; - public abstract class CompilerTest extends ParserTest { /** * Note that this `compile` method will not run import resolution. For now we just have tests that diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/TypeInferenceTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/TypeInferenceTest.java index f707d79f48a6c..219b00784fead 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/TypeInferenceTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/TypeInferenceTest.java @@ -10,31 +10,19 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import org.enso.compiler.context.FreshNameSupply; -import org.enso.compiler.context.ModuleContext; import org.enso.compiler.core.IR; -import org.enso.compiler.core.ir.Diagnostic; -import org.enso.compiler.core.ir.Expression; import org.enso.compiler.core.ir.Module; import org.enso.compiler.core.ir.ProcessingPass; import org.enso.compiler.core.ir.Warning; import org.enso.compiler.core.ir.expression.Application; import org.enso.compiler.core.ir.module.scope.definition.Method; -import org.enso.compiler.data.CompilerConfig; -import org.enso.compiler.pass.PassConfiguration; -import org.enso.compiler.pass.PassManager; import org.enso.compiler.pass.analyse.types.InferredType; import org.enso.compiler.pass.analyse.types.TypeInferencePropagation; import org.enso.compiler.pass.analyse.types.TypeRepresentation; -import org.enso.compiler.test.CompilerRunner; -import org.enso.pkg.QualifiedName; import org.graalvm.polyglot.Source; import org.junit.Ignore; import org.junit.Test; import scala.Option; -import scala.collection.immutable.Seq; -import scala.collection.immutable.Seq$; -import scala.jdk.javaapi.CollectionConverters; public class TypeInferenceTest extends CompilerTest { @Ignore("TODO resolving global methods") diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/compiler/TypesFromSignaturesTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/TypesFromSignaturesTest.java new file mode 100644 index 0000000000000..1707cc0339d9f --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/compiler/TypesFromSignaturesTest.java @@ -0,0 +1,103 @@ +package org.enso.compiler; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.net.URI; +import org.enso.compiler.core.IR; +import org.enso.compiler.core.ir.ProcessingPass; +import org.enso.compiler.pass.analyse.types.InferredType; +import org.enso.compiler.pass.analyse.types.TypeInferenceSignatures; +import org.enso.compiler.pass.analyse.types.TypeRepresentation; +import org.graalvm.polyglot.Source; +import org.junit.Test; +import scala.Option; + +public class TypesFromSignaturesTest extends CompilerTest { + + @Test + public void simpleCheck() throws Exception { + final URI uri = new URI("memory://simpleCheck.enso"); + final Source src = + Source.newBuilder( + "enso", + """ + type A + type B + type C + Value v + + f (x : A) (y : B) -> C = C.Value [x, y] + """, + uri.getAuthority()) + .uri(uri) + .buildLiteral(); + + var module = compile(src); + + var f1 = findStaticMethod(module, "f"); + + assertInferredType(f1, "(A -> (B -> C))"); + } + + + @Test + public void justArity() throws Exception { + final URI uri = new URI("memory://justArity.enso"); + final Source src = + Source.newBuilder( + "enso", + """ + type A + type B + type C + Value v + + f0 = 0 + + f4 x y z w = [x, y, z, w] + + f2 : A -> B -> C + f2 x y = [x, y] + """, + uri.getAuthority()) + .uri(uri) + .buildLiteral(); + + var module = compile(src); + + var f0 = findStaticMethod(module, "f0"); + var f4 = findStaticMethod(module, "f4"); + var f2 = findStaticMethod(module, "f2"); + + // For 0 arguments and unknown return type we know nothing useful, so no information is registered. + assertNoInferredType(f0); + + // For a function without ascriptions, we can at least infer the _arity_ + // Currently that is denoted by replacing unknowns with Any. Later this may be free type variables. + assertInferredType(f4, "(Any -> (Any -> (Any -> (Any -> Any))))"); + + + // For the 'opted-out' ascription, the types are ignored, because they are not checked types. But we still infer arity. + assertInferredType(f2, "(Any -> (Any -> Any))"); + } + + private void assertInferredType(IR ir, String expected) { + TypeRepresentation inferred = getInferredType(ir); + assertEquals(expected, inferred.toString()); + } + + private TypeRepresentation getInferredType(IR ir) { + Option metadata = ir.passData().get(TypeInferenceSignatures.INSTANCE); + assertTrue( + "Expecting " + ir.showCode() + " to contain a type within metadata.", metadata.isDefined()); + InferredType inferred = (InferredType) metadata.get(); + return inferred.type(); + } + + private void assertNoInferredType(IR ir) { + Option metadata = ir.passData().get(TypeInferenceSignatures.INSTANCE); + assertTrue( + "Expecting " + ir.showCode() + " to contain no type within metadata.", metadata.isEmpty()); + } +}