]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Filter: Implement multiple dispatch for methods
authorOndrej Zajicek <santiago@crfreenet.org>
Mon, 3 Jul 2023 15:00:58 +0000 (17:00 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Tue, 12 Sep 2023 14:31:52 +0000 (16:31 +0200)
 - Extend method descriptors with type signature
 - Daisy chain method descriptors for the same symbol
 - Dispatch methods for same symbol based on type signature
 - Split add/delete/filter operations to multiple methods
 - Replace ad-hoc dispatch of old-style syntax with scope-based dispatch
 - Also change method->arg_num to count initial arg

It still needs some improvements, like better handling of untyped
expressions and better error reporting when no dispatch can be done.

The multiple dispatch could also be extended to dispatch regular
function-like expressions in a uniform way.

conf/cf-lex.l
filter/config.Y
filter/data.h
filter/decl.m4
filter/f-inst.c
filter/f-inst.h
filter/f-util.c

index c4760e4037993741f9cac1f11de2a2afa1380951..dcd54b8134e55c8b1f8a655842deab36c67c73a2 100644 (file)
@@ -687,7 +687,7 @@ cf_lex_symbol(const char *data)
       return ENUM;
     }
     case SYM_METHOD:
-      return sym->method->arg_num ? CF_SYM_METHOD_ARGS : CF_SYM_METHOD_BARE;
+      return (sym->method->arg_num > 1) ? CF_SYM_METHOD_ARGS : CF_SYM_METHOD_BARE;
     case SYM_VOID:
       return CF_SYM_UNDEFINED;
     default:
index ffa8f115ccacfec47bfa1f338c21531ba2ef8aae..7cc6f88235a8d64c4a49ccd52e6fc61521fa727c 100644 (file)
@@ -869,12 +869,12 @@ static_attr:
 term_dot_method: term '.' { f_method_call_start($1); } method_name_cont { f_method_call_end(); $$ = $4; };
 method_name_cont:
    CF_SYM_METHOD_BARE {
-     $$ = $1->method->new_inst(FM.object, NULL);
+     $$ = f_dispatch_method($1, FM.object, NULL);
    }
  | CF_SYM_METHOD_ARGS {
      f_method_call_args();
    } '(' var_list ')' {
-     $$ = $1->method->new_inst(FM.object, $4);
+     $$ = f_dispatch_method($1, FM.object, $4);
    }
  ;
 
@@ -913,32 +913,15 @@ term:
  | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_const_empty(T_LCLIST); }
  | PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5); }
  | ADD '(' term ',' term ')' {
-     switch ($3->type) {
-       case T_CLIST: $$ = f_new_inst(FI_CLIST_ADD, $3, $5); break;
-       case T_ECLIST: $$ = f_new_inst(FI_ECLIST_ADD, $3, $5); break;
-       case T_LCLIST: $$ = f_new_inst(FI_LCLIST_ADD, $3, $5); break;
-       default: cf_error("Can't add to type %s", f_type_name($3->type));
-     }
+     $$ = f_dispatch_method_x("add", $3->type, $3, $5);
      cf_warn("add(x,y) function is deprecated, please use x.add(y)");
    }
  | DELETE '(' term ',' term ')' {
-     switch ($3->type) {
-       case T_PATH: $$ = f_new_inst(FI_PATH_DEL, $3, $5); break;
-       case T_CLIST: $$ = f_new_inst(FI_CLIST_DEL, $3, $5); break;
-       case T_ECLIST: $$ = f_new_inst(FI_ECLIST_DEL, $3, $5); break;
-       case T_LCLIST: $$ = f_new_inst(FI_LCLIST_DEL, $3, $5); break;
-       default: cf_error("Can't delete from type %s", f_type_name($3->type));
-     }
+     $$ = f_dispatch_method_x("delete", $3->type, $3, $5);
      cf_warn("delete(x,y) function is deprecated, please use x.delete(y)");
    }
  | FILTER '(' term ',' term ')' {
-     switch ($3->type) {
-       case T_PATH: $$ = f_new_inst(FI_PATH_FILTER, $3, $5); break;
-       case T_CLIST: $$ = f_new_inst(FI_CLIST_FILTER, $3, $5); break;
-       case T_ECLIST: $$ = f_new_inst(FI_ECLIST_FILTER, $3, $5); break;
-       case T_LCLIST: $$ = f_new_inst(FI_LCLIST_FILTER, $3, $5); break;
-       default: cf_error("Can't filter type %s", f_type_name($3->type));
-     }
+     $$ = f_dispatch_method_x("filter", $3->type, $3, $5);
      cf_warn("filter(x,y) function is deprecated, please use x.filter(y)");
    }
 
