[vala] Fix closures in property accessors



commit 7082c10201cd471e85f80a07ab161ea20442f33d
Author: Jürg Billeter <j bitron ch>
Date:   Sun Mar 21 16:14:58 2010 +0100

    Fix closures in property accessors
    
    Fixes bug 613483.

 codegen/valaccodebasemodule.vala         |  123 +++++++++++++++++-------------
 codegen/valaccodememberaccessmodule.vala |    5 +-
 tests/Makefile.am                        |    1 +
 tests/methods/bug613483.vala             |   13 +++
 vala/valamemberaccess.vala               |   25 +++++-
 vala/valasemanticanalyzer.vala           |   35 ++++++++-
 6 files changed, 141 insertions(+), 61 deletions(-)
---
diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala
index 0ded902..e68e889 100644
--- a/codegen/valaccodebasemodule.vala
+++ b/codegen/valaccodebasemodule.vala
@@ -1728,6 +1728,60 @@ internal class Vala.CCodeBaseModule : CCodeModule {
 		return result;
 	}
 
+	void capture_parameter (FormalParameter param, CCodeStruct data, CCodeBlock cblock, int block_id, CCodeBlock free_block) {
+		var param_type = param.parameter_type.copy ();
+		param_type.value_owned = true;
+		data.add_field (param_type.get_cname (), get_variable_cname (param.name));
+
+		bool is_unowned_delegate = param.parameter_type is DelegateType && !param.parameter_type.value_owned;
+
+		// create copy if necessary as captured variables may need to be kept alive
+		CCodeExpression cparam = get_variable_cexpression (param.name);
+		if (requires_copy (param_type) && !param.parameter_type.value_owned && !is_unowned_delegate)  {
+			var ma = new MemberAccess.simple (param.name);
+			ma.symbol_reference = param;
+			ma.value_type = param.parameter_type.copy ();
+			// directly access parameters in ref expressions
+			param.captured = false;
+			cparam = get_ref_cexpression (param.parameter_type, cparam, ma, param);
+			param.captured = true;
+		}
+
+		cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_variable_cname (param.name)), cparam)));
+
+		if (param.parameter_type is ArrayType) {
+			var array_type = (ArrayType) param.parameter_type;
+			for (int dim = 1; dim <= array_type.rank; dim++) {
+				data.add_field ("gint", get_array_length_cname (get_variable_cname (param.name), dim));
+				cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_array_length_cname (get_variable_cname (param.name), dim)), new CCodeIdentifier (get_array_length_cname (get_variable_cname (param.name), dim)))));
+			}
+		} else if (param.parameter_type is DelegateType) {
+			data.add_field ("gpointer", get_delegate_target_cname (get_variable_cname (param.name)));
+			cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_delegate_target_cname (get_variable_cname (param.name))), new CCodeIdentifier (get_delegate_target_cname (get_variable_cname (param.name))))));
+			if (param.parameter_type.value_owned) {
+				data.add_field ("GDestroyNotify", get_delegate_target_destroy_notify_cname (get_variable_cname (param.name)));
+				cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_delegate_target_destroy_notify_cname (get_variable_cname (param.name))), new CCodeIdentifier (get_delegate_target_destroy_notify_cname (get_variable_cname (param.name))))));
+			}
+		}
+
+		if (requires_destroy (param_type) && !is_unowned_delegate) {
+			bool old_coroutine = false;
+			if (current_method != null) {
+				old_coroutine = current_method.coroutine;
+				current_method.coroutine = false;
+			}
+
+			var ma = new MemberAccess.simple (param.name);
+			ma.symbol_reference = param;
+			ma.value_type = param_type.copy ();
+			free_block.add_statement (new CCodeExpressionStatement (get_unref_expression (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), get_variable_cname (param.name)), param.parameter_type, ma)));
+
+			if (old_coroutine) {
+				current_method.coroutine = true;
+			}
+		}
+	}
+
 	public override void visit_block (Block b) {
 		var old_symbol = current_symbol;
 		current_symbol = b;
@@ -1759,7 +1813,8 @@ internal class Vala.CCodeBaseModule : CCodeModule {
 				var unref_call = new CCodeFunctionCall (new CCodeIdentifier ("block%d_data_unref".printf (parent_block_id)));
 				unref_call.add_argument (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id)));
 				free_block.add_statement (new CCodeExpressionStatement (unref_call));
