Example #1
0
static bool is_receiver_safe(typecheck_t* t, ast_t* ast)
{
  switch(ast_id(ast))
  {
     case TK_THIS:
     case TK_FLETREF:
     case TK_FVARREF:
     case TK_EMBEDREF:
     case TK_PARAMREF:
     case TK_TUPLEELEMREF:
     {
       ast_t* type = ast_type(ast);
       return sendable(type);
     }

     case TK_LETREF:
     case TK_VARREF:
     {
       ast_t* def = (ast_t*)ast_data(ast);
       pony_assert(def != NULL);
       ast_t* def_recover = ast_nearest(def, TK_RECOVER);
       if(t->frame->recover == def_recover)
         return true;
       ast_t* type = ast_type(ast);
       return sendable(type);
     }

     default:
       // Unsafe receivers inside expressions are catched before we get there.
       return true;
  }
}
Example #2
0
static bool is_receiver_safe(typecheck_t* t, ast_t* ast)
{
  switch(ast_id(ast))
  {
     case TK_THIS:
     case TK_FLETREF:
     case TK_FVARREF:
     case TK_EMBEDREF:
     case TK_PARAMREF:
     {
       ast_t* type = ast_type(ast);
       return sendable(type);
     }

     case TK_LETREF:
     case TK_VARREF:
     {
       const char* name = ast_name(ast_child(ast));
       sym_status_t status;
       ast_t* def = ast_get(ast, name, &status);
       ast_t* def_recover = ast_nearest(def, TK_RECOVER);
       if(t->frame->recover == def_recover)
         return true;
       ast_t* type = ast_type(ast);
       return sendable(type);
     }

     default:
       // Unsafe receivers inside expressions are catched before we get there.
       return true;
  }
}
Example #3
0
static bool check_nonsendable_recover(pass_opt_t* opt, ast_t* ast)
{
  if(opt->check.frame->recover != NULL)
  {
    AST_GET_CHILDREN(ast, positional, namedargs, question, lhs);

    ast_t* type = ast_type(lhs);

    AST_GET_CHILDREN(type, cap, typeparams, params, result);

    // If the method is tag, the call is always safe.
    if(ast_id(cap) == TK_TAG)
      return true;

    ast_t* receiver = ast_child(lhs);

    // Dig through function qualification.
    if((ast_id(receiver) == TK_FUNREF) || (ast_id(receiver) == TK_FUNAPP) ||
       (ast_id(receiver) == TK_FUNCHAIN))
      receiver = ast_child(receiver);

    if(!is_receiver_safe(&opt->check, receiver))
    {
      ast_t* arg = ast_child(positional);
      bool args_sendable = true;
      while(arg != NULL)
      {
        if(ast_id(arg) != TK_NONE)
        {
          // Don't typecheck arg_type, this was already done in
          // auto_recover_call.
          ast_t* arg_type = ast_type(arg);
          if(!sendable(arg_type))
          {
            args_sendable = false;
            break;
          }
        }
        arg = ast_sibling(arg);
      }
      if(!args_sendable || !sendable(result))
      {
        ast_error(opt->check.errors, ast, "can't call method on non-sendable "
          "object inside of a recover expression");
        ast_error_continue(opt->check.errors, ast, "this would be possible if "
          "the arguments and return value were all sendable");
        return false;
      }
    }
  }
  return true;
}
Example #4
0
static ast_result_t flatten_sendable_params(ast_t* params)
{
  ast_t* param = ast_child(params);
  ast_result_t r = AST_OK;

  while(param != NULL)
  {
    AST_GET_CHILDREN(param, id, type, def);

    if(!sendable(type))
    {
      ast_error(type, "this parameter must be sendable (iso, val or tag)");
      r = AST_ERROR;
    }

    param = ast_sibling(param);
  }

  return r;
}
Example #5
0
bool expr_reference(pass_opt_t* opt, ast_t** astp)
{
  typecheck_t* t = &opt->check;
  ast_t* ast = *astp;

  // Everything we reference must be in scope.
  const char* name = ast_name(ast_child(ast));

  sym_status_t status;
  ast_t* def = ast_get(ast, name, &status);

  if(def == NULL)
  {
    const char* alt_name = suggest_alt_name(ast, name);

    if(alt_name == NULL)
      ast_error(ast, "can't find declaration of '%s'", name);
    else
      ast_error(ast, "can't find declaration of '%s', did you mean '%s'?",
        name, alt_name);

    return false;
  }

  switch(ast_id(def))
  {
    case TK_PACKAGE:
    {
      // Only allowed if in a TK_DOT with a type.
      if(ast_id(ast_parent(ast)) != TK_DOT)
      {
        ast_error(ast, "a package can only appear as a prefix to a type");
        return false;
      }

      ast_setid(ast, TK_PACKAGEREF);
      return true;
    }

    case TK_INTERFACE:
    case TK_TRAIT:
    case TK_TYPE:
    case TK_TYPEPARAM:
    case TK_PRIMITIVE:
    case TK_STRUCT:
    case TK_CLASS:
    case TK_ACTOR:
    {
      // It's a type name. This may not be a valid type, since it may need
      // type arguments.
      ast_t* id = ast_child(def);
      const char* name = ast_name(id);
      ast_t* type = type_sugar(ast, NULL, name);
      ast_settype(ast, type);
      ast_setid(ast, TK_TYPEREF);

      return expr_typeref(opt, astp);
    }

    case TK_FVAR:
    case TK_FLET:
    case TK_EMBED:
    {
      // Transform to "this.f".
      if(!def_before_use(def, ast, name))
        return false;

      ast_t* dot = ast_from(ast, TK_DOT);
      ast_add(dot, ast_child(ast));

      ast_t* self = ast_from(ast, TK_THIS);
      ast_add(dot, self);

      ast_replace(astp, dot);

      if(!expr_this(opt, self))
        return false;

      return expr_dot(opt, astp);
    }

    case TK_PARAM:
    {
      if(t->frame->def_arg != NULL)
      {
        ast_error(ast, "can't reference a parameter in a default argument");
        return false;
      }

      if(!def_before_use(def, ast, name))
        return false;

      ast_t* type = ast_type(def);

      if(is_typecheck_error(type))
        return false;

      if(!valid_reference(opt, ast, type, status))
        return false;

      if(!sendable(type) && (t->frame->recover != NULL))
      {
        ast_error(ast,
          "can't access a non-sendable parameter from inside a recover "
          "expression");
        return false;
      }

      // Get the type of the parameter and attach it to our reference.
      // Automatically consume a parameter if the function is done.
      ast_t* r_type = type;

      if(is_method_return(t, ast))
        r_type = consume_type(type, TK_NONE);

      ast_settype(ast, r_type);
      ast_setid(ast, TK_PARAMREF);
      return true;
    }

    case TK_NEW:
    case TK_BE:
    case TK_FUN:
    {
      // Transform to "this.f".
      ast_t* dot = ast_from(ast, TK_DOT);
      ast_add(dot, ast_child(ast));

      ast_t* self = ast_from(ast, TK_THIS);
      ast_add(dot, self);

      ast_replace(astp, dot);

      if(!expr_this(opt, self))
        return false;

      return expr_dot(opt, astp);
    }

    case TK_ID:
    {
      if(!def_before_use(def, ast, name))
        return false;

      ast_t* type = ast_type(def);

      if(type != NULL && ast_id(type) == TK_INFERTYPE)
      {
        ast_error(ast, "cannot infer type of %s\n", ast_nice_name(def));
        ast_settype(def, ast_from(def, TK_ERRORTYPE));
        ast_settype(ast, ast_from(ast, TK_ERRORTYPE));
        return false;
      }

      if(is_typecheck_error(type))
        return false;

      if(!valid_reference(opt, ast, type, status))
        return false;

      ast_t* var = ast_parent(def);

      switch(ast_id(var))
      {
        case TK_VAR:
          ast_setid(ast, TK_VARREF);
          break;

        case TK_LET:
        case TK_MATCH_CAPTURE:
          ast_setid(ast, TK_LETREF);
          break;

        default:
          assert(0);
          return false;
      }

      if(!sendable(type))
      {
        if(t->frame->recover != NULL)
        {
          ast_t* def_recover = ast_nearest(def, TK_RECOVER);

          if(t->frame->recover != def_recover)
          {
            ast_error(ast, "can't access a non-sendable local defined outside "
              "of a recover expression from within that recover expression");
            return false;
          }
        }
      }

      // Get the type of the local and attach it to our reference.
      // Automatically consume a local if the function is done.
      ast_t* r_type = type;

      if(is_method_return(t, ast))
        r_type = consume_type(type, TK_NONE);

      ast_settype(ast, r_type);
      return true;
    }

    default: {}
  }

  assert(0);
  return false;
}
Example #6
0
bool expr_fieldref(pass_opt_t* opt, ast_t* ast, ast_t* find, token_id tid)
{
  AST_GET_CHILDREN(ast, left, right);
  ast_t* l_type = ast_type(left);

  if(is_typecheck_error(l_type))
    return false;

  AST_GET_CHILDREN(find, id, f_type, init);

  // Viewpoint adapted type of the field.
  ast_t* type = viewpoint_type(l_type, f_type);

  if(ast_id(type) == TK_ARROW)
  {
    ast_t* upper = viewpoint_upper(type);

    if(upper == NULL)
    {
      ast_error(opt->check.errors, ast, "can't read a field through %s",
        ast_print_type(l_type));
      return false;
    }

    ast_free_unattached(upper);
  }

  // In a recover expression, we can access obj.field if field is sendable
  // and not being assigned to, even if obj isn't sendable.

  typecheck_t* t = &opt->check;

  if(t->frame->recover != NULL)
  {
    if(!sendable(type))
    {
      if(!sendable(l_type))
      {
        errorframe_t frame = NULL;
        ast_error_frame(&frame, ast, "can't access field of non-sendable "
          "object inside of a recover expression");
        ast_error_frame(&frame, find, "this would be possible if the field was "
          "sendable");
        errorframe_report(&frame, opt->check.errors);
        return false;
      }
    }
    else
    {
      ast_t* parent = ast_parent(ast);
      ast_t* current = ast;
      while(ast_id(parent) != TK_RECOVER && ast_id(parent) != TK_ASSIGN)
      {
        current = parent;
        parent = ast_parent(parent);
      }
      if(ast_id(parent) == TK_ASSIGN && ast_child(parent) != current)
      {
        errorframe_t frame = NULL;
        ast_error_frame(&frame, ast, "can't access field of non-sendable "
          "object inside of a recover expression");
        ast_error_frame(&frame, parent, "this would be possible if the field "
          "wasn't assigned to");
        errorframe_report(&frame, opt->check.errors);
        return false;
      }
    }
  }

  // Set the unadapted field type.
  ast_settype(right, f_type);

  // Set the type so that it isn't free'd as unattached.
  ast_setid(ast, tid);
  ast_settype(ast, type);

  if(ast_id(left) == TK_THIS)
  {
    // Handle symbol status if the left side is 'this'.
    const char* name = ast_name(id);

    sym_status_t status;
    ast_get(ast, name, &status);

    if(!valid_reference(opt, ast, type, status))
      return false;
  }

  return true;
}