]> git.ipfire.org Git - thirdparty/vala.git/commitdiff
vala: Don't make GenericType nullable by default
authorRico Tzschichholz <ricotz@ubuntu.com>
Thu, 13 Apr 2023 18:36:08 +0000 (20:36 +0200)
committerRico Tzschichholz <ricotz@ubuntu.com>
Fri, 14 Apr 2023 07:14:11 +0000 (09:14 +0200)
Allow equality between nullable and non-nullable generic-types for now

Fixes https://gitlab.gnome.org/GNOME/vala/issues/1191

13 files changed:
tests/Makefile.am
tests/generics/foreach-iterator-nullable.c-expected [new file with mode: 0644]
tests/generics/foreach-iterator-nullable.vala [new file with mode: 0644]
tests/girwriter/GirTest-1.0.gir-expected
tests/girwriter/girtest.vala
tests/girwriter/girtest.vapi-expected
tests/nullability/generics-invalid.test [new file with mode: 0644]
tests/nullability/generics.c-expected [new file with mode: 0644]
tests/nullability/generics.vala [new file with mode: 0644]
vala/valadatatype.vala
vala/valagenerictype.vala
vala/valamemberaccess.vala
vala/valasymbolresolver.vala

index 99cee62061db351f505ea580bea68d2e06c957b9..daf2a8cf91ca0478f148f480f7c76e7917f058aa 100644 (file)
@@ -801,6 +801,7 @@ TESTS = \
        generics/constructor-chain-up.vala \
        generics/delegate-return-type-missing.test \
        generics/floating-type-cast.vala \
+       generics/foreach-iterator-nullable.vala \
        generics/gvariant-serialization.test \
        generics/inference-argument-may-fail.vala \
        generics/inference-static-function.vala \
@@ -1361,6 +1362,8 @@ TESTS = \
        nullability/array-element-class.vala \
        nullability/array-element-string.vala \
        nullability/bug611223.vala \
+       nullability/generics-invalid.test \
+       nullability/generics.vala \
        nullability/local-variable-invalid-convert.test \
        nullability/member-access-narrowed-instance.vala \
        nullability/member-access-nullable-instance.test \
