Browse Source

Merge pull request #60396 from vnen/gdscript-self-lambda

Rémi Verschelde 3 years ago
parent
commit
f626e8ef91

+ 1 - 0
modules/gdscript/gdscript.h

@@ -262,6 +262,7 @@ class GDScriptInstance : public ScriptInstance {
 	friend class GDScript;
 	friend class GDScript;
 	friend class GDScriptFunction;
 	friend class GDScriptFunction;
 	friend class GDScriptLambdaCallable;
 	friend class GDScriptLambdaCallable;
+	friend class GDScriptLambdaSelfCallable;
 	friend class GDScriptCompiler;
 	friend class GDScriptCompiler;
 	friend struct GDScriptUtilityFunctionsDefinitions;
 	friend struct GDScriptUtilityFunctionsDefinitions;
 
 

+ 54 - 14
modules/gdscript/gdscript_analyzer.cpp

@@ -2498,12 +2498,17 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
 		}
 		}
 
 
 		if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
 		if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
-			push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parser->current_function->identifier->name), p_call);
+			// Get the parent function above any lambda.
+			GDScriptParser::FunctionNode *parent_function = parser->current_function;
+			while (parent_function->source_lambda) {
+				parent_function = parent_function->source_lambda->parent_function;
+			}
+			push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call);
 		} else if (!is_self && base_type.is_meta_type && !is_static) {
 		} else if (!is_self && base_type.is_meta_type && !is_static) {
 			base_type.is_meta_type = false; // For `to_string()`.
 			base_type.is_meta_type = false; // For `to_string()`.
 			push_error(vformat(R"*(Cannot call non-static function "%s()" on the class "%s" directly. Make an instance instead.)*", p_call->function_name, base_type.to_string()), p_call);
 			push_error(vformat(R"*(Cannot call non-static function "%s()" on the class "%s" directly. Make an instance instead.)*", p_call->function_name, base_type.to_string()), p_call);
-		} else if (is_self && !is_static && !lambda_stack.is_empty()) {
-			push_error(vformat(R"*(Cannot call non-static function "%s()" from a lambda function.)*", p_call->function_name), p_call);
+		} else if (is_self && !is_static) {
+			mark_lambda_use_self();
 		}
 		}
 
 
 		call_type = return_type;
 		call_type = return_type;
@@ -2636,10 +2641,10 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node)
 
 
 	if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, result.native_type)) {
 	if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, result.native_type)) {
 		push_error(R"*(Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.)*", p_get_node);
 		push_error(R"*(Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.)*", p_get_node);
-	} else if (!lambda_stack.is_empty()) {
-		push_error(R"*(Cannot use shorthand "get_node()" notation ("$") inside a lambda. Use a captured variable instead.)*", p_get_node);
 	}
 	}
 
 
+	mark_lambda_use_self();
+
 	p_get_node->set_datatype(result);
 	p_get_node->set_datatype(result);
 }
 }
 
 
@@ -2854,21 +2859,25 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
 			MethodBind *getter = ClassDB::get_method(native, getter_name);
 			MethodBind *getter = ClassDB::get_method(native, getter_name);
 			if (getter != nullptr) {
 			if (getter != nullptr) {
 				p_identifier->set_datatype(type_from_property(getter->get_return_info()));
 				p_identifier->set_datatype(type_from_property(getter->get_return_info()));
+				p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
 			}
 			}
 			return;
 			return;
 		}
 		}
 		if (ClassDB::get_method_info(native, name, &method_info)) {
 		if (ClassDB::get_method_info(native, name, &method_info)) {
 			// Method is callable.
 			// Method is callable.
 			p_identifier->set_datatype(make_callable_type(method_info));
 			p_identifier->set_datatype(make_callable_type(method_info));
+			p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
 			return;
 			return;
 		}
 		}
 		if (ClassDB::get_signal(native, name, &method_info)) {
 		if (ClassDB::get_signal(native, name, &method_info)) {
 			// Signal is a type too.
 			// Signal is a type too.
 			p_identifier->set_datatype(make_signal_type(method_info));
 			p_identifier->set_datatype(make_signal_type(method_info));
+			p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
 			return;
 			return;
 		}
 		}
 		if (ClassDB::has_enum(native, name)) {
 		if (ClassDB::has_enum(native, name)) {
 			p_identifier->set_datatype(make_native_enum_type(native, name));
 			p_identifier->set_datatype(make_native_enum_type(native, name));
+			p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
 			return;
 			return;
 		}
 		}
 		bool valid = false;
 		bool valid = false;