index baa7114c448f299009287c241c7b0e99c1a433fe..e136b65f3ebfd61225a48b7cf027265ff244c5ef 100644 (file)
@@ -67,7 +67,9 @@ enum f_type {
 struct f_method {
   struct symbol *sym;
   struct f_inst *(*new_inst)(struct f_inst *obj, struct f_inst *args);
+  const struct f_method *next;
   uint arg_num;
+  enum f_type args_type[];
 };
 
 /* Filter value; size of this affects filter memory consumption */
index cc5e9d8e08a43c179d3986c70a9e2df9368f1f46..217488eca3b2d1b2ddc97aabada3ec6c5170b13d 100644 (file)
@@ -53,6 +53,7 @@ m4_define(FID_NEW_ATTRIBUTES, `m4_divert(110)')
 m4_define(FID_NEW_BODY, `m4_divert(103)')
 m4_define(FID_NEW_METHOD, `m4_divert(111)')
 m4_define(FID_METHOD_CALL, `m4_divert(112)')
+m4_define(FID_TYPE_SIGNATURE, `m4_divert(113)')
 m4_define(FID_DUMP_BODY, `m4_divert(104)m4_define([[FID_DUMP_BODY_EXISTS]])')
 m4_define(FID_LINEARIZE_BODY, `m4_divert(105)')
 m4_define(FID_SAME_BODY, `m4_divert(106)')
@@ -125,7 +126,7 @@ FID_IFCONST([[
     constargs = 0;
 ]])
 } while (child$1 = child$1->next);
-m4_define([[INST_METHOD_NUM_ARGS]],m4_eval($1-1))m4_dnl
+m4_define([[INST_METHOD_NUM_ARGS]],$1)m4_dnl
 m4_ifelse($1,1,,[[FID_NEW_METHOD()m4_dnl
   struct f_inst *arg$1 = args;
   if (args == NULL) cf_error("Not enough arguments"); /* INST_NAME */
@@ -183,6 +184,8 @@ m4_define(ARG_TYPE, `ARG_TYPE_STATIC($1,$2) ARG_TYPE_DYNAMIC($1,$2)')
 
 m4_define(ARG_TYPE_STATIC, `m4_dnl
 m4_ifelse($1,1,[[m4_define([[INST_METHOD_OBJECT_TYPE]],$2)]],)m4_dnl
+FID_TYPE_SIGNATURE()m4_dnl
+  method->args_type[m4_eval($1-1)] = $2;
 FID_NEW_BODY()m4_dnl
 if (f$1->type && (f$1->type != ($2)) && !f_const_promotion(f$1, ($2)))
   cf_error("Argument $1 of %s must be of type %s, got type %s",
@@ -230,7 +233,7 @@ FID_NEW_ARGS()m4_dnl
   , struct f_inst * f$1
 FID_NEW_BODY()m4_dnl
 whati->f$1 = f$1;
-m4_define([[INST_METHOD_NUM_ARGS]],m4_eval($1-1))m4_dnl
+m4_define([[INST_METHOD_NUM_ARGS]],$1)m4_dnl
 FID_NEW_METHOD()m4_dnl
   struct f_inst *arg$1 = args;
   if (args == NULL) cf_error("Not enough arguments"); /* INST_NAME */
@@ -421,17 +424,11 @@ m4_undivert(112)
 FID_METHOD_SCOPE_INIT()m4_dnl
   [INST_METHOD_OBJECT_TYPE] = {},
 FID_METHOD_REGISTER()m4_dnl
-  sym = cf_new_symbol(&f_type_method_scopes[INST_METHOD_OBJECT_TYPE],
-                     global_root_scope_pool, global_root_scope_linpool,
-                     INST_METHOD_NAME);
-  sym->class = SYM_METHOD;
-  sym->method = method = lp_allocz(global_root_scope_linpool, sizeof(struct f_method));
-
-  *method = (struct f_method) {
-    .sym = sym,
-    .new_inst = f_new_method_]]INST_NAME()[[,
-    .arg_num = INST_METHOD_NUM_ARGS,
-  };
+  method = lp_allocz(global_root_scope_linpool, sizeof(struct f_method) + INST_METHOD_NUM_ARGS * sizeof(enum f_type));
+  method->new_inst = f_new_method_]]INST_NAME()[[;
+  method->arg_num = INST_METHOD_NUM_ARGS;
+m4_undivert(113)
+  f_register_method(INST_METHOD_OBJECT_TYPE, INST_METHOD_NAME, method);
 
 ]])m4_dnl
 
@@ -634,10 +631,27 @@ struct sym_scope *f_type_method_scope(enum f_type t)
   return (t < ARRAY_SIZE(f_type_method_scopes)) ? &f_type_method_scopes[t] : NULL;
 }
 
+static void
+f_register_method(enum f_type t, const byte *name, struct f_method *dsc)
+{
+  struct sym_scope *scope = &f_type_method_scopes[t];
+  struct symbol *sym = cf_find_symbol_scope(scope, name);
+
+  if (!sym)
+  {
+    sym = cf_new_symbol(scope, global_root_scope_pool, global_root_scope_linpool, name);
+    sym->class = SYM_METHOD;
+  }
+
+  dsc->sym = sym;
+  dsc->next = sym->method;
+  sym->method = dsc;
+}
+
 void f_type_methods_register(void)
 {
-  struct symbol *sym;
   struct f_method *method;
+
 FID_WR_PUT(13)
 
   for (uint i = 0; i < ARRAY_SIZE(f_type_method_scopes); i++)
index 8c4d15d53fdb9f6ef81b9923f12101517bd91848..a9de0960f2916b3c8e6218182607488d28f09dd5 100644 (file)
     RESULT(T_PATH, ad, [[ as_path_prepend(fpool, v1.val.ad, v2.val.i) ]]);
   }
 
-  INST(FI_CLIST_ADD, 2, 1) {   /* (Extended) Community list add */
+  /* Community list add */
+  INST(FI_CLIST_ADD, 2, 1) {
     ARG(1, T_CLIST);
     ARG_ANY(2);
     METHOD_CONSTRUCTOR("add");
        runtime("Can't add non-pair");
   }
 
-  INST(FI_ECLIST_ADD, 2, 1) {
+  INST(FI_ECLIST_ADD_EC, 2, 1) {
     ARG(1, T_ECLIST);
-    ARG_ANY(2);
+    ARG(2, T_EC);
     METHOD_CONSTRUCTOR("add");
-      /* v2.val is either EC or EC-set */
-      if ((v2.type == T_SET) && eclist_set_type(v2.val.t))
-       runtime("Can't add set");
-      else if (v2.type == T_ECLIST)
-       RESULT(T_ECLIST, ad, [[ ec_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
-      else if (v2.type != T_EC)
-       runtime("Can't add non-ec");
-      else
-       RESULT(T_ECLIST, ad, [[ ec_set_add(fpool, v1.val.ad, v2.val.ec) ]]);
+    RESULT(T_ECLIST, ad, [[ ec_set_add(fpool, v1.val.ad, v2.val.ec) ]]);
+  }
+
+  INST(FI_ECLIST_ADD_ECLIST, 2, 1) {
+    ARG(1, T_ECLIST);
+    ARG(2, T_ECLIST);
+    METHOD_CONSTRUCTOR("add");
+    RESULT(T_ECLIST, ad, [[ ec_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
   }
 
-  INST(FI_LCLIST_ADD, 2, 1) {
+  INST(FI_LCLIST_ADD_LC, 2, 1) {
     ARG(1, T_LCLIST);
-    ARG_ANY(2);
+    ARG(2, T_LC);
     METHOD_CONSTRUCTOR("add");
-      /* v2.val is either LC or LC-set */
-      if ((v2.type == T_SET) && lclist_set_type(v2.val.t))
-       runtime("Can't add set");
-      else if (v2.type == T_LCLIST)
-       RESULT(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
-      else if (v2.type != T_LC)
-       runtime("Can't add non-lc");
-      else
-       RESULT(T_LCLIST, ad, [[ lc_set_add(fpool, v1.val.ad, v2.val.lc) ]]);
+    RESULT(T_LCLIST, ad, [[ lc_set_add(fpool, v1.val.ad, v2.val.lc) ]]);
   }
 
-  INST(FI_PATH_DEL, 2, 1) {    /* Path delete */
+  INST(FI_LCLIST_ADD_LCLIST, 2, 1) {
+    ARG(1, T_LCLIST);
+    ARG(2, T_LCLIST);
+    METHOD_CONSTRUCTOR("add");
+    RESULT(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
+  }
+
+  INST(FI_PATH_DELETE_INT, 2, 1) {
     ARG(1, T_PATH);
-    ARG_ANY(2);
+    ARG(2, T_INT);
     METHOD_CONSTRUCTOR("delete");
-      if ((v2.type == T_SET) && path_set_type(v2.val.t) || (v2.type == T_INT))
-       RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]);
-      else
-       runtime("Can't delete non-integer (set)");
+    RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]);
   }
 
-  INST(FI_CLIST_DEL, 2, 1) {   /* (Extended) Community list add or delete */
+  INST(FI_PATH_DELETE_SET, 2, 1) {
+    ARG(1, T_PATH);
+    ARG(2, T_SET);
+    METHOD_CONSTRUCTOR("delete");
+
+    if (!path_set_type(v2.val.t))
+      runtime("Mismatched set type");
+
+    RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]);
+  }
+
+  /* Community list delete */
+  INST(FI_CLIST_DELETE, 2, 1) {
     ARG(1, T_CLIST);
     ARG_ANY(2);
     METHOD_CONSTRUCTOR("delete");
        runtime("Can't delete non-pair");
   }
 
-  INST(FI_ECLIST_DEL, 2, 1) {  /* (Extended) Community list add or delete */
+  INST(FI_ECLIST_DELETE_EC, 2, 1) {
     ARG(1, T_ECLIST);
-    ARG_ANY(2);
+    ARG(2, T_EC);
     METHOD_CONSTRUCTOR("delete");
-      /* v2.val is either EC or EC-set */
-      if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST))
-       RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
-      else if (v2.type != T_EC)
-       runtime("Can't delete non-ec");
-      else
-       RESULT(T_ECLIST, ad, [[ ec_set_del(fpool, v1.val.ad, v2.val.ec) ]]);
+    RESULT(T_ECLIST, ad, [[ ec_set_del(fpool, v1.val.ad, v2.val.ec) ]]);
   }
 
-  INST(FI_LCLIST_DEL, 2, 1) {  /* (Extended) Community list add or delete */
+  INST(FI_ECLIST_DELETE_ECLIST, 2, 1) {
+    ARG(1, T_ECLIST);
+    ARG(2, T_ECLIST);
+    METHOD_CONSTRUCTOR("delete");
+    RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
+  }
+
+  INST(FI_ECLIST_DELETE_SET, 2, 1) {
+    ARG(1, T_ECLIST);
+    ARG(2, T_SET);
+    METHOD_CONSTRUCTOR("delete");
+
+    if (!eclist_set_type(v2.val.t))
+      runtime("Mismatched set type");
+
+    RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
+  }
+
+  INST(FI_LCLIST_DELETE_LC, 2, 1) {
     ARG(1, T_LCLIST);
-    ARG_ANY(2);
+    ARG(2, T_LC);
     METHOD_CONSTRUCTOR("delete");
-      /* v2.val is either LC or LC-set */
-      if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST))
-       RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
-      else if (v2.type != T_LC)
-       runtime("Can't delete non-lc");
-      else
-       RESULT(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]);
+    RESULT(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]);
   }
 
-  INST(FI_PATH_FILTER, 2, 1) { /* (Extended) Community list add or delete */
+  INST(FI_LCLIST_DELETE_LCLIST, 2, 1) {
+    ARG(1, T_LCLIST);
+    ARG(2, T_LCLIST);
+    METHOD_CONSTRUCTOR("delete");
+    RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
+  }
+
+  INST(FI_LCLIST_DELETE_SET, 2, 1) {
+    ARG(1, T_LCLIST);
+    ARG(2, T_SET);
+    METHOD_CONSTRUCTOR("delete");
+
+    if (!lclist_set_type(v2.val.t))
+      runtime("Mismatched set type");
+
+    RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
+  }
+
+  INST(FI_PATH_FILTER_SET, 2, 1) {
     ARG(1, T_PATH);
-    ARG_ANY(2);
+    ARG(2, T_SET);
     METHOD_CONSTRUCTOR("filter");
 
-      if ((v2.type == T_SET) && path_set_type(v2.val.t))
-       RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 1) ]]);
-      else
-       runtime("Can't filter integer");
-    }
+    if (!path_set_type(v2.val.t))
+      runtime("Mismatched set type");
+
+    RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 1) ]]);
+  }
 
