From f904c7e269c7f7b0a6dcefa4f8f892b981a71917 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Corentin=20No=C3=ABl?= Date: Thu, 15 Oct 2020 10:35:25 +0200 Subject: [PATCH] vala: Add foreach statement support for GLib.GenericArray It is now possible to use foreach with a GLib.GenericArray type --- codegen/valaccodebasemodule.vala | 4 +- codegen/valaccodecontrolflowmodule.vala | 29 +++++++++++ tests/Makefile.am | 1 + tests/basic-types/gptrarray.vala | 48 +++++++++++++++++++ tests/control-flow/foreach.vala | 36 ++++++++++++++ .../gptrarray-foreach-variable.test | 7 +++ vala/valaforeachstatement.vala | 3 +- vala/valasemanticanalyzer.vala | 2 + 8 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 tests/control-flow/gptrarray-foreach-variable.test diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala index fc52e7b8c..aa2a6ac49 100644 --- a/codegen/valaccodebasemodule.vala +++ b/codegen/valaccodebasemodule.vala @@ -337,7 +337,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { public TypeSymbol gstringbuilder_type; public TypeSymbol garray_type; public TypeSymbol gbytearray_type; - public TypeSymbol gptrarray_type; + public TypeSymbol genericarray_type; public TypeSymbol gthreadpool_type; public DataType gquark_type; public Struct gvalue_type; @@ -510,7 +510,7 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator { gstringbuilder_type = (TypeSymbol) glib_ns.scope.lookup ("StringBuilder"); garray_type = (TypeSymbol) glib_ns.scope.lookup ("Array"); gbytearray_type = (TypeSymbol) glib_ns.scope.lookup ("ByteArray"); - gptrarray_type = (TypeSymbol) glib_ns.scope.lookup ("PtrArray"); + genericarray_type = (TypeSymbol) glib_ns.scope.lookup ("GenericArray"); gthreadpool_type = (TypeSymbol) glib_ns.scope.lookup ("ThreadPool"); gerror = (Class) root_symbol.scope.lookup ("GLib").scope.lookup ("Error"); diff --git a/codegen/valaccodecontrolflowmodule.vala b/codegen/valaccodecontrolflowmodule.vala index 5cca19707..bbc530247 100644 --- a/codegen/valaccodecontrolflowmodule.vala +++ b/codegen/valaccodecontrolflowmodule.vala @@ -308,6 +308,35 @@ public abstract class Vala.CCodeControlFlowModule : CCodeMethodModule { stmt.body.emit (this); + ccode.close (); + } else if (stmt.collection.value_type.compatible (new ObjectType ((Class) genericarray_type))) { + // iterating over a GenericArray / GPtrArray + + var iterator_variable = new LocalVariable (uint_type.copy (), "%s_index".printf (stmt.variable_name)); + visit_local_variable (iterator_variable); + var arr_index = get_variable_cname (get_local_cname (iterator_variable)); + + var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, get_variable_cexpression (arr_index), new CCodeMemberAccess.pointer (get_variable_cexpression (get_local_cname (collection_backup)), "len")); + + ccode.open_for (new CCodeAssignment (get_variable_cexpression (arr_index), new CCodeConstant ("0")), + ccond, + new CCodeAssignment (get_variable_cexpression (arr_index), new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, get_variable_cexpression (arr_index), new CCodeConstant ("1")))); + + var get_item = new CCodeFunctionCall (new CCodeIdentifier ("g_ptr_array_index")); + get_item.add_argument (get_variable_cexpression (get_local_cname (collection_backup))); + get_item.add_argument (get_variable_cexpression (arr_index)); + + CCodeExpression element_expr = get_item; + + if (stmt.type_reference.value_owned) { + element_expr = get_cvalue_ (copy_value (new GLibValue (stmt.type_reference, element_expr), stmt.element_variable)); + } + + visit_local_variable (stmt.element_variable); + ccode.add_assignment (get_variable_cexpression (get_local_cname (stmt.element_variable)), element_expr); + + stmt.body.emit (this); + ccode.close (); } else if (stmt.collection.value_type.compatible (new ObjectType (gvaluearray_type))) { // iterating over a GValueArray diff --git a/tests/Makefile.am b/tests/Makefile.am index 64e7101cf..281531d22 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -257,6 +257,7 @@ TESTS = \ control-flow/for.vala \ control-flow/for-switch-continue.vala \ control-flow/foreach.vala \ + control-flow/gptrarray-foreach-variable.test \ control-flow/local-clash-with-implicit-this.vala \ control-flow/missing-break.test \ control-flow/missing-return.test \ diff --git a/tests/basic-types/gptrarray.vala b/tests/basic-types/gptrarray.vala index 6d8aa2d1a..1b5d6c622 100644 --- a/tests/basic-types/gptrarray.vala +++ b/tests/basic-types/gptrarray.vala @@ -26,6 +26,30 @@ void main () { assert (foo3.ref_count == 2); assert (array.length == 3); + int loop_size = 0; + foreach (weak Foo element in array) { + loop_size++; + assert (element.ref_count == 2); + switch (loop_size) { + case 1: assert (element == foo1); break; + case 2: assert (element == foo2); break; + case 3: assert (element == foo3); break; + } + } + assert (loop_size == 3); + + loop_size = 0; + foreach (Foo element in array) { + loop_size++; + assert (element.ref_count == 3); + switch (loop_size) { + case 1: assert (element == foo1); break; + case 2: assert (element == foo2); break; + case 3: assert (element == foo3); break; + } + } + assert (loop_size == 3); + assert (foo2 == array.get (1)); array.set (1, foo4); assert (foo4 == array.get (1)); @@ -73,6 +97,30 @@ void main () { assert (foo3.ref_count == 1); assert (array.length == 3); + int loop_size = 0; + foreach (weak Foo element in array) { + loop_size++; + assert (element.ref_count == 1); + switch (loop_size) { + case 1: assert (element == foo1); break; + case 2: assert (element == foo2); break; + case 3: assert (element == foo3); break; + } + } + assert (loop_size == 3); + + loop_size = 0; + foreach (Foo element in array) { + loop_size++; + assert (element.ref_count == 2); + switch (loop_size) { + case 1: assert (element == foo1); break; + case 2: assert (element == foo2); break; + case 3: assert (element == foo3); break; + } + } + assert (loop_size == 3); + assert (foo2 == array.get (1)); array.set (1, foo4); assert (foo4 == array.get (1)); diff --git a/tests/control-flow/foreach.vala b/tests/control-flow/foreach.vala index 6a36453fd..052e03d3c 100644 --- a/tests/control-flow/foreach.vala +++ b/tests/control-flow/foreach.vala @@ -33,6 +33,41 @@ void test_foreach_gvaluearray () { test_unowned (array); } +void test_generic_array_owned (GenericArray array) { + uint i = 0; + + foreach (Value? item in array) { + i++; + } + + assert (i == 3); +} + +void test_generic_array_unowned (GenericArray array) { + uint i = 0; + + foreach (unowned Value? item in array) { + i++; + } + + assert (i == 3); +} + +void test_foreach_genericarray () { + Value value; + var array = new GenericArray (); + + value = 1; + array.add (value); + value = 2.0; + array.add (value); + value = "three"; + array.add (value); + + test_generic_array_owned (array); + test_generic_array_unowned (array); +} + void test_foreach_multidim_array () { int[,] foo = { { 1, 2 }, { 3, 4 }, { 5, 6 } }; string result = ""; @@ -70,6 +105,7 @@ void test_foreach_slice_array () { void main () { test_foreach_gvaluearray (); + test_foreach_genericarray (); test_foreach_const_array (); test_foreach_multidim_array (); test_foreach_slice_array (); diff --git a/tests/control-flow/gptrarray-foreach-variable.test b/tests/control-flow/gptrarray-foreach-variable.test new file mode 100644 index 000000000..3cef84805 --- /dev/null +++ b/tests/control-flow/gptrarray-foreach-variable.test @@ -0,0 +1,7 @@ +Invalid Code + +void main () { + var array = new GenericArray (); + foreach (int element in array) { + } +} diff --git a/vala/valaforeachstatement.vala b/vala/valaforeachstatement.vala index 0af0b52ba..d18de773e 100644 --- a/vala/valaforeachstatement.vala +++ b/vala/valaforeachstatement.vala @@ -180,7 +180,8 @@ public class Vala.ForeachStatement : Block { array_type.inline_allocated = false; return check_without_iterator (context, collection_type, array_type.element_type); - } else if (context.profile == Profile.GOBJECT && (collection_type.compatible (context.analyzer.glist_type) || collection_type.compatible (context.analyzer.gslist_type))) { + } else if (context.profile == Profile.GOBJECT && (collection_type.compatible (context.analyzer.glist_type) + || collection_type.compatible (context.analyzer.gslist_type) || collection_type.compatible (context.analyzer.genericarray_type))) { if (collection_type.get_type_arguments ().size != 1) { error = true; Report.error (collection.source_reference, "missing type argument for collection"); diff --git a/vala/valasemanticanalyzer.vala b/vala/valasemanticanalyzer.vala index 378c301bd..fa101b6c8 100644 --- a/vala/valasemanticanalyzer.vala +++ b/vala/valasemanticanalyzer.vala @@ -163,6 +163,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor { public DataType gslist_type; public DataType garray_type; public DataType gvaluearray_type; + public DataType genericarray_type; public Class gerror_type; public DataType list_type; public DataType tuple_type; @@ -228,6 +229,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor { gslist_type = new ObjectType ((Class) glib_ns.scope.lookup ("SList")); garray_type = new ObjectType ((Class) glib_ns.scope.lookup ("Array")); gvaluearray_type = new ObjectType ((Class) glib_ns.scope.lookup ("ValueArray")); + genericarray_type = new ObjectType ((Class) glib_ns.scope.lookup ("GenericArray")); gerror_type = (Class) glib_ns.scope.lookup ("Error"); regex_type = new ObjectType ((Class) root_symbol.scope.lookup ("GLib").scope.lookup ("Regex")); -- 2.47.2