mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 17:34:10 +03:00
Allow Java Enums in "case of" branches (#7607)
Fixes #7551 by looking up static symbols of a `JavaClass` polyglot object.
This commit is contained in:
parent
06b2280ee6
commit
a2946d2d27
@ -918,6 +918,7 @@
|
||||
- [Allow users to give a project other than Upper_Snake_Case name][7397]
|
||||
- [Support renaming variable or function][7515]
|
||||
- [Only use types as State keys][7585]
|
||||
- [Allow Java Enums in case of branches][7607]
|
||||
|
||||
[3227]: https://github.com/enso-org/enso/pull/3227
|
||||
[3248]: https://github.com/enso-org/enso/pull/3248
|
||||
@ -1050,6 +1051,7 @@
|
||||
[7397]: https://github.com/enso-org/enso/pull/7397
|
||||
[7515]: https://github.com/enso-org/enso/pull/7515
|
||||
[7585]: https://github.com/enso-org/enso/pull/7585
|
||||
[7607]: https://github.com/enso-org/enso/pull/7607
|
||||
|
||||
# Enso 2.0.0-alpha.18 (2021-10-12)
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.enso.compiler.codegen
|
||||
|
||||
import com.oracle.truffle.api.source.{Source, SourceSection}
|
||||
import com.oracle.truffle.api.interop.InteropLibrary
|
||||
import org.enso.compiler.core.CompilerError
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Module.Scope.Import
|
||||
@ -369,6 +370,10 @@ class IrToTruffle(
|
||||
throw new CompilerError(
|
||||
"Impossible polyglot symbol, should be caught by MethodDefinitions pass."
|
||||
)
|
||||
case BindingsMap.ResolvedPolyglotField(_, _) =>
|
||||
throw new CompilerError(
|
||||
"Impossible polyglot field, should be caught by MethodDefinitions pass."
|
||||
)
|
||||
case _: BindingsMap.ResolvedMethod =>
|
||||
throw new CompilerError(
|
||||
"Impossible here, should be caught by MethodDefinitions pass."
|
||||
@ -730,6 +735,10 @@ class IrToTruffle(
|
||||
throw new CompilerError(
|
||||
"Impossible polyglot symbol, should be caught by MethodDefinitions pass."
|
||||
)
|
||||
case BindingsMap.ResolvedPolyglotField(_, _) =>
|
||||
throw new CompilerError(
|
||||
"Impossible polyglot field, should be caught by MethodDefinitions pass."
|
||||
)
|
||||
case _: BindingsMap.ResolvedMethod =>
|
||||
throw new CompilerError(
|
||||
"Impossible here, should be caught by MethodDefinitions pass."
|
||||
@ -861,6 +870,7 @@ class IrToTruffle(
|
||||
fun
|
||||
)
|
||||
case BindingsMap.ResolvedPolyglotSymbol(_, _) =>
|
||||
case BindingsMap.ResolvedPolyglotField(_, _) =>
|
||||
}
|
||||
}
|
||||
case _ => throw new CompilerError("Unreachable")
|
||||
@ -1218,6 +1228,51 @@ class IrToTruffle(
|
||||
),
|
||||
BadPatternMatch.NonVisiblePolyglotSymbol(symbol.name)
|
||||
)
|
||||
case Some(
|
||||
BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedPolyglotField(typ, symbol)
|
||||
)
|
||||
) =>
|
||||
val mod = typ.module
|
||||
val polyClass = mod
|
||||
.unsafeAsModule()
|
||||
.getScope
|
||||
.getPolyglotSymbols
|
||||
.get(typ.symbol.name)
|
||||
|
||||
val polyValueOrError =
|
||||
if (polyClass == null)
|
||||
Left(
|
||||
BadPatternMatch.NonVisiblePolyglotSymbol(
|
||||
typ.symbol.name
|
||||
)
|
||||
)
|
||||
else
|
||||
try {
|
||||
val iop = InteropLibrary.getUncached()
|
||||
if (!iop.isMemberReadable(polyClass, symbol)) {
|
||||
Left(BadPatternMatch.NonVisiblePolyglotSymbol(symbol))
|
||||
} else {
|
||||
if (iop.isMemberModifiable(polyClass, symbol)) {
|
||||
Left(
|
||||
BadPatternMatch.NonConstantPolyglotSymbol(symbol)
|
||||
)
|
||||
} else {
|
||||
Right(iop.readMember(polyClass, symbol))
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
case _: Throwable =>
|
||||
Left(BadPatternMatch.NonVisiblePolyglotSymbol(symbol))
|
||||
}
|
||||
polyValueOrError.map(polyValue => {
|
||||
ObjectEqualityBranchNode
|
||||
.build(
|
||||
branchCodeNode.getCallTarget,
|
||||
polyValue,
|
||||
branch.terminalBranch
|
||||
)
|
||||
})
|
||||
case Some(
|
||||
BindingsMap.Resolution(
|
||||
BindingsMap.ResolvedMethod(_, _)
|
||||
@ -1570,6 +1625,14 @@ class IrToTruffle(
|
||||
.getPolyglotSymbols
|
||||
.get(symbol.name)
|
||||
)
|
||||
case BindingsMap.ResolvedPolyglotField(symbol, name) =>
|
||||
ConstantObjectNode.build(
|
||||
symbol.module
|
||||
.unsafeAsModule()
|
||||
.getScope
|
||||
.getPolyglotSymbols
|
||||
.get(name)
|
||||
)
|
||||
case BindingsMap.ResolvedMethod(_, method) =>
|
||||
throw new CompilerError(
|
||||
s"Impossible here, ${method.name} should be caught when translating application"
|
||||
|
@ -212,6 +212,9 @@ case class BindingsMap(
|
||||
}
|
||||
}
|
||||
currentScope.resolveExportedSymbol(finalItem)
|
||||
case s @ ResolvedPolyglotSymbol(_, _) =>
|
||||
val found = s.findExportedSymbolFor(finalItem)
|
||||
Right(found)
|
||||
case _ => Left(ResolutionNotFound)
|
||||
}
|
||||
|
||||
@ -937,6 +940,21 @@ object BindingsMap {
|
||||
|
||||
override def qualifiedName: QualifiedName =
|
||||
module.getName.createChild(symbol.name)
|
||||
|
||||
def findExportedSymbolFor(
|
||||
name: String
|
||||
): org.enso.compiler.data.BindingsMap.ResolvedName =
|
||||
ResolvedPolyglotField(this, name)
|
||||
}
|
||||
|
||||
case class ResolvedPolyglotField(symbol: ResolvedPolyglotSymbol, name: String)
|
||||
extends ResolvedName {
|
||||
def module: BindingsMap.ModuleReference = symbol.module
|
||||
def qualifiedName: QualifiedName = symbol.qualifiedName.createChild(name)
|
||||
def toAbstract: ResolvedName =
|
||||
ResolvedPolyglotField(symbol.toAbstract, name)
|
||||
def toConcrete(moduleMap: ModuleMap): Option[ResolvedName] =
|
||||
symbol.toConcrete(moduleMap).map(ResolvedPolyglotField(_, name))
|
||||
}
|
||||
|
||||
/** A representation of an error during name resolution.
|
||||
@ -962,6 +980,8 @@ object BindingsMap {
|
||||
s" The module ${module.getName};"
|
||||
case BindingsMap.ResolvedPolyglotSymbol(_, symbol) =>
|
||||
s" The imported polyglot symbol ${symbol.name};"
|
||||
case BindingsMap.ResolvedPolyglotField(_, name) =>
|
||||
s" The imported polyglot field ${name};"
|
||||
case BindingsMap.ResolvedMethod(module, symbol) =>
|
||||
s" The method ${symbol.name} defined in module ${module.getName}"
|
||||
case BindingsMap.ResolvedType(module, typ) =>
|
||||
|
@ -43,10 +43,19 @@ object BadPatternMatch {
|
||||
/** Where the pattern match is matching on a polyglot symbol not visible in the
|
||||
* current scope.
|
||||
*
|
||||
* @param name the name of the requested constructor
|
||||
* @param name the name of the requested symbol
|
||||
*/
|
||||
sealed case class NonVisiblePolyglotSymbol(name: String)
|
||||
extends BadPatternMatch {
|
||||
override val message: String = s"$name is not visible in this scope"
|
||||
}
|
||||
|
||||
/** Where the pattern match is matching on a non-final polyglot symbol.
|
||||
*
|
||||
* @param name the name of the requested symbol
|
||||
*/
|
||||
sealed case class NonConstantPolyglotSymbol(name: String)
|
||||
extends BadPatternMatch {
|
||||
override val message: String = s"$name is not a constant"
|
||||
}
|
||||
}
|
||||
|
@ -165,6 +165,13 @@ case object MethodDefinitions extends IRPass {
|
||||
"a method definition target"
|
||||
)
|
||||
)
|
||||
case Right(_: BindingsMap.ResolvedPolyglotField) =>
|
||||
IR.Error.Resolution(
|
||||
typePointer,
|
||||
IR.Error.Resolution.UnexpectedPolyglot(
|
||||
"a method definition target"
|
||||
)
|
||||
)
|
||||
case Right(_: BindingsMap.ResolvedMethod) =>
|
||||
IR.Error.Resolution(
|
||||
typePointer,
|
||||
|
@ -133,6 +133,10 @@ object Patterns extends IRPass {
|
||||
consName.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(value)
|
||||
)
|
||||
case Right(value: BindingsMap.ResolvedPolyglotField) =>
|
||||
consName.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(value)
|
||||
)
|
||||
|
||||
case Right(_: BindingsMap.ResolvedMethod) =>
|
||||
IR.Error.Resolution(
|
||||
@ -150,6 +154,7 @@ object Patterns extends IRPass {
|
||||
case BindingsMap.ResolvedConstructor(_, cons) => cons.arity
|
||||
case BindingsMap.ResolvedModule(_) => 0
|
||||
case BindingsMap.ResolvedPolyglotSymbol(_, _) => 0
|
||||
case BindingsMap.ResolvedPolyglotField(_, _) => 0
|
||||
case BindingsMap.ResolvedMethod(_, _) =>
|
||||
throw new CompilerError(
|
||||
"Impossible, should be transformed into an error before."
|
||||
@ -207,6 +212,10 @@ object Patterns extends IRPass {
|
||||
tpeName.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(value)
|
||||
)
|
||||
case Right(value: BindingsMap.ResolvedPolyglotField) =>
|
||||
tpeName.updateMetadata(
|
||||
this -->> BindingsMap.Resolution(value)
|
||||
)
|
||||
/*IR.Error.Resolution(
|
||||
tpeName,
|
||||
IR.Error.Resolution.UnexpectedPolyglot(s"type pattern case")
|
||||
|
@ -5,6 +5,8 @@ import java.util.function.Function;
|
||||
|
||||
/** A class used for testing Java Interop from Enso code */
|
||||
public class TestClass {
|
||||
public static final int FINAL_ONE = 1;
|
||||
public static int nonFinalTwo = 2;
|
||||
|
||||
private final Function<Long, Long> function;
|
||||
|
||||
|
@ -5,6 +5,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import org.graalvm.polyglot.Context;
|
||||
import org.graalvm.polyglot.PolyglotException;
|
||||
import org.graalvm.polyglot.Value;
|
||||
import org.junit.AfterClass;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
@ -97,6 +98,48 @@ public class JavaInteropTest extends TestBase {
|
||||
checkPrint(code, List.of("ENUM_VALUE_1", "ENUM_VALUE_2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCaseOnEnum() {
|
||||
var code = """
|
||||
from Standard.Base import IO
|
||||
polyglot java import org.enso.example.TestClass
|
||||
polyglot java import org.enso.example.TestClass.InnerEnum
|
||||
|
||||
to_string x = case x of
|
||||
InnerEnum.ENUM_VALUE_1 -> "one"
|
||||
InnerEnum.ENUM_VALUE_2 -> "two"
|
||||
_ -> "none"
|
||||
|
||||
main =
|
||||
IO.println <| to_string TestClass.InnerEnum.ENUM_VALUE_1
|
||||
IO.println <| to_string TestClass.InnerEnum.ENUM_VALUE_2
|
||||
""";
|
||||
checkPrint(code, List.of("one", "two"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCaseNonFinal() {
|
||||
var code = """
|
||||
from Standard.Base import IO
|
||||
polyglot java import org.enso.example.TestClass
|
||||
|
||||
to_string x = case x of
|
||||
TestClass.FINAL_ONE -> "one"
|
||||
TestClass.nonFinalTwo -> "two"
|
||||
_ -> "none"
|
||||
|
||||
main =
|
||||
IO.println <| to_string 1
|
||||
IO.println <| to_string 2
|
||||
""";
|
||||
try {
|
||||
checkPrint(code, List.of());
|
||||
fail("Expecting exception");
|
||||
} catch (PolyglotException e) {
|
||||
assertEquals("Compile error: nonFinalTwo is not a constant.", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportOuterClassAndReferenceInner() {
|
||||
var code = """
|
||||
|
@ -1,5 +1,6 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Errors.Common.No_Such_Method
|
||||
import Standard.Base.Errors.Common.Compile_Error
|
||||
|
||||
from Standard.Test import Test, Test_Suite
|
||||
import Standard.Test.Extensions
|
||||
@ -9,6 +10,7 @@ polyglot java import java.lang.Integer as Java_Integer
|
||||
polyglot java import java.lang.Long
|
||||
polyglot java import java.lang.String
|
||||
polyglot java import java.lang.StringBuilder as Java_String_Builder
|
||||
polyglot java import java.lang.Thread.State
|
||||
polyglot java import java.util.ArrayList
|
||||
polyglot java import java.time.LocalDate
|
||||
polyglot java import java.time.LocalTime
|
||||
@ -66,4 +68,27 @@ spec =
|
||||
april1st.month.should_equal 4
|
||||
april1st.day.should_equal 1
|
||||
|
||||
Test.group "Java case of" <|
|
||||
Test.specify "case on Thread.State enum" <|
|
||||
match x = case x of
|
||||
State.NEW -> "new"
|
||||
_ -> "unknown"
|
||||
match State.NEW . should_equal "new"
|
||||
match State.BLOCKED . should_equal "unknown"
|
||||
|
||||
Test.specify "case on String static field" <|
|
||||
match x = case x of
|
||||
String.CASE_INSENSITIVE_ORDER -> "match"
|
||||
_ -> "unknown"
|
||||
match String.CASE_INSENSITIVE_ORDER . should_equal "match"
|
||||
|
||||
Test.specify "case on non-existing field yields Compile_Error" <|
|
||||
match x = case x of
|
||||
State.NON_EXISTING -> "match"
|
||||
_ -> "unknown"
|
||||
err = Panic.recover Any (match State.BLOCKED)
|
||||
err . should_fail_with Compile_Error
|
||||
err.to_text . contains "NON_EXISTING" . should_be_true
|
||||
err.to_text . contains "is not visible in this scope" . should_be_true
|
||||
|
||||
main = Test_Suite.run_main spec
|
||||
|
Loading…
Reference in New Issue
Block a user