[vala/wip/nested-methods: 45/45] vala: Add support for nested methods (local functions)




commit 048aaba0cd88b784fe9fa421dbad494019abd42e
Author: Rico Tzschichholz <ricotz ubuntu com>
Date:   Thu Sep 30 16:42:14 2021 +0200

    vala: Add support for nested methods (local functions)
    
    Fixes https://gitlab.gnome.org/GNOME/vala/issues/1232

 tests/Makefile.am                        |   1 +
 tests/methods/local-functions.c-expected | 203 +++++++++++++++++++++++++++++++
 tests/methods/local-functions.vala       |  34 ++++++
 vala/valaparser.vala                     |  61 +++++++++-
 4 files changed, 298 insertions(+), 1 deletion(-)
---
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 518a22f43..2628320bb 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -174,6 +174,7 @@ TESTS = \
        methods/ellipsis-preceding.test \
        methods/extern.vala \
        methods/iterator.vala \
+       methods/local-functions.vala \
        methods/parameter-fixed-array-initializer.vala \
        methods/parameter-out-free-on-error.vala \
        methods/parameter-ref-array-resize.vala \
diff --git a/tests/methods/local-functions.c-expected b/tests/methods/local-functions.c-expected
new file mode 100644
index 000000000..6e49f842b
--- /dev/null
+++ b/tests/methods/local-functions.c-expected
@@ -0,0 +1,203 @@
+/* methods_local_functions.c generated by valac, the Vala compiler
+ * generated from methods_local_functions.vala, do not modify */
+
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef gint (*_LocalFunc0_) (gpointer user_data);
+typedef gboolean (*_LocalFunc1_) (gint a, gint b, gpointer user_data);
+typedef void (*_LocalFunc2_) (gchar* * s, gpointer user_data);
+typedef const gchar* (*_LocalFunc3_) (gchar* * s, gpointer user_data);
+typedef struct _Block1Data Block1Data;
+#define _g_free0(var) (var = (g_free (var), NULL))
+#define _vala_assert(expr, msg) if G_LIKELY (expr) ; else g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, 
__LINE__, G_STRFUNC, msg);
+#define _vala_return_if_fail(expr, msg) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, 
G_STRFUNC, msg); return; }
+#define _vala_return_val_if_fail(expr, msg, val) if G_LIKELY (expr) ; else { g_return_if_fail_warning 
(G_LOG_DOMAIN, G_STRFUNC, msg); return val; }
+#define _vala_warn_if_fail(expr, msg) if G_LIKELY (expr) ; else g_warn_message (G_LOG_DOMAIN, __FILE__, 
__LINE__, G_STRFUNC, msg);
+
+struct _Block1Data {
+       int _ref_count_;
+       gint x;
+       gint y;
+};
+
+static void _vala_main (void);
+static Block1Data* block1_data_ref (Block1Data* _data1_);
+static void block1_data_unref (void * _userdata_);
+static gint ___lambda4_ (Block1Data* _data1_);
+static gint ____lambda4___localfunc0_ (gpointer self);
+static gboolean ___lambda5_ (gint a,
+                      gint b);
+static gboolean ____lambda5___localfunc1_ (gint a,
+                                    gint b,
+                                    gpointer self);
+static void ___lambda6_ (gchar* * s);
+static void ____lambda6___localfunc2_ (gchar* * s,
+                                gpointer self);
+static const gchar* ___lambda7_ (gchar* * s);
+static const gchar* ____lambda7___localfunc3_ (gchar* * s,
+                                        gpointer self);
+
+static Block1Data*
+block1_data_ref (Block1Data* _data1_)
+{
+       g_atomic_int_inc (&_data1_->_ref_count_);
+       return _data1_;
+}
+
+static void
+block1_data_unref (void * _userdata_)
+{
+       Block1Data* _data1_;
+       _data1_ = (Block1Data*) _userdata_;
+       if (g_atomic_int_dec_and_test (&_data1_->_ref_count_)) {
+               g_slice_free (Block1Data, _data1_);
+       }
+}
+
+static gint
+___lambda4_ (Block1Data* _data1_)
+{
+       gint result = 0;
+       result = _data1_->x + _data1_->y;
+       return result;
+}
+
+static gint
+____lambda4___localfunc0_ (gpointer self)
+{
+       gint result;
+       result = ___lambda4_ (self);
+       return result;
+}
+
+static gboolean
+___lambda5_ (gint a,
+             gint b)
+{
+       gboolean result = FALSE;
+       _vala_assert ((a * b) == 966, "a * b == 966");
+       result = TRUE;
+       return result;
+}
+
+static gboolean
+____lambda5___localfunc1_ (gint a,
+                           gint b,
+                           gpointer self)
+{
+       gboolean result;
+       result = ___lambda5_ (a, b);
+       return result;
+}
+
+static void
+___lambda6_ (gchar* * s)
+{
+       gchar* _vala_s = NULL;
+       gchar* _tmp0_;
+       _tmp0_ = g_strdup ("foo");
+       _g_free0 (_vala_s);
+       _vala_s = _tmp0_;
+       if (s) {
+               *s = _vala_s;
+       } else {
+               _g_free0 (_vala_s);
+       }
+}
+
+static void
+____lambda6___localfunc2_ (gchar* * s,
+                           gpointer self)
+{
+       ___lambda6_ (s);
+}
+
+static const gchar*
+___lambda7_ (gchar* * s)
+{
+       gchar* _tmp0_;
+       const gchar* result = NULL;
+       g_return_val_if_fail (*s != NULL, NULL);
+       _vala_assert (g_strcmp0 (*s, "foo") == 0, "s == \"foo\"");
+       _tmp0_ = g_strdup ("bar");
+       _g_free0 (*s);
+       *s = _tmp0_;
+       result = *s;
+       return result;
+}
+
+static const gchar*
+____lambda7___localfunc3_ (gchar* * s,
+                           gpointer self)
+{
+       const gchar* result;
+       result = ___lambda7_ (s);
+       return result;
+}
+
+static void
+_vala_main (void)
+{
+       {
+               Block1Data* _data1_;
+               _LocalFunc0_ foo = NULL;
+               gpointer foo_target;
+               _data1_ = g_slice_new0 (Block1Data);
+               _data1_->_ref_count_ = 1;
+               _data1_->x = 23;
+               _data1_->y = 42;
+               foo = ____lambda4___localfunc0_;
+               foo_target = _data1_;
+               _vala_assert (foo (foo_target) == 65, "foo () == 65");
+               block1_data_unref (_data1_);
+               _data1_ = NULL;
+       }
+       {
+               _LocalFunc1_ foo = NULL;
+               gpointer foo_target;
+               foo = ____lambda5___localfunc1_;
+               foo_target = NULL;
+               _vala_assert (foo (23, 42, foo_target), "foo (23, 42)");
+       }
+       {
+               _LocalFunc2_ foo = NULL;
+               gpointer foo_target;
+               gchar* s = NULL;
+               gchar* _tmp0_ = NULL;
+               foo = ____lambda6___localfunc2_;
+               foo_target = NULL;
+               foo (&_tmp0_, foo_target);
+               _g_free0 (s);
+               s = _tmp0_;
+               _vala_assert (g_strcmp0 (s, "foo") == 0, "s == \"foo\"");
+               _g_free0 (s);
+       }
+       {
+               _LocalFunc3_ foo = NULL;
+               gpointer foo_target;
+               gchar* s = NULL;
+               gchar* _tmp1_;
+               const gchar* _tmp2_;
+               const gchar* _tmp3_;
+               foo = ____lambda7___localfunc3_;
+               foo_target = NULL;
+               _tmp1_ = g_strdup ("foo");
+               s = _tmp1_;
+               _tmp2_ = foo (&s, foo_target);
+               _vala_assert (g_strcmp0 (_tmp2_, "bar") == 0, "foo (ref s) == \"bar\"");
+               _tmp3_ = s;
+               _vala_assert (g_strcmp0 (_tmp3_, "bar") == 0, "s == \"bar\"");
+               _g_free0 (s);
+       }
+}
+
+int
+main (int argc,
+      char ** argv)
+{
+       _vala_main ();
+       return 0;
+}
+
diff --git a/tests/methods/local-functions.vala b/tests/methods/local-functions.vala
new file mode 100644
index 000000000..02b9b0164
--- /dev/null
+++ b/tests/methods/local-functions.vala
@@ -0,0 +1,34 @@
+void main () {
+       {
+               int x = 23, y = 42;
+               int foo () {
+                       return x + y;
+               }
+               assert (foo () == 65);
+       }
+       {
+               bool foo (int a, int b) {
+                       assert (a * b == 966);
+                       return true;
+               }
+               assert (foo (23, 42));
+       }
+       {
+               void foo (out string s) {
+                       s = "foo";
+               }
+               string s;
+               foo (out s);
+               assert (s == "foo");
+       }
+       {
+               unowned string foo (ref string s) {
+                       assert (s == "foo");
+                       s = "bar";
+                       return s;
+               }
+               string s = "foo";
+               assert (foo (ref s) == "bar");
+               assert (s == "bar");
+       }
+}
diff --git a/vala/valaparser.vala b/vala/valaparser.vala
index a2b99f9ed..93031cee1 100644
--- a/vala/valaparser.vala
+++ b/vala/valaparser.vala
@@ -42,6 +42,7 @@ public class Vala.Parser : CodeVisitor {
        const int BUFFER_SIZE = 32;
 
        static List<TypeParameter> _empty_type_parameter_list;
+       static int next_local_func_id = 0;
 
        struct TokenInfo {
                public TokenType type;
@@ -1754,7 +1755,12 @@ public class Vala.Parser : CodeVisitor {
                                                stmt = parse_expression_statement ();
                                        } else {
                                                is_decl = true;
-                                               parse_local_variable_declarations (block);
+                                               bool is_local_func = is_local_function ();
+                                               if (is_local_func) {
+                                                       parse_local_function_declaration (block);
+                                               } else {
+                                                       parse_local_variable_declarations (block);
+                                               }
                                        }
                                        break;
                                }
@@ -1871,6 +1877,22 @@ public class Vala.Parser : CodeVisitor {
                return false;
        }
 
+       bool is_local_function () {
+               var begin = get_location ();
+
+               try {
+                       skip_type ();
+                       if (accept (TokenType.IDENTIFIER) && accept (TokenType.OPEN_PARENS)) {
+                               rollback (begin);
+                               return true;
+                       }
+               } catch {
+               }
+
+               rollback (begin);
+               return false;
+       }
+
        Block parse_embedded_statement (string statement_name, bool accept_empty_body = true) throws 
ParseError {
                if (current () == TokenType.OPEN_BRACE) {
                        var block = parse_block ();
@@ -2084,6 +2106,43 @@ public class Vala.Parser : CodeVisitor {
                return new Constant (id, type, initializer, src);
        }
 
+       void parse_local_function_declaration (Block block) throws ParseError {
+               var begin = get_location ();
+               var type = parse_type (true, false);
+               var sym = parse_symbol_name ();
+
+               expect (TokenType.OPEN_PARENS);
+               List<Parameter> params = new ArrayList<Parameter> ();
+               if (current () != TokenType.CLOSE_PARENS) {
+                       do {
+                               params.add (parse_parameter ());
+                       } while (accept (TokenType.COMMA));
+               }
+               expect (TokenType.CLOSE_PARENS);
+
+               var src = get_src (begin);
+
+               var d = new Delegate ("_LocalFunc%i_".printf (next_local_func_id++), type, src);
+               foreach (var param in params) {
+                       d.add_parameter (param);
+               }
+               context.root.add_delegate (d);
+
+               var lambda = new LambdaExpression.with_statement_body (parse_block (), src);
+               foreach (var p in params) {
+                       var param = new Parameter (p.name, null, p.source_reference);
+                       param.direction = p.direction;
+                       lambda.add_parameter (param);
+               }
+
+               if (!context.experimental) {
+                       Report.warning (src, "local functions are experimental");
+               }
+
+               var local = new LocalVariable (new DelegateType (d), sym.name, lambda, src);
+               block.add_statement (new DeclarationStatement (local, src));
+       }
+
        Statement parse_expression_statement () throws ParseError {
                var begin = get_location ();
                var expr = parse_statement_expression ();


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