mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 22:01:42 +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]
|
- [Allow users to give a project other than Upper_Snake_Case name][7397]
|
||||||
- [Support renaming variable or function][7515]
|
- [Support renaming variable or function][7515]
|
||||||
- [Only use types as State keys][7585]
|
- [Only use types as State keys][7585]
|
||||||
|
- [Allow Java Enums in case of branches][7607]
|
||||||
|
|
||||||
[3227]: https://github.com/enso-org/enso/pull/3227
|
[3227]: https://github.com/enso-org/enso/pull/3227
|
||||||
[3248]: https://github.com/enso-org/enso/pull/3248
|
[3248]: https://github.com/enso-org/enso/pull/3248
|
||||||
@ -1050,6 +1051,7 @@
|
|||||||
[7397]: https://github.com/enso-org/enso/pull/7397
|
[7397]: https://github.com/enso-org/enso/pull/7397
|
||||||
[7515]: https://github.com/enso-org/enso/pull/7515
|
[7515]: https://github.com/enso-org/enso/pull/7515
|
||||||
[7585]: https://github.com/enso-org/enso/pull/7585
|
[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)
|
# Enso 2.0.0-alpha.18 (2021-10-12)
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.enso.compiler.codegen
|
package org.enso.compiler.codegen
|
||||||
|
|
||||||
import com.oracle.truffle.api.source.{Source, SourceSection}
|
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.CompilerError
|
||||||
import org.enso.compiler.core.IR
|
import org.enso.compiler.core.IR
|
||||||
import org.enso.compiler.core.IR.Module.Scope.Import
|
import org.enso.compiler.core.IR.Module.Scope.Import
|
||||||
@ -369,6 +370,10 @@ class IrToTruffle(
|
|||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
"Impossible polyglot symbol, should be caught by MethodDefinitions pass."
|
"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 =>
|
case _: BindingsMap.ResolvedMethod =>
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
"Impossible here, should be caught by MethodDefinitions pass."
|
"Impossible here, should be caught by MethodDefinitions pass."
|
||||||
@ -730,6 +735,10 @@ class IrToTruffle(
|
|||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
"Impossible polyglot symbol, should be caught by MethodDefinitions pass."
|
"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 =>
|
case _: BindingsMap.ResolvedMethod =>
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
"Impossible here, should be caught by MethodDefinitions pass."
|
"Impossible here, should be caught by MethodDefinitions pass."
|
||||||
@ -861,6 +870,7 @@ class IrToTruffle(
|
|||||||
fun
|
fun
|
||||||
)
|
)
|
||||||
case BindingsMap.ResolvedPolyglotSymbol(_, _) =>
|
case BindingsMap.ResolvedPolyglotSymbol(_, _) =>
|
||||||
|
case BindingsMap.ResolvedPolyglotField(_, _) =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case _ => throw new CompilerError("Unreachable")
|
case _ => throw new CompilerError("Unreachable")
|
||||||
@ -1218,6 +1228,51 @@ class IrToTruffle(
|
|||||||
),
|
),
|
||||||
BadPatternMatch.NonVisiblePolyglotSymbol(symbol.name)
|
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(
|
case Some(
|
||||||
BindingsMap.Resolution(
|
BindingsMap.Resolution(
|
||||||
BindingsMap.ResolvedMethod(_, _)
|
BindingsMap.ResolvedMethod(_, _)
|
||||||
@ -1570,6 +1625,14 @@ class IrToTruffle(
|
|||||||
.getPolyglotSymbols
|
.getPolyglotSymbols
|
||||||
.get(symbol.name)
|
.get(symbol.name)
|
||||||
)
|
)
|
||||||
|
case BindingsMap.ResolvedPolyglotField(symbol, name) =>
|
||||||
|
ConstantObjectNode.build(
|
||||||
|
symbol.module
|
||||||
|
.unsafeAsModule()
|
||||||
|
.getScope
|
||||||
|
.getPolyglotSymbols
|
||||||
|
.get(name)
|
||||||
|
)
|
||||||
case BindingsMap.ResolvedMethod(_, method) =>
|
case BindingsMap.ResolvedMethod(_, method) =>
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
s"Impossible here, ${method.name} should be caught when translating application"
|
s"Impossible here, ${method.name} should be caught when translating application"
|
||||||
|
@ -212,6 +212,9 @@ case class BindingsMap(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentScope.resolveExportedSymbol(finalItem)
|
currentScope.resolveExportedSymbol(finalItem)
|
||||||
|
case s @ ResolvedPolyglotSymbol(_, _) =>
|
||||||
|
val found = s.findExportedSymbolFor(finalItem)
|
||||||
|
Right(found)
|
||||||
case _ => Left(ResolutionNotFound)
|
case _ => Left(ResolutionNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -937,6 +940,21 @@ object BindingsMap {
|
|||||||
|
|
||||||
override def qualifiedName: QualifiedName =
|
override def qualifiedName: QualifiedName =
|
||||||
module.getName.createChild(symbol.name)
|
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.
|
/** A representation of an error during name resolution.
|
||||||
@ -962,6 +980,8 @@ object BindingsMap {
|
|||||||
s" The module ${module.getName};"
|
s" The module ${module.getName};"
|
||||||
case BindingsMap.ResolvedPolyglotSymbol(_, symbol) =>
|
case BindingsMap.ResolvedPolyglotSymbol(_, symbol) =>
|
||||||
s" The imported polyglot symbol ${symbol.name};"
|
s" The imported polyglot symbol ${symbol.name};"
|
||||||
|
case BindingsMap.ResolvedPolyglotField(_, name) =>
|
||||||
|
s" The imported polyglot field ${name};"
|
||||||
case BindingsMap.ResolvedMethod(module, symbol) =>
|
case BindingsMap.ResolvedMethod(module, symbol) =>
|
||||||
s" The method ${symbol.name} defined in module ${module.getName}"
|
s" The method ${symbol.name} defined in module ${module.getName}"
|
||||||
case BindingsMap.ResolvedType(module, typ) =>
|
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
|
/** Where the pattern match is matching on a polyglot symbol not visible in the
|
||||||
* current scope.
|
* current scope.
|
||||||
*
|
*
|
||||||
* @param name the name of the requested constructor
|
* @param name the name of the requested symbol
|
||||||
*/
|
*/
|
||||||
sealed case class NonVisiblePolyglotSymbol(name: String)
|
sealed case class NonVisiblePolyglotSymbol(name: String)
|
||||||
extends BadPatternMatch {
|
extends BadPatternMatch {
|
||||||
override val message: String = s"$name is not visible in this scope"
|
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"
|
"a method definition target"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
case Right(_: BindingsMap.ResolvedPolyglotField) =>
|
||||||
|
IR.Error.Resolution(
|
||||||
|
typePointer,
|
||||||
|
IR.Error.Resolution.UnexpectedPolyglot(
|
||||||
|
"a method definition target"
|
||||||
|
)
|
||||||
|
)
|
||||||
case Right(_: BindingsMap.ResolvedMethod) =>
|
case Right(_: BindingsMap.ResolvedMethod) =>
|
||||||
IR.Error.Resolution(
|
IR.Error.Resolution(
|
||||||
typePointer,
|
typePointer,
|
||||||
|
@ -133,6 +133,10 @@ object Patterns extends IRPass {
|
|||||||
consName.updateMetadata(
|
consName.updateMetadata(
|
||||||
this -->> BindingsMap.Resolution(value)
|
this -->> BindingsMap.Resolution(value)
|
||||||
)
|
)
|
||||||
|
case Right(value: BindingsMap.ResolvedPolyglotField) =>
|
||||||
|
consName.updateMetadata(
|
||||||
|
this -->> BindingsMap.Resolution(value)
|
||||||
|
)
|
||||||
|
|
||||||
case Right(_: BindingsMap.ResolvedMethod) =>
|
case Right(_: BindingsMap.ResolvedMethod) =>
|
||||||
IR.Error.Resolution(
|
IR.Error.Resolution(
|
||||||
@ -150,6 +154,7 @@ object Patterns extends IRPass {
|
|||||||
case BindingsMap.ResolvedConstructor(_, cons) => cons.arity
|
case BindingsMap.ResolvedConstructor(_, cons) => cons.arity
|
||||||
case BindingsMap.ResolvedModule(_) => 0
|
case BindingsMap.ResolvedModule(_) => 0
|
||||||
case BindingsMap.ResolvedPolyglotSymbol(_, _) => 0
|
case BindingsMap.ResolvedPolyglotSymbol(_, _) => 0
|
||||||
|
case BindingsMap.ResolvedPolyglotField(_, _) => 0
|
||||||
case BindingsMap.ResolvedMethod(_, _) =>
|
case BindingsMap.ResolvedMethod(_, _) =>
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
"Impossible, should be transformed into an error before."
|
"Impossible, should be transformed into an error before."
|
||||||
@ -207,6 +212,10 @@ object Patterns extends IRPass {
|
|||||||
tpeName.updateMetadata(
|
tpeName.updateMetadata(
|
||||||
this -->> BindingsMap.Resolution(value)
|
this -->> BindingsMap.Resolution(value)
|
||||||
)
|
)
|
||||||
|
case Right(value: BindingsMap.ResolvedPolyglotField) =>
|
||||||
|
tpeName.updateMetadata(
|
||||||
|
this -->> BindingsMap.Resolution(value)
|
||||||
|
)
|
||||||
/*IR.Error.Resolution(
|
/*IR.Error.Resolution(
|
||||||
tpeName,
|
tpeName,
|
||||||
IR.Error.Resolution.UnexpectedPolyglot(s"type pattern case")
|
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 */
|
/** A class used for testing Java Interop from Enso code */
|
||||||
public class TestClass {
|
public class TestClass {
|
||||||
|
public static final int FINAL_ONE = 1;
|
||||||
|
public static int nonFinalTwo = 2;
|
||||||
|
|
||||||
private final Function<Long, Long> function;
|
private final Function<Long, Long> function;
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.graalvm.polyglot.Context;
|
import org.graalvm.polyglot.Context;
|
||||||
|
import org.graalvm.polyglot.PolyglotException;
|
||||||
import org.graalvm.polyglot.Value;
|
import org.graalvm.polyglot.Value;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
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"));
|
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
|
@Test
|
||||||
public void testImportOuterClassAndReferenceInner() {
|
public void testImportOuterClassAndReferenceInner() {
|
||||||
var code = """
|
var code = """
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from Standard.Base import all
|
from Standard.Base import all
|
||||||
import Standard.Base.Errors.Common.No_Such_Method
|
import Standard.Base.Errors.Common.No_Such_Method
|
||||||
|
import Standard.Base.Errors.Common.Compile_Error
|
||||||
|
|
||||||
from Standard.Test import Test, Test_Suite
|
from Standard.Test import Test, Test_Suite
|
||||||
import Standard.Test.Extensions
|
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.Long
|
||||||
polyglot java import java.lang.String
|
polyglot java import java.lang.String
|
||||||
polyglot java import java.lang.StringBuilder as Java_String_Builder
|
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.util.ArrayList
|
||||||
polyglot java import java.time.LocalDate
|
polyglot java import java.time.LocalDate
|
||||||
polyglot java import java.time.LocalTime
|
polyglot java import java.time.LocalTime
|
||||||
@ -66,4 +68,27 @@ spec =
|
|||||||
april1st.month.should_equal 4
|
april1st.month.should_equal 4
|
||||||
april1st.day.should_equal 1
|
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
|
main = Test_Suite.run_main spec
|
||||||
|
Loading…
Reference in New Issue
Block a user