@@ -2877,6 +2886,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
 			p_identifier->is_constant = true;
 			p_identifier->is_constant = true;
 			p_identifier->reduced_value = int_constant;
 			p_identifier->reduced_value = int_constant;
 			p_identifier->set_datatype(type_from_variant(int_constant, p_identifier));
 			p_identifier->set_datatype(type_from_variant(int_constant, p_identifier));
+			p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
 			return;
 			return;
 		}
 		}
 	}
 	}
@@ -2927,7 +2937,11 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
 			p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
 			p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
 			found_source = true;
 			found_source = true;
 			break;
 			break;
+		case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:
+			mark_lambda_use_self();
+			break;
 		case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
 		case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
+			mark_lambda_use_self();
 			p_identifier->variable_source->usages++;
 			p_identifier->variable_source->usages++;
 			[[fallthrough]];
 			[[fallthrough]];
 		case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
 		case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
@@ -2958,18 +2972,37 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
 	}
 	}
 
 
 	if (found_source) {
 	if (found_source) {
-		// If the identifier is local, check if it's any kind of capture by comparing their source function.
-		// Only capture locals and members and enum values. Constants are still accessible from the lambda using the script reference.
-		if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT || lambda_stack.is_empty()) {
-			return;
+		if ((p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) && parser->current_function && parser->current_function->is_static) {
+			// Get the parent function above any lambda.
+			GDScriptParser::FunctionNode *parent_function = parser->current_function;
+			while (parent_function->source_lambda) {
+				parent_function = parent_function->source_lambda->parent_function;
+			}
+			push_error(vformat(R"*(Cannot access instance variable "%s" from the static function "%s()".)*", p_identifier->name, parent_function->identifier->name), p_identifier);
 		}
 		}
 
 
-		GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->function;
-		while (function_test != nullptr && function_test != p_identifier->source_function && function_test->source_lambda != nullptr && !function_test->source_lambda->captures_indices.has(p_identifier->name)) {
-			function_test->source_lambda->captures_indices[p_identifier->name] = function_test->source_lambda->captures.size();
-			function_test->source_lambda->captures.push_back(p_identifier);
-			function_test = function_test->source_lambda->parent_function;
+		if (!lambda_stack.is_empty()) {
+			// If the identifier is a member variable (including the native class properties), we consider the lambda to be using `self`, so we keep a reference to the current instance.
+			if (p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) {
+				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;
+			}
+
+			GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->function;
+			// Make sure we aren't capturing variable in the same lambda.
+			// This also add captures for nested lambdas.
+			while (function_test != nullptr && function_test != p_identifier->source_function && function_test->source_lambda != nullptr && !function_test->source_lambda->captures_indices.has(p_identifier->name)) {
+				function_test->source_lambda->captures_indices[p_identifier->name] = function_test->source_lambda->captures.size();
+				function_test->source_lambda->captures.push_back(p_identifier);
+				function_test = function_test->source_lambda->parent_function;
+			}
 		}
 		}
+
 		return;
 		return;
 	}
 	}
 
 
@@ -3149,6 +3182,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
 void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) {
 void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) {
 	p_self->is_constant = false;
 	p_self->is_constant = false;
 	p_self->set_datatype(type_from_metatype(parser->current_class->get_datatype()));
 	p_self->set_datatype(type_from_metatype(parser->current_class->get_datatype()));
+	mark_lambda_use_self();
 }
 }
 
 
 void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscript) {
 void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscript) {
@@ -4141,6 +4175,12 @@ void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) {
 #endif
 #endif
 }
 }
 
 