diff --git a/tests/generics/foreach-iterator-nullable.c-expected b/tests/generics/foreach-iterator-nullable.c-expected
new file mode 100644 (file)
index 0000000..ba88ee2
--- /dev/null
@@ -0,0 +1,98 @@
+/* generics_foreach_iterator_nullable.c generated by valac, the Vala compiler
+ * generated from generics_foreach_iterator_nullable.vala, do not modify */
+
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define _g_hash_table_unref0(var) ((var == NULL) ? NULL : (var = (g_hash_table_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);
+
+static void _vala_main (void);
+static void _g_free0_ (gpointer var);
+
+static void
+_g_free0_ (gpointer var)
+{
+       var = (g_free (var), NULL);
+}
+
+static gconstpointer
+_vala_hash_set_next_value (GHashTableIter *self)
+{
+       void* vi = NULL;
+       GHashTableIter* htp = NULL;
+       void* _tmp0_;
+       gconstpointer value = NULL;
+       gconstpointer _tmp1_ = NULL;
+       gconstpointer _tmp2_ = NULL;
+       gboolean _tmp3_;
+       gconstpointer result;
+       vi = &(*self);
+       _tmp0_ = vi;
+       htp = _tmp0_;
+       _tmp3_ = g_hash_table_iter_next (htp, (gpointer*) (&_tmp2_), (gpointer*) NULL);
+       value = _tmp2_;
+       if (_tmp3_) {
+               gconstpointer _tmp4_;
+               _tmp4_ = value;
+               _tmp1_ = _tmp4_;
+       } else {
+               _tmp1_ = NULL;
+       }
+       result = _tmp1_;
+       return result;
+}
+
+static void
+_vala_main (void)
+{
+       GHashTable* gs = NULL;
+       GHashFunc _tmp0_;
+       GEqualFunc _tmp1_;
+       GHashTable* _tmp2_;
+       GHashTable* _tmp3_;
+       gchar* _tmp4_;
+       _tmp0_ = g_str_hash;
+       _tmp1_ = g_str_equal;
+       _tmp2_ = g_hash_table_new_full (_tmp0_, _tmp1_, NULL, _g_free0_);
+       gs = _tmp2_;
+       _tmp3_ = gs;
+       _tmp4_ = g_strdup ("foo");
+       g_hash_table_add (_tmp3_, _tmp4_);
+       {
+               GHashTableIter _s_it = {0};
+               GHashTable* _tmp5_;
+               GHashTableIter _tmp6_ = {0};
+               const gchar* s = NULL;
+               _tmp5_ = gs;
+               g_hash_table_iter_init (&_tmp6_, _tmp5_);
+               _s_it = _tmp6_;
+               while (TRUE) {
+                       gconstpointer _tmp7_;
+                       const gchar* _tmp8_;
+                       const gchar* _tmp9_;
+                       _tmp7_ = _vala_hash_set_next_value (&_s_it);
+                       s = (const gchar*) _tmp7_;
+                       _tmp8_ = s;
+                       if (!(_tmp8_ != NULL)) {
+                               break;
+                       }
+                       _tmp9_ = s;
+                       _vala_assert (g_strcmp0 (_tmp9_, "foo") == 0, "s == \"foo\"");
+               }
+       }
+       _g_hash_table_unref0 (gs);
+}
+
+int
+main (int argc,
+      char ** argv)
+{
+       _vala_main ();
+       return 0;
+}
+
diff --git a/tests/generics/foreach-iterator-nullable.vala b/tests/generics/foreach-iterator-nullable.vala
new file mode 100644 (file)
index 0000000..3c23d3d
--- /dev/null
@@ -0,0 +1,7 @@
+void main () {
+       var gs = new GenericSet<string> (GLib.str_hash, GLib.str_equal);
+       gs.add ("foo");
+       foreach (var s in gs) {
+               assert (s == "foo");
+       }
+}
index c308e62a1661de423ba487b76938364da02beaee..5aafe512636071cab06943bed4ef3bbd64240e07 100644 (file)
                        <parameter name="t_destroy_func" transfer-ownership="none">
                                <type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
                        </parameter>
-                       <parameter name="g" transfer-ownership="none" nullable="1">
+                       <parameter name="g" transfer-ownership="none">
                                <type name="gpointer" c:type="gpointer"/>
                        </parameter>
                        <parameter name="t" transfer-ownership="none" nullable="1">
index b2b1073856cb1d266ffb24249afd6c9f024008e1..1125bfa1cb9a75fca48be794b145f1f625adce57 100644 (file)
@@ -163,7 +163,7 @@ namespace GirTest {
 
        public delegate bool DelegateErrorTest () throws ErrorTest;
 
-       public delegate bool DelegateGenericsTest<G,T> (G g, T t);
+       public delegate bool DelegateGenericsTest<G,T> (G g, T? t);
 
        [GIR (visible = false)]
        public delegate void SkippedDelegate ();
@@ -436,7 +436,7 @@ namespace GirTest {
                public GenericsTest.typed (owned DelegateGenericsTest<G,T> cb) {
                }
 
-               public void method (T param) {
+               public void method (T? param) {
                }
        }
 
index 578d588dc841ac6cc5469a416146d1ab6d9c838a..b63aad4aa7711b1644e5a9e36b3d7434b4cdc6ad 100644 (file)
@@ -40,7 +40,7 @@ namespace GirTest {
        [CCode (cheader_filename = "girtest.h")]
        public class GenericsTest<G,T> {
                public GenericsTest (owned GirTest.DelegateTest cb);
-               public void method (T param);
+               public void method (T? param);
                public GenericsTest.typed (owned GirTest.DelegateGenericsTest<G,T> cb);
        }
        [CCode (cheader_filename = "girtest.h")]
@@ -258,7 +258,7 @@ namespace GirTest {
        [CCode (cheader_filename = "girtest.h")]
        public delegate bool DelegateErrorTest () throws GirTest.ErrorTest;
        [CCode (cheader_filename = "girtest.h")]
-       public delegate bool DelegateGenericsTest<G,T> (G g, T t);
+       public delegate bool DelegateGenericsTest<G,T> (G g, T? t);
        [CCode (cheader_filename = "girtest.h")]
        public delegate bool DelegateTest (void* a, void* b);
        [CCode (cheader_filename = "girtest.h")]
diff --git a/tests/nullability/generics-invalid.test b/tests/nullability/generics-invalid.test
new file mode 100644 (file)
index 0000000..e82fd74
--- /dev/null
@@ -0,0 +1,16 @@
+Invalid Code
+
+G? foo<G> () {
+       return null;
+}
+
+void manam<K> (K k) {
+}
+
+void bar<T> () {
+       T t = foo<T> ();
+       manam (t);
+}
+
+void main () {
+}
diff --git a/tests/nullability/generics.c-expected b/tests/nullability/generics.c-expected
new file mode 100644 (file)
index 0000000..ca4783a
--- /dev/null
@@ -0,0 +1,58 @@
+/* nullability_generics.c generated by valac, the Vala compiler
+ * generated from nullability_generics.vala, do not modify */
+
+#include <glib-object.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if !defined(VALA_EXTERN)
+#if defined(_WIN32) || defined(__CYGWIN__)
+#define VALA_EXTERN __declspec(dllexport) extern
+#elif __GNUC__ >= 4
+#define VALA_EXTERN __attribute__((visibility("default"))) extern
+#else
+#define VALA_EXTERN extern
+#endif
+#endif
+
+#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);
+
+VALA_EXTERN gpointer foo (GType g_type,
+              GBoxedCopyFunc g_dup_func,
+              GDestroyNotify g_destroy_func);
+static void _vala_main (void);
+
+gpointer
+foo (GType g_type,
+     GBoxedCopyFunc g_dup_func,
+     GDestroyNotify g_destroy_func)
+{
+       gpointer result;
+       result = NULL;
+       return result;
+}
+
+static void
+_vala_main (void)
+{
+       gchar* s = NULL;
+       gpointer _tmp0_;
+       _tmp0_ = foo (G_TYPE_STRING, (GBoxedCopyFunc) g_strdup, (GDestroyNotify) g_free);
+       s = (gchar*) _tmp0_;
+       _vala_assert (s == NULL, "s == null");
+       _g_free0 (s);
+}
+
+int
+main (int argc,
+      char ** argv)
+{
+       _vala_main ();
+       return 0;
+}
+
diff --git a/tests/nullability/generics.vala b/tests/nullability/generics.vala
new file mode 100644 (file)
index 0000000..8568f8b
--- /dev/null
@@ -0,0 +1,8 @@
+G? foo<G> () {
+       return null;
+}
+
+void main () {
+       var s = foo<string> ();
+       assert (s == null);
+}
index fec7e901018276e2f152b205cb1f1425accfd37e..897eaf72c391999acf36d3f6849b3f6769889660 100644 (file)
@@ -204,7 +204,11 @@ public abstract class Vala.DataType : CodeNode {
                        return false;
                }
                if (type2.nullable != nullable) {
-                       return false;
+                       //TODO Allow equality between nullable and non-nullable generic-types
+                       // This mitigation allows fixing affected source code without breaking it.
+                       // It has to be removed at some point
+                       var context = CodeContext.get ();
+                       return !context.experimental_non_null && this is GenericType == type2 is GenericType;
                }
                if (type2.type_symbol != type_symbol) {
                        return false;
index 32b32ba0e57147662db7b16b96dd0f0584bcf9ba..50c713bced94934a2687c239b5ef5e5d53827516 100644 (file)
@@ -40,8 +40,6 @@ public class Vala.GenericType : DataType {
 
        public GenericType (TypeParameter type_parameter, SourceReference? source_reference = null) {
                base.with_symbol (type_parameter, source_reference);
-               // type parameters are always considered nullable
-               this.nullable = true;
        }
 
        public override DataType copy () {
@@ -62,6 +60,9 @@ public class Vala.GenericType : DataType {
                }
 
                result = SemanticAnalyzer.get_actual_type (derived_instance_type, method_type_arguments, (GenericType) result, node_reference);
+               if (!result.is_non_null_simple_type ()) {
+                       result.nullable = result.nullable || nullable;
+               }
 
                return result;
        }
@@ -77,7 +78,7 @@ public class Vala.GenericType : DataType {
        }
 
        public override string to_qualified_string (Scope? scope = null) {
-               return type_parameter.name;
+               return "%s%s".printf (type_parameter.name, nullable ? "?" : "");
        }
 
        public override Symbol? get_member (string member_name) {
index 2b75adcc5545011eaf6a6ff3c085497658fed91b..23078f25b9b68e111f9eef658cba0467dbab0c64 100644 (file)
@@ -620,6 +620,7 @@ public class Vala.MemberAccess : Expression {
                } else if (symbol_reference.error) {
                        //ignore previous error
                        error = true;
+                       value_type = new InvalidType ();
                        return false;
                }
 
index 87352bd0064cabd5aaf7f77669d878cbb5fd1673..d029aa9a77f79e8fd2228840c8d411120f37d82b 100644 (file)
@@ -473,16 +473,9 @@ public class Vala.SymbolResolver : CodeVisitor {
 
                type.source_reference = unresolved_type.source_reference;
                type.value_owned = unresolved_type.value_owned;
+               type.nullable = unresolved_type.nullable;
                sym.used = true;
 
-               if (type is GenericType) {
-                       // type parameters are always considered nullable
-                       // actual type argument may or may not be nullable
-                       type.nullable = true;
-               } else {
-                       type.nullable = unresolved_type.nullable;
-               }
-
                type.is_dynamic = unresolved_type.is_dynamic;
                foreach (DataType type_arg in unresolved_type.get_type_arguments ()) {
                        type.add_type_argument (type_arg);