patch 9.0.2160: instanceof() should use varargs as second arg

Commit: 
https://github.com/vim/vim/commit/2025af165ec68d831f0f0f668a3ceac3f39142ef
Author: Ernie Rael <err...@raelity.com>
Date:   Tue Dec 12 16:58:00 2023 +0100

    patch 9.0.2160: instanceof() should use varargs as second arg
    
    Problem:  instanceof() should use varargs as second arg
    Solution: Modify `instanceof()` to use varargs instead of list
    
    Modify `instanceof()` to use varargs instead of list
    Valid `instanceof()` arguments are `type`s. A `type` is not a value;
    it cannot be added to a list.
    
    This change is non-compatible with the current usage of instanceof;
    but instanceof is relatively new and it's a trivial change.
    
    fixes: #13421
    closes: #13644
    
    Signed-off-by: Ernie Rael <err...@raelity.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index a4a097847..c9b9938dd 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -5058,12 +5058,12 @@ insert({object}, {item} [, {idx}])                      
*insert()*
                                        *instanceof()* *E614* *E616* *E693*
 instanceof({object}, {class})
                The result is a Number, which is |TRUE| when the {object}
-               argument is a direct or indirect instance of a |Class|
-               specified by {class}.
-               When {class} is a |List| the function returns |TRUE| when
+               argument is a direct or indirect instance of a |Class|,
+               |Interface|, or class |:type| alias specified by {class}.
+               If {class} is varargs, the function returns |TRUE| when
                {object} is an instance of any of the specified classes.
                Example: >
-                       instanceof(animal, [Dog, Cat])
+                       instanceof(animal, Dog, Cat)
 
 <              Can also be used as a |method|: >
                        myobj->instanceof(mytype)
diff --git a/src/errors.h b/src/errors.h
index 2788800c0..7192514f0 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -1778,8 +1778,8 @@ EXTERN char e_can_only_compare_list_with_list[]
        INIT(= N_("E691: Can only compare List with List"));
 EXTERN char e_invalid_operation_for_list[]
        INIT(= N_("E692: Invalid operation for List"));
-EXTERN char e_list_or_class_required_for_argument_nr[]
-       INIT(= N_("E693: List or Class required for argument %d"));
+EXTERN char e_class_or_typealias_required_for_argument_nr[]
+       INIT(= N_("E693: Class or class typealias required for argument %d"));
 EXTERN char e_invalid_operation_for_funcrefs[]
        INIT(= N_("E694: Invalid operation for Funcrefs"));
 EXTERN char e_cannot_index_a_funcref[]
@@ -3562,13 +3562,13 @@ EXTERN char e_using_typealias_as_value_str[]
 EXTERN char e_abstract_cannot_be_used_in_interface[]
        INIT(= N_("E1404: Abstract cannot be used in an interface"));
 EXTERN char e_using_class_as_value_str[]
-       INIT(= N_("E1403: Class \"%s\" cannot be used as a value"));
+       INIT(= N_("E1405: Class \"%s\" cannot be used as a value"));
 EXTERN char e_using_class_as_var_val[]
-       INIT(= N_("E1405: Cannot use a Class as a variable or value"));
+       INIT(= N_("E1406: Cannot use a Class as a variable or value"));
 EXTERN char e_using_typealias_as_var_val[]
-       INIT(= N_("E1406: Cannot use a Typealias as a variable or value"));
+       INIT(= N_("E1407: Cannot use a Typealias as a variable or value"));
 #endif