-  INST(FI_CLIST_FILTER, 2, 1) {
+  INST(FI_CLIST_FILTER_CLIST, 2, 1) {
     ARG(1, T_CLIST);
-    ARG_ANY(2);
+    ARG(2, T_CLIST);
     METHOD_CONSTRUCTOR("filter");
-      /* Community (or cluster) list */
-      struct f_val dummy;
+    RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
+  }
 
-      if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST))
-       RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
-      else
-       runtime("Can't filter pair");
+  INST(FI_CLIST_FILTER_SET, 2, 1) {
+    ARG(1, T_CLIST);
+    ARG(2, T_SET);
+    METHOD_CONSTRUCTOR("filter");
+
+    if (!clist_set_type(v2.val.t, &(struct f_val){}))
+      runtime("Mismatched set type");
+
+    RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
   }
 
-  INST(FI_ECLIST_FILTER, 2, 1) {
+  INST(FI_ECLIST_FILTER_ECLIST, 2, 1) {
     ARG(1, T_ECLIST);
-    ARG_ANY(2);
+    ARG(2, T_ECLIST);
     METHOD_CONSTRUCTOR("filter");
-      /* v2.val is either EC or EC-set */
-      if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST))
-       RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
-      else
-       runtime("Can't filter ec");
+    RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
   }
 