+void GDScriptAnalyzer::mark_lambda_use_self() {
+	for (GDScriptParser::LambdaNode *lambda : lambda_stack) {
+		lambda->use_self = true;
+	}
+}
+
 bool GDScriptAnalyzer::class_exists(const StringName &p_class) const {
 bool GDScriptAnalyzer::class_exists(const StringName &p_class) const {
 	return ClassDB::class_exists(p_class) && ClassDB::is_class_exposed(p_class);
 	return ClassDB::class_exists(p_class) && ClassDB::is_class_exposed(p_class);
 }
 }

+ 2 - 1
modules/gdscript/gdscript_analyzer.h

@@ -42,7 +42,7 @@ class GDScriptAnalyzer {
 	HashMap<String, Ref<GDScriptParserRef>> depended_parsers;
 	HashMap<String, Ref<GDScriptParserRef>> depended_parsers;
 
 
 	const GDScriptParser::EnumNode *current_enum = nullptr;
 	const GDScriptParser::EnumNode *current_enum = nullptr;
-	List<const GDScriptParser::LambdaNode *> lambda_stack;
+	List<GDScriptParser::LambdaNode *> lambda_stack;
 
 
 	// Tests for detecting invalid overloading of script members
 	// Tests for detecting invalid overloading of script members
 	static _FORCE_INLINE_ bool has_member_name_conflict_in_script_class(const StringName &p_name, const GDScriptParser::ClassNode *p_current_class_node);
 	static _FORCE_INLINE_ bool has_member_name_conflict_in_script_class(const StringName &p_name, const GDScriptParser::ClassNode *p_current_class_node);
@@ -115,6 +115,7 @@ class GDScriptAnalyzer {
 	bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
 	bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
 	void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
 	void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
 	void mark_node_unsafe(const GDScriptParser::Node *p_node);
 	void mark_node_unsafe(const GDScriptParser::Node *p_node);
+	void mark_lambda_use_self();
 	bool class_exists(const StringName &p_class) const;
 	bool class_exists(const StringName &p_class) const;
 	Ref<GDScriptParserRef> get_parser_for(const String &p_path);
 	Ref<GDScriptParserRef> get_parser_for(const String &p_path);
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED

+ 2 - 2
modules/gdscript/gdscript_byte_codegen.cpp

@@ -1211,8 +1211,8 @@ void GDScriptByteCodeGenerator::write_call_script_function(const Address &p_targ
 	append(p_function_name);
 	append(p_function_name);
 }
 }
 
 
-void GDScriptByteCodeGenerator::write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) {
-	append(GDScriptFunction::OPCODE_CREATE_LAMBDA, 1 + p_captures.size());
+void GDScriptByteCodeGenerator::write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures, bool p_use_self) {
+	append(p_use_self ? GDScriptFunction::OPCODE_CREATE_SELF_LAMBDA : GDScriptFunction::OPCODE_CREATE_LAMBDA, 1 + p_captures.size());
 	for (int i = 0; i < p_captures.size(); i++) {
 	for (int i = 0; i < p_captures.size(); i++) {
 		append(p_captures[i]);
 		append(p_captures[i]);
 	}
 	}

+ 1 - 1
modules/gdscript/gdscript_byte_codegen.h

@@ -470,7 +470,7 @@ public:
 	virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
 	virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
 	virtual void write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
 	virtual void write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
 	virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
 	virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
-	virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) override;
+	virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures, bool p_use_self) override;
 	virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) override;
 	virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) override;
 	virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) override;
 	virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) override;
 	virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) override;
 	virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) override;

+ 1 - 1
modules/gdscript/gdscript_codegen.h

