Beispiel #1
0
static bool check_partial_ffi_call(pass_opt_t* opt, ast_t* ast)
{
  pony_assert(ast_id(ast) == TK_FFICALL);
  AST_GET_CHILDREN(ast, call_name, call_ret_typeargs, args, named_args,
    call_error);

  // The expr pass (expr_ffi) should have stored the declaration here, if found.
  ast_t* decl = (ast_t*)ast_data(ast);

  if(decl == NULL) {
    if(ast_id(call_error) == TK_QUESTION)
      ast_seterror(ast);
  } else {
    pony_assert(ast_id(decl) == TK_FFIDECL);
    AST_GET_CHILDREN(decl, decl_name, decl_ret_typeargs, params, named_params,
      decl_error);

    if((ast_id(decl_error) == TK_QUESTION) ||
      (ast_id(call_error) == TK_QUESTION))
      ast_seterror(ast);

    if((ast_id(decl_error) == TK_NONE) && (ast_id(call_error) != TK_NONE))
    {
      ast_error(opt->check.errors, call_error,
        "call is partial but the declaration is not");
      ast_error_continue(opt->check.errors, decl_error,
        "declaration is here");
      return false;
    }
  }

  return true;
}
Beispiel #2
0
static bool check_partial_function_call(pass_opt_t* opt, ast_t* ast)
{
  pony_assert((ast_id(ast) == TK_FUNREF) || (ast_id(ast) == TK_FUNCHAIN) ||
    (ast_id(ast) == TK_NEWREF));
  AST_GET_CHILDREN(ast, receiver, method);

  // Receiver might be wrapped in another funref/newref
  // if the method had type parameters for qualification.
  if(ast_id(receiver) == ast_id(ast))
    AST_GET_CHILDREN_NO_DECL(receiver, receiver, method);

  // Look up the original method definition for this method call.
  ast_t* method_def = lookup(opt, receiver, ast_type(receiver),
    ast_name(method));
  pony_assert(ast_id(method_def) == TK_FUN || ast_id(method_def) == TK_BE ||
    ast_id(method_def) == TK_NEW);

  token_id can_error = ast_id(ast_childidx(method_def, 5));
  if(can_error == TK_QUESTION)
    ast_seterror(ast);

  ast_free_unattached(method_def);

  return true;
}
Beispiel #3
0
ast_result_t pass_verify(ast_t** astp, pass_opt_t* options)
{
  ast_t* ast = *astp;
  bool r = true;

  switch(ast_id(ast))
  {
    case TK_FUN:
    case TK_NEW:
    case TK_BE:           r = verify_fun(options, ast); break;
    case TK_FUNREF:
    case TK_FUNCHAIN:
    case TK_NEWREF:       r = verify_function_call(options, ast); break;
    case TK_BEREF:
    case TK_BECHAIN:
    case TK_NEWBEREF:     r = verify_behaviour_call(options, ast); break;
    case TK_FFICALL:      r = verify_ffi_call(options, ast); break;
    case TK_TRY:
    case TK_TRY_NO_CHECK: r = verify_try(options, ast); break;
    case TK_LETREF:
    case TK_VARREF:
    case TK_FLETREF:
    case TK_FVARREF:
    case TK_EMBEDREF:
    case TK_CALL:
    case TK_QUALIFY:
    case TK_TUPLE:
    case TK_ASSIGN:
    case TK_MATCH:
    case TK_CASES:
    case TK_CASE:
    case TK_IS:
    case TK_ISNT:
    case TK_SEQ:
    case TK_BREAK:
    case TK_RETURN:
    case TK_IF:
    case TK_IFDEF:
    case TK_WHILE:
    case TK_REPEAT:
    case TK_RECOVER:
    case TK_POSITIONALARGS:
    case TK_NAMEDARGS:
    case TK_NAMEDARG:
    case TK_UPDATEARG:    ast_inheritflags(ast); break;
    case TK_ERROR:        ast_seterror(ast); break;

    default: {}
  }

  if(!r)
  {
    assert(errors_get_count(options->check.errors) > 0);
    return AST_ERROR;
  }

  return AST_OK;
}
Beispiel #4
0
bool expr_error(ast_t* ast)
{
    // error is always the last expression in a sequence
    assert(ast_sibling(ast) == NULL);

    ast_settype(ast, ast_from(ast, TK_ERROR));
    ast_seterror(ast);
    return true;
}
Beispiel #5
0
ast_result_t expr_ffi(pass_opt_t* opt, ast_t* ast)
{
  if(!package_allow_ffi(&opt->check))
  {
    ast_error(opt->check.errors, ast, "This package isn't allowed to do C FFI");
    return AST_FATAL;
  }

  AST_GET_CHILDREN(ast, name, return_typeargs, args, namedargs, question);
  assert(name != NULL);

  ast_t* decl;
  if(!ffi_get_decl(&opt->check, ast, &decl, opt))
    return AST_ERROR;

  if(decl != NULL)  // We have a declaration
    return declared_ffi(opt, ast, decl);

  // We do not have a declaration
  for(ast_t* arg = ast_child(args); arg != NULL; arg = ast_sibling(arg))
  {
    ast_t* a_type = ast_type(arg);

    if((a_type != NULL) && is_type_literal(a_type))
    {
      ast_error(opt->check.errors, arg,
        "Cannot pass number literals as unchecked FFI arguments");
      return AST_ERROR;
    }
  }

  ast_t* return_type = ast_child(return_typeargs);

  if(return_type == NULL)
  {
    ast_error(opt->check.errors, name,
      "FFIs without declarations must specify return type");
    return AST_ERROR;
  }

  ast_settype(ast, return_type);
  ast_inheritflags(ast);

  if(ast_id(question) == TK_QUESTION)
    ast_seterror(ast);

  return AST_OK;
}
Beispiel #6
0
static bool method_access(pass_opt_t* opt, ast_t* ast, ast_t* method)
{
  AST_GET_CHILDREN(method, cap, id, typeparams, params, result, can_error,
    body);

  switch(ast_id(method))
  {
    case TK_NEW:
    {
      AST_GET_CHILDREN(ast, left, right);
      ast_t* type = ast_type(left);

      if(is_typecheck_error(type))
        return false;

      if(!constructor_type(opt, ast, ast_id(cap), type, &result))
        return false;
      break;
    }

    case TK_BE:
      ast_setid(ast, TK_BEREF);
      break;

    case TK_FUN:
      ast_setid(ast, TK_FUNREF);
      break;

    default:
      assert(0);
      return false;
  }

  ast_settype(ast, type_for_fun(method));

  if(ast_id(can_error) == TK_QUESTION)
    ast_seterror(ast);

  return is_method_called(opt, ast);
}
Beispiel #7
0
bool expr_try(pass_opt_t* opt, ast_t* ast)
{
    AST_GET_CHILDREN(ast, body, else_clause, then_clause);

    // It has to be possible for the left side to result in an error.
    if((ast_id(ast) == TK_TRY) && !ast_canerror(body))
    {
        ast_error(body, "try expression never results in an error");
        return false;
    }

    ast_t* body_type = ast_type(body);
    ast_t* else_type = ast_type(else_clause);
    ast_t* then_type = ast_type(then_clause);

    if(is_typecheck_error(body_type) ||
            is_typecheck_error(else_type) ||
            is_typecheck_error(then_type))
        return false;

    ast_t* type = NULL;

    if(!is_control_type(body_type))
        type = control_type_add_branch(type, body);

    if(!is_control_type(else_type))
        type = control_type_add_branch(type, else_clause);

    if(type == NULL)
    {
        if(ast_sibling(ast) != NULL)
        {
            ast_error(ast_sibling(ast), "unreachable code");
            return false;
        }

        type = ast_from(ast, TK_TRY);
    }

    // The then clause does not affect the type of the expression.
    if(is_control_type(then_type))
    {
        ast_error(then_clause, "then clause always terminates the function");
        return false;
    }

    if(is_type_literal(then_type))
    {
        ast_error(then_clause, "Cannot infer type of unused literal");
        return false;
    }

    ast_settype(ast, type);

    // Doesn't inherit error from the body.
    if(ast_canerror(else_clause) || ast_canerror(then_clause))
        ast_seterror(ast);

    if(ast_cansend(body) || ast_cansend(else_clause) || ast_cansend(then_clause))
        ast_setsend(ast);

    if(ast_mightsend(body) || ast_mightsend(else_clause) ||
            ast_mightsend(then_clause))
        ast_setmightsend(ast);

    literal_unify_control(ast, opt);

    // Push the symbol status from the then clause to our parent scope.
    ast_inheritstatus(ast_parent(ast), then_clause);
    return true;
}
Beispiel #8
0
static ast_result_t declared_ffi(pass_opt_t* opt, ast_t* call, ast_t* decl)
{
  assert(call != NULL);
  assert(decl != NULL);
  assert(ast_id(decl) == TK_FFIDECL);

  AST_GET_CHILDREN(call, call_name, call_ret_typeargs, args, named_args,
    call_error);
  AST_GET_CHILDREN(decl, decl_name, decl_ret_typeargs, params, named_params,
    decl_error);

  // Check args vs params
  ast_t* param = ast_child(params);
  ast_t* arg = ast_child(args);

  while((arg != NULL) && (param != NULL) && ast_id(param) != TK_ELLIPSIS)
  {
    ast_t* p_type = ast_childidx(param, 1);

    if(!coerce_literals(&arg, p_type, opt))
      return AST_ERROR;

    ast_t* a_type = ast_type(arg);

    errorframe_t info = NULL;
    if((a_type != NULL) &&
      !void_star_param(p_type, a_type) &&
      !is_subtype(a_type, p_type, &info, opt))
    {
      errorframe_t frame = NULL;
      ast_error_frame(&frame, arg, "argument not a subtype of parameter");
      ast_error_frame(&frame, param, "parameter type: %s",
        ast_print_type(p_type));
      ast_error_frame(&frame, arg, "argument type: %s",
        ast_print_type(a_type));
      errorframe_append(&frame, &info);
      errorframe_report(&frame, opt->check.errors);
      return AST_ERROR;
    }

    arg = ast_sibling(arg);
    param = ast_sibling(param);
  }

  if(arg != NULL && param == NULL)
  {
    ast_error(opt->check.errors, arg, "too many arguments");
    return AST_ERROR;
  }

  if(param != NULL && ast_id(param) != TK_ELLIPSIS)
  {
    ast_error(opt->check.errors, named_args, "too few arguments");
    return AST_ERROR;
  }

  for(; arg != NULL; arg = ast_sibling(arg))
  {
    ast_t* a_type = ast_type(arg);

    if((a_type != NULL) && is_type_literal(a_type))
    {
      ast_error(opt->check.errors, arg,
        "Cannot pass number literals as unchecked FFI arguments");
      return AST_ERROR;
    }
  }

  // Check return types
  ast_t* call_ret_type = ast_child(call_ret_typeargs);
  ast_t* decl_ret_type = ast_child(decl_ret_typeargs);

  errorframe_t info = NULL;
  if((call_ret_type != NULL) &&
    !is_eqtype(call_ret_type, decl_ret_type, &info, opt))
  {
    errorframe_t frame = NULL;
    ast_error_frame(&frame, call_ret_type,
      "call return type does not match declaration");
    errorframe_append(&frame, &info);
    errorframe_report(&frame, opt->check.errors);
    return AST_ERROR;
  }

  // Check partiality
  if((ast_id(decl_error) == TK_NONE) && (ast_id(call_error) != TK_NONE))
  {
    ast_error(opt->check.errors, call_error,
      "call is partial but the declaration is not");
    return AST_ERROR;
  }

  if((ast_id(decl_error) == TK_QUESTION) ||
    (ast_id(call_error) == TK_QUESTION))
  {
    ast_seterror(call);
  }

  // Store the declaration so that codegen can generate a non-variadic
  // signature for the FFI call.
  ast_setdata(call, decl);
  ast_settype(call, decl_ret_type);
  ast_inheritflags(call);
  return AST_OK;
}