-  INST(FI_LCLIST_FILTER, 2, 1) {
+  INST(FI_ECLIST_FILTER_SET, 2, 1) {
+    ARG(1, T_ECLIST);
+    ARG(2, T_SET);
+    METHOD_CONSTRUCTOR("filter");
+
+    if (!eclist_set_type(v2.val.t))
+      runtime("Mismatched set type");
+
+    RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
+  }
+
+  INST(FI_LCLIST_FILTER_LCLIST, 2, 1) {
     ARG(1, T_LCLIST);
-    ARG_ANY(2);
+    ARG(2, T_LCLIST);
     METHOD_CONSTRUCTOR("filter");
-      /* v2.val is either LC or LC-set */
-      if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST))
-       RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
-      else
-       runtime("Can't filter lc");
+    RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
+  }
+
+  INST(FI_LCLIST_FILTER_SET, 2, 1) {
+    ARG(1, T_LCLIST);
+    ARG(2, T_SET);
+    METHOD_CONSTRUCTOR("filter");
+
+    if (!lclist_set_type(v2.val.t))
+      runtime("Mismatched set type");
+
+    RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
   }
 
   INST(FI_ROA_CHECK_IMPLICIT, 0, 1) {  /* ROA Check */
index 8c304de5d200cd45b89184407b2e8fdedbf0877d..f5dfcd2021c983dded74c48d60e4a747ea1f2fa9 100644 (file)
@@ -96,6 +96,8 @@ void f_add_lines(const struct f_line_item *what, struct filter_iterator *fit);
 
 
 struct filter *f_new_where(struct f_inst *);
+struct f_inst *f_dispatch_method(struct symbol *sym, struct f_inst *obj, struct f_inst *args);
+struct f_inst *f_dispatch_method_x(const char *name, enum f_type t, struct f_inst *obj, struct f_inst *args);
 struct f_inst *f_for_cycle(struct symbol *var, struct f_inst *term, struct f_inst *block);
 struct f_inst *f_print(struct f_inst *vars, int flush, enum filter_return fret);
 
index 98b7d1a873134ac23757c80ef9f009da394394ef..7ce3f9c8c9422220827a72f5f8e85137958a6e09 100644 (file)
@@ -30,7 +30,8 @@ filter_name(const struct filter *filter)
     return filter->sym->name;
 }
 