@@ -131,7 +131,7 @@ public:
 	virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
 	virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
-	virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) = 0;
+	virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures, bool p_use_self) = 0;
 	virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) = 0;
 	virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) = 0;
 	virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) = 0;
 	virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) = 0;
 	virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) = 0;
 	virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) = 0;

+ 1 - 1
modules/gdscript/gdscript_compiler.cpp

@@ -1197,7 +1197,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 				return GDScriptCodeGenerator::Address();
 				return GDScriptCodeGenerator::Address();
 			}
 			}
 
 
-			gen->write_lambda(result, function, captures);
+			gen->write_lambda(result, function, captures, lambda->use_self);
 
 
 			for (int i = 0; i < captures.size(); i++) {
 			for (int i = 0; i < captures.size(); i++) {
 				if (captures[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
 				if (captures[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {

+ 19 - 0
modules/gdscript/gdscript_disassembler.cpp

@@ -792,6 +792,25 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
 
 
 				incr = 3 + captures_count;
 				incr = 3 + captures_count;
 			} break;
 			} break;
+			case OPCODE_CREATE_SELF_LAMBDA: {
+				int captures_count = _code_ptr[ip + 1 + instr_var_args];
+				GDScriptFunction *lambda = _lambdas_ptr[_code_ptr[ip + 2 + instr_var_args]];
+
+				text += DADDR(1 + captures_count);
+				text += "create self lambda from ";
+				text += lambda->name.operator String();
+				text += "function, captures (";
+
+				for (int i = 0; i < captures_count; i++) {
+					if (i > 0) {
+						text += ", ";
+					}
+					text += DADDR(1 + i);
+				}
+				text += ")";
+
+				incr = 3 + captures_count;
+			} break;
 			case OPCODE_JUMP: {
 			case OPCODE_JUMP: {
 				text += "jump ";
 				text += "jump ";
 				text += itos(_code_ptr[ip + 1]);
 				text += itos(_code_ptr[ip + 1]);

+ 1 - 0
modules/gdscript/gdscript_function.h

@@ -299,6 +299,7 @@ public:
 		OPCODE_AWAIT,
 		OPCODE_AWAIT,
 		OPCODE_AWAIT_RESUME,
 		OPCODE_AWAIT_RESUME,
 		OPCODE_CREATE_LAMBDA,
 		OPCODE_CREATE_LAMBDA,
+		OPCODE_CREATE_SELF_LAMBDA,
 		OPCODE_JUMP,
 		OPCODE_JUMP,
 		OPCODE_JUMP_IF,
 		OPCODE_JUMP_IF,
 		OPCODE_JUMP_IF_NOT,
 		OPCODE_JUMP_IF_NOT,

+ 78 - 0
modules/gdscript/gdscript_lambda_callable.cpp

@@ -93,3 +93,81 @@ GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptF
 
 
 	h = (uint32_t)hash_djb2_one_64((uint64_t)this);
 	h = (uint32_t)hash_djb2_one_64((uint64_t)this);
 }
 }
+
+bool GDScriptLambdaSelfCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+	// Lambda callables are only compared by reference.
+	return p_a == p_b;
+}
+
+bool GDScriptLambdaSelfCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+	// Lambda callables are only compared by reference.
+	return p_a < p_b;
+}
+
+uint32_t GDScriptLambdaSelfCallable::hash() const {
+	return h;
+}
+
+String GDScriptLambdaSelfCallable::get_as_text() const {
+	if (function->get_name() != StringName()) {
+		return function->get_name().operator String() + "(lambda)";
+	}
+	return "(anonymous lambda)";
+}
+
+CallableCustom::CompareEqualFunc GDScriptLambdaSelfCallable::get_compare_equal_func() const {
+	return compare_equal;
+}
+
+CallableCustom::CompareLessFunc GDScriptLambdaSelfCallable::get_compare_less_func() const {
+	return compare_less;
+}
+
+ObjectID GDScriptLambdaSelfCallable::get_object() const {
+	return object->get_instance_id();
+}
+
+void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+#ifdef DEBUG_ENABLED
+	if (object->get_script_instance() == nullptr || object->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
+		ERR_PRINT("Trying to call a lambda with an invalid instance.");
+		r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
+		return;
+	}
+#endif
+
+	int captures_amount = captures.size();
+
+	if (captures_amount > 0) {
+		Vector<const Variant *> args;
+		args.resize(p_argcount + captures_amount);
+		for (int i = 0; i < captures_amount; i++) {
+			args.write[i] = &captures[i];
+		}
+		for (int i = 0; i < p_argcount; i++) {
+			args.write[i + captures_amount] = p_arguments[i];
+		}
+
+		r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), args.ptrw(), args.size(), r_call_error);
+		r_call_error.argument -= captures_amount;
+	} else {
+		r_return_value = function->call(static_cast<GDScriptInstance *>(object->get_script_instance()), p_arguments, p_argcount, r_call_error);
+	}
+}
+
+GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
+	reference = p_self;
+	object = p_self.ptr();
+	function = p_function;
+	captures = p_captures;
+
+	h = (uint32_t)hash_djb2_one_64((uint64_t)this);
+}
+
+GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
+	object = p_self;
+	function = p_function;
+	captures = p_captures;
+
+	h = (uint32_t)hash_djb2_one_64((uint64_t)this);
+}

