[vala] Fix closures in property accessors
- From: Jürg Billeter <juergbi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vala] Fix closures in property accessors
- Date: Sun, 21 Mar 2010 15:20:09 +0000 (UTC)
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]