-struct filter *f_new_where(struct f_inst *where)
+struct filter *
+f_new_where(struct f_inst *where)
 {
   struct f_inst *cond = f_new_inst(FI_CONDITION, where,
                                   f_new_inst(FI_DIE, F_ACCEPT),
@@ -41,6 +42,43 @@ struct filter *f_new_where(struct f_inst *where)
   return f;
 }
 
+static inline int
+f_match_signature(const struct f_method *dsc, struct f_inst *args)
+{
+  uint i;
+
+  for (i = 1; args && (i < dsc->arg_num); args = args->next, i++)
+    if (dsc->args_type[i] && (args->type != dsc->args_type[i]))
+      return 0;
+
+  return !args && !(i < dsc->arg_num);
+}
+
+struct f_inst *
+f_dispatch_method(struct symbol *sym, struct f_inst *obj, struct f_inst *args)
+{
+  /* Note! We should revert args */
+
+  for (const struct f_method *dsc = sym->method; dsc; dsc = dsc->next)
+    if (f_match_signature(dsc, args))
+      return dsc->new_inst(obj, args);
+
+  cf_error("Cannot dispatch method '%s'", sym->name);
+}
+
+struct f_inst *
+f_dispatch_method_x(const char *name, enum f_type t, struct f_inst *obj, struct f_inst *args)
+{
+  struct sym_scope *scope = f_type_method_scope(t);
+  struct symbol *sym = cf_find_symbol_scope(scope, name);
+
+  if (!sym)
+    cf_error("Cannot dispatch method '%s'", name);
+
+  return f_dispatch_method(sym, obj, args);
+}
+
+
 struct f_inst *
 f_for_cycle(struct symbol *var, struct f_inst *term, struct f_inst *block)
 {