Browse Source

Merge pull request #92241 from dalexeev/gds-fix-lambda-captures-non-local-vars

GDScript: Fix lambdas capturing non-local variables
Rémi Verschelde 1 year ago
parent
commit
dbc6f2af1d

+ 17 - 4
modules/gdscript/gdscript_analyzer.cpp

@@ -4061,10 +4061,23 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
 				mark_lambda_use_self();
 				return; // No need to capture.
 			}
-			// If the identifier is local, check if it's any kind of capture by comparing their source function.
-			// Only capture locals and enum values. Constants are still accessible from the lambda using the script reference. If not, this method is done.
-			if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT) {
-				return;
+
+			switch (p_identifier->source) {
+				case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER:
+				case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
+				case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
+				case GDScriptParser::IdentifierNode::LOCAL_BIND:
+					break; // Need to capture.
+				case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE: // A global.
+				case GDScriptParser::IdentifierNode::LOCAL_CONSTANT:
+				case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
+				case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
+				case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
+				case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:
+				case GDScriptParser::IdentifierNode::MEMBER_CLASS:
+				case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:
+				case GDScriptParser::IdentifierNode::STATIC_VARIABLE:
+					return; // No need to capture.
 			}
 
 			GDScriptParser::FunctionNode *function_test = current_lambda->function;

+ 26 - 0
modules/gdscript/tests/scripts/runtime/features/lambda_captures.gd

@@ -0,0 +1,26 @@
+# GH-92217
+# TODO: Add more tests.
+
+static var static_var: int:
+	set(value):
+		prints("set static_var", value)
+	get:
+		print("get static_var")
+		return 0
+
+var member_var: int:
+	set(value):
+		prints("set member_var", value)
+	get:
+		print("get member_var")
+		return 0
+
+func test():
+	var lambda := func ():
+		var _tmp := static_var
+		_tmp = member_var
+
+		static_var = 1
+		member_var = 1
+
+	lambda.call()

+ 5 - 0
modules/gdscript/tests/scripts/runtime/features/lambda_captures.out

@@ -0,0 +1,5 @@
+GDTEST_OK
+get static_var
+get member_var
+set static_var 1
+set member_var 1