godot icon indicating copy to clipboard operation
godot copied to clipboard

GDScript: Add `Self` to reference a class from inside

Open vonagam opened this issue 2 years ago • 1 comments

Add Self as a way to reference a class from inside its script.

class_name Global

const Constant := Self

class Extended extends Self:
  pass

static func static_func(instance: Self) -> Self:
  return instance

func instance_func() -> Self:
  return self

func test():
  assert(Self == Constant)
  assert(Self == Global)

  var extended := Extended.new()
  assert(extended is Self)

  var constructed := Self.new()
  assert(constructed is Self)

  var static_funced := Self.static_func(self)
  assert(static_funced is Self)

  var instance_funced := instance_func()
  assert(instance_funced is Self)

  var variable: Self = self
  assert(variable is Self)

While at it converted extends in ClassNode from Vector<StringName> to Vector<IdentifierNode *> - for better error placement.

And in is_shadowing removed bool return type and added a check for shadowing built-in types (var Array := 3, such check was done only for members before).

Implements proposal #391.

This is a simple implementation and Self here always points to the class where it is used, but it does not change its meaning with inheritance:

class A:
  static func create() -> Self:
    return Self.new()
    
class B extends A:
  pass
  
func _ready():
  var x := B.create()

Here x will be A, not B. I think that maybe it is desirable for it to be B - to match context dependent behavior of (potential future) type self. In that case there is a need for a separate name for simple type presented here.

vonagam avatar Mar 08 '23 20:03 vonagam

Here x will be A, not B. I think that maybe it is desirable for it to be B - to match context dependent behavior of (potential future) type self. In that case there is a need for a separate name for simple type presented here.

Yes, if we decide to add late static binding (like in PHP), then it should be a separate keyword/identifier.

dalexeev avatar Mar 09 '23 08:03 dalexeev

I'll close this one for now. I think that Self type can be expected to be a type of self, what @dalexeev mentioned as late static binding. I've moved fixes into separate PRs and now they are merged.

As for accessing class without global pollution, I think maybe some new annotation can solve it:

@global(false)
class_name Foo

And if it can support strings as a value for a global name it might allow to expose nested classes and maybe have uses in plugin development:

@global("LongPluginNameFoo")
class_name Foo

  @global("GlobalBar")
  class Bar: pass

vonagam avatar Mar 20 '23 18:03 vonagam