godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.49k stars 21.07k forks source link

[4.4 dev2] Compile Error: Identifier not found (parser bug) when accessing typed dictionary value's members and functions #96881

Open filpano opened 1 month ago

filpano commented 1 month ago

Tested versions

System information

Godot v4.4.dev2 - Windows 10.0.19045 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 4070 (NVIDIA; 32.0.15.6094) - AMD Ryzen 7 5700X 8-Core Processor (16 Threads)

Issue description

image Relevant log:

  res://globals/resources/quests/test.gd:8 - Parse Error: Parser bug (please report): Could not find external parser for class "Step". (Trying to resolve class member)

When iterating over a typed dictionary using the for key in typed_dict: ... syntax, accessing the dictionary value's:

Causes the above error to be produced. In most cases, the error can be worked around by assigning the typed dictionary's value to an intermediate variable. Sometimes explicit typing is required - see the examples below to see what works and what doesn't.

Steps to reproduce

class_name Step extends Resource

signal test_step(param: String)

@export var step_id: String

func nop() -> void:
    pass

func get_id() -> String:
    return step_id

enum StepState {
    ACTIVE
}
class_name Parent extends Resource

@export 
var steps: Dictionary[String, Step]
# Test class for https://github.com/godotengine/godot/issues/96881
# Uncommenting any of these functions yield a GDScript compile error
extends Node

func _on_connect() -> void:
    pass

#func _connect_signal(parent: Parent) -> void:
    #for step_id: String in parent.steps:
        #parent.steps[step_id].test_step.connect(_on_connect)
        ## But this works! (with or without type)
        #var x = parent.steps[step_id]
        #x.test_step.connect(_on_connect)

#func access_member(parent: Parent) -> void:
    #for step_id: String in parent.steps:
        #parent.steps[step_id].step_id
        ## This doesn't work either
        #var x: String = parent.steps[step_id].step_id
        ## Neither does this
        #var x: String = parent.steps[step_id].step_id as String
        ## But this works! (with or without type)
        #var x = parent.steps[step_id]
        #x.step_id

#func call_func(parent: Parent) -> void:
    #for step_id: String in parent.steps:
        #parent.steps[step_id].nop()
        ## This doesn't work either
        #var id = parent.steps[step_id].get_id()
        ## This doesn't work...
        #var x = parent.steps[step_id]
        #x.get_id()
        ## But this works!
        #var x: Step = parent.steps[step_id]
        #x.get_id()

#func access_enum(parent: Parent) -> void:
    #for step_id: String in parent.steps:
        #parent.steps[step_id].StepState
        ## This doesn't work either
        #var state = parent.steps[step_id].StepState
        ## But this works!
        #var state: Step.StepState = parent.steps[step_id].StepState

Minimal reproduction project (MRP)

See above.

tracefree commented 4 days ago

I can confirm this issue still exists in 4.4dev3. I also discovered a very cursed workaround: simply writing the word Step anywhere in the script before a line that throws the error fixes it. So this works:

func _connect_signal(parent: Parent) -> void:
    for step_id: String in parent.steps:
        Step # Just writing the identifier shouldn't have any effect, yet it fixes the problem somehow
        parent.steps[step_id].test_step.connect(_on_connect)
tracefree commented 4 days ago

Fixed by #98336 hopefully, could someone please test if it resolves all issues (and doesn't introduce new ones)?

filpano commented 3 days ago

Thanks! I won't be able to test this until Wednesday - if no one chimes in I'll take a look then.