"Fossies" - the Fresh Open Source Software Archive

Member "ponyc-0.33.2/src/libponyc/pass/verify.c" (3 Feb 2020, 14190 Bytes) of package /linux/misc/ponyc-0.33.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "verify.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.33.1_vs_0.33.2.

    1 #include "verify.h"
    2 #include "../type/assemble.h"
    3 #include "../type/cap.h"
    4 #include "../type/lookup.h"
    5 #include "../verify/call.h"
    6 #include "../verify/control.h"
    7 #include "../verify/fun.h"
    8 #include "../verify/type.h"
    9 #include "expr.h"
   10 #include "../ast/ast.h"
   11 #include "ponyassert.h"
   12 #include "../../libponyrt/mem/pool.h"
   13 #include <string.h>
   14 
   15 
   16 static bool verify_assign_lvalue(pass_opt_t* opt, ast_t* ast)
   17 {
   18   switch(ast_id(ast))
   19   {
   20     case TK_FLETREF:
   21     {
   22       AST_GET_CHILDREN(ast, left, right);
   23 
   24       if(ast_id(left) != TK_THIS)
   25       {
   26         ast_error(opt->check.errors, ast, "can't reassign to a let field");
   27         return false;
   28       }
   29 
   30       if(opt->check.frame->loop_body != NULL)
   31       {
   32         ast_error(opt->check.errors, ast,
   33           "can't assign to a let field in a loop");
   34         return false;
   35       }
   36 
   37       break;
   38     }
   39 
   40     case TK_EMBEDREF:
   41     {
   42       AST_GET_CHILDREN(ast, left, right);
   43 
   44       if(ast_id(left) != TK_THIS)
   45       {
   46         ast_error(opt->check.errors, ast, "can't assign to an embed field");
   47         return false;
   48       }
   49 
   50       if(opt->check.frame->loop_body != NULL)
   51       {
   52         ast_error(opt->check.errors, ast,
   53           "can't assign to an embed field in a loop");
   54         return false;
   55       }
   56 
   57       break;
   58     }
   59 
   60     case TK_TUPLEELEMREF:
   61     {
   62       ast_error(opt->check.errors, ast,
   63         "can't assign to an element of a tuple");
   64       return false;
   65     }
   66 
   67     case TK_TUPLE:
   68     {
   69       // Verify every lvalue in the tuple.
   70       ast_t* child = ast_child(ast);
   71 
   72       while(child != NULL)
   73       {
   74         if(!verify_assign_lvalue(opt, child))
   75           return false;
   76 
   77         child = ast_sibling(child);
   78       }
   79 
   80       break;
   81     }
   82 
   83     case TK_SEQ:
   84     {
   85       // This is used because the elements of a tuple are sequences,
   86       // each sequence containing a single child.
   87       ast_t* child = ast_child(ast);
   88       pony_assert(ast_sibling(child) == NULL);
   89 
   90       return verify_assign_lvalue(opt, child);
   91     }
   92 
   93     default: {}
   94   }
   95 
   96   return true;
   97 }
   98 
   99 static size_t funref_hash(ast_t* key)
  100 {
  101   return ponyint_hash_ptr(key);
  102 }
  103 
  104 static bool funref_cmp(ast_t* a, ast_t* b)
  105 {
  106   return a == b;
  107 }
  108 
  109 DECLARE_HASHMAP(consume_funs, consume_funs_t, ast_t);
  110 
  111 DEFINE_HASHMAP(consume_funs, consume_funs_t, ast_t, funref_hash,
  112   funref_cmp, NULL);
  113 
  114 // This function generates the fully qualified string (without the `this`) for a
  115 // reference (i.e. `a.b.c.d.e`) and ensures it is part of the compiler
  116 // `stringtab`. This is basically the same logic as the `generate_multi_dot_name`
  117 // from `refer.c`. It would likely be possible to combine the two into a single
  118 // function. It is used to generate this fully qualified string for the field
  119 // being consumed for tracking its `consume`d status via the ast `symtab`. It is
  120 // used to ensure that no parent (e.g. `a.b.c`) of a consumed field
  121 // (e.g. `a.b.c.d.e`) is consumed.
  122 static const char* get_multi_ref_name(ast_t* ast)
  123 {
  124   ast_t* def = NULL;
  125   size_t len = 0;
  126   ast_t* temp_ast = ast;
  127 
  128   def = (ast_t*)ast_data(temp_ast);
  129   while(def == NULL)
  130   {
  131     AST_GET_CHILDREN(temp_ast, left, right);
  132     def = (ast_t*)ast_data(left);
  133     temp_ast = left;
  134 
  135     // the `+ 1` is for the '.' needed in the string
  136     len += strlen(ast_name(right)) + 1;
  137   }
  138 
  139   switch(ast_id(temp_ast))
  140   {
  141     case TK_DOT:
  142     {
  143       AST_GET_CHILDREN(temp_ast, left, right);
  144       if(ast_id(left) == TK_THIS)
  145       {
  146         temp_ast = right;
  147         len += strlen(ast_name(temp_ast));
  148       }
  149       else
  150         pony_assert(0);
  151       break;
  152     }
  153 
  154     case TK_FLETREF:
  155     case TK_FVARREF:
  156     case TK_EMBEDREF:
  157     case TK_REFERENCE:
  158     {
  159       temp_ast = ast_sibling(ast_child(temp_ast));
  160       len += strlen(ast_name(temp_ast));
  161       break;
  162     }
  163 
  164     case TK_PARAMREF:
  165     case TK_LETREF:
  166     case TK_VARREF:
  167     {
  168       temp_ast = ast_child(temp_ast);
  169       len += strlen(ast_name(temp_ast));
  170       break;
  171     }
  172 
  173     case TK_THIS:
  174     {
  175       temp_ast = ast_sibling(temp_ast);
  176       // string len already added by loop above
  177       // subtract 1 because we don't have to add '.'
  178       // since we're ignoring the `this`
  179       len -= 1;
  180       break;
  181     }
  182 
  183     default:
  184     {
  185       pony_assert(0);
  186     }
  187   }
  188 
  189   // for the \0 at the end
  190   len = len + 1;
  191 
  192   char* buf = (char*)ponyint_pool_alloc_size(len);
  193   size_t offset = 0;
  194   const char* name = ast_name(temp_ast);
  195   size_t slen = strlen(name);
  196   memcpy(buf + offset, name, slen);
  197   offset += slen;
  198   temp_ast = ast_parent(temp_ast);
  199 
  200   while(temp_ast != ast)
  201   {
  202     buf[offset] = '.';
  203     offset += 1;
  204     temp_ast = ast_sibling(temp_ast);
  205     name = ast_name(temp_ast);
  206     slen = strlen(name);
  207     memcpy(buf + offset, name, slen);
  208     offset += slen;
  209     temp_ast = ast_parent(temp_ast);
  210   }
  211 
  212   pony_assert((offset + 1) == len);
  213   buf[offset] = '\0';
  214 
  215   return stringtab_consume(buf, len);
  216 }
  217 
  218 static ast_t* get_fun_def(pass_opt_t* opt, ast_t* ast)
  219 {
  220   pony_assert((ast_id(ast) == TK_FUNREF) || (ast_id(ast) == TK_FUNCHAIN) ||
  221     (ast_id(ast) == TK_NEWREF));
  222   AST_GET_CHILDREN(ast, receiver, method);
  223 
  224   // Receiver might be wrapped in another funref/newref
  225   // if the method had type parameters for qualification.
  226   if(ast_id(receiver) == ast_id(ast))
  227     AST_GET_CHILDREN_NO_DECL(receiver, receiver, method);
  228 
  229   // Look up the original method definition for this method call.
  230   deferred_reification_t* method_def = lookup_try(opt, receiver,
  231     ast_type(receiver), ast_name(method), true);
  232 
  233   pony_assert(method_def != NULL);
  234 
  235   ast_t* method_ast = method_def->ast;
  236   deferred_reify_free(method_def);
  237 
  238   pony_assert(ast_id(method_ast) == TK_FUN || ast_id(method_ast) == TK_BE ||
  239     ast_id(method_ast) == TK_NEW);
  240 
  241   // behavior call
  242   if(ast_id(method_ast) == TK_BE)
  243     return NULL;
  244   else
  245     return method_ast;
  246 }
  247 
  248 static bool verify_fun_field_not_referenced(pass_opt_t* opt, ast_t* ast,
  249   ast_t* consume_ast, const char* origin_type_name, ast_t* consumed_type,
  250   const char* consumed_field_name, const char* consumed_field_full_name,
  251   consume_funs_t* consume_funs_map)
  252 {
  253   token_id ast_token = ast_id(ast);
  254   if((ast_token == TK_FUNREF) || (ast_token == TK_NEWREF)
  255     || (ast_token == TK_FUNCHAIN))
  256   {
  257     ast_t* fun_def = get_fun_def(opt, ast);
  258 
  259     if(fun_def != NULL)
  260     {
  261       token_id entity_type = ast_id(ast_parent(ast_parent(fun_def)));
  262       if((entity_type == TK_TRAIT) || (entity_type == TK_INTERFACE))
  263       {
  264         ast_error(opt->check.errors, ast,
  265           "Cannot call functions on traits or interfaces after"
  266           " field consume or in any function called.");
  267         return false;
  268       }
  269 
  270       size_t index = HASHMAP_UNKNOWN;
  271 
  272       ast_t* fref = consume_funs_get(consume_funs_map, fun_def, &index);
  273 
  274       // add to hashmap and evaluate if we haven't seen this function yet
  275       if(fref == NULL)
  276       {
  277         // add to hashmap
  278         consume_funs_putindex(consume_funs_map, fun_def, index);
  279 
  280         // recursive check child
  281         // TODO: open question, print full function call tree?
  282         if(!verify_fun_field_not_referenced(opt, fun_def, consume_ast,
  283           origin_type_name, consumed_type, consumed_field_name,
  284           consumed_field_full_name, consume_funs_map))
  285           return false;
  286       }
  287     }
  288   }
  289   else if (ast_token == TK_FVARREF)
  290   {
  291     // check actual field definition to ensure it's safe
  292     const char* fname = ast_name(ast_sibling(ast_child(ast)));
  293 
  294     if(fname == consumed_field_name)
  295     {
  296       ast_t* child_ref = ast_child(ast);
  297       ast_t* ref_type = NULL;
  298 
  299       switch(ast_id(child_ref))
  300       {
  301         case TK_FVARREF:
  302         case TK_FLETREF:
  303         case TK_EMBEDREF:
  304         {
  305           ref_type = ast_type(ast_sibling(ast_child(child_ref)));
  306           break;
  307         }
  308 
  309         case TK_VARREF:
  310         case TK_LETREF:
  311         case TK_THIS:
  312         {
  313           ref_type = ast_type(child_ref);
  314           break;
  315         }
  316 
  317         default:
  318         {
  319           pony_assert(0);
  320           return false;
  321         }
  322       }
  323 
  324       const char* consumed_type_name =
  325         ast_name(ast_sibling(ast_child(consumed_type)));
  326       const char* ref_type_name = ast_name(ast_sibling(ast_child(ref_type)));
  327 
  328       if(consumed_type_name == ref_type_name)
  329       {
  330         ast_error(opt->check.errors, ast,
  331           "Cannot reference consumed field or a field of the same type after"
  332           " consume or in any function called.");
  333         return false;
  334       }
  335     }
  336   }
  337   else if (ast_token == TK_CONSUME)
  338   {
  339     if(ast == consume_ast)
  340       return true;
  341 
  342     ast_t* cfield = ast_sibling(ast_child(ast));
  343 
  344     if((ast_id(cfield) == TK_FVARREF) || (ast_id(cfield) == TK_FLETREF)
  345       || (ast_id(cfield) == TK_EMBEDREF))
  346     {
  347       ast_t* temp_ast = cfield;
  348 
  349       const char* ctype_name = NULL;
  350 
  351       do
  352       {
  353         if(ast_id(temp_ast) == TK_THIS)
  354            ctype_name = ast_name(ast_sibling(ast_child(ast_type(temp_ast))));
  355 
  356         temp_ast = ast_child(temp_ast);
  357       } while(temp_ast != NULL);
  358 
  359       // ensure we're in the same object type as original consume
  360       if((ctype_name != NULL) && (ctype_name == origin_type_name))
  361       {
  362         const char* cname = get_multi_ref_name(cfield);
  363 
  364         if(strncmp(cname, consumed_field_full_name, strlen(cname)) == 0)
  365         {
  366           ast_error(opt->check.errors, ast,
  367             "Cannot consume parent object of a consumed field in function call"
  368             " tree.");
  369           return false;
  370         }
  371       }
  372     }
  373   }
  374 
  375   ast_t* child = ast_child(ast);
  376 
  377   while(child != NULL)
  378   {
  379     // recursive check child
  380     if(!verify_fun_field_not_referenced(opt, child, consume_ast,
  381       origin_type_name, consumed_type, consumed_field_name,
  382       consumed_field_full_name, consume_funs_map))
  383       return false;
  384 
  385     // next child
  386     child = ast_sibling(child);
  387   }
  388 
  389   return true;
  390 }
  391 
  392 static bool verify_consume_field_not_referenced(pass_opt_t* opt,
  393   ast_t* assign_ast, ast_t* ast)
  394 {
  395   if(!ast_checkflag(ast, AST_FLAG_FCNSM_REASGN))
  396     return true;
  397 
  398   token_id tk = ast_id(ast);
  399 
  400   // found consume; extract type/field name and check if used anywhere in
  401   // function calls between here and assignment
  402   if(tk == TK_CONSUME)
  403   {
  404     consume_funs_t* consume_funs_map =
  405       (consume_funs_t*)POOL_ALLOC(consume_funs_t);
  406     memset(consume_funs_map, 0, sizeof(consume_funs_t));
  407 
  408     ast_t* cfield = ast_sibling(ast_child(ast));
  409     ast_t* consumed_type = ast_type(ast_child(cfield));
  410     const char* consumed_field = ast_name(ast_sibling(ast_child(cfield)));
  411 
  412     const char* cname = get_multi_ref_name(cfield);
  413 
  414     const char* origin_type_name = ast_name(ast_child(opt->check.frame->type));
  415 
  416     ast_t* consume_ast = ast;
  417 
  418     ast_t* parent = ast_parent(ast);
  419 
  420     while(parent != assign_ast)
  421     {
  422       ast = parent;
  423       parent = ast_parent(ast);
  424     }
  425 
  426     while(ast != NULL)
  427     {
  428       if(!verify_fun_field_not_referenced(opt, ast, consume_ast,
  429         origin_type_name, consumed_type, consumed_field, cname,
  430         consume_funs_map))
  431       {
  432         consume_funs_destroy(consume_funs_map);
  433         POOL_FREE(consume_funs_t, consume_funs_map);
  434 
  435         ast_error_continue(opt->check.errors, consume_ast,
  436           "field consumed here");
  437         return false;
  438       }
  439 
  440       ast = ast_sibling(ast);
  441     }
  442 
  443     consume_funs_destroy(consume_funs_map);
  444     POOL_FREE(consume_funs_t, consume_funs_map);
  445 
  446     return true;
  447   }
  448 
  449   ast_t* child = ast_child(ast);
  450 
  451   while(child != NULL)
  452   {
  453     // recursive check child
  454     if(!verify_consume_field_not_referenced(opt, assign_ast, child))
  455       return false;
  456 
  457     // next child
  458     child = ast_sibling(child);
  459   }
  460 
  461   // no errors in any children or this node
  462   return true;
  463 }
  464 
  465 static bool verify_reassign_consumed(pass_opt_t* opt, ast_t* ast)
  466 {
  467   uint32_t ast_flags = ast_checkflag(ast,
  468     AST_FLAG_CAN_ERROR | AST_FLAG_CNSM_REASGN | AST_FLAG_FCNSM_REASGN);
  469 
  470   if(!(ast_flags & (AST_FLAG_CNSM_REASGN | AST_FLAG_FCNSM_REASGN)))
  471     return true;
  472 
  473   // if it's a consume/reassign of a field and an error can be thrown
  474   if(ast_flags == (AST_FLAG_CAN_ERROR | AST_FLAG_FCNSM_REASGN))
  475   {
  476     ast_error(opt->check.errors, ast,
  477       "can't reassign to a consumed field in an expression if there is a partial"
  478       " call involved");
  479     return false;
  480   }
  481   // if it's a consume/reassign of a field, check to ensure field is not
  482   // referenced by a function call
  483   else if(ast_flags & AST_FLAG_FCNSM_REASGN)
  484   {
  485     if(!verify_consume_field_not_referenced(opt, ast, ast))
  486       return false;
  487   }
  488   // if it's a consume/reassign and an error can be thrown
  489   // and we're in a try
  490   else if((ast_flags == (AST_FLAG_CAN_ERROR | AST_FLAG_CNSM_REASGN))
  491     && (opt->check.frame->try_expr != NULL))
  492   {
  493     ast_error(opt->check.errors, ast,
  494       "can't reassign to a consumed identifier in a try expression if there is a"
  495       " partial call involved");
  496     return false;
  497   }
  498 
  499   return true;
  500 }
  501 
  502 
  503 static bool verify_assign(pass_opt_t* opt, ast_t* ast)
  504 {
  505   pony_assert(ast_id(ast) == TK_ASSIGN);
  506   AST_GET_CHILDREN(ast, left, right);
  507 
  508   if(!verify_assign_lvalue(opt, left))
  509     return false;
  510 
  511   ast_inheritflags(ast);
  512 
  513   // Reassign of consumed identifiers only allowed if there are no partial
  514   // calls involved
  515   if(!verify_reassign_consumed(opt, ast))
  516     return false;
  517 
  518   return true;
  519 }
  520 
  521 
  522 ast_result_t pass_verify(ast_t** astp, pass_opt_t* options)
  523 {
  524   ast_t* ast = *astp;
  525   bool r = true;
  526 
  527   switch(ast_id(ast))
  528   {
  529     case TK_STRUCT:       r = verify_struct(options, ast); break;
  530     case TK_ASSIGN:       r = verify_assign(options, ast); break;
  531     case TK_FUN:
  532     case TK_NEW:
  533     case TK_BE:           r = verify_fun(options, ast); break;
  534     case TK_FUNREF:
  535     case TK_FUNCHAIN:
  536     case TK_NEWREF:       r = verify_function_call(options, ast); break;
  537     case TK_BEREF:
  538     case TK_BECHAIN:
  539     case TK_NEWBEREF:     r = verify_behaviour_call(options, ast); break;
  540     case TK_FFICALL:      r = verify_ffi_call(options, ast); break;
  541     case TK_TRY:
  542     case TK_TRY_NO_CHECK: r = verify_try(options, ast); break;
  543     case TK_ERROR:        ast_seterror(ast); break;
  544 
  545     default:              ast_inheritflags(ast); break;
  546   }
  547 
  548   if(!r)
  549   {
  550     pony_assert(errors_get_count(options->check.errors) > 0);
  551     return AST_ERROR;
  552   }
  553 
  554   return AST_OK;
  555 }