From: Rico Tzschichholz Date: Thu, 15 Apr 2021 14:49:14 +0000 (+0200) Subject: Add native support to specify non default length-type for arrays X-Git-Tag: 0.55.1~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a24f44294041cc87dd3cadab38e3caa919f774e0;p=thirdparty%2Fvala.git Add native support to specify non default length-type for arrays string[:long] foo = new string[:long] { "foo" }; This applies to generated bindings where this new syntax will be picked up. It will apply a stricter variable type checking for such array types. Fixes https://gitlab.gnome.org/GNOME/vala/issues/607 --- diff --git a/tests/Makefile.am b/tests/Makefile.am index c34a84fa1..5da9114b3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -129,6 +129,7 @@ TESTS = \ arrays/inline-struct-field.test \ arrays/in-operator-with-boxed-needle.vala \ arrays/length-inline-assignment.vala \ + arrays/length-type.vala \ arrays/length-type-include.vala \ arrays/length-no-int-type.test \ arrays/struct-field-length-cname.vala \ @@ -868,6 +869,7 @@ TESTS = \ scanner/string-escape-x.vala \ parser/argument-list-incomplete.test \ parser/array-creation-invalid.test \ + parser/array-length.vala \ parser/assignment.vala \ parser/attribute-duplicate.test \ parser/attribute-missing-literal.test \ @@ -992,6 +994,8 @@ TESTS = \ semantic/array-incompatible-initializer.test \ semantic/array-incompatible-initializer2.test \ semantic/array-invalid-type-argument.test \ + semantic/array-length-invalid.test \ + semantic/array-length-nullable.test \ semantic/array-too-few-type-arguments.test \ semantic/array-too-many-type-arguments.test \ semantic/assignment-element-incompatible-ownership.test \ diff --git a/tests/arrays/length-type.c-expected b/tests/arrays/length-type.c-expected new file mode 100644 index 000000000..a854f2029 --- /dev/null +++ b/tests/arrays/length-type.c-expected @@ -0,0 +1,210 @@ +/* arrays_length_type.c generated by valac, the Vala compiler + * generated from arrays_length_type.vala, do not modify */ + +#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 guint8* (*ManamFunc) (guint8* param, gsize param_length1, guint64* result_length1); +#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 guint8* field; +VALA_EXTERN gsize field_length1; +guint8* field = NULL; +gsize field_length1 = 0; +static gsize _field_size_ = 0; + +VALA_EXTERN guint8* manam (guint8* param, + gsize param_length1, + guint64* result_length1); +VALA_EXTERN void test_pass (void); +static guint8* _manam_manam_func (guint8* param, + gsize param_length1, + guint64* result_length1); +VALA_EXTERN void foo (guint8** param, + gssize* param_length1); +VALA_EXTERN void test_ref (void); +VALA_EXTERN void bar (guint8** param, + gssize* param_length1); +VALA_EXTERN void test_out (void); +static void _vala_main (void); + +guint8* +manam (guint8* param, + gsize param_length1, + guint64* result_length1) +{ + guint8* _tmp0_; + guint64 _tmp0__length1; + guint8* result; + _vala_assert (((gint64) param_length1) == 2147483648LL, "param.length == 2147483648LL"); + _tmp0_ = param; + _tmp0__length1 = param_length1; + if (result_length1) { + *result_length1 = _tmp0__length1; + } + result = _tmp0_; + return result; +} + +static guint8* +_manam_manam_func (guint8* param, + gsize param_length1, + guint64* result_length1) +{ + guint8* result; + result = manam (param, param_length1, result_length1); + return result; +} + +void +test_pass (void) +{ + guint8* foo = NULL; + guint8* _tmp0_; + gssize foo_length1; + gssize _foo_size_; + guint8* _tmp1_; + gssize _tmp1__length1; + ManamFunc func = NULL; + guint8* bar = NULL; + guint8* _tmp2_; + gssize _tmp2__length1; + guint64 _tmp3_ = 0; + guint8* _tmp4_; + guint64 bar_length1; + guint64 _bar_size_; + guint8* _tmp5_; + gssize _tmp5__length1; + guint8* _tmp6_; + gsize _tmp6__length1; + _tmp0_ = g_new0 (guint8, 2147483648LL); + foo = _tmp0_; + foo_length1 = 2147483648LL; + _foo_size_ = foo_length1; + _tmp1_ = foo; + _tmp1__length1 = foo_length1; + _vala_assert (((gint64) _tmp1__length1) == 2147483648LL, "foo.length == 2147483648LL"); + func = _manam_manam_func; + _tmp2_ = foo; + _tmp2__length1 = foo_length1; + _tmp4_ = func (_tmp2_, (gsize) _tmp2__length1, &_tmp3_); + bar = _tmp4_; + bar_length1 = _tmp3_; + _bar_size_ = bar_length1; + _vala_assert (bar_length1 == ((guint64) 2147483648LL), "bar.length == 2147483648LL"); + _tmp5_ = foo; + _tmp5__length1 = foo_length1; + foo = NULL; + foo_length1 = 0; + field = (g_free (field), NULL); + field = _tmp5_; + field_length1 = _tmp5__length1; + _field_size_ = field_length1; + _tmp6_ = field; + _tmp6__length1 = field_length1; + _vala_assert (((gint64) _tmp6__length1) == 2147483648LL, "field.length == 2147483648LL"); + field = (g_free (field), NULL); + field = NULL; + field_length1 = 0; + _field_size_ = field_length1; + foo = (g_free (foo), NULL); +} + +void +foo (guint8** param, + gssize* param_length1) +{ + guint8* _tmp0_; + _tmp0_ = g_new0 (guint8, 2147483648LL); + *param = (g_free (*param), NULL); + *param = _tmp0_; + *param_length1 = 2147483648LL; +} + +void +test_ref (void) +{ + guint8* a = NULL; + guint8* _tmp0_; + gssize a_length1; + gssize _a_size_; + guint8* _tmp1_; + gssize _tmp1__length1; + _tmp0_ = g_new0 (guint8, 0); + a = _tmp0_; + a_length1 = 0; + _a_size_ = a_length1; + foo (&a, (gssize*) (&a_length1)); + _a_size_ = a_length1; + _tmp1_ = a; + _tmp1__length1 = a_length1; + _vala_assert (((gint64) _tmp1__length1) == 2147483648LL, "a.length == 2147483648LL"); + a = (g_free (a), NULL); +} + +void +bar (guint8** param, + gssize* param_length1) +{ + guint8* _vala_param = NULL; + gssize _vala_param_length1 = 0; + guint8* _tmp0_; + _tmp0_ = g_new0 (guint8, 2147483648LL); + _vala_param = (g_free (_vala_param), NULL); + _vala_param = _tmp0_; + _vala_param_length1 = 2147483648LL; + if (param) { + *param = _vala_param; + } else { + _vala_param = (g_free (_vala_param), NULL); + } + if (param_length1) { + *param_length1 = _vala_param_length1; + } +} + +void +test_out (void) +{ + guint8* a = NULL; + gssize a_length1 = 0L; + gssize _a_size_ = 0L; + guint8* _tmp0_ = NULL; + gssize _tmp1_ = 0; + bar (&_tmp0_, &_tmp1_); + a = (g_free (a), NULL); + a = _tmp0_; + a_length1 = _tmp1_; + _a_size_ = a_length1; + _vala_assert (((gint64) a_length1) == 2147483648LL, "a.length == 2147483648LL"); + a = (g_free (a), NULL); +} + +static void +_vala_main (void) +{ + test_pass (); + test_ref (); + test_out (); +} + +int +main (int argc, + char ** argv) +{ + _vala_main (); + return 0; +} + diff --git a/tests/arrays/length-type.vala b/tests/arrays/length-type.vala new file mode 100644 index 000000000..63f6124d6 --- /dev/null +++ b/tests/arrays/length-type.vala @@ -0,0 +1,49 @@ +[CCode (has_target = false)] +delegate unowned uint8[:uint64] ManamFunc (uint8[:size_t] param); + +unowned uint8[:uint64] manam (uint8[:size_t] param) { + assert (param.length == 2147483648LL); + return param; +} + +uint8[:size_t] field; + +void test_pass () { + var foo = new uint8[2147483648LL:ssize_t]; + assert (foo.length == 2147483648LL); + + ManamFunc func = manam; + unowned var bar = func (foo); + assert (bar.length == 2147483648LL); + + field = (owned) foo; + assert (field.length == 2147483648LL); + + field = null; +} + +void foo (ref uint8[:ssize_t] param) { + param = new uint8[2147483648LL:ssize_t]; +} + +void test_ref () { + var a = new uint8[:ssize_t] {}; + foo (ref a); + assert (a.length == 2147483648LL); +} + +void bar (out uint8[:ssize_t] param) { + param = new uint8[2147483648LL:ssize_t]; +} + +void test_out () { + uint8[:ssize_t] a; + bar (out a); + assert (a.length == 2147483648LL); +} + +void main () { + test_pass (); + test_ref (); + test_out (); +} diff --git a/tests/parser/array-length.c-expected b/tests/parser/array-length.c-expected new file mode 100644 index 000000000..8cca3c9c3 --- /dev/null +++ b/tests/parser/array-length.c-expected @@ -0,0 +1,90 @@ +/* parser_array_length.c generated by valac, the Vala compiler + * generated from parser_array_length.vala, do not modify */ + +#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 guint8* (*FooFunc) (guint8* param0, gsize param0_length1, guint8** param1, gint64* param1_length1, guint8** param2, guint* param2_length1, guint8* param3, guint64* result_length1, gpointer user_data); + +VALA_EXTERN guint8* field0; +VALA_EXTERN gssize field0_length1; +guint8* field0 = NULL; +gssize field0_length1 = 0; +static gssize _field0_size_ = 0; +VALA_EXTERN guint8 field1[4711]; +guint8 field1[4711] = {0}; + +VALA_EXTERN guint8* func (guint8* param0, + gsize param0_length1, + guint8** param1, + gint64* param1_length1, + guint8** param2, + guint* param2_length1, + guint8* param3, + guint64* result_length1); +static void _vala_main (void); + +guint8* +func (guint8* param0, + gsize param0_length1, + guint8** param1, + gint64* param1_length1, + guint8** param2, + guint* param2_length1, + guint8* param3, + guint64* result_length1) +{ + guint8* _vala_param2 = NULL; + guint _vala_param2_length1 = 0; + guint8* _tmp0_; + guint64 _tmp0__length1; + guint8* result; + _tmp0_ = param0; + _tmp0__length1 = param0_length1; + if (result_length1) { + *result_length1 = _tmp0__length1; + } + result = _tmp0_; + if (param2) { + *param2 = _vala_param2; + } else { + _vala_param2 = (g_free (_vala_param2), NULL); + } + if (param2_length1) { + *param2_length1 = _vala_param2_length1; + } + return result; +} + +static void +_vala_main (void) +{ + guint8* local_heap = NULL; + guint8* _tmp0_; + gssize local_heap_length1; + gssize _local_heap_size_; + guint8 local_stack[42] = {0}; + _tmp0_ = g_new0 (guint8, 23); + local_heap = _tmp0_; + local_heap_length1 = 23; + _local_heap_size_ = local_heap_length1; + local_heap = (g_free (local_heap), NULL); +} + +int +main (int argc, + char ** argv) +{ + _vala_main (); + return 0; +} + diff --git a/tests/parser/array-length.vala b/tests/parser/array-length.vala new file mode 100644 index 000000000..4de401cf4 --- /dev/null +++ b/tests/parser/array-length.vala @@ -0,0 +1,13 @@ +delegate unowned uint8[:uint64] FooFunc (uint8[:size_t] param0, ref uint8[:int64] param1, out uint8[:uint] param2, uint8 param3[42:ssize_t]); + +unowned uint8[:uint64] func (uint8[:size_t] param0, ref uint8[:int64] param1, out uint8[:uint] param2, uint8 param3[42:ssize_t]) { + return param0; +} + +uint8[:ssize_t] field0; +uint8 field1[4711:ssize_t]; + +void main () { + var local_heap = new uint8[23:ssize_t]; + uint8 local_stack[42:ssize_t]; +} diff --git a/tests/semantic/array-length-invalid.test b/tests/semantic/array-length-invalid.test new file mode 100644 index 000000000..aee2cfdb8 --- /dev/null +++ b/tests/semantic/array-length-invalid.test @@ -0,0 +1,5 @@ +Invalid Code + +void main () { + var foo = new uint8[23:float]; +} diff --git a/tests/semantic/array-length-nullable.test b/tests/semantic/array-length-nullable.test new file mode 100644 index 000000000..fa56e48ee --- /dev/null +++ b/tests/semantic/array-length-nullable.test @@ -0,0 +1,5 @@ +Invalid Code + +void main () { + var foo = new uint8[23:size_t?]; +} diff --git a/vala/valaarraycreationexpression.vala b/vala/valaarraycreationexpression.vala index 70fc4425a..3b423b224 100644 --- a/vala/valaarraycreationexpression.vala +++ b/vala/valaarraycreationexpression.vala @@ -167,6 +167,10 @@ public class Vala.ArrayCreationExpression : Expression { builder.append_printf (", %s", size.to_string ()); } } + var length_str = length_type.to_string (); + if (length_str != "int") { + builder.append_printf (":%s", length_str); + } builder.append_c (']'); if (initializer_list != null) { builder.append (initializer_list.to_string ()); diff --git a/vala/valaarraytype.vala b/vala/valaarraytype.vala index 55847d5db..b032de4f7 100644 --- a/vala/valaarraytype.vala +++ b/vala/valaarraytype.vala @@ -199,7 +199,13 @@ public class Vala.ArrayType : ReferenceType { } if (!fixed_length) { - return "%s[%s]%s".printf (elem_str, string.nfill (rank - 1, ','), nullable ? "?" : ""); + var length_str = length_type == null ? "int" : length_type.to_qualified_string (scope); + if (length_str != "int") { + length_str = ":%s".printf (length_str); + } else { + length_str = ""; + } + return "%s[%s%s]%s".printf (elem_str, string.nfill (rank - 1, ','), length_str, nullable ? "?" : ""); } else { return elem_str; } diff --git a/vala/valacodewriter.vala b/vala/valacodewriter.vala index cb3bd998d..f9f1fccd1 100644 --- a/vala/valacodewriter.vala +++ b/vala/valacodewriter.vala @@ -1569,6 +1569,11 @@ public class Vala.CodeWriter : CodeVisitor { if (array_type != null && array_type.fixed_length) { write_string ("["); array_type.length.accept (this); + var length_type = array_type.length_type.to_qualified_string (current_scope); + if (length_type != "int") { + write_string (":"); + write_string (length_type); + } write_string ("]"); } } diff --git a/vala/valaparser.vala b/vala/valaparser.vala index 112a0d04d..87817fd6b 100644 --- a/vala/valaparser.vala +++ b/vala/valaparser.vala @@ -491,10 +491,13 @@ public class Vala.Parser : CodeVisitor { while (accept (TokenType.OPEN_BRACKET)) { do { // required for decision between expression and declaration statement - if (current () != TokenType.COMMA && current () != TokenType.CLOSE_BRACKET) { + if (current () != TokenType.COMMA && current () != TokenType.CLOSE_BRACKET && current () != TokenType.COLON) { parse_expression (); } } while (accept (TokenType.COMMA)); + if (accept (TokenType.COLON)) { + skip_symbol_name (); + } expect (TokenType.CLOSE_BRACKET); accept (TokenType.INTERR); } @@ -581,18 +584,22 @@ public class Vala.Parser : CodeVisitor { // array brackets in types are read from right to left, // this is more logical, especially when nullable arrays // or pointers are involved + DataType? array_length_type = null; while (accept (TokenType.OPEN_BRACKET)) { bool invalid_array = false; int array_rank = 0; do { array_rank++; // required for decision between expression and declaration statement - if (current () != TokenType.COMMA && current () != TokenType.CLOSE_BRACKET) { + if (current () != TokenType.COMMA && current () != TokenType.CLOSE_BRACKET && current () != TokenType.COLON) { parse_expression (); // only used for parsing, reject use as real type invalid_array = true; } } while (accept (TokenType.COMMA)); + if (accept (TokenType.COLON)) { + array_length_type = parse_type (true, false); + } expect (TokenType.CLOSE_BRACKET); type.value_owned = inner_type_owned; @@ -600,6 +607,9 @@ public class Vala.Parser : CodeVisitor { var array_type = new ArrayType (type, array_rank, get_src (begin)); array_type.nullable = accept (TokenType.INTERR); array_type.invalid_syntax = invalid_array; + if (array_length_type != null) { + array_type.length_type = array_length_type.copy (); + } type = array_type; } @@ -623,10 +633,14 @@ public class Vala.Parser : CodeVisitor { // inline-allocated array if (type != null && accept (TokenType.OPEN_BRACKET)) { Expression array_length = null; + DataType? array_length_type = null; if (current () != TokenType.CLOSE_BRACKET) { array_length = parse_expression (); } + if (accept (TokenType.COLON)) { + array_length_type = parse_type (true, false); + } expect (TokenType.CLOSE_BRACKET); var array_type = new ArrayType (type, 1, get_src (begin)); @@ -635,6 +649,9 @@ public class Vala.Parser : CodeVisitor { array_type.fixed_length = true; array_type.length = array_length; } + if (array_length_type != null) { + array_type.length_type = array_length_type; + } array_type.value_owned = type.value_owned; return array_type; @@ -1065,6 +1082,7 @@ public class Vala.Parser : CodeVisitor { bool size_specified = false; List size_specifier_list = null; bool first = true; + DataType? array_length_type = null; do { if (!first) { // array of arrays: new T[][42] @@ -1074,6 +1092,9 @@ public class Vala.Parser : CodeVisitor { } element_type = new ArrayType (element_type, size_specifier_list.size, element_type.source_reference); + if (array_length_type != null) { + ((ArrayType) element_type).length_type = array_length_type.copy (); + } } else { first = false; } @@ -1081,12 +1102,15 @@ public class Vala.Parser : CodeVisitor { size_specifier_list = new ArrayList (); do { Expression size = null; - if (current () != TokenType.CLOSE_BRACKET && current () != TokenType.COMMA) { + if (current () != TokenType.CLOSE_BRACKET && current () != TokenType.COMMA && current () != TokenType.COLON) { size = parse_expression (); size_specified = true; } size_specifier_list.add (size); } while (accept (TokenType.COMMA)); + if (accept (TokenType.COLON)) { + array_length_type = parse_type (true, false); + } expect (TokenType.CLOSE_BRACKET); } while (accept (TokenType.OPEN_BRACKET)); @@ -1097,6 +1121,9 @@ public class Vala.Parser : CodeVisitor { initializer = parse_initializer (); } var expr = new ArrayCreationExpression (element_type, size_specifier_list.size, initializer, src); + if (array_length_type != null) { + expr.length_type = array_length_type.copy (); + } if (size_specified) { foreach (Expression size in size_specifier_list) { expr.append_size (size);