+ 25 - 0
modules/gdscript/gdscript_lambda_callable.h

@@ -62,4 +62,29 @@ public:
 	virtual ~GDScriptLambdaCallable() = default;
 	virtual ~GDScriptLambdaCallable() = default;
 };
 };
 
 
+// Lambda callable that references a particular object, so it can use `self` in the body.
+class GDScriptLambdaSelfCallable : public CallableCustom {
+	GDScriptFunction *function = nullptr;
+	Ref<RefCounted> reference; // For objects that are RefCounted, keep a reference.
+	Object *object = nullptr; // For non RefCounted objects, use a direct pointer.
+	uint32_t h;
+
+	Vector<Variant> captures;
+
+	static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
+	static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
+
+public:
+	uint32_t hash() const override;
+	String get_as_text() const override;
+	CompareEqualFunc get_compare_equal_func() const override;
+	CompareLessFunc get_compare_less_func() const override;
+	ObjectID get_object() const override;
+	void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
+
+	GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
+	GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
+	virtual ~GDScriptLambdaSelfCallable() = default;
+};
+
 #endif // GDSCRIPT_LAMBDA_CALLABLE
 #endif // GDSCRIPT_LAMBDA_CALLABLE

+ 0 - 3
modules/gdscript/gdscript_parser.cpp

@@ -2200,9 +2200,6 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_pre
 	if (current_function && current_function->is_static) {
 	if (current_function && current_function->is_static) {
 		push_error(R"(Cannot use "self" inside a static function.)");
 		push_error(R"(Cannot use "self" inside a static function.)");
 	}
 	}
-	if (in_lambda) {
-		push_error(R"(Cannot use "self" inside a lambda.)");
-	}
 	SelfNode *self = alloc_node<SelfNode>();
 	SelfNode *self = alloc_node<SelfNode>();
 	self->current_class = current_class;
 	self->current_class = current_class;
 	return self;
 	return self;

+ 2 - 0
modules/gdscript/gdscript_parser.h

@@ -767,6 +767,7 @@ public:
 			LOCAL_BIND, // Pattern bind.
 			LOCAL_BIND, // Pattern bind.
 			MEMBER_VARIABLE,
 			MEMBER_VARIABLE,
 			MEMBER_CONSTANT,
 			MEMBER_CONSTANT,
+			INHERITED_VARIABLE,
 		};
 		};
 		Source source = UNDEFINED_SOURCE;
 		Source source = UNDEFINED_SOURCE;
 
 