-			} else if (in_constructor || (current_method != null && current_method.binding == MemberBinding.INSTANCE)) {
+			} else if (in_constructor || (current_method != null && current_method.binding == MemberBinding.INSTANCE) ||
+			           (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE)) {
 				data.add_field ("%s *".printf (current_class.get_cname ()), "self");
 
 				var ma = new MemberAccess.simple ("this");
@@ -1828,7 +1883,8 @@ internal class Vala.CCodeBaseModule : CCodeModule {
 
 				cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id)), ref_call)));
 			} else if (in_constructor || (current_method != null && current_method.binding == MemberBinding.INSTANCE &&
-			                              (!(current_method is CreationMethod) || current_method.body != b))) {
+			                              (!(current_method is CreationMethod) || current_method.body != b)) ||
+			           (current_property_accessor != null && current_property_accessor.prop.binding == MemberBinding.INSTANCE)) {
 				var ref_call = new CCodeFunctionCall (get_dup_func_expression (new ObjectType (current_class), b.source_reference));
 				ref_call.add_argument (get_result_cexpression ("self"));
 
@@ -1841,57 +1897,7 @@ internal class Vala.CCodeBaseModule : CCodeModule {
 				// parameters are captured with the top-level block of the method
 				foreach (var param in m.get_parameters ()) {
 					if (param.captured) {
-						var param_type = param.parameter_type.copy ();
-						param_type.value_owned = true;
-						data.add_field (param_type.get_cname (), get_variable_cname (param.name));
-
-						bool is_unowned_delegate = param.parameter_type is DelegateType && !param.parameter_type.value_owned;
-
-						// create copy if necessary as captured variables may need to be kept alive
-						CCodeExpression cparam = get_variable_cexpression (param.name);
-						if (requires_copy (param_type) && !param.parameter_type.value_owned && !is_unowned_delegate)  {
-							var ma = new MemberAccess.simple (param.name);
-							ma.symbol_reference = param;
-							ma.value_type = param.parameter_type.copy ();
-							// directly access parameters in ref expressions
-							param.captured = false;
-							cparam = get_ref_cexpression (param.parameter_type, cparam, ma, param);
-							param.captured = true;
-						}
-
-						cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_variable_cname (param.name)), cparam)));
-
-						if (param.parameter_type is ArrayType) {
-							var array_type = (ArrayType) param.parameter_type;
-							for (int dim = 1; dim <= array_type.rank; dim++) {
-								data.add_field ("gint", get_array_length_cname (get_variable_cname (param.name), dim));
-								cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_array_length_cname (get_variable_cname (param.name), dim)), new CCodeIdentifier (get_array_length_cname (get_variable_cname (param.name), dim)))));
-							}
-						} else if (param.parameter_type is DelegateType) {
-							data.add_field ("gpointer", get_delegate_target_cname (get_variable_cname (param.name)));
-							cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_delegate_target_cname (get_variable_cname (param.name))), new CCodeIdentifier (get_delegate_target_cname (get_variable_cname (param.name))))));
-							if (param.parameter_type.value_owned) {
-								data.add_field ("GDestroyNotify", get_delegate_target_destroy_notify_cname (get_variable_cname (param.name)));
-								cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), get_delegate_target_destroy_notify_cname (get_variable_cname (param.name))), new CCodeIdentifier (get_delegate_target_destroy_notify_cname (get_variable_cname (param.name))))));
-							}
-						}
-
-						if (requires_destroy (param_type) && !is_unowned_delegate) {
-							bool old_coroutine = false;
-							if (current_method != null) {
-								old_coroutine = current_method.coroutine;
-								current_method.coroutine = false;
-							}
-
-							var ma = new MemberAccess.simple (param.name);
-							ma.symbol_reference = param;
-							ma.value_type = param_type.copy ();
-							free_block.add_statement (new CCodeExpressionStatement (get_unref_expression (new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), get_variable_cname (param.name)), param.parameter_type, ma)));
-
-							if (old_coroutine) {
-								current_method.coroutine = true;
-							}
-						}
+						capture_parameter (param, data, cblock, block_id, free_block);
 					}
 				}
 
@@ -1908,6 +1914,17 @@ internal class Vala.CCodeBaseModule : CCodeModule {
 				append_temp_decl (cfrag, temp_vars);
 				temp_vars.clear ();
 				cblock.add_statement (cfrag);
+			} else if (b.parent_symbol is PropertyAccessor) {
+				var acc = (PropertyAccessor) b.parent_symbol;
+
+				if (!acc.readable && acc.value_parameter.captured) {
+					capture_parameter (acc.value_parameter, data, cblock, block_id, free_block);
+				}
+
+				var cfrag = new CCodeFragment ();
+				append_temp_decl (cfrag, temp_vars);
+				temp_vars.clear ();
+				cblock.add_statement (cfrag);
 			}
 
 			var data_free = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_free"));
