]> git.ipfire.org Git - thirdparty/vala.git/commitdiff
vala: Improve check of delegate assignments and initializers
authorRico Tzschichholz <ricotz@ubuntu.com>
Fri, 8 Nov 2019 15:17:58 +0000 (16:17 +0100)
committerRico Tzschichholz <ricotz@ubuntu.com>
Fri, 8 Nov 2019 15:33:37 +0000 (16:33 +0100)
Fixes https://gitlab.gnome.org/GNOME/vala/issues/875

codegen/valaccodedelegatemodule.vala
tests/Makefile.am
tests/delegates/incompatible-assignment.test [new file with mode: 0644]
tests/delegates/incompatible-initializer.test [new file with mode: 0644]
tests/delegates/instance-method-to-no-target-2.test [new file with mode: 0644]
vala/valaassignment.vala
vala/valadelegate.vala
vala/valalocalvariable.vala
vala/valapointertype.vala

index 35d5c4342dea80fb4437785fdb5e6badb0beb70c..64a83411f66fbb9e5bbaa72972e6df047cf83121 100644 (file)
@@ -294,8 +294,8 @@ public class Vala.CCodeDelegateModule : CCodeArrayModule {
                        } else {
                                // use first delegate parameter as instance
                                if (d_params.size == 0 || m.closure) {
-                                       Report.error (node != null ? node.source_reference : null, "Cannot create delegate without target for instance method or closure");
-                                       arg = new CCodeConstant ("NULL");
+                                       Report.error (node != null ? node.source_reference : null, "internal: Cannot create delegate wrapper");
+                                       arg = new CCodeInvalidExpression ();
                                } else {
                                        arg = new CCodeIdentifier (get_ccode_name (d_params.get (0)));
                                        i = 1;
index 7b78d25ee6025b7e62d34a46e4c0481ea0dff659..3a7d6d01e4b009bf3b8d00234e405e189d685b7a 100644 (file)
@@ -282,8 +282,11 @@ TESTS = \
        delegates/fields.vala \
        delegates/fields-no-target.vala \
        delegates/incompatible.test \
+       delegates/incompatible-assignment.test \
+       delegates/incompatible-initializer.test \
        delegates/incompatible-target.test \
        delegates/instance-method-to-no-target.test \
+       delegates/instance-method-to-no-target-2.test \
        delegates/lambda-mixed-instance-static.vala \
        delegates/lambda-shared-closure.vala \
        delegates/member-target-destroy.vala \
diff --git a/tests/delegates/incompatible-assignment.test b/tests/delegates/incompatible-assignment.test
new file mode 100644 (file)
index 0000000..1cd8c9f
--- /dev/null
@@ -0,0 +1,15 @@
+Invalid Code
+
+[CCode (has_target = false)]
+delegate void Foo (string s);
+
+class Bar {
+       public void foo () {
+       }
+}
+
+void main () {
+       var bar = new Bar ();
+       Foo foo;
+       foo = bar.foo;
+}
diff --git a/tests/delegates/incompatible-initializer.test b/tests/delegates/incompatible-initializer.test
new file mode 100644 (file)
index 0000000..0a23d4a
--- /dev/null
@@ -0,0 +1,14 @@
+Invalid Code
+
+[CCode (has_target = false)]
+delegate void Foo (string s);
+
+class Bar {
+       public void foo () {
+       }
+}
+
+void main () {
+       var bar = new Bar ();
+       Foo foo = bar.foo;
+}
diff --git a/tests/delegates/instance-method-to-no-target-2.test b/tests/delegates/instance-method-to-no-target-2.test
new file mode 100644 (file)
index 0000000..124fbdb
--- /dev/null
@@ -0,0 +1,15 @@
+Invalid Code
+
+[CCode (has_target = false)]
+delegate void Foo ();
+
+class Bar {
+       public void foo () {
+       }
+}
+
+void main () {
+       var bar = new Bar ();
+       Foo foo;
+       foo = bar.foo;
+}
index a19228dbe43d996df0d1480ae8b85b7424c5a2f7..9b5c7da5b46d4f90ad5dad0bccba786aa201a723 100644 (file)
@@ -294,23 +294,18 @@ public class Vala.Assignment : Expression {
                                error = true;
                                Report.error (source_reference, "`length' field of fixed length arrays is read-only");
                                return false;
-                       } else if (ma.symbol_reference is Variable && right.value_type == null) {
+                       } else if (ma.symbol_reference is Variable && right.value_type is MethodType) {
                                unowned Variable variable = (Variable) ma.symbol_reference;
 
-                               if (right.symbol_reference is Method &&
-                                   variable.variable_type is DelegateType) {
-                                       unowned Method m = (Method) right.symbol_reference;
-                                       unowned DelegateType dt = (DelegateType) variable.variable_type;
-                                       unowned Delegate cb = dt.delegate_symbol;
-
+                               if (variable.variable_type is DelegateType) {
                                        /* check whether method matches callback type */
-                                       if (!cb.matches_method (m, dt)) {
+                                       if (!right.value_type.compatible (variable.variable_type)) {
+                                               unowned Method m = (Method) right.symbol_reference;
+                                               unowned Delegate cb = ((DelegateType) variable.variable_type).delegate_symbol;
                                                error = true;
-                                               Report.error (source_reference, "declaration of method `%s' doesn't match declaration of callback `%s'".printf (m.get_full_name (), cb.get_full_name ()));
+                                               Report.error (source_reference, "Declaration of method `%s' is not compatible with delegate `%s'".printf (m.get_full_name (), cb.get_full_name ()));
                                                return false;
                                        }
-
-                                       right.value_type = variable.variable_type.copy ();
                                } else {
                                        error = true;
                                        Report.error (source_reference, "Assignment: Invalid assignment attempt");
index 813a73479c5bac7d68eef8eb9dbfad37a2c0f3c8..200b2dbc20e1274e674b3d66a8a8cb80803c4f72 100644 (file)
@@ -165,27 +165,33 @@ public class Vala.Delegate : TypeSymbol, Callable {
 
                bool first = true;
                foreach (Parameter param in parameters) {
+                       DataType method_param_type;
                        /* use first callback parameter as instance parameter if
                         * an instance method is being compared to a static
                         * callback
                         */
                        if (first && m.binding == MemberBinding.INSTANCE && !has_target) {
                                first = false;
-                               continue;
-                       }
-
-                       /* method is allowed to accept less arguments */
-                       if (!method_params_it.next ()) {
-                               break;
+                               method_param_type = SemanticAnalyzer.get_data_type_for_symbol (m.parent_symbol);
+                       } else {
+                               /* method is allowed to accept less arguments */
+                               if (!method_params_it.next ()) {
+                                       break;
+                               }
+                               method_param_type = method_params_it.get ().variable_type;
                        }
 
                        // method is allowed to accept arguments of looser types (weaker precondition)
-                       var method_param = method_params_it.get ();
-                       if (!param.variable_type.get_actual_type (dt, null, this).stricter (method_param.variable_type)) {
+                       if (!param.variable_type.get_actual_type (dt, null, this).stricter (method_param_type)) {
                                return false;
                        }
                }
 
+               // delegate without target for instance method or closure
+               if (first && m.binding == MemberBinding.INSTANCE && !has_target) {
+                       return false;
+               }
+
                /* method may not expect more arguments */
                if (method_params_it.next ()) {
                        return false;
index e334301c536061f3d31b2575b00a9e2302eccad2..f441e16b8bd220fe06ffdebc42cf76520dcbacee 100644 (file)
@@ -162,27 +162,22 @@ public class Vala.LocalVariable : Variable {
                }
 
                if (initializer != null && !initializer.error) {
-                       if (initializer.value_type == null) {
+                       if (initializer.value_type is MethodType) {
                                if (!(initializer is MemberAccess) && !(initializer is LambdaExpression)) {
                                        error = true;
                                        Report.error (source_reference, "expression type not allowed as initializer");
                                        return false;
                                }
 
-                               if (initializer.symbol_reference is Method &&
-                                   variable_type is DelegateType) {
-                                       var m = (Method) initializer.symbol_reference;
-                                       var dt = (DelegateType) variable_type;
-                                       var cb = dt.delegate_symbol;
-
+                               if (variable_type is DelegateType) {
                                        /* check whether method matches callback type */
-                                       if (!cb.matches_method (m, dt)) {
+                                       if (!initializer.value_type.compatible (variable_type)) {
+                                               unowned Method m = (Method) initializer.symbol_reference;
+                                               unowned Delegate cb = ((DelegateType) variable_type).delegate_symbol;
                                                error = true;
-                                               Report.error (source_reference, "declaration of method `%s' doesn't match declaration of callback `%s'".printf (m.get_full_name (), cb.get_full_name ()));
+                                               Report.error (source_reference, "Declaration of method `%s' is not compatible with delegate `%s'".printf (m.get_full_name (), cb.get_full_name ()));
                                                return false;
                                        }
-
-                                       initializer.value_type = variable_type;
                                } else {
                                        error = true;
                                        Report.error (source_reference, "expression type not allowed as initializer");
index 2a26f92cbd128fef9970982b988727409dea677b..6efbad9bd8ed5f2441c560cb1560bfa6f4305f54 100644 (file)
@@ -113,6 +113,19 @@ public class Vala.PointerType : DataType {
                base_type.accept (visitor);
        }
 
+       public override bool stricter (DataType type2) {
+               if (type2 is PointerType) {
+                       return compatible (type2);
+               }
+
+               if (base_type is VoidType) {
+                       // void* can hold reference types
+                       return type2 is ReferenceType;
+               }
+
+               return base_type.stricter (type2);
+       }
+
        public override void replace_type (DataType old_type, DataType new_type) {
                if (base_type == old_type) {
                        base_type = new_type;