-// E1405 - E1499 unused (reserved for Vim9 class support)
+// E1408 - E1499 unused (reserved for Vim9 class support)
 EXTERN char e_cannot_mix_positional_and_non_positional_str[]
        INIT(= N_("E1500: Cannot mix positional and non-positional arguments: 
%s"));
 EXTERN char e_fmt_arg_nr_unused_str[]
diff --git a/src/evalfunc.c b/src/evalfunc.c
index b0b975061..84d0a338a 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -758,17 +758,23 @@ arg_string_or_func(type_T *type, type_T *decl_type 
UNUSED, argcontext_T *context
 }
 
 /*
- * Check "type" is a list of 'any' or a class.
+ * Check varargs' "type" are class.
  */
     static int
-arg_class_or_list(type_T *type, type_T *decl_type UNUSED, argcontext_T 
*context)
+varargs_class(type_T *type UNUSED,
+             type_T *decl_type UNUSED,
+             argcontext_T *context)
 {
-    if (type->tt_type == VAR_CLASS
-           || type->tt_type == VAR_LIST
-           || type_any_or_unknown(type))
-       return OK;
-    arg_type_mismatch(&t_class, type, context->arg_idx + 1);
-    return FAIL;
+    for (int i = context->arg_idx; i < context->arg_count; ++i)
+    {
+       type2_T *types = &context->arg_types[i];
+       if (types->type_curr->tt_type != VAR_CLASS)
+       {
+           semsg(_(e_class_or_typealias_required_for_argument_nr), i + 1);
+           return FAIL;
+       }
+    }
+    return OK;
 }
 
 /*
@@ -1152,7 +1158,7 @@ static argcheck_T arg1_len[] = {arg_len1};
 static argcheck_T arg3_libcall[] = {arg_string, arg_string, arg_string_or_nr};
 static argcheck_T arg14_maparg[] = {arg_string, arg_string, arg_bool, 
arg_bool};
 static argcheck_T arg2_filter[] = {arg_list_or_dict_or_blob_or_string_mod, 
arg_filter_func};
-static argcheck_T arg2_instanceof[] = {arg_object, arg_class_or_list};
+static argcheck_T arg2_instanceof[] = {arg_object, varargs_class, NULL };
 static argcheck_T arg2_map[] = {arg_list_or_dict_or_blob_or_string_mod, 
arg_map_func};
 static argcheck_T arg2_mapnew[] = {arg_list_or_dict_or_blob_or_string, NULL};
 static argcheck_T arg25_matchadd[] = {arg_string, arg_string, arg_number, 
arg_number, arg_dict_any};
@@ -1621,6 +1627,12 @@ ret_maparg(int argcount,
 /*
  * Array with names and number of arguments of all internal functions
  * MUST BE KEPT SORTED IN strcmp() ORDER FOR BINARY SEARCH!
+ *
+ * The function may be varargs. In that case
+ *     - f_max_argc == VARGS
+ *     - f_argcheck must be NULL terminated. Last non-null argument
+ *       should check all the remaining args.
+ *       NOTE: if varargs, there can only be one NULL in f_argcheck array.
  */
 typedef struct
 {
@@ -1636,6 +1648,9 @@ typedef struct
                                // implementation of function
 } funcentry_T;
 
+// Set f_max_argc to VARGS for varargs.
+#define VARGS    CHAR_MAX
+
 // values for f_argtype; zero means it cannot be used as a method
 #define FEARG_1    1       // base is the first argument
 #define FEARG_2    2       // base is the second argument
@@ -2152,7 +2167,7 @@ static funcentry_T global_functions[] =
                        ret_string,         f_inputsecret},
     {"insert",         2, 3, FEARG_1,      arg23_insert,
                        ret_first_arg,      f_insert},
-    {"instanceof",     2, 2, FEARG_1,      arg2_instanceof,
+    {"instanceof",     2, VARGS, FEARG_1,  arg2_instanceof,
                        ret_bool,           f_instanceof},
     {"interrupt",      0, 0, 0,            NULL,
                        ret_void,           f_interrupt},
@@ -3035,13 +3050,20 @@ internal_func_check_arg_types(
     if (argchecks == NULL)
        return OK;
 
+    int has_varargs = global_functions[idx].f_max_argc == VARGS;
+
     argcontext_T context;
 
     context.arg_count = argcount;
     context.arg_types = types;
     context.arg_cctx = cctx;
     for (int i = 0; i < argcount; ++i)
-       if (argchecks[i] != NULL)
+       if (argchecks[i] == NULL)
+       {
+           if (has_varargs)
+               break;
+       }
+       else
        {
            context.arg_idx = i;
            if (argchecks[i](types[i].type_curr, types[i].type_decl,
diff --git a/src/proto/typval.pro b/src/proto/typval.pro
index 3f577adf7..c9845e0d1 100644
--- a/src/proto/typval.pro
+++ b/src/proto/typval.pro
@@ -53,7 +53,7 @@ int check_for_list_or_dict_or_blob_or_string_arg(typval_T 
*args, int idx);
 int check_for_opt_buffer_or_dict_arg(typval_T *args, int idx);
 int check_for_object_arg(typval_T *args, int idx);
 int tv_class_alias(typval_T *tv);
-int check_for_class_or_list_arg(typval_T *args, int idx);
+int check_for_class_or_typealias_args(typval_T *args, int idx);
 char_u *tv_get_string(typval_T *varp);
 char_u *tv_get_string_strict(typval_T *varp);
 char_u *tv_get_string_buf(typval_T *varp, char_u *buf);
diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim
index 8be0769b7..e288d0306 100644
--- a/src/testdir/test_vim9_assign.vim
+++ b/src/testdir/test_vim9_assign.vim
@@ -3064,7 +3064,7 @@ def Test_class_assign()
     endclass
     class D
     endclass
-    assert_fails('C = D', 'E1403: Class "D" cannot be used as a value')
+    assert_fails('C = D', 'E1405: Class "D" cannot be used as a value')
   END
   v9.CheckSourceSuccess(lines)
 enddef
@@ -3105,7 +3105,7 @@ def Test_type_check()
     assert_fails('N = l', 'E1012: Type mismatch; expected number but got 
list<number>')
     assert_fails('N = b', 'E1012: Type mismatch; expected number but got blob')
     assert_fails('N = Fn', 'E1012: Type mismatch; expected number but got 
func([unknown]): number')
-    assert_fails('N = A', 'E1403: Class "A" cannot be used as a value')
+    assert_fails('N = A', 'E1405: Class "A" cannot be used as a value')
     assert_fails('N = o', 'E1012: Type mismatch; expected number but got 
object<A>')
 
     # Use a compound operator with different RHS types
@@ -3113,7 +3113,7 @@ def Test_type_check()
     assert_fails('N += l', 'E734: Wrong variable type for +=')
     assert_fails('N += b', 'E974: Using a Blob as a Number')
     assert_fails('N += Fn', 'E734: Wrong variable type for +=')
-    assert_fails('N += A', 'E1403: Class "A" cannot be used as a value')
+    assert_fails('N += A', 'E1405: Class "A" cannot be used as a value')
     assert_fails('N += o', 'E1320: Using an Object as a Number')
 
     # Initialize multiple variables using []
@@ -3121,7 +3121,7 @@ def Test_type_check()
     assert_fails('var [X2: number, Y: number] = [1, l]', 'E1012: Type 
mismatch; expected number but got list<number>')
     assert_fails('var [X3: number, Y: number] = [1, b]', 'E1012: Type 
mismatch; expected number but got blob')
     assert_fails('var [X4: number, Y: number] = [1, Fn]', 'E1012: Type 
mismatch; expected number but got func([unknown]): number')
-    assert_fails('var [X7: number, Y: number] = [1, A]', 'E1403: Class "A" 
cannot be used as a value')
+    assert_fails('var [X7: number, Y: number] = [1, A]', 'E1405: Class "A" 
cannot be used as a value')
     assert_fails('var [X8: number, Y: number] = [1, o]', 'E1012: Type 
mismatch; expected number but got object<A>')
 
     # String concatenation with various LHS types
@@ -3129,7 +3129,7 @@ def Test_type_check()
     assert_fails('S ..= l', 'E734: Wrong variable type for .=')
     assert_fails('S ..= b', 'E976: Using a Blob as a String')
     assert_fails('S ..= Fn', 'E734: Wrong variable type for .=')
-    assert_fails('S ..= A', 'E1403: Class "A" cannot be used as a value')
+    assert_fails('S ..= A', 'E1405: Class "A" cannot be used as a value')
     assert_fails('S ..= o', 'E1324: Using an Object as a String')
 
     # String concatenation with various RHS types
diff --git a/src/testdir/test_vim9_builtin.vim 
b/src/testdir/test_vim9_builtin.vim
index f9d2e4181..216118ec6 100644
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -2316,7 +2316,7 @@ def Test_instanceof()
     endclass
     instanceof(Foo.new(), 123)
   END
-  v9.CheckScriptFailure(lines, 'E693: List or Class required for argument 2')
+  v9.CheckScriptFailure(lines, 'E693: Class or class typealias required for 
argument 2')
 
   lines =<< trim END
     vim9script
@@ -2338,7 +2338,7 @@ def Test_instanceof()
     enddef
     Bar()
   END
-  v9.CheckScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected 
class<Unknown> but got number')
+  v9.CheckScriptFailure(lines, 'E693: Class or class typealias required for 
argument 2')
 
   lines =<< trim END
     vim9script
@@ -2346,7 +2346,7 @@ def Test_instanceof()
     endclass
     instanceof(Foo.new(), [{}])
   END
-  v9.CheckSourceFailure(lines, 'E614: Class required')
+  v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for 
argument 2')
 
   lines =<< trim END
     vim9script
@@ -2357,7 +2357,7 @@ def Test_instanceof()
     enddef
     Bar()
   END
-  v9.CheckSourceFailure(lines, 'E614: Class required')
+  v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for 
argument 2')
 enddef
 
 def Test_invert()
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index 84ea1cb35..e0f5db625 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -3328,6 +3328,11 @@ def Test_instanceof()
     class Base3 extends Mix1
     endclass
 
+    type AliasBase1 = Base1
+    type AliasBase2 = Base2
+    type AliasIntf1 = Intf1
+    type AliasMix1 = Mix1
+
     var b1 = Base1.new()
     var b2 = Base2.new()
     var b3 = Base3.new()
@@ -3336,8 +3341,13 @@ def Test_instanceof()
     assert_true(instanceof(b2, Base1))
     assert_false(instanceof(b1, Base2))
     assert_true(instanceof(b3, Mix1))
-    assert_false(instanceof(b3, []))
-    assert_true(instanceof(b3, [Base1, Base2, Intf1]))
+    assert_true(instanceof(b3, Base1, Base2, Intf1))
+
+    assert_true(instanceof(b1, AliasBase1))
+    assert_true(instanceof(b2, AliasBase1))
+    assert_false(instanceof(b1, AliasBase2))
+    assert_true(instanceof(b3, AliasMix1))
+    assert_true(instanceof(b3, AliasBase1, AliasBase2, AliasIntf1))
 
     def Foo()
       var a1 = Base1.new()
@@ -3348,8 +3358,13 @@ def Test_instanceof()
       assert_true(instanceof(a2, Base1))
       assert_false(instanceof(a1, Base2))
       assert_true(instanceof(a3, Mix1))
-      assert_false(instanceof(a3, []))
-      assert_true(instanceof(a3, [Base1, Base2, Intf1]))
+      assert_true(instanceof(a3, Base1, Base2, Intf1))
+
+      assert_true(instanceof(a1, AliasBase1))
+      assert_true(instanceof(a2, AliasBase1))
+      assert_false(instanceof(a1, AliasBase2))
+      assert_true(instanceof(a3, AliasMix1))
+      assert_true(instanceof(a3, AliasBase1, AliasBase2, AliasIntf1))
     enddef
     Foo()
 
@@ -3358,6 +3373,58 @@ def Test_instanceof()
 
   END
   v9.CheckSourceSuccess(lines)
+
+  lines =<< trim END
+    vim9script
+
+    class Base1
+    endclass
+    instanceof(Base1.new())
+  END
+  v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: 
instanceof')
+
+  lines =<< trim END
+    vim9script
+
+    class Base1
+    endclass
+    def F()
+      instanceof(Base1.new())
+    enddef
+    F()
+  END
+  v9.CheckSourceFailure(lines, 'E119: Not enough arguments for function: 
instanceof')
+
+  lines =<< trim END
+    vim9script
+
+    class Base1
+    endclass
+
+    class Base2
+    endclass
+
+    var o = Base2.new()
+    instanceof(o, Base1, Base2, 3)
+  END
+  v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for 
argument 4', 10)
+
+  lines =<< trim END
+    vim9script
+
+    class Base1
+    endclass
+
+    class Base2
+    endclass
+
+    def F()
+      var o = Base2.new()
+      instanceof(o, Base1, Base2, 3)
+    enddef
+    F()
+  END
+  v9.CheckSourceFailure(lines, 'E693: Class or class typealias required for 
argument 4')
 enddef
 
 " Test for calling a method in the parent class that is extended partially.
diff --git a/src/testdir/test_vim9_typealias.vim 
b/src/testdir/test_vim9_typealias.vim
index 34c9e0ea7..20829340f 100644
--- a/src/testdir/test_vim9_typealias.vim
+++ b/src/testdir/test_vim9_typealias.vim
@@ -212,7 +212,7 @@ def Test_typealias()
     enddef
     Foo()
   END
-  v9.CheckSourceFailure(lines, 'E1406: Cannot use a Typealias as a variable or 
value', 1)
+  v9.CheckSourceFailure(lines, 'E1407: Cannot use a Typealias as a variable or 
value', 1)
 
   # Using type alias in an expression (script level)
   lines =<< trim END
@@ -516,8 +516,18 @@ def Test_typealias_instanceof()
     var o = C.new()
     assert_equal(1, instanceof(o, Ctype))
     type Ntype = number
-    assert_fails('instanceof(o, Ntype)', 'E693: List or Class required for 
argument 2')
-    assert_equal(1, instanceof(o, [Ctype]))
+    assert_fails('instanceof(o, Ntype)', 'E693: Class or class typealias 
required for argument 2')
+    assert_fails('instanceof(o, Ctype, Ntype)', 'E693: Class or class 
typealias required for argument 3')
+
+    def F()
+      var x = instanceof(o, Ntype)
+    enddef
+    assert_fails('F()', 'E693: Class or class typealias required for argument 
2')
+
+    def G(): bool
+      return instanceof(o, Ctype)
+    enddef
+    assert_equal(1, G())
   END
   v9.CheckScriptSuccess(lines)
 enddef
diff --git a/src/typval.c b/src/typval.c
index 078e2eb05..65cd912c3 100644
--- a/src/typval.c
+++ b/src/typval.c
@@ -1014,16 +1014,19 @@ tv_class_alias(typval_T *tv)
 }
 
 /*
- * Give an error and return FAIL unless "args[idx]" is a class or a list.
+ * Give an error and return FAIL unless "args[idx]" is a class
+ * or class typealias.
  */
     int
-check_for_class_or_list_arg(typval_T *args, int idx)
+check_for_class_or_typealias_args(typval_T *args, int idx)
 {
-    if (args[idx].v_type != VAR_CLASS && args[idx].v_type != VAR_LIST
-                                       && !tv_class_alias(&args[idx]))
+    for (int i = idx; args[i].v_type != VAR_UNKNOWN; ++i)
     {
-       semsg(_(e_list_or_class_required_for_argument_nr), idx + 1);
-       return FAIL;
+       if (args[i].v_type != VAR_CLASS && !tv_class_alias(&args[idx]))
+       {
+           semsg(_(e_class_or_typealias_required_for_argument_nr), i + 1);
+           return FAIL;
+       }
     }
     return OK;
 }
diff --git a/src/version.c b/src/version.c
index 15ff51dd7..ad5bda644 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2160,
 /**/
     2159,
 /**/
diff --git a/src/vim9class.c b/src/vim9class.c
index c0705ff3c..4b065c859 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -3161,58 +3161,37 @@ class_instance_of(class_T *cl, class_T *other_cl)
 }
 
 /*
- * "instanceof(object, classinfo)" function
+ * "instanceof(object, classinfo, ...)" function
  */
     void
 f_instanceof(typval_T *argvars, typval_T *rettv)
 {
     typval_T   *object_tv = &argvars[0];
     typval_T   *classinfo_tv = &argvars[1];
-    listitem_T *li;
     class_T    *c;
 
     rettv->vval.v_number = VVAL_FALSE;
 
     if (check_for_object_arg(argvars, 0) == FAIL
-           || check_for_class_or_list_arg(argvars, 1) == FAIL)
+           || check_for_class_or_typealias_args(argvars, 1) == FAIL)
        return;
 
     if (object_tv->vval.v_object == NULL)
        return;
 
-    if (classinfo_tv->v_type == VAR_LIST)
+    for (; classinfo_tv->v_type != VAR_UNKNOWN; ++classinfo_tv)
     {
-       FOR_ALL_LIST_ITEMS(classinfo_tv->vval.v_list, li)
-       {
-           if (li->li_tv.v_type != VAR_CLASS && !tv_class_alias(&li->li_tv))
-           {
-               emsg(_(e_class_required));
-               return;
-           }
-
-           if (li->li_tv.v_type == VAR_TYPEALIAS)
-               c = li->li_tv.vval.v_typealias->ta_type->tt_class;
-           else
-               c = li->li_tv.vval.v_class;
+       if (classinfo_tv->v_type == VAR_TYPEALIAS)
+           c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
+       else
+           c = classinfo_tv->vval.v_class;
 
-           if (class_instance_of(object_tv->vval.v_object->obj_class, c)
-                                                               == TRUE)
-           {
-               rettv->vval.v_number = VVAL_TRUE;
-               return;
-           }
+       if (class_instance_of(object_tv->vval.v_object->obj_class, c))
+       {
+           rettv->vval.v_number = VVAL_TRUE;
+           return;
        }
-
-       return;
     }
-
-    if (classinfo_tv->v_type == VAR_TYPEALIAS)
-       c = classinfo_tv->vval.v_typealias->ta_type->tt_class;
-    else
-       c = classinfo_tv->vval.v_class;
-
-    rettv->vval.v_number =
-               class_instance_of(object_tv->vval.v_object->obj_class, c);
 }
 
 #endif // FEAT_EVAL

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1rD5PT-00GB53-P3%40256bit.org.

Raspunde prin e-mail lui