From: Jeremy Philippe Date: Wed, 18 Dec 2019 15:54:05 +0000 (+0100) Subject: vala: Add support for null-safe access operator X-Git-Tag: 0.53.1~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9572175213c300e50476835fe1201ff4b040ec36;p=thirdparty%2Fvala.git vala: Add support for null-safe access operator This implements support for the "null-safe" operator of C# (also called "safe navigation" operator). var foo = bar?.foo; var foo = bar?.foo(); var foo = bar?[3]; var foo = bar?[1:2]; It applies to plain member access, method calls, element access and slice expressions (including void method calls, which are simply bypassed if the inner expression is null). Fixes https://gitlab.gnome.org/GNOME/vala/issues/522 --- diff --git a/tests/Makefile.am b/tests/Makefile.am index fd840eb30..f0ec0e9c4 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -289,6 +289,9 @@ TESTS = \ control-flow/bug691514.vala \ control-flow/bug736774-1.vala \ control-flow/bug736774-2.vala \ + control-flow/bug761267-1.vala \ + control-flow/bug761267-2.vala \ + control-flow/bug761267-3.test \ control-flow/bug764440.vala \ control-flow/bug790903.test \ control-flow/bug790903-2.test \ diff --git a/tests/control-flow/bug761267-1.c-expected b/tests/control-flow/bug761267-1.c-expected new file mode 100644 index 000000000..21e8db20b --- /dev/null +++ b/tests/control-flow/bug761267-1.c-expected @@ -0,0 +1,540 @@ +/* control_flow_bug761267_1.c generated by valac, the Vala compiler + * generated from control_flow_bug761267_1.vala, do not modify */ + +#include +#include +#include +#include + +#if !defined(VALA_EXTERN) +#if defined(_MSC_VER) +#define VALA_EXTERN __declspec(dllexport) extern +#elif __GNUC__ >= 4 +#define VALA_EXTERN __attribute__((visibility("default"))) extern +#else +#define VALA_EXTERN extern +#endif +#endif + +#define TYPE_FOO (foo_get_type ()) +#define FOO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_FOO, Foo)) +#define FOO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_FOO, FooClass)) +#define IS_FOO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_FOO)) +#define IS_FOO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_FOO)) +#define FOO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_FOO, FooClass)) + +typedef struct _Foo Foo; +typedef struct _FooClass FooClass; +typedef struct _FooPrivate FooPrivate; +typedef struct _ParamSpecFoo ParamSpecFoo; +#define _g_free0(var) ((var == NULL) ? NULL : (var = (g_free (var), NULL))) +#define _foo_unref0(var) ((var == NULL) ? NULL : (var = (foo_unref (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 _Foo { + GTypeInstance parent_instance; + volatile int ref_count; + FooPrivate * priv; + gint i; +}; + +struct _FooClass { + GTypeClass parent_class; + void (*finalize) (Foo *self); +}; + +struct _ParamSpecFoo { + GParamSpec parent_instance; +}; + +static gpointer foo_parent_class = NULL; + +VALA_EXTERN gpointer foo_ref (gpointer instance); +VALA_EXTERN void foo_unref (gpointer instance); +VALA_EXTERN GParamSpec* param_spec_foo (const gchar* name, + const gchar* nick, + const gchar* blurb, + GType object_type, + GParamFlags flags); +VALA_EXTERN void value_set_foo (GValue* value, + gpointer v_object); +VALA_EXTERN void value_take_foo (GValue* value, + gpointer v_object); +VALA_EXTERN gpointer value_get_foo (const GValue* value); +VALA_EXTERN GType foo_get_type (void) G_GNUC_CONST ; +G_DEFINE_AUTOPTR_CLEANUP_FUNC (Foo, foo_unref) +VALA_EXTERN Foo* foo_foo (Foo* self); +VALA_EXTERN void foo_faz (Foo* self); +VALA_EXTERN Foo* foo_new (void); +VALA_EXTERN Foo* foo_construct (GType object_type); +static void foo_finalize (Foo * obj); +static GType foo_get_type_once (void); +VALA_EXTERN void bar (Foo* f); +static gint* _int_dup (gint* self); +VALA_EXTERN void baz (Foo* f); +static void _vala_main (void); + +Foo* +foo_foo (Foo* self) +{ + Foo* result = NULL; + g_return_val_if_fail (IS_FOO (self), NULL); + result = NULL; + return result; +} + +void +foo_faz (Foo* self) +{ + g_return_if_fail (IS_FOO (self)); + g_assert_not_reached (); +} + +Foo* +foo_construct (GType object_type) +{ + Foo* self = NULL; + self = (Foo*) g_type_create_instance (object_type); + return self; +} + +Foo* +foo_new (void) +{ + return foo_construct (TYPE_FOO); +} + +static void +value_foo_init (GValue* value) +{ + value->data[0].v_pointer = NULL; +} + +static void +value_foo_free_value (GValue* value) +{ + if (value->data[0].v_pointer) { + foo_unref (value->data[0].v_pointer); + } +} + +static void +value_foo_copy_value (const GValue* src_value, + GValue* dest_value) +{ + if (src_value->data[0].v_pointer) { + dest_value->data[0].v_pointer = foo_ref (src_value->data[0].v_pointer); + } else { + dest_value->data[0].v_pointer = NULL; + } +} + +static gpointer +value_foo_peek_pointer (const GValue* value) +{ + return value->data[0].v_pointer; +} + +static gchar* +value_foo_collect_value (GValue* value, + guint n_collect_values, + GTypeCValue* collect_values, + guint collect_flags) +{ + if (collect_values[0].v_pointer) { + Foo * object; + object = collect_values[0].v_pointer; + if (object->parent_instance.g_class == NULL) { + return g_strconcat ("invalid unclassed object pointer for value type `", G_VALUE_TYPE_NAME (value), "'", NULL); + } else if (!g_value_type_compatible (G_TYPE_FROM_INSTANCE (object), G_VALUE_TYPE (value))) { + return g_strconcat ("invalid object type `", g_type_name (G_TYPE_FROM_INSTANCE (object)), "' for value type `", G_VALUE_TYPE_NAME (value), "'", NULL); + } + value->data[0].v_pointer = foo_ref (object); + } else { + value->data[0].v_pointer = NULL; + } + return NULL; +} + +static gchar* +value_foo_lcopy_value (const GValue* value, + guint n_collect_values, + GTypeCValue* collect_values, + guint collect_flags) +{ + Foo ** object_p; + object_p = collect_values[0].v_pointer; + if (!object_p) { + return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value)); + } + if (!value->data[0].v_pointer) { + *object_p = NULL; + } else if (collect_flags & G_VALUE_NOCOPY_CONTENTS) { + *object_p = value->data[0].v_pointer; + } else { + *object_p = foo_ref (value->data[0].v_pointer); + } + return NULL; +} + +GParamSpec* +param_spec_foo (const gchar* name, + const gchar* nick, + const gchar* blurb, + GType object_type, + GParamFlags flags) +{ + ParamSpecFoo* spec; + g_return_val_if_fail (g_type_is_a (object_type, TYPE_FOO), NULL); + spec = g_param_spec_internal (G_TYPE_PARAM_OBJECT, name, nick, blurb, flags); + G_PARAM_SPEC (spec)->value_type = object_type; + return G_PARAM_SPEC (spec); +} + +gpointer +value_get_foo (const GValue* value) +{ + g_return_val_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_FOO), NULL); + return value->data[0].v_pointer; +} + +void +value_set_foo (GValue* value, + gpointer v_object) +{ + Foo * old; + g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_FOO)); + old = value->data[0].v_pointer; + if (v_object) { + g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (v_object, TYPE_FOO)); + g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (v_object), G_VALUE_TYPE (value))); + value->data[0].v_pointer = v_object; + foo_ref (value->data[0].v_pointer); + } else { + value->data[0].v_pointer = NULL; + } + if (old) { + foo_unref (old); + } +} + +void +value_take_foo (GValue* value, + gpointer v_object) +{ + Foo * old; + g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_FOO)); + old = value->data[0].v_pointer; + if (v_object) { + g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (v_object, TYPE_FOO)); + g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (v_object), G_VALUE_TYPE (value))); + value->data[0].v_pointer = v_object; + } else { + value->data[0].v_pointer = NULL; + } + if (old) { + foo_unref (old); + } +} + +static void +foo_class_init (FooClass * klass, + gpointer klass_data) +{ + foo_parent_class = g_type_class_peek_parent (klass); + ((FooClass *) klass)->finalize = foo_finalize; +} + +static void +foo_instance_init (Foo * self, + gpointer klass) +{ + self->i = 42; + self->ref_count = 1; +} + +static void +foo_finalize (Foo * obj) +{ + Foo * self; + self = G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_FOO, Foo); + g_signal_handlers_destroy (self); +} + +static GType +foo_get_type_once (void) +{ + static const GTypeValueTable g_define_type_value_table = { value_foo_init, value_foo_free_value, value_foo_copy_value, value_foo_peek_pointer, "p", value_foo_collect_value, "p", value_foo_lcopy_value }; + static const GTypeInfo g_define_type_info = { sizeof (FooClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) foo_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (Foo), 0, (GInstanceInitFunc) foo_instance_init, &g_define_type_value_table }; + static const GTypeFundamentalInfo g_define_type_fundamental_info = { (G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE) }; + GType foo_type_id; + foo_type_id = g_type_register_fundamental (g_type_fundamental_next (), "Foo", &g_define_type_info, &g_define_type_fundamental_info, 0); + return foo_type_id; +} + +GType +foo_get_type (void) +{ + static volatile gsize foo_type_id__volatile = 0; + if (g_once_init_enter (&foo_type_id__volatile)) { + GType foo_type_id; + foo_type_id = foo_get_type_once (); + g_once_init_leave (&foo_type_id__volatile, foo_type_id); + } + return foo_type_id__volatile; +} + +gpointer +foo_ref (gpointer instance) +{ + Foo * self; + self = instance; + g_atomic_int_inc (&self->ref_count); + return instance; +} + +void +foo_unref (gpointer instance) +{ + Foo * self; + self = instance; + if (g_atomic_int_dec_and_test (&self->ref_count)) { + FOO_GET_CLASS (self)->finalize (self); + g_type_free_instance ((GTypeInstance *) self); + } +} + +static gint* +_int_dup (gint* self) +{ + gint* dup; + dup = g_new0 (gint, 1); + memcpy (dup, self, sizeof (gint)); + return dup; +} + +static gpointer +__int_dup0 (gpointer self) +{ + return self ? _int_dup (self) : NULL; +} + +void +bar (Foo* f) +{ + g_return_if_fail ((f == NULL) || IS_FOO (f)); + { + Foo* _tmp0_ = NULL; + gint* _tmp1_ = NULL; + gint* j = NULL; + gint* _tmp3_; + gint* _tmp4_; + _tmp0_ = f; + _tmp1_ = NULL; + if (_tmp0_ != NULL) { + gint* _tmp2_; + _tmp2_ = __int_dup0 (&_tmp0_->i); + _g_free0 (_tmp1_); + _tmp1_ = _tmp2_; + } + _tmp3_ = _tmp1_; + _tmp1_ = NULL; + j = _tmp3_; + _tmp4_ = j; + _vala_assert (_tmp4_ == NULL, "j == null"); + _g_free0 (j); + _g_free0 (_tmp1_); + } + { + gint k = 0; + Foo* _tmp5_ = NULL; + gint* _tmp6_ = NULL; + gint* _tmp8_ = NULL; + gint* _tmp9_; + gint* _tmp12_; + gint* _tmp13_; + k = 23; + _tmp5_ = f; + _tmp6_ = NULL; + if (_tmp5_ != NULL) { + gint* _tmp7_; + _tmp7_ = __int_dup0 (&_tmp5_->i); + _g_free0 (_tmp6_); + _tmp6_ = _tmp7_; + } + _tmp9_ = _tmp6_; + _tmp6_ = NULL; + _tmp8_ = _tmp9_; + if (_tmp8_ == NULL) { + gint _tmp10_; + gint* _tmp11_; + _tmp10_ = 0; + _tmp11_ = __int_dup0 (&_tmp10_); + _g_free0 (_tmp8_); + _tmp8_ = _tmp11_; + } + _tmp12_ = _tmp8_; + _tmp8_ = NULL; + _tmp13_ = _tmp12_; + k = *_tmp13_; + _g_free0 (_tmp13_); + _vala_assert (k == 0, "k == 0"); + _g_free0 (_tmp8_); + _g_free0 (_tmp6_); + } +} + +void +baz (Foo* f) +{ + g_return_if_fail ((f == NULL) || IS_FOO (f)); + { + Foo* _tmp0_ = NULL; + gint* _tmp1_ = NULL; + gint i = 0; + gint* _tmp3_; + gint* _tmp4_; + gint _tmp5_; + _tmp0_ = f; + _tmp1_ = NULL; + if (_tmp0_ != NULL) { + gint* _tmp2_; + _tmp2_ = __int_dup0 (&_tmp0_->i); + _g_free0 (_tmp1_); + _tmp1_ = _tmp2_; + } + _tmp3_ = _tmp1_; + _tmp1_ = NULL; + _tmp4_ = _tmp3_; + _tmp5_ = *_tmp4_; + _g_free0 (_tmp4_); + i = _tmp5_; + _vala_assert (i == 42, "i == 42"); + _g_free0 (_tmp1_); + } + { + gint* j = NULL; + gint _tmp6_; + gint* _tmp7_; + Foo* _tmp8_ = NULL; + Foo* _tmp9_ = NULL; + Foo* _tmp11_ = NULL; + gint* _tmp12_ = NULL; + gint* _tmp14_; + gint* _tmp15_; + _tmp6_ = 23; + _tmp7_ = __int_dup0 (&_tmp6_); + j = _tmp7_; + _tmp8_ = f; + _tmp9_ = NULL; + if (_tmp8_ != NULL) { + Foo* _tmp10_; + _tmp10_ = foo_foo (_tmp8_); + _foo_unref0 (_tmp9_); + _tmp9_ = _tmp10_; + } + _tmp11_ = _tmp9_; + _tmp12_ = NULL; + if (_tmp11_ != NULL) { + gint* _tmp13_; + _tmp13_ = __int_dup0 (&_tmp11_->i); + _g_free0 (_tmp12_); + _tmp12_ = _tmp13_; + } + _tmp14_ = _tmp12_; + _tmp12_ = NULL; + _g_free0 (j); + j = _tmp14_; + _tmp15_ = j; + _vala_assert (_tmp15_ == NULL, "j == null"); + _g_free0 (_tmp12_); + _foo_unref0 (_tmp9_); + _g_free0 (j); + } + { + gint k = 0; + Foo* _tmp16_ = NULL; + Foo* _tmp17_ = NULL; + Foo* _tmp19_ = NULL; + gint* _tmp20_ = NULL; + gint* _tmp22_ = NULL; + gint* _tmp23_; + gint* _tmp26_; + gint* _tmp27_; + k = 23; + _tmp16_ = f; + _tmp17_ = NULL; + if (_tmp16_ != NULL) { + Foo* _tmp18_; + _tmp18_ = foo_foo (_tmp16_); + _foo_unref0 (_tmp17_); + _tmp17_ = _tmp18_; + } + _tmp19_ = _tmp17_; + _tmp20_ = NULL; + if (_tmp19_ != NULL) { + gint* _tmp21_; + _tmp21_ = __int_dup0 (&_tmp19_->i); + _g_free0 (_tmp20_); + _tmp20_ = _tmp21_; + } + _tmp23_ = _tmp20_; + _tmp20_ = NULL; + _tmp22_ = _tmp23_; + if (_tmp22_ == NULL) { + gint _tmp24_; + gint* _tmp25_; + _tmp24_ = 0; + _tmp25_ = __int_dup0 (&_tmp24_); + _g_free0 (_tmp22_); + _tmp22_ = _tmp25_; + } + _tmp26_ = _tmp22_; + _tmp22_ = NULL; + _tmp27_ = _tmp26_; + k = *_tmp27_; + _g_free0 (_tmp27_); + _vala_assert (k == 0, "k == 0"); + _g_free0 (_tmp22_); + _g_free0 (_tmp20_); + _foo_unref0 (_tmp17_); + } +} + +static void +_vala_main (void) +{ + { + Foo* foo = NULL; + Foo* _tmp0_ = NULL; + Foo* _tmp1_; + foo = NULL; + _tmp1_ = foo; + _tmp0_ = _tmp1_; + if (_tmp0_ != NULL) { + foo_faz (_tmp0_); + } + _foo_unref0 (foo); + } + { + Foo* _tmp2_; + Foo* _tmp3_; + bar (NULL); + _tmp2_ = foo_new (); + _tmp3_ = _tmp2_; + baz (_tmp3_); + _foo_unref0 (_tmp3_); + } +} + +int +main (int argc, + char ** argv) +{ + _vala_main (); + return 0; +} + diff --git a/tests/control-flow/bug761267-1.vala b/tests/control-flow/bug761267-1.vala new file mode 100644 index 000000000..cfee7872d --- /dev/null +++ b/tests/control-flow/bug761267-1.vala @@ -0,0 +1,51 @@ +class Foo { + public int i = 42; + + public Foo? foo () { + return null; + } + + public void faz () { + assert_not_reached (); + } +} + +void bar (Foo? f) { + { + int? j = f?.i; + assert (j == null); + } + { + int k = 23; + k = f?.i ?? 0; + assert (k == 0); + } +} + +void baz (Foo? f) { + { + int i = f?.i; + assert (i == 42); + } + { + int? j = 23; + j = f?.foo ()?.i; + assert (j == null); + } + { + int k = 23; + k = f?.foo ()?.i ?? 0; + assert (k == 0); + } +} + +void main () { + { + Foo? foo = null; + foo?.faz (); + } + { + bar (null); + baz (new Foo ()); + } +} diff --git a/tests/control-flow/bug761267-2.c-expected b/tests/control-flow/bug761267-2.c-expected new file mode 100644 index 000000000..804e6a91d --- /dev/null +++ b/tests/control-flow/bug761267-2.c-expected @@ -0,0 +1,1014 @@ +/* control_flow_bug761267_2.c generated by valac, the Vala compiler + * generated from control_flow_bug761267_2.vala, do not modify */ + +#include +#include +#include +#include + +#if !defined(VALA_EXTERN) +#if defined(_MSC_VER) +#define VALA_EXTERN __declspec(dllexport) extern +#elif __GNUC__ >= 4 +#define VALA_EXTERN __attribute__((visibility("default"))) extern +#else +#define VALA_EXTERN extern +#endif +#endif + +typedef struct _Bar Bar; + +#define TYPE_FOO (foo_get_type ()) +#define FOO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_FOO, Foo)) +#define FOO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_FOO, FooClass)) +#define IS_FOO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_FOO)) +#define IS_FOO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_FOO)) +#define FOO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_FOO, FooClass)) + +typedef struct _Foo Foo; +typedef struct _FooClass FooClass; +typedef struct _FooPrivate FooPrivate; +#define _bar_free0(var) ((var == NULL) ? NULL : (var = (bar_free (var), NULL))) +typedef struct _ParamSpecFoo ParamSpecFoo; +#define _g_free0(var) ((var == NULL) ? NULL : (var = (g_free (var), NULL))) +#define _foo_unref0(var) ((var == NULL) ? NULL : (var = (foo_unref (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 _Bar { + gint k; +}; + +struct _Foo { + GTypeInstance parent_instance; + volatile int ref_count; + FooPrivate * priv; + gint i; + Bar* b; +}; + +struct _FooClass { + GTypeClass parent_class; + void (*finalize) (Foo *self); +}; + +struct _FooPrivate { + gint _j; +}; + +struct _ParamSpecFoo { + GParamSpec parent_instance; +}; + +static gint Foo_private_offset; +static gpointer foo_parent_class = NULL; + +VALA_EXTERN void bar_free (Bar * self); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (Bar, bar_free) +static void bar_instance_init (Bar * self); +VALA_EXTERN Bar* bar_new (void); +VALA_EXTERN gpointer foo_ref (gpointer instance); +VALA_EXTERN void foo_unref (gpointer instance); +VALA_EXTERN GParamSpec* param_spec_foo (const gchar* name, + const gchar* nick, + const gchar* blurb, + GType object_type, + GParamFlags flags); +VALA_EXTERN void value_set_foo (GValue* value, + gpointer v_object); +VALA_EXTERN void value_take_foo (GValue* value, + gpointer v_object); +VALA_EXTERN gpointer value_get_foo (const GValue* value); +VALA_EXTERN GType foo_get_type (void) G_GNUC_CONST ; +G_DEFINE_AUTOPTR_CLEANUP_FUNC (Foo, foo_unref) +VALA_EXTERN gint foo_foo (Foo* self); +VALA_EXTERN Bar* foo_bar (Foo* self); +VALA_EXTERN Bar** foo_bars (Foo* self, + gint n, + gint* result_length1); +VALA_EXTERN gint* foo_seq (Foo* self, + gint n, + gint* result_length1); +VALA_EXTERN Foo* foo_new (void); +VALA_EXTERN Foo* foo_construct (GType object_type); +VALA_EXTERN gint foo_get_j (Foo* self); +VALA_EXTERN void foo_set_j (Foo* self, + gint value); +static void foo_finalize (Foo * obj); +static GType foo_get_type_once (void); +VALA_EXTERN void bar (Foo* f); +static gint* _int_dup (gint* self); +static gint* _vala_array_dup1 (gint* self, + gssize length); +VALA_EXTERN void baz (Foo* f); +static gboolean _int_equal (const gint * s1, + const gint * s2); +static gint* _vala_array_dup2 (gint* self, + gssize length); +static void _vala_main (void); +static void _vala_array_destroy (gpointer array, + gssize array_length, + GDestroyNotify destroy_func); +static void _vala_array_free (gpointer array, + gssize array_length, + GDestroyNotify destroy_func); +static inline gpointer _vala_memdup2 (gconstpointer mem, + gsize byte_size); + +Bar* +bar_new (void) +{ + Bar* self; + self = g_slice_new0 (Bar); + bar_instance_init (self); + return self; +} + +static void +bar_instance_init (Bar * self) +{ + self->k = 17; +} + +void +bar_free (Bar * self) +{ + g_slice_free (Bar, self); +} + +static inline gpointer +foo_get_instance_private (Foo* self) +{ + return G_STRUCT_MEMBER_P (self, Foo_private_offset); +} + +gint +foo_foo (Foo* self) +{ + gint result = 0; + g_return_val_if_fail (IS_FOO (self), 0); + result = self->i; + return result; +} + +Bar* +foo_bar (Foo* self) +{ + Bar* _tmp0_; + Bar* result = NULL; + g_return_val_if_fail (IS_FOO (self), NULL); + _tmp0_ = bar_new (); + result = _tmp0_; + return result; +} + +Bar** +foo_bars (Foo* self, + gint n, + gint* result_length1) +{ + Bar** a = NULL; + Bar** _tmp0_; + gint a_length1; + gint _a_size_; + Bar** _tmp5_; + gint _tmp5__length1; + Bar** result = NULL; + g_return_val_if_fail (IS_FOO (self), NULL); + _tmp0_ = g_new0 (Bar*, n + 1); + a = _tmp0_; + a_length1 = n; + _a_size_ = a_length1; + { + gint i = 0; + i = 0; + { + gboolean _tmp1_ = FALSE; + _tmp1_ = TRUE; + while (TRUE) { + Bar** _tmp3_; + gint _tmp3__length1; + Bar* _tmp4_ = NULL; + if (!_tmp1_) { + gint _tmp2_; + _tmp2_ = i; + i = _tmp2_ + 1; + } + _tmp1_ = FALSE; + if (!(i < n)) { + break; + } + _tmp3_ = a; + _tmp3__length1 = a_length1; + _tmp4_ = bar_new (); + _tmp4_->k = i; + _bar_free0 (_tmp3_[i]); + _tmp3_[i] = _tmp4_; + } + } + } + _tmp5_ = a; + _tmp5__length1 = a_length1; + if (result_length1) { + *result_length1 = _tmp5__length1; + } + result = _tmp5_; + return result; +} + +gint* +foo_seq (Foo* self, + gint n, + gint* result_length1) +{ + gint* a = NULL; + gint* _tmp0_; + gint a_length1; + gint _a_size_; + gint* _tmp4_; + gint _tmp4__length1; + gint* result = NULL; + g_return_val_if_fail (IS_FOO (self), NULL); + _tmp0_ = g_new0 (gint, n); + a = _tmp0_; + a_length1 = n; + _a_size_ = a_length1; + { + gint i = 0; + i = 0; + { + gboolean _tmp1_ = FALSE; + _tmp1_ = TRUE; + while (TRUE) { + gint* _tmp3_; + gint _tmp3__length1; + if (!_tmp1_) { + gint _tmp2_; + _tmp2_ = i; + i = _tmp2_ + 1; + } + _tmp1_ = FALSE; + if (!(i < n)) { + break; + } + _tmp3_ = a; + _tmp3__length1 = a_length1; + _tmp3_[i] = i; + } + } + } + _tmp4_ = a; + _tmp4__length1 = a_length1; + if (result_length1) { + *result_length1 = _tmp4__length1; + } + result = _tmp4_; + return result; +} + +Foo* +foo_construct (GType object_type) +{ + Foo* self = NULL; + self = (Foo*) g_type_create_instance (object_type); + return self; +} + +Foo* +foo_new (void) +{ + return foo_construct (TYPE_FOO); +} + +gint +foo_get_j (Foo* self) +{ + gint result; + g_return_val_if_fail (IS_FOO (self), 0); + result = self->priv->_j; + return result; +} + +void +foo_set_j (Foo* self, + gint value) +{ + g_return_if_fail (IS_FOO (self)); + self->priv->_j = value; +} + +static void +value_foo_init (GValue* value) +{ + value->data[0].v_pointer = NULL; +} + +static void +value_foo_free_value (GValue* value) +{ + if (value->data[0].v_pointer) { + foo_unref (value->data[0].v_pointer); + } +} + +static void +value_foo_copy_value (const GValue* src_value, + GValue* dest_value) +{ + if (src_value->data[0].v_pointer) { + dest_value->data[0].v_pointer = foo_ref (src_value->data[0].v_pointer); + } else { + dest_value->data[0].v_pointer = NULL; + } +} + +static gpointer +value_foo_peek_pointer (const GValue* value) +{ + return value->data[0].v_pointer; +} + +static gchar* +value_foo_collect_value (GValue* value, + guint n_collect_values, + GTypeCValue* collect_values, + guint collect_flags) +{ + if (collect_values[0].v_pointer) { + Foo * object; + object = collect_values[0].v_pointer; + if (object->parent_instance.g_class == NULL) { + return g_strconcat ("invalid unclassed object pointer for value type `", G_VALUE_TYPE_NAME (value), "'", NULL); + } else if (!g_value_type_compatible (G_TYPE_FROM_INSTANCE (object), G_VALUE_TYPE (value))) { + return g_strconcat ("invalid object type `", g_type_name (G_TYPE_FROM_INSTANCE (object)), "' for value type `", G_VALUE_TYPE_NAME (value), "'", NULL); + } + value->data[0].v_pointer = foo_ref (object); + } else { + value->data[0].v_pointer = NULL; + } + return NULL; +} + +static gchar* +value_foo_lcopy_value (const GValue* value, + guint n_collect_values, + GTypeCValue* collect_values, + guint collect_flags) +{ + Foo ** object_p; + object_p = collect_values[0].v_pointer; + if (!object_p) { + return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value)); + } + if (!value->data[0].v_pointer) { + *object_p = NULL; + } else if (collect_flags & G_VALUE_NOCOPY_CONTENTS) { + *object_p = value->data[0].v_pointer; + } else { + *object_p = foo_ref (value->data[0].v_pointer); + } + return NULL; +} + +GParamSpec* +param_spec_foo (const gchar* name, + const gchar* nick, + const gchar* blurb, + GType object_type, + GParamFlags flags) +{ + ParamSpecFoo* spec; + g_return_val_if_fail (g_type_is_a (object_type, TYPE_FOO), NULL); + spec = g_param_spec_internal (G_TYPE_PARAM_OBJECT, name, nick, blurb, flags); + G_PARAM_SPEC (spec)->value_type = object_type; + return G_PARAM_SPEC (spec); +} + +gpointer +value_get_foo (const GValue* value) +{ + g_return_val_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_FOO), NULL); + return value->data[0].v_pointer; +} + +void +value_set_foo (GValue* value, + gpointer v_object) +{ + Foo * old; + g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_FOO)); + old = value->data[0].v_pointer; + if (v_object) { + g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (v_object, TYPE_FOO)); + g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (v_object), G_VALUE_TYPE (value))); + value->data[0].v_pointer = v_object; + foo_ref (value->data[0].v_pointer); + } else { + value->data[0].v_pointer = NULL; + } + if (old) { + foo_unref (old); + } +} + +void +value_take_foo (GValue* value, + gpointer v_object) +{ + Foo * old; + g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (value, TYPE_FOO)); + old = value->data[0].v_pointer; + if (v_object) { + g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (v_object, TYPE_FOO)); + g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (v_object), G_VALUE_TYPE (value))); + value->data[0].v_pointer = v_object; + } else { + value->data[0].v_pointer = NULL; + } + if (old) { + foo_unref (old); + } +} + +static void +foo_class_init (FooClass * klass, + gpointer klass_data) +{ + foo_parent_class = g_type_class_peek_parent (klass); + ((FooClass *) klass)->finalize = foo_finalize; + g_type_class_adjust_private_offset (klass, &Foo_private_offset); +} + +static void +foo_instance_init (Foo * self, + gpointer klass) +{ + Bar* _tmp0_; + self->priv = foo_get_instance_private (self); + self->i = 42; + self->priv->_j = 23; + _tmp0_ = bar_new (); + self->b = _tmp0_; + self->ref_count = 1; +} + +static void +foo_finalize (Foo * obj) +{ + Foo * self; + self = G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_FOO, Foo); + g_signal_handlers_destroy (self); + _bar_free0 (self->b); +} + +static GType +foo_get_type_once (void) +{ + static const GTypeValueTable g_define_type_value_table = { value_foo_init, value_foo_free_value, value_foo_copy_value, value_foo_peek_pointer, "p", value_foo_collect_value, "p", value_foo_lcopy_value }; + static const GTypeInfo g_define_type_info = { sizeof (FooClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) foo_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (Foo), 0, (GInstanceInitFunc) foo_instance_init, &g_define_type_value_table }; + static const GTypeFundamentalInfo g_define_type_fundamental_info = { (G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE) }; + GType foo_type_id; + foo_type_id = g_type_register_fundamental (g_type_fundamental_next (), "Foo", &g_define_type_info, &g_define_type_fundamental_info, 0); + Foo_private_offset = g_type_add_instance_private (foo_type_id, sizeof (FooPrivate)); + return foo_type_id; +} + +GType +foo_get_type (void) +{ + static volatile gsize foo_type_id__volatile = 0; + if (g_once_init_enter (&foo_type_id__volatile)) { + GType foo_type_id; + foo_type_id = foo_get_type_once (); + g_once_init_leave (&foo_type_id__volatile, foo_type_id); + } + return foo_type_id__volatile; +} + +gpointer +foo_ref (gpointer instance) +{ + Foo * self; + self = instance; + g_atomic_int_inc (&self->ref_count); + return instance; +} + +void +foo_unref (gpointer instance) +{ + Foo * self; + self = instance; + if (g_atomic_int_dec_and_test (&self->ref_count)) { + FOO_GET_CLASS (self)->finalize (self); + g_type_free_instance ((GTypeInstance *) self); + } +} + +static gint* +_int_dup (gint* self) +{ + gint* dup; + dup = g_new0 (gint, 1); + memcpy (dup, self, sizeof (gint)); + return dup; +} + +static gpointer +__int_dup0 (gpointer self) +{ + return self ? _int_dup (self) : NULL; +} + +static gint* +_vala_array_dup1 (gint* self, + gssize length) +{ + if (length > 0) { + return _vala_memdup2 (self, length * sizeof (gint)); + } + return NULL; +} + +void +bar (Foo* f) +{ + g_return_if_fail ((f == NULL) || IS_FOO (f)); + { + Foo* _tmp0_ = NULL; + Bar* _tmp1_ = NULL; + Bar* b = NULL; + Bar* _tmp3_; + Bar* _tmp4_; + _tmp0_ = f; + _tmp1_ = NULL; + if (_tmp0_ != NULL) { + Bar* _tmp2_; + _tmp2_ = foo_bar (_tmp0_); + _bar_free0 (_tmp1_); + _tmp1_ = _tmp2_; + } + _tmp3_ = _tmp1_; + _tmp1_ = NULL; + b = _tmp3_; + _tmp4_ = b; + _vala_assert (_tmp4_ == NULL, "b == null"); + _bar_free0 (b); + _bar_free0 (_tmp1_); + } + { + gint* a = NULL; + gint a_length1; + gint _a_size_; + gint* _tmp5_ = NULL; + gint* _tmp6_; + gint _tmp6__length1; + gint _tmp5__length1; + gint __tmp5__size_; + gint* _tmp7_ = NULL; + gint* i = NULL; + gint* _tmp10_; + gint* _tmp11_; + a = NULL; + a_length1 = 0; + _a_size_ = a_length1; + _tmp6_ = a; + _tmp6__length1 = a_length1; + _tmp5_ = _tmp6_; + _tmp5__length1 = _tmp6__length1; + __tmp5__size_ = _tmp5__length1; + _tmp7_ = NULL; + if (_tmp5_ != NULL) { + gint _tmp8_; + gint* _tmp9_; + _tmp8_ = _tmp5_[3]; + _tmp9_ = __int_dup0 (&_tmp8_); + _g_free0 (_tmp7_); + _tmp7_ = _tmp9_; + } + _tmp10_ = _tmp7_; + _tmp7_ = NULL; + i = _tmp10_; + _tmp11_ = i; + _vala_assert (_tmp11_ == NULL, "i == null"); + _g_free0 (i); + _g_free0 (_tmp7_); + a = (g_free (a), NULL); + } + { + gint* a = NULL; + gint a_length1; + gint _a_size_; + gint* _tmp12_ = NULL; + gint* _tmp13_; + gint _tmp13__length1; + gint _tmp12__length1; + gint __tmp12__size_; + gint* _tmp14_ = NULL; + gint _tmp14__length1; + gint __tmp14__size_; + gint* s = NULL; + gint* _tmp15_; + gint _tmp15__length1; + gint s_length1; + gint _s_size_; + a = NULL; + a_length1 = 0; + _a_size_ = a_length1; + _tmp13_ = a; + _tmp13__length1 = a_length1; + _tmp12_ = _tmp13_; + _tmp12__length1 = _tmp13__length1; + __tmp12__size_ = _tmp12__length1; + _tmp14_ = NULL; + _tmp14__length1 = 0; + __tmp14__size_ = _tmp14__length1; + if (_tmp12_ != NULL) { + _tmp14_ = _tmp12_ + 1; + _tmp14__length1 = 3 - 1; + __tmp14__size_ = _tmp14__length1; + } + _tmp15_ = (_tmp14_ != NULL) ? _vala_array_dup1 (_tmp14_, _tmp14__length1) : _tmp14_; + _tmp15__length1 = _tmp14__length1; + s = _tmp15_; + s_length1 = _tmp15__length1; + _s_size_ = s_length1; + _vala_assert (s == NULL, "s == null"); + s = (g_free (s), NULL); + a = (g_free (a), NULL); + } +} + +static gboolean +_int_equal (const gint * s1, + const gint * s2) +{ + if (s1 == s2) { + return TRUE; + } + if (s1 == NULL) { + return FALSE; + } + if (s2 == NULL) { + return FALSE; + } + return (*s1) == (*s2); +} + +static gint* +_vala_array_dup2 (gint* self, + gssize length) +{ + if (length > 0) { + return _vala_memdup2 (self, length * sizeof (gint)); + } + return NULL; +} + +void +baz (Foo* f) +{ + g_return_if_fail ((f == NULL) || IS_FOO (f)); + { + gint* a = NULL; + gint _tmp0_ = 0; + gint* _tmp1_; + gint a_length1; + gint _a_size_; + gint* _tmp2_ = NULL; + gint* _tmp3_; + gint _tmp3__length1; + gint _tmp2__length1; + gint __tmp2__size_; + gint* _tmp4_ = NULL; + gint* i = NULL; + gint* _tmp7_; + gint* _tmp8_; + gint _tmp9_; + _tmp1_ = foo_seq (f, 10, &_tmp0_); + a = _tmp1_; + a_length1 = _tmp0_; + _a_size_ = a_length1; + _tmp3_ = a; + _tmp3__length1 = a_length1; + _tmp2_ = _tmp3_; + _tmp2__length1 = _tmp3__length1; + __tmp2__size_ = _tmp2__length1; + _tmp4_ = NULL; + if (_tmp2_ != NULL) { + gint _tmp5_; + gint* _tmp6_; + _tmp5_ = _tmp2_[3]; + _tmp6_ = __int_dup0 (&_tmp5_); + _g_free0 (_tmp4_); + _tmp4_ = _tmp6_; + } + _tmp7_ = _tmp4_; + _tmp4_ = NULL; + i = _tmp7_; + _tmp8_ = i; + _tmp9_ = 3; + _vala_assert (_int_equal (_tmp8_, &_tmp9_) == TRUE, "i == 3"); + _g_free0 (i); + _g_free0 (_tmp4_); + a = (g_free (a), NULL); + } + { + gint* a = NULL; + gint _tmp10_ = 0; + gint* _tmp11_; + gint a_length1; + gint _a_size_; + gint* _tmp12_ = NULL; + gint* _tmp13_; + gint _tmp13__length1; + gint _tmp12__length1; + gint __tmp12__size_; + gint* _tmp14_ = NULL; + gint _tmp14__length1; + gint __tmp14__size_; + gint* s = NULL; + gint* _tmp15_; + gint _tmp15__length1; + gint s_length1; + gint _s_size_; + gint* _tmp16_; + gint _tmp16__length1; + gint _tmp17_; + _tmp11_ = foo_seq (f, 10, &_tmp10_); + a = _tmp11_; + a_length1 = _tmp10_; + _a_size_ = a_length1; + _tmp13_ = a; + _tmp13__length1 = a_length1; + _tmp12_ = _tmp13_; + _tmp12__length1 = _tmp13__length1; + __tmp12__size_ = _tmp12__length1; + _tmp14_ = NULL; + _tmp14__length1 = 0; + __tmp14__size_ = _tmp14__length1; + if (_tmp12_ != NULL) { + _tmp14_ = _tmp12_ + 1; + _tmp14__length1 = 3 - 1; + __tmp14__size_ = _tmp14__length1; + } + _tmp15_ = (_tmp14_ != NULL) ? _vala_array_dup2 (_tmp14_, _tmp14__length1) : _tmp14_; + _tmp15__length1 = _tmp14__length1; + s = _tmp15_; + s_length1 = _tmp15__length1; + _s_size_ = s_length1; + _tmp16_ = s; + _tmp16__length1 = s_length1; + _tmp17_ = _tmp16_[0]; + _vala_assert (_tmp17_ == 1, "s[0] == 1"); + s = (g_free (s), NULL); + a = (g_free (a), NULL); + } + { + Foo* _tmp18_ = NULL; + Bar* _tmp19_ = NULL; + Bar* b = NULL; + Bar* _tmp21_; + Bar* _tmp22_; + _tmp18_ = f; + _tmp19_ = NULL; + if (_tmp18_ != NULL) { + Bar* _tmp20_; + _tmp20_ = _tmp18_->b; + _tmp18_->b = NULL; + _bar_free0 (_tmp19_); + _tmp19_ = _tmp20_; + } + _tmp21_ = _tmp19_; + _tmp19_ = NULL; + b = _tmp21_; + _tmp22_ = b; + _vala_assert (_tmp22_->k == 17, "b.k == 17"); + _bar_free0 (b); + _bar_free0 (_tmp19_); + } + { + Foo* _tmp23_ = NULL; + Bar* _tmp24_ = NULL; + Bar* b = NULL; + Bar* _tmp26_; + Bar* _tmp27_; + _tmp23_ = f; + _tmp24_ = NULL; + if (_tmp23_ != NULL) { + Bar* _tmp25_; + _tmp25_ = foo_bar (_tmp23_); + _bar_free0 (_tmp24_); + _tmp24_ = _tmp25_; + } + _tmp26_ = _tmp24_; + _tmp24_ = NULL; + b = _tmp26_; + _tmp27_ = b; + _vala_assert (_tmp27_->k == 17, "b.k == 17"); + _bar_free0 (b); + _bar_free0 (_tmp24_); + } + { + Foo* _tmp28_ = NULL; + Bar** _tmp29_ = NULL; + gint _tmp29__length1; + gint __tmp29__size_; + Bar** a = NULL; + Bar** _tmp32_; + gint _tmp32__length1; + gint a_length1; + gint _a_size_; + Bar** _tmp33_ = NULL; + Bar** _tmp34_; + gint _tmp34__length1; + gint _tmp33__length1; + gint __tmp33__size_; + Bar* _tmp35_ = NULL; + Bar* b = NULL; + Bar* _tmp37_; + Bar* _tmp38_; + _tmp28_ = f; + _tmp29_ = NULL; + _tmp29__length1 = 0; + __tmp29__size_ = _tmp29__length1; + if (_tmp28_ != NULL) { + gint _tmp30_ = 0; + Bar** _tmp31_; + _tmp31_ = foo_bars (_tmp28_, 10, &_tmp30_); + _tmp29_ = (_vala_array_free (_tmp29_, _tmp29__length1, (GDestroyNotify) bar_free), NULL); + _tmp29_ = _tmp31_; + _tmp29__length1 = _tmp30_; + __tmp29__size_ = _tmp29__length1; + } + _tmp32_ = _tmp29_; + _tmp32__length1 = _tmp29__length1; + _tmp29_ = NULL; + _tmp29__length1 = 0; + a = _tmp32_; + a_length1 = _tmp32__length1; + _a_size_ = a_length1; + _tmp34_ = a; + _tmp34__length1 = a_length1; + _tmp33_ = _tmp34_; + _tmp33__length1 = _tmp34__length1; + __tmp33__size_ = _tmp33__length1; + _tmp35_ = NULL; + if (_tmp33_ != NULL) { + Bar* _tmp36_; + _tmp36_ = _tmp33_[3]; + _tmp33_[3] = NULL; + _bar_free0 (_tmp35_); + _tmp35_ = _tmp36_; + } + _tmp37_ = _tmp35_; + _tmp35_ = NULL; + b = _tmp37_; + _tmp38_ = b; + _vala_assert (_tmp38_->k == 3, "b.k == 3"); + _bar_free0 (b); + _bar_free0 (_tmp35_); + a = (_vala_array_free (a, a_length1, (GDestroyNotify) bar_free), NULL); + _tmp29_ = (_vala_array_free (_tmp29_, _tmp29__length1, (GDestroyNotify) bar_free), NULL); + } + { + Foo* _tmp39_ = NULL; + gint* _tmp40_ = NULL; + gint* j = NULL; + gint* _tmp44_; + gint* _tmp45_; + gint _tmp46_; + _tmp39_ = f; + _tmp40_ = NULL; + if (_tmp39_ != NULL) { + gint _tmp41_; + gint _tmp42_; + gint* _tmp43_; + _tmp41_ = foo_get_j (_tmp39_); + _tmp42_ = _tmp41_; + _tmp43_ = __int_dup0 (&_tmp42_); + _g_free0 (_tmp40_); + _tmp40_ = _tmp43_; + } + _tmp44_ = _tmp40_; + _tmp40_ = NULL; + j = _tmp44_; + _tmp45_ = j; + _tmp46_ = 23; + _vala_assert (_int_equal (_tmp45_, &_tmp46_) == TRUE, "j == 23"); + _g_free0 (j); + _g_free0 (_tmp40_); + } + { + Foo* _tmp47_ = NULL; + gint* _tmp48_ = NULL; + gint* i = NULL; + gint* _tmp51_; + gint _tmp52_; + _tmp47_ = f; + _tmp48_ = NULL; + if (_tmp47_ != NULL) { + gint _tmp49_; + gint* _tmp50_; + _tmp49_ = foo_foo (_tmp47_); + _tmp50_ = __int_dup0 (&_tmp49_); + _g_free0 (_tmp48_); + _tmp48_ = _tmp50_; + } + _tmp51_ = _tmp48_; + _tmp48_ = NULL; + i = _tmp51_; + _tmp52_ = 42; + _vala_assert (_int_equal (i, &_tmp52_) == TRUE, "i == 42"); + _g_free0 (i); + _g_free0 (_tmp48_); + } +} + +static void +_vala_main (void) +{ + { + Foo* _tmp0_; + Foo* _tmp1_; + bar (NULL); + _tmp0_ = foo_new (); + _tmp1_ = _tmp0_; + baz (_tmp1_); + _foo_unref0 (_tmp1_); + } + { + Foo* _tmp2_ = NULL; + Foo* _tmp3_; + gint* _tmp4_ = NULL; + gint* i = NULL; + gint* _tmp6_; + gint _tmp7_; + _tmp3_ = foo_new (); + _tmp2_ = _tmp3_; + _tmp4_ = NULL; + if (_tmp2_ != NULL) { + gint* _tmp5_; + _tmp5_ = __int_dup0 (&_tmp2_->i); + _g_free0 (_tmp4_); + _tmp4_ = _tmp5_; + } + _tmp6_ = _tmp4_; + _tmp4_ = NULL; + i = _tmp6_; + _tmp7_ = 42; + _vala_assert (_int_equal (i, &_tmp7_) == TRUE, "i == 42"); + _g_free0 (i); + _g_free0 (_tmp4_); + _foo_unref0 (_tmp2_); + } +} + +int +main (int argc, + char ** argv) +{ + _vala_main (); + return 0; +} + +static void +_vala_array_destroy (gpointer array, + gssize array_length, + GDestroyNotify destroy_func) +{ + if ((array != NULL) && (destroy_func != NULL)) { + gssize i; + for (i = 0; i < array_length; i = i + 1) { + if (((gpointer*) array)[i] != NULL) { + destroy_func (((gpointer*) array)[i]); + } + } + } +} + +static void +_vala_array_free (gpointer array, + gssize array_length, + GDestroyNotify destroy_func) +{ + _vala_array_destroy (array, array_length, destroy_func); + g_free (array); +} + +static inline gpointer +_vala_memdup2 (gconstpointer mem, + gsize byte_size) +{ + gpointer new_mem; + if (mem && byte_size != 0) { + new_mem = g_malloc (byte_size); + memcpy (new_mem, mem, byte_size); + } else { + new_mem = NULL; + } + return new_mem; +} + diff --git a/tests/control-flow/bug761267-2.vala b/tests/control-flow/bug761267-2.vala new file mode 100644 index 000000000..0bafe04d4 --- /dev/null +++ b/tests/control-flow/bug761267-2.vala @@ -0,0 +1,109 @@ +[Compact] +class Bar { + public int k = 17; +} + +class Foo { + public int i = 42; + + public int j { get ; set ; default = 23; } + + public Bar b = new Bar (); + + public unowned int foo () { + return i; + } + + public Bar bar () { + return new Bar (); + } + + public Bar[] bars (int n) { + Bar[] a = new Bar[n]; + for (int i = 0; i < n; i++) { + a[i] = new Bar () { k = i }; + } + return a; + } + + public int[] seq (int n) { + int[] a = new int[n]; + for (int i = 0; i < n; i++) { + a[i] = i; + } + return a; + } +} + +void bar (Foo? f) { + { + // null method call + Bar? b = f?.bar (); + assert (b == null); + } + { + // null element access + int[]? a = null; + int? i = a?[3]; + assert (i == null); + } + { + // null slice expression + int[]? a = null; + int[]? s = a?[1:3]; + assert (s == null); + } +} + +void baz (Foo? f) { + { + // non-null element access + int[]? a = f.seq (10); + int? i = a?[3]; + assert (i == 3); + } + { + // non-null slice access + int[]? a = f.seq (10); + int[]? s = a?[1:3]; + assert (s[0] == 1); + } + { + // ownership transfer through member access + Bar? b = (owned) f?.b; + assert (b.k == 17); + } + { + // ownership transfer through method call + Bar? b = f?.bar (); + assert (b.k == 17); + } + { + // ownership transfer through element access + Bar[]? a = f?.bars (10); + Bar? b = (owned) a?[3]; + assert (b.k == 3); + } + { + // member access to non-nullable unowned value type + int? j = f?.j; + assert (j == 23); + } + { + // method call returns non-nullable unowned value type + int? i = f?.foo (); + assert (i == 42); + } +} + +void main () { + { + bar (null); + baz (new Foo ()); + } + { + // owned inner expression + int? i = new Foo ()?.i; + assert (i == 42); + } +} diff --git a/tests/control-flow/bug761267-3.test b/tests/control-flow/bug761267-3.test new file mode 100644 index 000000000..b13a8fbd5 --- /dev/null +++ b/tests/control-flow/bug761267-3.test @@ -0,0 +1,11 @@ +Invalid Code + +class Foo { + public int i; +} + +void main () { + // cannot use null-safe expression as lvalue + Foo? f = new Foo (); + f?.i = 56; +} diff --git a/vala/valaelementaccess.vala b/vala/valaelementaccess.vala index d866d2f0b..ce6281870 100644 --- a/vala/valaelementaccess.vala +++ b/vala/valaelementaccess.vala @@ -43,6 +43,11 @@ public class Vala.ElementAccess : Expression { } } + /** + * Null-safe access. + */ + public bool null_safe_access { get; set; } + /** * Expressions representing the indices we want to access inside the container. */ @@ -136,6 +141,11 @@ public class Vala.ElementAccess : Expression { checked = true; + if (null_safe_access) { + error = !base.check (context); + return !error; + } + if (!container.check (context)) { /* don't proceed if a child expression failed */ error = true; diff --git a/vala/valaexpression.vala b/vala/valaexpression.vala index 53983682e..e46d875d6 100644 --- a/vala/valaexpression.vala +++ b/vala/valaexpression.vala @@ -121,4 +121,182 @@ public abstract class Vala.Expression : CodeNode { public void insert_statement (Block block, Statement stmt) { block.insert_before (parent_statement, stmt); } + + public override bool check (CodeContext context) { + //Add null checks to a null-safe expression + + unowned MethodCall? call = this as MethodCall; + unowned Expression access = call != null ? call.call : this; + unowned MemberAccess? member_access = access as MemberAccess; + unowned ElementAccess? elem_access = access as ElementAccess; + unowned SliceExpression? slice_expr = access as SliceExpression; + + unowned Expression? inner = null; + if (member_access != null && member_access.null_safe_access) { + inner = member_access.inner; + } else if (elem_access != null && elem_access.null_safe_access) { + inner = elem_access.container; + } else if (slice_expr != null && slice_expr.null_safe_access) { + inner = slice_expr.container; + } else { + // Nothing to do here + return true; + } + + // get the type of the inner expression + if (!inner.check (context)) { + error = true; + return false; + } + + // the inner expression may have been replaced by the check, reload it + if (member_access != null) { + inner = member_access.inner; + } else if (elem_access != null) { + inner = elem_access.container; + } else if (slice_expr != null) { + inner = slice_expr.container; + } + + if (inner.value_type == null) { + Report.error (inner.source_reference, "invalid inner expression"); + return false; + } + + // declare the inner expression as a local variable to check for null + var inner_type = inner.value_type.copy (); + if (context.experimental_non_null && !inner_type.nullable) { + Report.warning (inner.source_reference, "inner expression is never null"); + // make it nullable, otherwise the null check will not compile in non-null mode + inner_type.nullable = true; + } + var inner_local = new LocalVariable (inner_type, get_temp_name (), inner, inner.source_reference); + + var inner_decl = new DeclarationStatement (inner_local, inner.source_reference); + insert_statement (context.analyzer.insert_block, inner_decl); + + if (!inner_decl.check (context)) { + return false; + } + + // create an equivalent non null-safe expression + Expression? non_null_expr = null; + + Expression inner_access = new MemberAccess.simple (inner_local.name, source_reference); + + if (context.experimental_non_null) { + inner_access = new CastExpression.non_null (inner_access, source_reference); + } + + if (member_access != null) { + non_null_expr = new MemberAccess (inner_access, member_access.member_name, source_reference); + } else if (elem_access != null) { + var non_null_elem_access = new ElementAccess (inner_access, source_reference); + foreach (Expression index in elem_access.get_indices ()) { + non_null_elem_access.append_index (index); + } + non_null_expr = non_null_elem_access; + } else if (slice_expr != null) { + non_null_expr = new SliceExpression (inner_access, slice_expr.start, slice_expr.stop, source_reference); + } + + if ((member_access != null || elem_access != null) + && access.parent_node is ReferenceTransferExpression) { + // preserve ownership transfer + non_null_expr = new ReferenceTransferExpression (non_null_expr, source_reference); + } + + if (!non_null_expr.check (context)) { + return false; + } + + if (non_null_expr.value_type == null) { + Report.error (source_reference, "invalid null-safe expression"); + error = true; + return false; + } + + DataType result_type; + + if (call != null) { + // if the expression is a method call, create an equivalent non-conditional method call + var non_null_call = new MethodCall (non_null_expr, source_reference); + foreach (Expression arg in call.get_argument_list ()) { + non_null_call.add_argument (arg); + } + result_type = non_null_expr.value_type.get_return_type ().copy (); + non_null_expr = non_null_call; + } else { + result_type = non_null_expr.value_type.copy (); + } + + if (result_type is VoidType) { + // void result type, replace the parent expression statement by a conditional statement + var non_null_stmt = new ExpressionStatement (non_null_expr, source_reference); + var non_null_block = new Block (source_reference); + non_null_block.add_statement (non_null_stmt); + + var non_null_safe = new BinaryExpression (BinaryOperator.INEQUALITY, new MemberAccess.simple (inner_local.name, source_reference), new NullLiteral (source_reference), source_reference); + var non_null_ifstmt = new IfStatement (non_null_safe, non_null_block, null, source_reference); + + unowned ExpressionStatement? parent_stmt = parent_node as ExpressionStatement; + unowned Block? parent_block = parent_stmt != null ? parent_stmt.parent_node as Block : null; + + if (parent_stmt == null || parent_block == null) { + Report.error (source_reference, "void method call not allowed here"); + error = true; + return false; + } + + context.analyzer.replaced_nodes.add (parent_stmt); + parent_block.replace_statement (parent_stmt, non_null_ifstmt); + return non_null_ifstmt.check (context); + } else { + // non-void result type, replace the expression by an access to a local variable + if (!result_type.nullable) { + if (result_type is ValueType) { + // the value must be owned, otherwise the local variable may receive a stale pointer to the stack + result_type.value_owned = true; + } + result_type.nullable = true; + } + var result_local = new LocalVariable (result_type, get_temp_name (), new NullLiteral (source_reference), source_reference); + + var result_decl = new DeclarationStatement (result_local, source_reference); + insert_statement (context.analyzer.insert_block, result_decl); + + if (!result_decl.check (context)) { + return false; + } + + // assign the non-conditional member access if the inner expression is not null + var non_null_safe = new BinaryExpression (BinaryOperator.INEQUALITY, new MemberAccess.simple (inner_local.name, source_reference), new NullLiteral (source_reference), source_reference); + var non_null_stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (result_local.name, source_reference), non_null_expr, AssignmentOperator.SIMPLE, source_reference), source_reference); + var non_null_block = new Block (source_reference); + non_null_block.add_statement (non_null_stmt); + var non_null_ifstmt = new IfStatement (non_null_safe, non_null_block, null, source_reference); + insert_statement (context.analyzer.insert_block, non_null_ifstmt); + + if (!non_null_ifstmt.check (context)) { + return false; + } + + var result_access = SemanticAnalyzer.create_temp_access (result_local, target_type); + context.analyzer.replaced_nodes.add (this); + parent_node.replace_expression (this, result_access); + + if (lvalue) { + if (non_null_expr is ReferenceTransferExpression) { + // ownership can be transferred transitively + result_access.lvalue = true; + } else { + Report.error (source_reference, "null-safe expression not supported as lvalue"); + error = true; + return false; + } + } + + return result_access.check (context); + } + } } diff --git a/vala/valamemberaccess.vala b/vala/valamemberaccess.vala index ae8079bd2..6806c55a6 100644 --- a/vala/valamemberaccess.vala +++ b/vala/valamemberaccess.vala @@ -72,6 +72,11 @@ public class Vala.MemberAccess : Expression { */ public bool qualified { get; set; } + /** + * Null-safe access. + */ + public bool null_safe_access { get; set; } + private Expression? _inner; private List type_argument_list = new ArrayList (); bool is_with_variable_access; @@ -213,6 +218,11 @@ public class Vala.MemberAccess : Expression { checked = true; + if (null_safe_access) { + error = !base.check (context); + return !error; + } + if (inner != null) { inner.check (context); } diff --git a/vala/valamethodcall.vala b/vala/valamethodcall.vala index 3a7325e8b..de05c9075 100644 --- a/vala/valamethodcall.vala +++ b/vala/valamethodcall.vala @@ -174,6 +174,11 @@ public class Vala.MethodCall : Expression, CallableExpression { checked = true; + if ((call is MemberAccess) && ((MemberAccess) call).null_safe_access) { + error = !base.check (context); + return !error; + } + if (!call.check (context)) { /* if method resolving didn't succeed, skip this check */ error = true; diff --git a/vala/valaparser.vala b/vala/valaparser.vala index 9ecc96e9b..4bd0114c9 100644 --- a/vala/valaparser.vala +++ b/vala/valaparser.vala @@ -712,6 +712,22 @@ public class Vala.Parser : CodeVisitor { bool found = true; while (found) { switch (current ()) { + case TokenType.INTERR: + // check for null-safe member or element access + next (); + switch (current ()) { + case TokenType.DOT: + expr = parse_member_access (begin, expr); + break; + case TokenType.OPEN_BRACKET: + expr = parse_element_access (begin, expr); + break; + default: + prev (); + found = false; + break; + } + break; case TokenType.DOT: expr = parse_member_access (begin, expr); break; @@ -806,10 +822,13 @@ public class Vala.Parser : CodeVisitor { } Expression parse_member_access (SourceLocation begin, Expression inner) throws ParseError { + bool null_safe = previous () == TokenType.INTERR; + expect (TokenType.DOT); string id = parse_identifier (); List type_arg_list = parse_type_argument_list (true); var expr = new MemberAccess (inner, id, get_src (begin)); + expr.null_safe_access = null_safe; if (type_arg_list != null) { foreach (DataType type_arg in type_arg_list) { expr.add_type_argument (type_arg); @@ -863,6 +882,7 @@ public class Vala.Parser : CodeVisitor { Expression parse_element_access (SourceLocation begin, Expression inner) throws ParseError { Expression? stop = null; List index_list; + bool null_safe = previous () == TokenType.INTERR; expect (TokenType.OPEN_BRACKET); if (current () == TokenType.COLON) { @@ -885,12 +905,15 @@ public class Vala.Parser : CodeVisitor { if (stop == null) { var expr = new ElementAccess (inner, get_src (begin)); + expr.null_safe_access = null_safe; foreach (Expression index in index_list) { expr.append_index (index); } return expr; } else { - return new SliceExpression (inner, index_list[0], stop, get_src (begin)); + var expr = new SliceExpression (inner, index_list[0], stop, get_src (begin)); + expr.null_safe_access = null_safe; + return expr; } } diff --git a/vala/valasliceexpression.vala b/vala/valasliceexpression.vala index a9502b3e8..98b009b5e 100644 --- a/vala/valasliceexpression.vala +++ b/vala/valasliceexpression.vala @@ -60,6 +60,11 @@ public class Vala.SliceExpression : Expression { } } + /** + * Null-safe access. + */ + public bool null_safe_access { get; set; } + Expression _container; Expression _start; Expression _stop; @@ -111,6 +116,11 @@ public class Vala.SliceExpression : Expression { checked = true; + if (null_safe_access) { + error = !base.check (context); + return !error; + } + if (!container.check (context)) { error = true; return false;