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

Conversation

JaroslavTulach
Copy link
Member

@JaroslavTulach JaroslavTulach commented Jan 24, 2025

Pull Request Description

Step towards #5226 - one can reference a function without arguments as fn... and can get a hold on a reference to it. Program:

fn = 42
main = fn...

now prints r.fn[r.enso:2:1-7] self=_ while it used to print 42 - ignoring suspended execution ....

Checklist

Please ensure that the following checklist has been satisfied before submitting the PR:

  • The documentation has been updated, if necessary.
  • All code follows the
    Scala,
    Java,
  • Unit tests have been written where possible.

@JaroslavTulach
Copy link
Member Author

We also want this functionality to work for type methods without arguments. E.g. T.fn in following example:

type T
    fn = 10

main = T.fn...

To make that work we need to propagate resolution when there isMethod=true on a resolved type (the only argument) and the arguments are suspended- e.g. in following IR:

  body = Function.Lambda(
    arguments = List(),
    body = Application.Prefix(
      function = Name.Literal(
        name = fn,
        isMethod = true,
        location = Some(
          IdentifiedLocation[start=30,
          end=32,
          uuid=None]
        ),
        passData = MetadataStorage[AliasMetadata.Occurrence,
        DataflowAnalysis.DependencyInfo],
        diagnostics = null,
        id = 49201b83-9f93-4be3-bfb5-734912b1b0c9
      ),
      arguments = List(
        CallArgument.Specified(
          diagnostics = null,
          passData = MetadataStorage[AliasMetadata.ChildScope,
          DataflowAnalysis.DependencyInfo,
          FrameVariableNames,
          TailCall.TailPosition.Tail],
          location = IdentifiedLocation[start=28,
          end=29,
          uuid=None],
          id = 5d3000e3-7cc8-423d-b65d-6d739c34c292,
          name = None,
          value = Name.Literal(
            name = T,
            isMethod = false,
            location = Some(
              IdentifiedLocation[start=28,
              end=29,
              uuid=None]
            ),
            passData = MetadataStorage[AliasMetadata.Occurrence,
            CachePreferenceAnalysis.Weights,
            DataflowAnalysis.DependencyInfo,
            Resolution,
            TailCall.TailPosition.Tail],
            diagnostics = null,
            id = 4ed73ea9-6f95-4ba4-92b1-5ab56c55ce4b
          ),
          isSynthetic = false
        )
      ),
      hasDefaultsSuspended = true,
      location = Some(
        IdentifiedLocation[start=28,
        end=35,
        uuid=None]
      ),
      passData = MetadataStorage[DataflowAnalysis.DependencyInfo,
      TailCall.TailPosition.Tail],
      diagnostics = null,
      id = 20223c25-38e0-4526-a265-849e2194fac0
    ),

@enso-bot enso-bot bot mentioned this pull request Jan 25, 2025
@JaroslavTulach
Copy link
Member Author

JaroslavTulach commented Jan 25, 2025

type T
    fn = 10

main = T.fn...

To make that work we need to propagate resolution when there isMethod=true on a resolved type (the only argument) and the arguments are suspended- e.g. in following IR:

The problem is that there is no ResolvedTypeMethod structure yet - e.g. such a change is becoming quite complicated. Let's leave it for another round.

@JaroslavTulach JaroslavTulach changed the title Use fn... to reference argumentless module function Use fn... to reference any module function Jan 25, 2025
@JaroslavTulach JaroslavTulach added the CI: Clean build required CI runners will be cleaned before and after this PR is built. label Jan 25, 2025
@JaroslavTulach JaroslavTulach force-pushed the wip/jtulach/ReferenceToModuleFunction5226 branch from 7bf455f to ed7d2fc Compare January 25, 2025 05:42
@JaroslavTulach
Copy link
Member Author

The problem is that there is no ResolvedTypeMethod structure yet - e.g. such a change is becoming quite complicated. Let's leave it for another round.

diff --git engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala
index c9df1f19a5..c4a879387c 100644
--- engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala
+++ engine/runtime-compiler/src/main/scala/org/enso/compiler/data/BindingsMap.scala
@@ -371,6 +371,8 @@ case class BindingsMap(
               ExportedModule(ResolvedModule(modRef), exportAs, symbols)
             case ResolvedConstructor(ResolvedType(modRef, _), _) =>
               ExportedModule(ResolvedModule(modRef), exportAs, symbols)
+            case ResolvedTypeMethod(ResolvedType(modRef, _), _) =>
+              ExportedModule(ResolvedModule(modRef), exportAs, symbols)
             case ResolvedModuleMethod(modRef, _) =>
               ExportedModule(ResolvedModule(modRef), exportAs, symbols)
             case ResolvedExtensionMethod(modRef, _) =>
@@ -829,6 +831,20 @@ object BindingsMap {
     }
   }
 
+  case class ResolvedTypeMethod(tpe: ResolvedType, methodName: String) extends ResolvedMethod {
+    override def module: ModuleReference = tpe.module
+    override def toAbstract: ResolvedTypeMethod = {
+      this.copy(tpe = tpe.toAbstract)
+    }
+
+    /** @inheritdoc */
+    override def toConcrete(
+      moduleMap: ModuleMap
+    ): Option[ResolvedTypeMethod] = {
+      tpe.toConcrete(moduleMap).map(typ => this.copy(tpe = typ))
+    }
+  }
+
   /** A representation of a resolved method defined directly on module.
     *
     * @param module the module defining the method.
@@ -976,6 +992,8 @@ object BindingsMap {
           s"    The imported polyglot symbol ${symbol.name};"
         case BindingsMap.ResolvedPolyglotField(_, name) =>
           s"    The imported polyglot field ${name};"
+        case BindingsMap.ResolvedTypeMethod(tpe, symbol) =>
+          s"    The method ${symbol} defined in module ${tpe.module.getName}"
         case BindingsMap.ResolvedModuleMethod(module, symbol) =>
           s"    The method ${symbol.name} defined in module ${module.getName}"
         case BindingsMap.ResolvedExtensionMethod(module, staticMethod) =>
diff --git engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/GlobalNames.scala engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/GlobalNames.scala
index 34c0f94b43..c321ec6394 100644
--- engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/GlobalNames.scala
+++ engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/GlobalNames.scala
@@ -21,7 +21,9 @@ import org.enso.compiler.data.BindingsMap.{
   ResolutionNotFound,
   ResolvedExtensionMethod,
   ResolvedModule,
-  ResolvedModuleMethod
+  ResolvedModuleMethod,
+  ResolvedType,
+  ResolvedTypeMethod,
 }
 import org.enso.compiler.core.CompilerError
 import org.enso.compiler.core.ConstantsNames
@@ -291,6 +293,7 @@ case object GlobalNames extends IRPass {
             else
               resolveLocalApplication(
                 app,
+                lit,
                 bindings,
                 params,
                 freshNameSupply,
@@ -382,6 +385,7 @@ case object GlobalNames extends IRPass {
 
   private def resolveLocalApplication(
     app: Application.Prefix,
+    method: Name.Literal,
     bindings: BindingsMap,
     params: List[DefinitionArgument],
     freshNameSupply: FreshNameSupply,
@@ -416,6 +420,19 @@ case object GlobalNames extends IRPass {
       cons              <- resolveQualName(thisArgResolution, funAsVar)
     } yield (thisArgPos, funAsVar, cons)
 
+    if (!appData.isDefined) {
+      if (app.arguments.size == 1) {
+        val arg = app.arguments(0)
+        val res = arg.value.getMetadata(this)
+        res match {
+          case Some(Resolution(typ @ ResolvedType(_, t))) =>
+            System.err.println("Found type " + t + " and method " + method)
+            val resolvedMethod = ResolvedTypeMethod(typ, method.name)
+            method.updateMetadata(new MetadataPair(this, Resolution(resolvedMethod)))
+          case _ =>
+        }
+      }
+    }
     val newApp = appData.flatMap {
       case (
             thisArgPos,
diff --git engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/Patterns.scala engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/Patterns.scala
index 840c93d69b..cff04a4e98 100644
--- engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/Patterns.scala
+++ engine/runtime-compiler/src/main/scala/org/enso/compiler/pass/resolve/Patterns.scala
@@ -230,6 +230,10 @@ object Patterns extends IRPass {
                 case BindingsMap.ResolvedModule(_)            => 0
                 case BindingsMap.ResolvedPolyglotSymbol(_, _) => 0
                 case BindingsMap.ResolvedPolyglotField(_, _)  => 0
+                case BindingsMap.ResolvedTypeMethod(_, _) =>
+                throw new CompilerError(
+                  "Impossible, should be transformed into an error before."
+                )
                 case BindingsMap.ResolvedModuleMethod(_, _) =>
                   throw new CompilerError(
                     "Impossible, should be transformed into an error before."
@@ -309,6 +313,12 @@ object Patterns extends IRPass {
                     errors.Resolution
                       .UnexpectedMethod(s"method type pattern case")
                   )
+                case Right(_: BindingsMap.ResolvedTypeMethod) =>
+                  errors.Resolution(
+                    tpeName,
+                    errors.Resolution
+                      .UnexpectedMethod(s"method type pattern case")
+                  )
                 case Right(_: BindingsMap.ResolvedExtensionMethod) =>
                   errors.Resolution(
                     tpeName,
diff --git engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java
index ded60a3452..9aaaaf10b8 100644
--- engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java
+++ engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java
@@ -134,7 +134,6 @@ public final class Function extends EnsoObject {
   /**
    * @return the name of this function.
    */
-  @TruffleBoundary
   public String getName() {
     return getCallTarget().getRootNode().getName();
   }
diff --git engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala
index fa692ea187..cdbda7bc1f 100644
--- engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala
+++ engine/runtime/src/main/scala/org/enso/interpreter/runtime/IrToTruffle.scala
@@ -932,6 +932,10 @@ class IrToTruffle(
             throw new CompilerError(
               "Impossible module method here, should be caught by MethodDefinitions pass."
             )
+          case _: BindingsMap.ResolvedTypeMethod =>
+            throw new CompilerError(
+              "Impossible module method here, should be caught by MethodDefinitions pass."
+            )
           case _: BindingsMap.ResolvedExtensionMethod =>
             throw new CompilerError(
               "Impossible static method here, should be caught by MethodDefinitions pass."

This diff is showing the direction I was exploring. However, as said, let's leave this to another PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI: Clean build required CI runners will be cleaned before and after this PR is built.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant