]> git.ipfire.org Git - thirdparty/vala.git/commitdiff
Fix closures in property accessors
authorJürg Billeter <j@bitron.ch>
Sun, 21 Mar 2010 15:14:58 +0000 (16:14 +0100)
committerJürg Billeter <j@bitron.ch>
Sun, 21 Mar 2010 15:19:35 +0000 (16:19 +0100)
Fixes bug 613483.

codegen/valaccodebasemodule.vala
codegen/valaccodememberaccessmodule.vala
tests/Makefile.am
tests/methods/bug613483.vala [new file with mode: 0644]
vala/valamemberaccess.vala
vala/valasemanticanalyzer.vala

index 0ded9029b54e1f7f418d0f35068b3d21c33ff2c9..e68e88986b89c8763edca388ddaf7171fc914ff9 100644 (file)
@@ -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);
                                        }
                                }
 
@@ -1904,6 +1910,17 @@ internal class Vala.CCodeBaseModule : CCodeModule {
                                        cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), "_async_data_"), new CCodeIdentifier ("data"))));
                                }
 
+                               var cfrag = new CCodeFragment ();
+                               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 ();
index 3ebb4bf5dba4555025eb37df69ce2f8303b2e64d..edfa1333b608d525ee37012e3cb37642e92b040e 100644 (file)
@@ -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
index b813e18c5d4dcc55a2932942b4485901547a0b76..dbad7ba043c70f39062095e35902615988498fee 100644 (file)
@@ -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 (file)
index 0000000..34d7993
--- /dev/null
@@ -0,0 +1,13 @@
+class Foo {
+       int bar {
+               set {
+                       Idle.add (() => {
+                               int i = value;
+                               return false;
+                       });
+               }
+       }
+}
+
+void main () {
+}
index 63127c24f2f26faf4d9a549adf7068d078f8f263..981981ef7374723b7b2470696174b43d548b7320 100644 (file)
@@ -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;
index bafc2466d44bcba34660014547a1a97d17cc5f57..130e2ca4d848d24a50cdd64f7d0ebf75f50b754d 100644 (file)
@@ -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) {