@@ -800,6 +801,7 @@ public:
 		FunctionNode *parent_function = nullptr;
 		FunctionNode *parent_function = nullptr;
 		Vector<IdentifierNode *> captures;
 		Vector<IdentifierNode *> captures;
 		Map<StringName, int> captures_indices;
 		Map<StringName, int> captures_indices;
+		bool use_self = false;
 
 
 		bool has_name() const {
 		bool has_name() const {
 			return function && function->identifier;
 			return function && function->identifier;

+ 36 - 0
modules/gdscript/gdscript_vm.cpp

@@ -306,6 +306,7 @@ void (*type_init_function_table[])(Variant *) = {
 		&&OPCODE_AWAIT,                              \
 		&&OPCODE_AWAIT,                              \
 		&&OPCODE_AWAIT_RESUME,                       \
 		&&OPCODE_AWAIT_RESUME,                       \
 		&&OPCODE_CREATE_LAMBDA,                      \
 		&&OPCODE_CREATE_LAMBDA,                      \
+		&&OPCODE_CREATE_SELF_LAMBDA,                 \
 		&&OPCODE_JUMP,                               \
 		&&OPCODE_JUMP,                               \
 		&&OPCODE_JUMP_IF,                            \
 		&&OPCODE_JUMP_IF,                            \
 		&&OPCODE_JUMP_IF_NOT,                        \
 		&&OPCODE_JUMP_IF_NOT,                        \
@@ -2277,6 +2278,41 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 			}
 			}
 			DISPATCH_OPCODE;
 			DISPATCH_OPCODE;
 
 
+			OPCODE(OPCODE_CREATE_SELF_LAMBDA) {
+				CHECK_SPACE(2 + instr_arg_count);
+
+				GD_ERR_BREAK(p_instance == nullptr);
+
+				ip += instr_arg_count;
+
+				int captures_count = _code_ptr[ip + 1];
+				GD_ERR_BREAK(captures_count < 0);
+
+				int lambda_index = _code_ptr[ip + 2];
+				GD_ERR_BREAK(lambda_index < 0 || lambda_index >= _lambdas_count);
+				GDScriptFunction *lambda = _lambdas_ptr[lambda_index];
+
+				Vector<Variant> captures;
+				captures.resize(captures_count);
+				for (int i = 0; i < captures_count; i++) {
+					GET_INSTRUCTION_ARG(arg, i);
+					captures.write[i] = *arg;
+				}
+
+				GDScriptLambdaSelfCallable *callable;
+				if (Object::cast_to<RefCounted>(p_instance->owner)) {
+					callable = memnew(GDScriptLambdaSelfCallable(Ref<RefCounted>(Object::cast_to<RefCounted>(p_instance->owner)), lambda, captures));
+				} else {
+					callable = memnew(GDScriptLambdaSelfCallable(p_instance->owner, lambda, captures));
+				}
+
+				GET_INSTRUCTION_ARG(result, captures_count);
+				*result = Callable(callable);
+
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
 			OPCODE(OPCODE_JUMP) {
 			OPCODE(OPCODE_JUMP) {
 				CHECK_SPACE(2);
 				CHECK_SPACE(2);
 				int to = _code_ptr[ip + 1];
 				int to = _code_ptr[ip + 1];

+ 23 - 0
modules/gdscript/tests/scripts/runtime/features/lambda_use_self.gd

@@ -0,0 +1,23 @@
+var member = "foo"
+
+func bar():
+	print("bar")
+
+func test():
+	var lambda1 = func():
+		print(member)
+	lambda1.call()
+
+	var lambda2 = func():
+		var nested = func():
+			print(member)
+		nested.call()
+	lambda2.call()
+
+	var lambda3 = func():
+		bar()
+	lambda3.call()
+
+	var lambda4 = func():
+		return self
+	print(lambda4.call() == self)

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

@@ -0,0 +1,5 @@
+GDTEST_OK
+foo
+foo
+bar
+true