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

Allow Java Enums in "case of" branches #7607

Merged
merged 8 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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."
Expand Down Expand Up @@ -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."
Expand Down Expand Up @@ -861,6 +870,7 @@ class IrToTruffle(
fun
)
case BindingsMap.ResolvedPolyglotSymbol(_, _) =>
case BindingsMap.ResolvedPolyglotField(_, _) =>
}
}
case _ => throw new CompilerError("Unreachable")
Expand Down Expand Up @@ -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(_, _)
Expand Down Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ case class BindingsMap(
}
}
currentScope.resolveExportedSymbol(finalItem)
case s @ ResolvedPolyglotSymbol(_, _) =>
val found = s.findExportedSymbolFor(finalItem)
Right(found)
case _ => Left(ResolutionNotFound)
}

Expand Down Expand Up @@ -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)
JaroslavTulach marked this conversation as resolved.
Show resolved Hide resolved
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.
Expand All @@ -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) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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."
Expand Down Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

/** 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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
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;
Expand Down Expand Up @@ -97,6 +98,48 @@ public void testImportInnerEnum() {
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 = """
Expand Down
25 changes: 25 additions & 0 deletions test/Tests/src/Semantic/Java_Interop_Spec.enso
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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"
JaroslavTulach marked this conversation as resolved.
Show resolved Hide resolved
_ -> "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