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

Use fn... to reference any module function #12128

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
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 @@ -46,6 +46,7 @@
- [Types without constructors can be public][12052]
- Symetric, transitive and reflexive [equality for intersection types][11897]
- [IR definitions are generated by an annotation processor][11770]
- [Use fn... to reference any module function](12128)

[11777]: https://github.com/enso-org/enso/pull/11777
[11600]: https://github.com/enso-org/enso/pull/11600
Expand All @@ -55,6 +56,7 @@
[12052]: https://github.com/enso-org/enso/pull/12052
[11897]: https://github.com/enso-org/enso/pull/11897
[11770]: https://github.com/enso-org/enso/pull/11770
[12128]: https://github.com/enso-org/enso/pull/12128

# Enso 2024.5

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,26 +340,41 @@ case object GlobalNames extends IRPass {
)
)
processedFun.getMetadata(this) match {
case Some(Resolution(ResolvedModuleMethod(mod, _))) if !isLocalVar(fun) =>
val self = freshNameSupply
.newName()
.updateMetadata(
new MetadataPair(
this,
BindingsMap.Resolution(
BindingsMap.ResolvedModule(mod)
case Some(Resolution(resMethod @ ResolvedModuleMethod(mod, _)))
if !isLocalVar(fun) =>
if (app.hasDefaultsSuspended && app.arguments.isEmpty) {
app
.updateMetadata(
new MetadataPair(
this,
BindingsMap.Resolution(resMethod)
)
)

} else {
val self = freshNameSupply
.newName()
.updateMetadata(
new MetadataPair(
this,
BindingsMap.Resolution(
BindingsMap.ResolvedModule(mod)
)
)
)
val selfArg =
new CallArgument.Specified(
None,
self,
true,
identifiedLocation = null
)
processedFun.passData.remove(this) // Necessary for IrToTruffle
app.copy(
function = processedFun,
arguments = selfArg :: processedArgs
)
val selfArg =
new CallArgument.Specified(
None,
self,
true,
identifiedLocation = null
)
processedFun.passData.remove(this) // Necessary for IrToTruffle
app.copy(function = processedFun, arguments = selfArg :: processedArgs)
}
case _ =>
app.copy(function = processedFun, arguments = processedArgs)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.enso.interpreter.test.semantic;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import com.oracle.truffle.api.interop.InteropLibrary;
import java.nio.file.Paths;
import java.util.Map;
import java.util.logging.Level;
import org.enso.common.RuntimeOptions;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Language;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.io.IOAccess;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class FunctionIdentityTest {

private Context ctx;

@Before
public void initContext() {
ctx =
Context.newBuilder()
.allowExperimentalOptions(true)
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.allowExperimentalOptions(true)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.build();

var engine = ctx.getEngine();
Map<String, Language> langs = engine.getLanguages();
Assert.assertNotNull("Enso found: " + langs, langs.get("enso"));
}

@After
public void disposeContext() {
ctx.close();
ctx = null;
}

@Test
public void functionWithArgIdentity() throws Exception {
var rawCode = """
am_i_me _ = am_i_me...
""";
assertFunctionIdentity(rawCode, "Am_I_Me_With_Arg");
}

@Test
public void functionIdentity() throws Exception {
var rawCode = """
am_i_me = am_i_me...
""";
assertFunctionIdentity(rawCode, "Am_I_Me");
}

private void assertFunctionIdentity(String code, String moduleName) throws Exception {
var src = Source.newBuilder("enso", code, moduleName + ".enso").build();
var module = ctx.eval(src);
var fn = module.invokeMember("eval_expression", "am_i_me").execute(0);

assertTrue("fn: " + fn, fn.canExecute());

var rawFn = ContextUtils.unwrapValue(ctx, fn);
assertTrue("is Function: " + rawFn, rawFn instanceof Function);

var iop = InteropLibrary.getUncached();
assertEquals(moduleName + ".am_i_me", iop.getExecutableName(rawFn));
assertTrue("Has location", iop.hasSourceLocation(rawFn));
var loc = iop.getSourceLocation(rawFn);
assertNotNull("Location found", loc);
assertEquals(
"am_i_me function definition is on the first line",
code.split("\n")[0],
loc.getCharacters().toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
Expand Down Expand Up @@ -133,13 +134,16 @@ public RootCallTarget getCallTarget() {
/**
* @return the name of this function.
*/
@TruffleBoundary
public String getName() {
return getCallTarget().getRootNode().getName();
}

/**
* @return the source section this function was defined in.
*/
@TruffleBoundary
@ExportMessage(name = "getSourceLocation")
public SourceSection getSourceSection() {
return getCallTarget().getRootNode().getSourceSection();
}
Expand Down Expand Up @@ -191,6 +195,21 @@ boolean isExecutable() {
return true;
}

@ExportMessage
boolean hasSourceLocation() {
return getSourceSection() != null;
}

@ExportMessage
boolean hasExecutableName() {
return this.getName() != null;
}

@ExportMessage
String getExecutableName() {
return this.getName();
}

/**
* A class representing the executable behaviour of the function.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2028,9 +2028,15 @@ class IrToTruffle(
asScope(symbol.module.unsafeAsModule())
.getPolyglotSymbolSupplier(name)
LazyObjectNode.build(name, s)
case BindingsMap.ResolvedModuleMethod(_, method) =>
throw new CompilerError(
s"Impossible here, module method ${method.name} should be caught when translating application"
case BindingsMap.ResolvedModuleMethod(module, method) =>
LazyObjectNode.build(
method.name,
() => {
val typ = asAssociatedType(module.unsafeAsModule())
val scope = asScope(module.unsafeAsModule())
val fn = scope.getMethodForType(typ, method.name)
fn
}
)
case BindingsMap.ResolvedExtensionMethod(_, staticMethod) =>
throw new CompilerError(
Expand Down
Loading