From: Jürg Billeter Date: Sun, 21 Mar 2010 15:14:58 +0000 (+0100) Subject: Fix closures in property accessors X-Git-Tag: 0.8.0~106 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7082c10201cd471e85f80a07ab161ea20442f33d;p=thirdparty%2Fvala.git Fix closures in property accessors Fixes bug 613483. --- diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala index 0ded9029b..e68e88986 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); } } @@ -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 (); diff --git a/codegen/valaccodememberaccessmodule.vala b/codegen/valaccodememberaccessmodule.vala index 3ebb4bf5d..edfa1333b 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 b813e18c5..dbad7ba04 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 000000000..34d799327 --- /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 63127c24f..981981ef7 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 bafc2466d..130e2ca4d 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) {