diff --git a/codegen/valaccodememberaccessmodule.vala b/codegen/valaccodememberaccessmodule.vala
index 3ebb4bf..edfa133 100644
--- a/codegen/valaccodememberaccessmodule.vala
+++ b/codegen/valaccodememberaccessmodule.vala
@@ -380,7 +380,10 @@ internal class Vala.CCodeMemberAccessModule : CCodeControlFlowModule {
 			} else {
 				if (p.captured) {
 					// captured variables are stored on the heap
-					var block = ((Method) p.parent_symbol).body;
+					var block = p.parent_symbol as Block;
+					if (block == null) {
+						block = ((Method) p.parent_symbol).body;
+					}
 					expr.ccodenode = new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id (block))), get_variable_cname (p.name));
 				} else if (current_method != null && current_method.coroutine) {
 					// use closure
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b813e18..dbad7ba 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -31,6 +31,7 @@ TESTS = \
 	methods/bug597426.vala \
 	methods/bug598738.vala \
 	methods/bug599892.vala \
+	methods/bug613483.vala \
 	control-flow/break.vala \
 	control-flow/expressions-conditional.vala \
 	control-flow/for.vala \
diff --git a/tests/methods/bug613483.vala b/tests/methods/bug613483.vala
new file mode 100644
index 0000000..34d7993
--- /dev/null
+++ b/tests/methods/bug613483.vala
@@ -0,0 +1,13 @@
+class Foo {
+	int bar {
+		set {
+			Idle.add (() => {
+				int i = value;
+				return false;
+			});
+		}
+	}
+}
+
+void main () {
+}
diff --git a/vala/valamemberaccess.vala b/vala/valamemberaccess.vala
index 63127c2..981981e 100644
--- a/vala/valamemberaccess.vala
+++ b/vala/valamemberaccess.vala
@@ -446,10 +446,10 @@ public class Vala.MemberAccess : Expression {
 		if (member is LocalVariable) {
 			var local = (LocalVariable) member;
 			var block = local.parent_symbol as Block;
-			if (block != null && analyzer.find_parent_method (block) != analyzer.current_method) {
+			if (block != null && analyzer.find_parent_method_or_property_accessor (block) != analyzer.current_method_or_property_accessor) {
 				// mark all methods between current method and the captured
 				// block as closures (to support nested closures)
-				Symbol sym = analyzer.current_method;
+				Symbol sym = analyzer.current_method_or_property_accessor;
 				while (sym != block) {
 					var method = sym as Method;
 					if (method != null) {
@@ -467,10 +467,10 @@ public class Vala.MemberAccess : Expression {
 		} else if (member is FormalParameter) {
 			var param = (FormalParameter) member;
 			var m = param.parent_symbol as Method;
-			if (m != null && m != analyzer.current_method && param != m.this_parameter) {
+			if (m != null && m != analyzer.current_method_or_property_accessor && param != m.this_parameter) {
 				// mark all methods between current method and the captured
 				// parameter as closures (to support nested closures)
-				Symbol sym = analyzer.current_method;
+				Symbol sym = analyzer.current_method_or_property_accessor;
 				while (sym != m) {
 					var method = sym as Method;
 					if (method != null) {
@@ -486,6 +486,23 @@ public class Vala.MemberAccess : Expression {
 					error = true;
 					Report.error (source_reference, "Cannot capture reference or output parameter `%s'".printf (param.get_full_name ()));
 				}
+			} else {
+				var acc = param.parent_symbol.parent_symbol as PropertyAccessor;
+				if (acc != null && acc != analyzer.current_method_or_property_accessor && param != acc.prop.this_parameter) {
+					// mark all methods between current method and the captured
+					// parameter as closures (to support nested closures)
+					Symbol sym = analyzer.current_method_or_property_accessor;
+					while (sym != m) {
+						var method = sym as Method;
+						if (method != null) {
+							method.closure = true;
+						}
+						sym = sym.parent_symbol;
+					}
+
+					param.captured = true;
+					acc.body.captured = true;
+				}
 			}
 		} else if (member is Field) {
 			var f = (Field) member;
diff --git a/vala/valasemanticanalyzer.vala b/vala/valasemanticanalyzer.vala
index bafc246..130e2ca 100644
--- a/vala/valasemanticanalyzer.vala
+++ b/vala/valasemanticanalyzer.vala
@@ -58,7 +58,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
 
 	public Method? current_method {
 		get {
-			var sym = current_symbol;
+			unowned Symbol sym = current_symbol;
 			while (sym is Block) {
 				sym = sym.parent_symbol;
 			}
@@ -68,7 +68,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
 
 	public Method? current_async_method {
 		get {
-			var sym = current_symbol;
+			unowned Symbol sym = current_symbol;
 			while (sym is Block || sym is Method) {
 				var m = sym as Method;
 				if (m != null && m.coroutine) {
@@ -83,7 +83,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
 
 	public PropertyAccessor? current_property_accessor {
 		get {
-			var sym = current_symbol;
+			unowned Symbol sym = current_symbol;
 			while (sym is Block) {
 				sym = sym.parent_symbol;
 			}
@@ -91,6 +91,22 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
 		}
 	}
 
+	public Symbol? current_method_or_property_accessor {
+		get {
+			unowned Symbol sym = current_symbol;
+			while (sym is Block) {
+				sym = sym.parent_symbol;
+			}
+			if (sym is Method) {
+				return sym;
+			} else if (sym is PropertyAccessor) {
+				return sym;
+			} else {
+				return null;
+			}
+		}
+	}
+
 	public DataType? current_return_type {
 		get {
 			var m = current_method;
@@ -809,6 +825,19 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
 		return sym as Method;
 	}
 
+	public Symbol? find_parent_method_or_property_accessor (Symbol sym) {
+		while (sym is Block) {
+			sym = sym.parent_symbol;
+		}
+		if (sym is Method) {
+			return sym;
+		} else if (sym is PropertyAccessor) {
+			return sym;
+		} else {
+			return null;
+		}
+	}
+
 	public bool is_in_constructor () {
 		var sym = current_symbol;
 		while (sym != null) {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]