Mercerenies / gdlisp

Lisp on the Godot platform
GNU General Public License v3.0
141 stars 1 forks source link

Accessing statics from inner scope #30

Closed Mercerenies closed 3 years ago

Mercerenies commented 3 years ago

So, evidently, Godot won't let you access static functions from outer scope in an inner class. That is to say, the following code compiles in GDLisp (correctly)

(defn foo () 1)

(defclass Example (Reference)
  (defn call-foo () (foo)))

and becomes (simplified slightly for the sake of example)

extends Node

static func foo():
    return 1

class Example extends Reference:
    func call_foo():
        return foo()

Which doesn't run in Godot. Godot thinks foo doesn't exist in the inner class scope. I suppose for now we need to preface all inner calls with load. This seems like a GDScript issue, not a GDLisp one, but we need to accommodate I suppose.

Mercerenies commented 3 years ago

Found the issue report on Godot. Apparently it's intended behavior. So we definitely have to work around it.

https://github.com/godotengine/godot/issues/25646

Mercerenies commented 3 years ago

Lambdas exhibit the same behavior. Basically, anything that creates an inner class in Godot and needs to reference an outer static.

(defn foo () 1)

(lambda () (foo))
Mercerenies commented 3 years ago

It goes without saying, but I'll say it anyway so there's a note of it. Lambda classes are also guilty of this.

(defn foo () 1)

(new (Reference) (defn example () (foo)))
Mercerenies commented 3 years ago

Two more for good measure

(defn foo () 1)

(defclass Example (Reference)
  (defn call-foo () static (foo)))

 

(defn foo () 1)

(new (Reference) (defn example () static (foo)))

Edit: As of dc08744, the latter example is no longer allowed. Lambda classes cannot have static functions.

Mercerenies commented 3 years ago

In summary, I can currently think of several places this is problematic.

  1. [X] Top-level classes (resolved by bb52bf6)
  2. [X] Top-level classes (static functions) (resolved by bb52bf6)
  3. [X] Lambdas (resolved by 9bed099)
  4. [X] Lambda classes (resolved by 9bed099)
  5. [X] Lambda classes (static functions) (resolved by 9bed099) (disallowed explicitly by dc08744)
  6. [X] labels forms which require full closure (resolved by 9bed099)
  7. [X] flet / labels forms which require (simple) closure (resolved by 9bed099)
  8. [X] labels / flet forms which do not require any closure (resolved by 16b7d86)

Other problems that need fixing

Mercerenies commented 3 years ago

As of fb84972, this issue has mercifully been resolved.

The only minor divergence from the original plan is that lambda classes can no longer have static methods at all, so static methods in lambda classes are a nonissue. That change arose while trying to fix this issue and realizing that static methods on lambda classes were horribly broken (they pretended they had access to a closure and a self argument that they had no right too) and are frankly not that useful to begin with, so I simply made it a compile error.

Aside from that, (GDScript) static functions in the outer scope are now accessible. If accessed from a non-static inner class scope (including explicit inner classes, lambdas, lambda classes, function references, and labels blocks), a reference to the enclosing class will be created on the instance using load. If accessed from a static inner class scope (which can only happen if explicitly requested in GDLisp; none of the implicit class constructs in GDLisp create static inner class methods as of right now), the current file will be loaded on-demand in the method using load. Note that the outer class reference is only created if it's actually going to be used.