From 539b1e296a7d17d046774664f8088804b0b31b59 Mon Sep 17 00:00:00 2001 From: Rico Tzschichholz Date: Tue, 2 Mar 2021 17:55:25 +0100 Subject: vala: Support variadic delegates including params array There can't be a generated wrapper to mitigate possible differences, therefore the signatures must perfectly match and an explicit cast is required. Fixes https://gitlab.gnome.org/GNOME/vala/issues/160 --- codegen/valaccodedelegatemodule.vala | 6 ++- tests/Makefile.am | 3 ++ tests/delegates/params-array-with-throws.vala | 35 +++++++++++++ tests/delegates/params-array.vala | 72 ++++++++++++++++++++++++++ tests/delegates/variadic.vala | 74 +++++++++++++++++++++++++++ vala/valadelegate.vala | 11 +++- vala/valadelegatetype.vala | 9 +++- 7 files changed, 207 insertions(+), 3 deletions(-) create mode 100644 tests/delegates/params-array-with-throws.vala create mode 100644 tests/delegates/params-array.vala create mode 100644 tests/delegates/variadic.vala diff --git a/codegen/valaccodedelegatemodule.vala b/codegen/valaccodedelegatemodule.vala index dd3e0fcf6..4da3d4374 100644 --- a/codegen/valaccodedelegatemodule.vala +++ b/codegen/valaccodedelegatemodule.vala @@ -162,7 +162,11 @@ public class Vala.CCodeDelegateModule : CCodeArrayModule { method = method.base_interface_method; } - return new CCodeIdentifier (generate_delegate_wrapper (method, dt, node)); + if (method.is_variadic ()) { + Report.warning (node.source_reference, "internal: Varidic method requires a direct cast to delegate"); + } else { + return new CCodeIdentifier (generate_delegate_wrapper (method, dt, node)); + } } return base.get_implicit_cast_expression (source_cexpr, expression_type, target_type, node); diff --git a/tests/Makefile.am b/tests/Makefile.am index a74714d9e..99cfa709b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -394,8 +394,11 @@ TESTS = \ delegates/lambda-mixed-instance-static.vala \ delegates/lambda-shared-closure.vala \ delegates/member-target-destroy.vala \ + delegates/params-array.vala \ + delegates/params-array-with-throws.vala \ delegates/reference_transfer.vala \ delegates/return-array-null-terminated.vala \ + delegates/variadic.vala \ delegates/wrapper.vala \ delegates/bug519949.test \ delegates/bug539166.vala \ diff --git a/tests/delegates/params-array-with-throws.vala b/tests/delegates/params-array-with-throws.vala new file mode 100644 index 000000000..aad013416 --- /dev/null +++ b/tests/delegates/params-array-with-throws.vala @@ -0,0 +1,35 @@ +errordomain FooError { + BAD, + WORSE +} + +[CCode (has_target = false)] +delegate void FooFunc (params string[] array) throws FooError; + +void foo (params string[] array) throws FooError { + assert (array.length == 3); + assert (array[0] == "foo"); + assert (array[1] == "bar"); + assert (array[2] == "manam"); +} + +void bar (params string[] array) throws FooError { + throw new FooError.BAD ("bad"); +} + +void main () { + { + FooFunc func = foo; + func ("foo", "bar", "manam"); + } + { + FooFunc func = bar; + try { + func ("foo", "bar", "manam"); + assert_not_reached (); + } catch (FooError.BAD e) { + } catch { + assert_not_reached (); + } + } +} diff --git a/tests/delegates/params-array.vala b/tests/delegates/params-array.vala new file mode 100644 index 000000000..d71b14547 --- /dev/null +++ b/tests/delegates/params-array.vala @@ -0,0 +1,72 @@ +[CCode (has_target = false)] +delegate void FooFunc (params string[] strv); + +void foo (params string[] strv) { + assert (strv.length == 3); + assert (strv[0] == "foo"); + assert (strv[1] == "bar"); + assert (strv[2] == "manam"); +} + +[CCode (has_target = false)] +delegate void BarFunc (params int[] intv); + +void bar (params int[] intv) { + assert (intv.length == 3); + assert (intv[0] == 23); + assert (intv[1] == 42); + assert (intv[2] == 4711); +} + +[CCode (has_target = false)] +delegate void ManamFunc (params Value?[] valuev); + +void manam (params Value?[] valuev) { + assert (valuev.length == 3); + assert (valuev[0] == "foo"); + assert (valuev[1] == 4711); + assert (valuev[2] == 3.1415); +} + +[CCode (has_target = false)] +delegate void ManamOwnedFunc (params owned Value?[] valuev); + +void manam_owned (params owned Value?[] valuev) { + assert (valuev.length == 3); + assert (valuev[0] == "foo"); + assert (valuev[1] == 4711); + assert (valuev[2] == 3.1415); +} + +[CCode (has_target = false)] +delegate void MinimFunc (params Variant[] variantv); + +void minim (params Variant[] variantv) { + assert (variantv.length == 3); + assert ((string) variantv[0] == "foo"); + assert ((int) variantv[1] == 4711); + assert ((double) variantv[2] == 3.1415); +} + +void main () { + { + FooFunc func = foo; + func ("foo", "bar", "manam"); + } + { + BarFunc func = bar; + func (23, 42, 4711); + } + { + ManamFunc func = manam; + func ("foo", 4711, 3.1415); + } + { + ManamOwnedFunc func = manam_owned; + func ("foo", 4711, 3.1415); + } + { + MinimFunc func = minim; + func ("foo", 4711, 3.1415); + } +} diff --git a/tests/delegates/variadic.vala b/tests/delegates/variadic.vala new file mode 100644 index 000000000..b96fb62b0 --- /dev/null +++ b/tests/delegates/variadic.vala @@ -0,0 +1,74 @@ +[CCode (has_target = false)] +delegate void FooFunc (string first, ...); + +[CCode (has_target = false)] +delegate void BarFunc (string first, ...); + +errordomain BazError { + BAD, + WORSE +} + +[CCode (has_target = false)] +delegate void BazFunc (string first, ...) throws BazError; + +void foo (string first, ...) { + assert (first == "foo"); + va_list args = va_list (); + int i = args.arg (); + assert (i == 42); + string s = args.arg (); + assert (s == "bar"); +} + +void baz (string first, ...) throws BazError { + assert (first == "baz"); + va_list args = va_list (); + int i = args.arg (); + assert (i == 23); + string s = args.arg (); + assert (s == "bar"); +} + +void baz_fail (string first, ...) throws BazError { + throw new BazError.BAD ("bad"); +} + +void mamam (FooFunc func) { + func ("foo", 42, "bar"); +} + +void main () { + { + FooFunc func = foo; + func ("foo", 42, "bar"); + } + { + FooFunc func = foo; + BarFunc f = func; + } + { + FooFunc func = (FooFunc) foo; + BarFunc f = (BarFunc) func; + } + { + BazFunc func = baz; + func ("baz", 23, "bar"); + } + { + BazFunc func = baz_fail; + try { + func ("baz", 23, "bar"); + assert_not_reached (); + } catch (BazError.BAD e) { + } catch { + assert_not_reached (); + } + } + { + mamam (foo); + } + { + mamam ((FooFunc) foo); + } +} diff --git a/vala/valadelegate.vala b/vala/valadelegate.vala index 83a3415dc..9b590ca47 100644 --- a/vala/valadelegate.vala +++ b/vala/valadelegate.vala @@ -165,6 +165,7 @@ public class Vala.Delegate : TypeSymbol, Callable { bool first = true; foreach (Parameter param in parameters) { + Parameter? method_param = null; DataType method_param_type; /* use first callback parameter as instance parameter if * an instance method is being compared to a static @@ -178,7 +179,15 @@ public class Vala.Delegate : TypeSymbol, Callable { if (!method_params_it.next ()) { break; } - method_param_type = method_params_it.get ().variable_type; + method_param = method_params_it.get (); + method_param_type = method_param.variable_type; + } + + if (method_param != null && (param.ellipsis || param.params_array)) { + if (param.ellipsis != method_param.ellipsis || param.params_array != method_param.params_array) { + return false; + } + break; } // method is allowed to accept arguments of looser types (weaker precondition) diff --git a/vala/valadelegatetype.vala b/vala/valadelegatetype.vala index 4029f00af..d7b9fc126 100644 --- a/vala/valadelegatetype.vala +++ b/vala/valadelegatetype.vala @@ -160,8 +160,15 @@ public class Vala.DelegateType : CallableType { return false; } - // target-delegate is allowed to accept arguments of looser types (weaker precondition) var p = params_it.get (); + if (p != null && (param.ellipsis || param.params_array)) { + if (param.ellipsis != p.ellipsis || param.params_array != p.params_array) { + return false; + } + break; + } + + // target-delegate is allowed to accept arguments of looser types (weaker precondition) if (!param.variable_type.get_actual_type (this, null, this).stricter (p.variable_type)) { return false; } -- cgit v1.2.1