From b42c4b54a3589cffcab9dcce603c82728bcd82d0 Mon Sep 17 00:00:00 2001 From: Rico Tzschichholz Date: Thu, 13 Apr 2023 20:36:08 +0200 Subject: vala: Don't make GenericType nullable by default Allow equality between nullable and non-nullable generic-types for now Fixes https://gitlab.gnome.org/GNOME/vala/issues/1191 --- tests/Makefile.am | 3 + .../generics/foreach-iterator-nullable.c-expected | 98 ++++++++++++++++++++++ tests/generics/foreach-iterator-nullable.vala | 7 ++ tests/girwriter/GirTest-1.0.gir-expected | 2 +- tests/girwriter/girtest.vala | 4 +- tests/girwriter/girtest.vapi-expected | 4 +- tests/nullability/generics-invalid.test | 16 ++++ tests/nullability/generics.c-expected | 58 +++++++++++++ tests/nullability/generics.vala | 8 ++ vala/valadatatype.vala | 6 +- vala/valagenerictype.vala | 7 +- vala/valamemberaccess.vala | 1 + vala/valasymbolresolver.vala | 9 +- 13 files changed, 206 insertions(+), 17 deletions(-) create mode 100644 tests/generics/foreach-iterator-nullable.c-expected create mode 100644 tests/generics/foreach-iterator-nullable.vala create mode 100644 tests/nullability/generics-invalid.test create mode 100644 tests/nullability/generics.c-expected create mode 100644 tests/nullability/generics.vala diff --git a/tests/Makefile.am b/tests/Makefile.am index 99cee6206..daf2a8cf9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 000000000..ba88ee255 --- /dev/null +++ b/tests/generics/foreach-iterator-nullable.c-expected @@ -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 +#include +#include + +#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 index 000000000..3c23d3df6 --- /dev/null +++ b/tests/generics/foreach-iterator-nullable.vala @@ -0,0 +1,7 @@ +void main () { + var gs = new GenericSet (GLib.str_hash, GLib.str_equal); + gs.add ("foo"); + foreach (var s in gs) { + assert (s == "foo"); + } +} diff --git a/tests/girwriter/GirTest-1.0.gir-expected b/tests/girwriter/GirTest-1.0.gir-expected index c308e62a1..5aafe5126 100644 --- a/tests/girwriter/GirTest-1.0.gir-expected +++ b/tests/girwriter/GirTest-1.0.gir-expected @@ -2935,7 +2935,7 @@ - + diff --git a/tests/girwriter/girtest.vala b/tests/girwriter/girtest.vala index b2b107385..1125bfa1c 100644 --- a/tests/girwriter/girtest.vala +++ b/tests/girwriter/girtest.vala @@ -163,7 +163,7 @@ namespace GirTest { public delegate bool DelegateErrorTest () throws ErrorTest; - public delegate bool DelegateGenericsTest (G g, T t); + public delegate bool DelegateGenericsTest (G g, T? t); [GIR (visible = false)] public delegate void SkippedDelegate (); @@ -436,7 +436,7 @@ namespace GirTest { public GenericsTest.typed (owned DelegateGenericsTest cb) { } - public void method (T param) { + public void method (T? param) { } } diff --git a/tests/girwriter/girtest.vapi-expected b/tests/girwriter/girtest.vapi-expected index 578d588dc..b63aad4aa 100644 --- a/tests/girwriter/girtest.vapi-expected +++ b/tests/girwriter/girtest.vapi-expected @@ -40,7 +40,7 @@ namespace GirTest { [CCode (cheader_filename = "girtest.h")] public class GenericsTest { public GenericsTest (owned GirTest.DelegateTest cb); - public void method (T param); + public void method (T? param); public GenericsTest.typed (owned GirTest.DelegateGenericsTest 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 g, T t); + public delegate bool DelegateGenericsTest (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 index 000000000..e82fd74d5 --- /dev/null +++ b/tests/nullability/generics-invalid.test @@ -0,0 +1,16 @@ +Invalid Code + +G? foo () { + return null; +} + +void manam (K k) { +} + +void bar () { + T t = foo (); + manam (t); +} + +void main () { +} diff --git a/tests/nullability/generics.c-expected b/tests/nullability/generics.c-expected new file mode 100644 index 000000000..ca4783adb --- /dev/null +++ b/tests/nullability/generics.c-expected @@ -0,0 +1,58 @@ +/* nullability_generics.c generated by valac, the Vala compiler + * generated from nullability_generics.vala, do not modify */ + +#include +#include +#include +#include + +#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 index 000000000..8568f8baa --- /dev/null +++ b/tests/nullability/generics.vala @@ -0,0 +1,8 @@ +G? foo () { + return null; +} + +void main () { + var s = foo (); + assert (s == null); +} diff --git a/vala/valadatatype.vala b/vala/valadatatype.vala index fec7e9010..897eaf72c 100644 --- a/vala/valadatatype.vala +++ b/vala/valadatatype.vala @@ -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; diff --git a/vala/valagenerictype.vala b/vala/valagenerictype.vala index 32b32ba0e..50c713bce 100644 --- a/vala/valagenerictype.vala +++ b/vala/valagenerictype.vala @@ -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) { diff --git a/vala/valamemberaccess.vala b/vala/valamemberaccess.vala index 2b75adcc5..23078f25b 100644 --- a/vala/valamemberaccess.vala +++ b/vala/valamemberaccess.vala @@ -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; } diff --git a/vala/valasymbolresolver.vala b/vala/valasymbolresolver.vala index 87352bd00..d029aa9a7 100644 --- a/vala/valasymbolresolver.vala +++ b/vala/valasymbolresolver.vala @@ -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); -- cgit v1.2.1