Esempio n. 1
0
// TODO: Make group signature indiependent of package load order.
const char* package_group_signature(package_group_t* group)
{
  if(group->signature == NULL)
  {
    pony_ctx_t ctx;
    memset(&ctx, 0, sizeof(pony_ctx_t));
    ponyint_array_t array;
    memset(&array, 0, sizeof(ponyint_array_t));
    char* buf = (char*)ponyint_pool_alloc_size(SIGNATURE_LENGTH);

    pony_serialise(&ctx, group, package_group_signature_pony_type(), &array,
      s_alloc_fn, s_throw_fn);
    int status = blake2b(buf, SIGNATURE_LENGTH, array.ptr, array.size, NULL, 0);
    (void)status;
    pony_assert(status == 0);

    group->signature = buf;
    ponyint_pool_free_size(array.size, array.ptr);
  }

  return group->signature;
}
Esempio n. 2
0
static trace_t trace_type(ast_t* type)
{
  switch(ast_id(type))
  {
    case TK_UNIONTYPE:
      return trace_type_union(type);

    case TK_ISECTTYPE:
      return trace_type_isect(type);

    case TK_TUPLETYPE:
      return TRACE_TUPLE;

    case TK_NOMINAL:
      return trace_type_nominal(type);

    default: {}
  }

  pony_assert(0);
  return TRACE_DYNAMIC;
}
Esempio n. 3
0
void printbuf(printbuf_t* buf, const char* fmt, ...)
{
  size_t avail = buf->size - buf->offset;
  va_list ap;

  va_start(ap, fmt);
  int r = vsnprintf(buf->m + buf->offset, avail, fmt, ap);
  va_end(ap);

  if(r < 0)
  {
#ifdef PLATFORM_IS_WINDOWS
    va_start(ap, fmt);
    r = _vscprintf(fmt, ap);
    va_end(ap);

    if(r < 0)
      return;
#else
    return;
#endif
  }

  if((size_t)r >= avail)
  {
    size_t new_size = buf->size + r + 1;
    buf->m = (char*)ponyint_pool_realloc_size(buf->size, new_size, buf->m);
    buf->size = new_size;
    avail = buf->size - buf->offset;

    va_start(ap, fmt);
    r = vsnprintf(buf->m + buf->offset, avail, fmt, ap);
    va_end(ap);

    pony_assert((r > 0) && ((size_t)r < buf->size));
  }

  buf->offset += r;
}
Esempio n. 4
0
static LLVMValueRef declare_ffi(compile_t* c, const char* f_name,
  reach_type_t* t, ast_t* args, bool intrinsic)
{
  ast_t* last_arg = ast_childlast(args);

  if((last_arg != NULL) && (ast_id(last_arg) == TK_ELLIPSIS))
    return declare_ffi_vararg(c, f_name, t);

  int count = (int)ast_childcount(args);
  size_t buf_size = count * sizeof(LLVMTypeRef);
  LLVMTypeRef* f_params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
  count = 0;

  ast_t* arg = ast_child(args);

  deferred_reification_t* reify = c->frame->reify;

  while(arg != NULL)
  {
    ast_t* p_type = ast_type(arg);

    if(p_type == NULL)
      p_type = ast_childidx(arg, 1);

    p_type = deferred_reify(reify, p_type, c->opt);
    reach_type_t* pt = reach_type(c->reach, p_type);
    pony_assert(pt != NULL);
    f_params[count++] = ((compile_type_t*)pt->c_type)->use_type;
    ast_free_unattached(p_type);
    arg = ast_sibling(arg);
  }

  LLVMTypeRef r_type = ffi_return_type(c, t, intrinsic);
  LLVMTypeRef f_type = LLVMFunctionType(r_type, f_params, count, false);
  LLVMValueRef func = LLVMAddFunction(c->module, f_name, f_type);

  ponyint_pool_free_size(buf_size, f_params);
  return func;
}
Esempio n. 5
0
// Flatten a provides type into a list, checking all types are traits or
// interfaces
static ast_result_t flatten_provides_list(pass_opt_t* opt, ast_t* provider,
  int index)
{
  pony_assert(provider != NULL);

  ast_t* provides = ast_childidx(provider, index);

  if(ast_id(provides) == TK_NONE)
    return AST_OK;

  ast_t* list = ast_from(provides, TK_PROVIDES);
  ast_t* list_end = NULL;

  if(!flatten_provided_type(opt, provides, provider, list, &list_end))
  {
    ast_free(list);
    return AST_ERROR;
  }

  ast_replace(&provides, list);
  return AST_OK;
}
Esempio n. 6
0
static const char* quoted_locator(pass_opt_t* opt, ast_t* use,
  const char* locator)
{
  pony_assert(locator != NULL);

  if(strpbrk(locator, INVALID_LOCATOR_CHARS) != NULL)
  {
    if(use != NULL)
      ast_error(opt->check.errors, use, "use URI contains invalid characters");

    return NULL;
  }

  size_t len = strlen(locator);
  char* quoted = (char*)ponyint_pool_alloc_size(len + 3);
  quoted[0] = '"';
  memcpy(quoted + 1, locator, len);
  quoted[len + 1] = '"';
  quoted[len + 2] = '\0';

  return stringtab_consume(quoted, len + 3);
}
Esempio n. 7
0
static LLVMTypeRef ffi_return_type(compile_t* c, reach_type_t* t,
  bool intrinsic)
{
  compile_type_t* c_t = (compile_type_t*)t->c_type;

  if(t->underlying == TK_TUPLETYPE)
  {
    pony_assert(intrinsic);

    // Can't use the named type. Build an unnamed type with the same elements.
    unsigned int count = LLVMCountStructElementTypes(c_t->use_type);
    size_t buf_size = count * sizeof(LLVMTypeRef);
    LLVMTypeRef* e_types = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
    LLVMGetStructElementTypes(c_t->use_type, e_types);

    ast_t* child = ast_child(t->ast);
    size_t i = 0;

    while(child != NULL)
    {
      // A Bool in an intrinsic tuple return type is an i1.
      if(is_bool(child))
        e_types[i] = c->i1;

      child = ast_sibling(child);
      i++;
    }

    LLVMTypeRef r_type = LLVMStructTypeInContext(c->context, e_types, count,
      false);
    ponyint_pool_free_size(buf_size, e_types);
    return r_type;
  } else if(is_none(t->ast_cap)) {
    return c->void_type;
  } else {
    return c_t->use_type;
  }
}
Esempio n. 8
0
bool method_check_type_params(pass_opt_t* opt, ast_t** astp)
{
  ast_t* lhs = *astp;
  ast_t* type = ast_type(lhs);

  if(is_typecheck_error(type))
    return false;

  ast_t* typeparams = ast_childidx(type, 1);
  pony_assert(ast_id(type) == TK_FUNTYPE);

  if(ast_id(typeparams) == TK_NONE)
    return true;

  BUILD(typeargs, ast_parent(lhs), NODE(TK_TYPEARGS));

  if(!reify_defaults(typeparams, typeargs, true, opt))
  {
    ast_free_unattached(typeargs);
    return false;
  }

  if(!check_constraints(lhs, typeparams, typeargs, true, opt))
  {
    ast_free_unattached(typeargs);
    return false;
  }

  type = reify(type, typeparams, typeargs, opt, true);
  typeparams = ast_childidx(type, 1);
  ast_replace(&typeparams, ast_from(typeparams, TK_NONE));

  REPLACE(astp, NODE(ast_id(lhs), TREE(lhs) TREE(typeargs)));
  ast_settype(*astp, type);

  return true;
}
Esempio n. 9
0
static trace_t trace_union_tag(trace_t a)
{
  switch(a)
  {
    case TRACE_MACHINE_WORD:
    case TRACE_PRIMITIVE:
    case TRACE_TAG_KNOWN:
    case TRACE_TAG_UNKNOWN:
      return TRACE_TAG_UNKNOWN;

    case TRACE_VAL_KNOWN:
    case TRACE_VAL_UNKNOWN:
    case TRACE_MUT_KNOWN:
    case TRACE_MUT_UNKNOWN:
    case TRACE_DYNAMIC:
    case TRACE_TUPLE:
      return TRACE_DYNAMIC;

    default: {}
  }

  pony_assert(0);
  return TRACE_NONE;
}
Esempio n. 10
0
static bool catch_up_provides(pass_opt_t* opt, ast_t* provides)
{
  // Make sure all traits have been through the expr pass before proceeding.
  // We run into dangling ast_data pointer problems when we inherit methods from
  // traits that have only been through the refer pass, and not the expr pass.

  ast_t* child = ast_child(provides);

  while(child != NULL)
  {
    if(!ast_passes_subtree(&child, opt, PASS_EXPR))
      return false;

    ast_t* def = (ast_t*)ast_data(child);
    pony_assert(def != NULL);

    if(!ast_passes_type(&def, opt, PASS_EXPR))
      return false;

    child = ast_sibling(child);
  }

  return true;
}
Esempio n. 11
0
const char* program_signature(ast_t* program)
{
  pony_assert(program != NULL);
  pony_assert(ast_id(program) == TK_PROGRAM);

  program_t* data = (program_t*)ast_data(program);
  pony_assert(data != NULL);

  if(data->signature == NULL)
  {
    ast_t* first_package = ast_child(program);
    pony_assert(first_package != NULL);

    pony_assert(data->package_groups == NULL);
    data->package_groups = package_dependency_groups(first_package);

    blake2b_state hash_state;
    int status = blake2b_init(&hash_state, SIGNATURE_LENGTH);
    pony_assert(status == 0);

    package_group_list_t* iter = data->package_groups;

    while(iter != NULL)
    {
      package_group_t* group = package_group_list_data(iter);
      const char* group_sig = package_group_signature(group);
      blake2b_update(&hash_state, group_sig, SIGNATURE_LENGTH);
      iter = package_group_list_next(iter);
    }

    data->signature = (char*)ponyint_pool_alloc_size(SIGNATURE_LENGTH);
    status = blake2b_final(&hash_state, data->signature, SIGNATURE_LENGTH);
    pony_assert(status == 0);
  }

  return data->signature;
}
Esempio n. 12
0
// Coerce a literal control block to be the specified target type
static bool coerce_control_block(ast_t** astp, ast_t* target_type,
  lit_chain_t* chain, pass_opt_t* opt, bool report_errors)
{
  pony_assert(astp != NULL);
  ast_t* literal_expr = *astp;
  pony_assert(literal_expr != NULL);

  ast_t* lit_type = ast_type(literal_expr);
  pony_assert(lit_type != NULL);
  pony_assert(ast_id(lit_type) == TK_LITERAL);
  ast_t* block_type = ast_type(lit_type);

  for(ast_t* p = ast_child(lit_type); p != NULL; p = ast_sibling(p))
  {
    pony_assert(ast_id(p) == TK_LITERALBRANCH);
    ast_t* branch = (ast_t*)ast_data(p);
    pony_assert(branch != NULL);

    if(!coerce_literal_to_type(&branch, target_type, chain, opt,
      report_errors))
    {
      ast_free_unattached(block_type);
      return false;
    }

    block_type = type_union(opt, block_type, ast_type(branch));
  }

  if(is_typecheck_error(block_type))
    return false;

  // block_type may be a sub-tree of the current type of literal_expr.
  // This means we must copy it before setting it as the type since ast_settype
  // will first free the existing type of literal_expr, which may include
  // block_type.
  if(ast_parent(block_type) != NULL)
    block_type = ast_dup(block_type);

  ast_settype(literal_expr, block_type);
  return true;
}
Esempio n. 13
0
LLVMValueRef gen_ffi(compile_t* c, ast_t* ast)
{
  AST_GET_CHILDREN(ast, id, typeargs, args, named_args, can_err);
  bool err = (ast_id(can_err) == TK_QUESTION);

  // Get the function name, +1 to skip leading @
  const char* f_name = ast_name(id) + 1;

  // Get the return type.
  ast_t* type = ast_type(ast);
  reach_type_t* t = reach_type(c->reach, type);
  pony_assert(t != NULL);

  // Get the function.
  LLVMValueRef func = LLVMGetNamedFunction(c->module, f_name);

  if(func == NULL)
  {
    // If we have no prototype, declare one.
    ast_t* decl = (ast_t*)ast_data(ast);

    if(decl != NULL)
    {
      // Define using the declared types.
      AST_GET_CHILDREN(decl, decl_id, decl_ret, decl_params, decl_err);
      err = (ast_id(decl_err) == TK_QUESTION);
      func = declare_ffi(c, f_name, t, decl_params, err, false);
    } else if(!strncmp(f_name, "llvm.", 5)) {
      // Intrinsic, so use the exact types we supply.
      func = declare_ffi(c, f_name, t, args, err, true);
    } else {
      // Make it varargs.
      func = declare_ffi_vararg(c, f_name, t, err);
    }
  }

  // Generate the arguments.
  int count = (int)ast_childcount(args);
  size_t buf_size = count * sizeof(LLVMValueRef);
  LLVMValueRef* f_args = (LLVMValueRef*)ponyint_pool_alloc_size(buf_size);

  LLVMTypeRef f_type = LLVMGetElementType(LLVMTypeOf(func));
  LLVMTypeRef* f_params = NULL;
  bool vararg = (LLVMIsFunctionVarArg(f_type) != 0);

  if(!vararg)
  {
    f_params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
    LLVMGetParamTypes(f_type, f_params);
  }

  ast_t* arg = ast_child(args);

  for(int i = 0; i < count; i++)
  {
    f_args[i] = gen_expr(c, arg);

    if(!vararg)
      f_args[i] = cast_ffi_arg(c, f_args[i], f_params[i]);

    if(f_args[i] == NULL)
    {
      ponyint_pool_free_size(buf_size, f_args);
      return NULL;
    }

    arg = ast_sibling(arg);
  }

  // If we can error out and we have an invoke target, generate an invoke
  // instead of a call.
  LLVMValueRef result;
  codegen_debugloc(c, ast);

  if(err && (c->frame->invoke_target != NULL))
    result = invoke_fun(c, func, f_args, count, "", false);
  else
    result = LLVMBuildCall(c->builder, func, f_args, count, "");

  codegen_debugloc(c, NULL);
  ponyint_pool_free_size(buf_size, f_args);

  if(!vararg)
    ponyint_pool_free_size(buf_size, f_params);

  // Special case a None return value, which is used for void functions.
  if(is_none(type))
    return t->instance;

  return result;
}
Esempio n. 14
0
void program_lib_build_args(ast_t* program, pass_opt_t* opt,
  const char* path_preamble, const char* rpath_preamble,
  const char* global_preamble, const char* global_postamble,
  const char* lib_premable, const char* lib_postamble)
{
  pony_assert(program != NULL);
  pony_assert(ast_id(program) == TK_PROGRAM);
  pony_assert(global_preamble != NULL);
  pony_assert(global_postamble != NULL);
  pony_assert(lib_premable != NULL);
  pony_assert(lib_postamble != NULL);

  program_t* data = (program_t*)ast_data(program);
  pony_assert(data != NULL);
  pony_assert(data->lib_args == NULL); // Not yet built args

  // Start with an arbitrary amount of space
  data->lib_args_alloced = 256;
  data->lib_args = (char*)ponyint_pool_alloc_size(data->lib_args_alloced);
  data->lib_args[0] = '\0';
  data->lib_args_size = 0;

  // Library paths defined in the source code.
  for(strlist_t* p = data->libpaths; p != NULL; p = strlist_next(p))
  {
    const char* libpath = strlist_data(p);
    append_to_args(data, path_preamble);
    append_to_args(data, libpath);
    append_to_args(data, " ");

    if(rpath_preamble != NULL)
    {
      append_to_args(data, rpath_preamble);
      append_to_args(data, libpath);
      append_to_args(data, " ");
    }
  }

  // Library paths from the command line and environment variable.
  for(strlist_t* p = opt->package_search_paths; p != NULL; p = strlist_next(p))
  {
    const char* libpath = quoted_locator(opt, NULL, strlist_data(p));

    if(libpath == NULL)
      continue;

    append_to_args(data, path_preamble);
    append_to_args(data, libpath);
    append_to_args(data, " ");

    if(rpath_preamble != NULL)
    {
      append_to_args(data, rpath_preamble);
      append_to_args(data, libpath);
      append_to_args(data, " ");
    }
  }

  // Library names.
  append_to_args(data, global_preamble);

  for(strlist_t* p = data->libs; p != NULL; p = strlist_next(p))
  {
    const char* lib = strlist_data(p);
    bool amble = !is_path_absolute(&lib[1]);

    if(amble)
      append_to_args(data, lib_premable);

    append_to_args(data, lib);

    if(amble)
      append_to_args(data, lib_postamble);

    append_to_args(data, " ");
  }

  append_to_args(data, global_postamble);
}
Esempio n. 15
0
// Sugar for partial application, which we convert to a lambda.
static bool partial_application(pass_opt_t* opt, ast_t** astp)
{
  /* Example that we refer to throughout this function.
   * ```pony
   * class C
   *   fun f[T](a: A, b: B = b_default): R
   *
   * let recv: T = ...
   * recv~f[T2](foo)
   * ```
   *
   * Partial call is converted to:
   * ```pony
   * {(b: B = b_default)($0 = recv, a = foo): R => $0.f[T2](a, consume b) }
   * ```
   */

  ast_t* ast = *astp;
  typecheck_t* t = &opt->check;

  if(!method_application(opt, ast, true))
    return false;

  AST_GET_CHILDREN(ast, positional, namedargs, question, lhs);

  // LHS must be an application, possibly wrapped in another application
  // if the method had type parameters for qualification.
  pony_assert(ast_id(lhs) == TK_FUNAPP || ast_id(lhs) == TK_BEAPP ||
    ast_id(lhs) == TK_NEWAPP);
  AST_GET_CHILDREN(lhs, receiver, method);
  ast_t* type_args = NULL;

  if(ast_id(receiver) == ast_id(lhs))
  {
    type_args = method;
    AST_GET_CHILDREN_NO_DECL(receiver, receiver, method);
  }

  // Look up the original method definition for this method call.
  ast_t* method_def = lookup(opt, lhs, 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);

  // The TK_FUNTYPE of the LHS.
  ast_t* type = ast_type(lhs);
  pony_assert(ast_id(type) == TK_FUNTYPE);

  if(is_typecheck_error(type))
    return false;

  AST_GET_CHILDREN(type, cap, type_params, target_params, result);

  bool bare = ast_id(cap) == TK_AT;

  token_id apply_cap = TK_AT;
  if(!bare)
    apply_cap = partial_application_cap(opt, type, receiver, positional);

  token_id can_error = ast_id(ast_childidx(method_def, 5));
  const char* recv_name = package_hygienic_id(t);

  // Build lambda expression.
  ast_t* call_receiver = NULL;
  if(bare)
  {
    ast_t* arg = ast_child(positional);
    while(arg != NULL)
    {
      if(ast_id(arg) != TK_NONE)
      {
        ast_error(opt->check.errors, arg, "the partial application of a bare "
          "method cannot take arguments");
        return false;
      }

      arg = ast_sibling(arg);
    }

    ast_t* receiver_type = ast_type(receiver);
    if(is_bare(receiver_type))
    {
      // Partial application on a bare object, simply return the object itself.
      ast_replace(astp, receiver);
      return true;
    }

    AST_GET_CHILDREN(receiver_type, recv_type_package, recv_type_name);

    const char* recv_package_str = ast_name(recv_type_package);
    const char* recv_name_str = ast_name(recv_type_name);

    ast_t* module = ast_nearest(ast, TK_MODULE);
    ast_t* package = ast_parent(module);
    ast_t* pkg_id = package_id(package);
    const char* pkg_str = ast_name(pkg_id);

    const char* pkg_alias = NULL;

    if(recv_package_str != pkg_str)
      pkg_alias = package_alias_from_id(module, recv_package_str);

    ast_free_unattached(pkg_id);

    if(pkg_alias != NULL)
    {
      // `package.Type.f`
      BUILD_NO_DECL(call_receiver, ast,
        NODE(TK_DOT,
          NODE(TK_DOT,
            NODE(TK_REFERENCE, ID(pkg_alias))
            ID(recv_name_str))
          TREE(method)));
    } else {
      // `Type.f`
      BUILD_NO_DECL(call_receiver, ast,
        NODE(TK_DOT,
          NODE(TK_REFERENCE, ID(recv_name_str))
          TREE(method)));
    }
  } else {
    // `$0.f`
    BUILD_NO_DECL(call_receiver, ast,
      NODE(TK_DOT,
        NODE(TK_REFERENCE, ID(recv_name))
        TREE(method)));
  }

  ast_t* captures = NULL;
  if(bare)
  {
    captures = ast_from(receiver, TK_NONE);
  } else {
    // Build captures. We always have at least one capture, for receiver.
    // Capture: `$0 = recv`
    BUILD_NO_DECL(captures, receiver,
      NODE(TK_LAMBDACAPTURES,
        NODE(TK_LAMBDACAPTURE,
          ID(recv_name)
          NONE  // Infer type.
          TREE(receiver))));
  }

  // Process arguments.
  ast_t* target_param = ast_child(target_params);
  ast_t* lambda_params = ast_from(target_params, TK_NONE);
  ast_t* lambda_call_args = ast_from(positional, TK_NONE);
  ast_t* given_arg = ast_child(positional);

  while(given_arg != NULL)
  {
    pony_assert(target_param != NULL);
    const char* target_p_name = ast_name(ast_child(target_param));

    if(ast_id(given_arg) == TK_NONE)
    {
      // This argument is not supplied already, must be a lambda parameter.
      // Like `b` in example above.
      // Build a new a new TK_PARAM node rather than copying the target one,
      // since the target has already been processed to expr pass, and we need
      // a clean one.
      AST_GET_CHILDREN(target_param, p_id, p_type, p_default);

      // Parameter: `b: B = b_default`
      BUILD(lambda_param, target_param,
        NODE(TK_PARAM,
          TREE(p_id)
          TREE(sanitise_type(p_type))
          TREE(p_default)));

      ast_append(lambda_params, lambda_param);
      ast_setid(lambda_params, TK_PARAMS);

      // Argument: `consume b`
      BUILD(target_arg, lambda_param,
        NODE(TK_SEQ,
          NODE(TK_CONSUME,
            NONE
            NODE(TK_REFERENCE, ID(target_p_name)))));

      ast_append(lambda_call_args, target_arg);
      ast_setid(lambda_call_args, TK_POSITIONALARGS);
    }
    else
    {
      // This argument is supplied to the partial, capture it.
      // Like `a` in example above.
      // Capture: `a = foo`
      BUILD(capture, given_arg,
        NODE(TK_LAMBDACAPTURE,
          ID(target_p_name)
          NONE
          TREE(given_arg)));

      ast_append(captures, capture);

      // Argument: `a`
      BUILD(target_arg, given_arg,
        NODE(TK_SEQ,
          NODE(TK_REFERENCE, ID(target_p_name))));

      ast_append(lambda_call_args, target_arg);
      ast_setid(lambda_call_args, TK_POSITIONALARGS);
    }

    given_arg = ast_sibling(given_arg);
    target_param = ast_sibling(target_param);
  }

  pony_assert(target_param == NULL);

  if(type_args != NULL)
  {
    // The partial call has type args, add them to the actual call in apply().
    // `$0.f[T2]`
    BUILD(qualified, type_args,
      NODE(TK_QUALIFY,
        TREE(call_receiver)
        TREE(type_args)));
    call_receiver = qualified;
  }

  REPLACE(astp,
    NODE((bare ? TK_BARELAMBDA : TK_LAMBDA),
      NODE(apply_cap)
      NONE  // Lambda function name.
      NONE  // Lambda type params.
      TREE(lambda_params)
      TREE(captures)
      TREE(sanitise_type(result))
      NODE(can_error)
      NODE(TK_SEQ,
        NODE(TK_CALL,
          TREE(lambda_call_args)
          NONE  // Named args.
          NODE(can_error)
          TREE(call_receiver)))
      NONE)); // Lambda reference capability.

  // Need to preserve various lambda children.
  ast_setflag(ast_childidx(*astp, 2), AST_FLAG_PRESERVE); // Type params.
  ast_setflag(ast_childidx(*astp, 3), AST_FLAG_PRESERVE); // Parameters.
  ast_setflag(ast_childidx(*astp, 5), AST_FLAG_PRESERVE); // Return type.
  ast_setflag(ast_childidx(*astp, 7), AST_FLAG_PRESERVE); // Body.

  // Catch up to this pass.
  return ast_passes_subtree(astp, opt, PASS_EXPR);
}
Esempio n. 16
0
// Normalise the given ifdef condition.
static void cond_normalise(ast_t** astp)
{
  pony_assert(astp != NULL);

  ast_t* ast = *astp;
  pony_assert(ast != NULL);

  switch(ast_id(ast))
  {
    case TK_AND:
    {
      ast_setid(ast, TK_IFDEFAND);

      AST_GET_CHILDREN(ast, left, right, question);
      pony_assert(ast_id(question) == TK_NONE);
      ast_remove(question);
      cond_normalise(&left);
      cond_normalise(&right);
      break;
    }

    case TK_OR:
    {
      ast_setid(ast, TK_IFDEFOR);

      AST_GET_CHILDREN(ast, left, right, question);
      pony_assert(ast_id(question) == TK_NONE);
      ast_remove(question);
      cond_normalise(&left);
      cond_normalise(&right);
      break;
    }

    case TK_NOT:
    {
      ast_setid(ast, TK_IFDEFNOT);

      AST_GET_CHILDREN(ast, child);
      cond_normalise(&child);
      break;
    }

    case TK_STRING:
    {
      ast_setid(ast, TK_ID);

      REPLACE(astp,
        NODE(TK_IFDEFFLAG,
          TREE(*astp)));

      break;
    }

    case TK_REFERENCE:
    {
      const char* name = ast_name(ast_child(ast));
      if(strcmp(name, OS_POSIX_NAME) == 0)
      {
        REPLACE(astp,
          NODE(TK_IFDEFOR,
            NODE(TK_IFDEFOR,
              NODE(TK_IFDEFFLAG, ID(OS_LINUX_NAME))
              NODE(TK_IFDEFFLAG, ID(OS_MACOSX_NAME)))
            NODE(TK_IFDEFFLAG, ID(OS_BSD_NAME))));
        break;
      }

      ast_setid(ast, TK_IFDEFFLAG);
      break;
    }

    case TK_SEQ:
    {
      // Remove the sequence node.
      pony_assert(ast_childcount(ast) == 1);
      ast_t* child = ast_pop(ast);
      pony_assert(child != NULL);

      cond_normalise(&child);
      ast_replace(astp, child);
      break;
    }

    case TK_IFDEFAND:
    case TK_IFDEFOR:
    case TK_IFDEFNOT:
    case TK_IFDEFFLAG:
      // Already normalised.
      break;

    default:
      pony_assert(0);
      break;
  }
}
Esempio n. 17
0
// Find the declaration for the specified FFI name that is valid for the given
// build config.
// The declaration found is stored in the given decl info argument.
// There must be exactly one valid declaration.
// If a declaration is already specified in the given decl info this must be
// the same as the one found.
// All other cases are errors, which will be reported by this function.
// Returns: true on success, false on failure.
static bool find_decl_for_config(ast_t* call, ast_t* package,
  const char* ffi_name, buildflagset_t* config, ffi_decl_t* decl_info,
  pass_opt_t* opt)
{
  pony_assert(call != NULL);
  pony_assert(package != NULL);
  pony_assert(ffi_name != NULL);
  pony_assert(config != NULL);
  pony_assert(decl_info != NULL);

  bool had_valid_decl = false;

  // FFI declarations are package wide, so check all modules in package.
  for(ast_t* m = ast_child(package); m != NULL; m = ast_sibling(m))
  {
    // Find all the FFI declarations in this module.
    for(ast_t* use = ast_child(m); use != NULL; use = ast_sibling(use))
    {
      if(ast_id(use) == TK_USE)
      {
        AST_GET_CHILDREN(use, alias, decl, guard);

        if(ast_id(decl) == TK_FFIDECL && ffi_name == ast_name(ast_child(decl)))
        {
          // We have an FFI declaration for the specified name.
          if(cond_eval(guard, config, false, opt))
          {
            // This declaration is valid for this config.
            had_valid_decl = true;

            if(decl_info->decl != NULL)
            {
              // We already have a decalaration, is it the same one?
              if(decl_info->decl != decl)
              {
                ast_error(opt->check.errors, call,
                  "Multiple possible declarations for FFI call");
                ast_error_continue(opt->check.errors, decl_info->decl,
                  "This declaration valid for config: %s", decl_info->config);
                ast_error_continue(opt->check.errors, decl,
                  "This declaration valid for config: %s",
                  buildflagset_print(config));
                return false;
              }
            }
            else
            {
              // Store the declaration found.
              // We store the config string incase we need it for error
              // messages later. We stringtab it because the version we are
              // given is in a temporary buffer.
              decl_info->decl = decl;
              decl_info->config = stringtab(buildflagset_print(config));
            }
          }
        }
      }
    }
  }

  if(!had_valid_decl)
  {
    ast_error(opt->check.errors, call,
      "No FFI declaration found for '%s' in config: %s", ffi_name,
      buildflagset_print(config));
    return false;
  }

  return true;
}
Esempio n. 18
0
static LLVMValueRef declare_ffi(compile_t* c, const char* f_name,
  reach_type_t* t, ast_t* args, bool err, bool intrinsic)
{
  ast_t* last_arg = ast_childlast(args);

  if((last_arg != NULL) && (ast_id(last_arg) == TK_ELLIPSIS))
    return declare_ffi_vararg(c, f_name, t, err);

  int count = (int)ast_childcount(args);
  size_t buf_size = count * sizeof(LLVMTypeRef);
  LLVMTypeRef* f_params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
  count = 0;

  ast_t* arg = ast_child(args);

  while(arg != NULL)
  {
    ast_t* p_type = ast_type(arg);

    if(p_type == NULL)
      p_type = ast_childidx(arg, 1);

    reach_type_t* pt = reach_type(c->reach, p_type);
    pony_assert(pt != NULL);

    // An intrinsic that takes a Bool should be i1, not ibool.
    if(intrinsic && is_bool(pt->ast))
      f_params[count++] = c->i1;
    else
      f_params[count++] = pt->use_type;

    arg = ast_sibling(arg);
  }

  LLVMTypeRef r_type;

  if(t->underlying == TK_TUPLETYPE)
  {
    // Can't use the named type. Build an unnamed type with the same
    // elements.
    unsigned int count = LLVMCountStructElementTypes(t->use_type);
    size_t buf_size = count * sizeof(LLVMTypeRef);
    LLVMTypeRef* e_types = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
    LLVMGetStructElementTypes(t->use_type, e_types);

    if(intrinsic)
    {
      ast_t* child = ast_child(t->ast);
      size_t i = 0;

      while(child != NULL)
      {
        // A Bool in an intrinsic tuple return type is an i1, not an ibool.
        if(is_bool(child))
          e_types[i] = c->i1;

        child = ast_sibling(child);
        i++;
      }
    }

    r_type = LLVMStructTypeInContext(c->context, e_types, count, false);
    ponyint_pool_free_size(buf_size, e_types);
  } else {
    // An intrinsic that returns a Bool returns an i1, not an ibool.
    if(intrinsic && is_bool(t->ast))
      r_type = c->i1;
    else
      r_type = t->use_type;
  }

  LLVMTypeRef f_type = LLVMFunctionType(r_type, f_params, count, false);
  LLVMValueRef func = LLVMAddFunction(c->module, f_name, f_type);

  if(!err)
  {
#if PONY_LLVM >= 309
    LLVM_DECLARE_ATTRIBUTEREF(nounwind_attr, nounwind, 0);

    LLVMAddAttributeAtIndex(func, LLVMAttributeFunctionIndex, nounwind_attr);
#else
    LLVMAddFunctionAttr(func, LLVMNoUnwindAttribute);
#endif
  }

  ponyint_pool_free_size(buf_size, f_params);
  return func;
}
Esempio n. 19
0
static void release_local_actor(gc_t* gc)
{
  pony_assert(gc->rc > 0);
  gc->rc--;
}
Esempio n. 20
0
LLVMValueRef gen_pattern_eq(compile_t* c, ast_t* pattern, LLVMValueRef r_value)
{
  // This is used for structural equality in pattern matching.
  ast_t* pattern_type = ast_type(pattern);

  if(ast_id(pattern_type) == TK_NOMINAL)
  {
    AST_GET_CHILDREN(pattern_type, package, id);

    // Special case equality on primitive types.
    if(ast_name(package) == c->str_builtin)
    {
      const char* name = ast_name(id);

      if((name == c->str_Bool) ||
        (name == c->str_I8) ||
        (name == c->str_I16) ||
        (name == c->str_I32) ||
        (name == c->str_I64) ||
        (name == c->str_I128) ||
        (name == c->str_ILong) ||
        (name == c->str_ISize) ||
        (name == c->str_U8) ||
        (name == c->str_U16) ||
        (name == c->str_U32) ||
        (name == c->str_U64) ||
        (name == c->str_U128) ||
        (name == c->str_ULong) ||
        (name == c->str_USize) ||
        (name == c->str_F32) ||
        (name == c->str_F64)
        )
      {
        return gen_eq_rvalue(c, pattern, r_value, true);
      }
    }
  }

  // Generate the receiver.
  LLVMValueRef l_value = gen_expr(c, pattern);
  reach_type_t* t = reach_type(c->reach, pattern_type);
  pony_assert(t != NULL);

  // Static or virtual dispatch.
  token_id cap = cap_dispatch(pattern_type);
  reach_method_t* m = reach_method(t, cap, c->str_eq, NULL);
  LLVMValueRef func = dispatch_function(c, t, m, l_value);

  if(func == NULL)
    return NULL;

  // Call the function. We know it isn't partial.
  LLVMValueRef args[2];
  args[0] = l_value;
  args[1] = r_value;

  codegen_debugloc(c, pattern);
  LLVMValueRef result = codegen_call(c, func, args, 2);
  codegen_debugloc(c, NULL);

  return result;
}
Esempio n. 21
0
// Report whether the named platform attribute is true
bool os_is_target(const char* attribute, bool release, bool* out_is_target, pass_opt_t* options)
{
  pony_assert(attribute != NULL);
  pony_assert(out_is_target != NULL);
  pony_assert(options != NULL);

  if(!strcmp(attribute, OS_BSD_NAME))
  {
    *out_is_target = target_is_bsd(options->triple);
    return true;
  }

  if(!strcmp(attribute, OS_FREEBSD_NAME))
  {
    *out_is_target = target_is_freebsd(options->triple);
    return true;
  }

  if(!strcmp(attribute, OS_DRAGONFLY_NAME))
  {
    *out_is_target = target_is_dragonfly(options->triple);
    return true;
  }

  if(!strcmp(attribute, OS_LINUX_NAME))
  {
    *out_is_target = target_is_linux(options->triple);
    return true;
  }

  if(!strcmp(attribute, OS_MACOSX_NAME))
  {
    *out_is_target = target_is_macosx(options->triple);
    return true;
  }

  if(!strcmp(attribute, OS_WINDOWS_NAME))
  {
    *out_is_target = target_is_windows(options->triple);
    return true;
  }

  if(!strcmp(attribute, OS_POSIX_NAME))
  {
    *out_is_target = target_is_posix(options->triple);
    return true;
  }

  if(!strcmp(attribute, OS_X86_NAME))
  {
    *out_is_target = target_is_x86(options->triple);
    return true;
  }

  if(!strcmp(attribute, OS_ARM_NAME))
  {
    *out_is_target = target_is_arm(options->triple);
    return true;
  }

  if(!strcmp(attribute, OS_LP64_NAME))
  {
    *out_is_target = target_is_lp64(options->triple);
    return true;
  }

  if(!strcmp(attribute, OS_LLP64_NAME))
  {
    *out_is_target = target_is_llp64(options->triple);
    return true;
  }

  if(!strcmp(attribute, OS_ILP32_NAME))
  {
    *out_is_target = target_is_ilp32(options->triple);
    return true;
  }

  if(!strcmp(attribute, OS_NATIVE128_NAME))
  {
    *out_is_target = target_is_native128(options->triple);
    return true;
  }

  if(!strcmp(attribute, OS_DEBUG_NAME))
  {
    *out_is_target = !release;
    return true;
  }

  return false;
}
Esempio n. 22
0
void errorframev(errorframe_t* frame, source_t* source, size_t line,
  size_t pos, const char* fmt, va_list ap)
{
  pony_assert(frame != NULL);
  append_to_frame(frame, make_errorv(source, line, pos, fmt, ap));
}
Esempio n. 23
0
bool check_constraints(ast_t* orig, ast_t* typeparams, ast_t* typeargs,
  bool report_errors, pass_opt_t* opt)
{
  ast_t* typeparam = ast_child(typeparams);
  ast_t* typearg = ast_child(typeargs);

  while(typeparam != NULL)
  {
    if(is_bare(typearg))
    {
      if(report_errors)
      {
        ast_error(opt->check.errors, typearg,
          "a bare type cannot be used as a type argument");
      }

      return false;
    }

    switch(ast_id(typearg))
    {
      case TK_NOMINAL:
      {
        ast_t* def = (ast_t*)ast_data(typearg);

        if(ast_id(def) == TK_STRUCT)
        {
          if(report_errors)
          {
            ast_error(opt->check.errors, typearg,
              "a struct cannot be used as a type argument");
          }

          return false;
        }
        break;
      }

      case TK_TYPEPARAMREF:
      {
        ast_t* def = (ast_t*)ast_data(typearg);

        if(def == typeparam)
        {
          typeparam = ast_sibling(typeparam);
          typearg = ast_sibling(typearg);
          continue;
        }
        break;
      }

      default: {}
    }

    // Reify the constraint.
    ast_t* constraint = ast_childidx(typeparam, 1);
    ast_t* r_constraint = reify(constraint, typeparams, typeargs, opt,
      true);

    // A bound type must be a subtype of the constraint.
    errorframe_t info = NULL;
    errorframe_t* infop = (report_errors ? &info : NULL);
    if(!is_subtype_constraint(typearg, r_constraint, infop, opt))
    {
      if(report_errors)
      {
        errorframe_t frame = NULL;
        ast_error_frame(&frame, orig,
          "type argument is outside its constraint");
        ast_error_frame(&frame, typearg,
          "argument: %s", ast_print_type(typearg));
        ast_error_frame(&frame, typeparam,
          "constraint: %s", ast_print_type(r_constraint));
        errorframe_append(&frame, &info);
        errorframe_report(&frame, opt->check.errors);
      }

      ast_free_unattached(r_constraint);
      return false;
    }

    ast_free_unattached(r_constraint);

    // A constructable constraint can only be fulfilled by a concrete typearg.
    if(is_constructable(constraint) && !is_concrete(typearg))
    {
      if(report_errors)
      {
        ast_error(opt->check.errors, orig, "a constructable constraint can "
          "only be fulfilled by a concrete type argument");
        ast_error_continue(opt->check.errors, typearg, "argument: %s",
          ast_print_type(typearg));
        ast_error_continue(opt->check.errors, typeparam, "constraint: %s",
          ast_print_type(constraint));
      }

      return false;
    }

    typeparam = ast_sibling(typeparam);
    typearg = ast_sibling(typearg);
  }

  pony_assert(typeparam == NULL);
  pony_assert(typearg == NULL);
  return true;
}
Esempio n. 24
0
PONY_API void pony_asio_event_subscribe(asio_event_t* ev)
{
  if((ev == NULL) ||
    (ev->flags == ASIO_DISPOSABLE) ||
    (ev->flags == ASIO_DESTROYED))
  {
    pony_assert(0);
    return;
  }

  asio_backend_t* b = ponyint_asio_get_backend();
  pony_assert(b != NULL);

  if(ev->noisy)
  {
    uint64_t old_count = ponyint_asio_noisy_add();
    // tell scheduler threads that asio has at least one noisy actor
    // if the old_count was 0
    if (old_count == 0)
      ponyint_sched_noisy_asio(SPECIAL_THREADID_EPOLL);
  }

  struct epoll_event ep;
  ep.data.ptr = ev;
  ep.events = EPOLLRDHUP;

  if(ev->flags & ASIO_READ)
    ep.events |= EPOLLIN;

  if(ev->flags & ASIO_WRITE)
    ep.events |= EPOLLOUT;

  if(ev->flags & ASIO_TIMER)
  {
    ev->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
    timer_set_nsec(ev->fd, ev->nsec);
    ep.events |= EPOLLIN;
  }

  if(ev->flags & ASIO_SIGNAL)
  {
    int sig = (int)ev->nsec;
    asio_event_t* prev = NULL;

#ifdef USE_VALGRIND
    ANNOTATE_HAPPENS_BEFORE(&b->sighandlers[sig]);
#endif
    if((sig < MAX_SIGNAL) &&
      atomic_compare_exchange_strong_explicit(&b->sighandlers[sig], &prev, ev,
      memory_order_release, memory_order_relaxed))
    {
      struct sigaction new_action;
      new_action.sa_handler = signal_handler;
      sigemptyset (&new_action.sa_mask);

      // ask to restart interrupted syscalls to match `signal` behavior
      new_action.sa_flags = SA_RESTART;

      sigaction(sig, &new_action, NULL);

      ev->fd = eventfd(0, EFD_NONBLOCK);
      ep.events |= EPOLLIN;
    } else {
      return;
    }
  }

  if(ev->flags & ASIO_ONESHOT)
  {
    ep.events |= EPOLLONESHOT;
  } else {
    // Only use edge triggering if one shot isn't enabled.
    // This is because of how the runtime gets notifications
    // from epoll in this ASIO thread and then notifies the
    // appropriate actor to read/write as necessary.
    // specifically, it seems there's an edge case/race condition
    // with edge triggering where if there is already data waiting
    // on the socket, then epoll might not be triggering immediately
    // when an edge triggered epoll request is made.

    ep.events |= EPOLLET;
  }

  epoll_ctl(b->epfd, EPOLL_CTL_ADD, ev->fd, &ep);
}
Esempio n. 25
0
PONY_API void pony_asio_event_unsubscribe(asio_event_t* ev)
{
  if((ev == NULL) ||
    (ev->flags == ASIO_DISPOSABLE) ||
    (ev->flags == ASIO_DESTROYED))
  {
    pony_assert(0);
    return;
  }

  asio_backend_t* b = ponyint_asio_get_backend();
  pony_assert(b != NULL);

  if(ev->noisy)
  {
    uint64_t old_count = ponyint_asio_noisy_remove();
    // tell scheduler threads that asio has no noisy actors
    // if the old_count was 1
    if (old_count == 1)
    {
      ponyint_sched_unnoisy_asio(SPECIAL_THREADID_EPOLL);

      // maybe wake up a scheduler thread if they've all fallen asleep
      ponyint_sched_maybe_wakeup_if_all_asleep(-1);
    }

    ev->noisy = false;
  }

  epoll_ctl(b->epfd, EPOLL_CTL_DEL, ev->fd, NULL);

  if(ev->flags & ASIO_TIMER)
  {
    if(ev->fd != -1)
    {
      close(ev->fd);
      ev->fd = -1;
    }
  }

  if(ev->flags & ASIO_SIGNAL)
  {
    int sig = (int)ev->nsec;
    asio_event_t* prev = ev;

#ifdef USE_VALGRIND
    ANNOTATE_HAPPENS_BEFORE(&b->sighandlers[sig]);
#endif
    if((sig < MAX_SIGNAL) &&
      atomic_compare_exchange_strong_explicit(&b->sighandlers[sig], &prev, NULL,
      memory_order_release, memory_order_relaxed))
    {
      struct sigaction new_action;

#if !defined(USE_SCHEDULER_SCALING_PTHREADS)
      // Make sure we ignore signals related to scheduler sleeping/waking
      // as the default for those signals is termination
      if(sig == PONY_SCHED_SLEEP_WAKE_SIGNAL)
        new_action.sa_handler = empty_signal_handler;
      else
#endif
        new_action.sa_handler = SIG_DFL;

      sigemptyset (&new_action.sa_mask);

      // ask to restart interrupted syscalls to match `signal` behavior
      new_action.sa_flags = SA_RESTART;

      sigaction(sig, &new_action, NULL);

      close(ev->fd);
      ev->fd = -1;
    }
  }

  ev->flags = ASIO_DISPOSABLE;
  send_request(ev, ASIO_DISPOSABLE);
}
Esempio n. 26
0
static size_t tuple_indices_pop(call_tuple_indices_t* ti)
{
  pony_assert(ti->count > 0);

  return ti->data[--ti->count];
}
Esempio n. 27
0
static LLVMValueRef gen_digestof_value(compile_t* c, ast_t* type,
  LLVMValueRef value)
{
  LLVMTypeRef impl_type = LLVMTypeOf(value);

  switch(LLVMGetTypeKind(impl_type))
  {
    case LLVMFloatTypeKind:
      value = LLVMBuildBitCast(c->builder, value, c->i32, "");
      return LLVMBuildZExt(c->builder, value, c->intptr, "");

    case LLVMDoubleTypeKind:
      value = LLVMBuildBitCast(c->builder, value, c->i64, "");
      return gen_digestof_int64(c, value);

    case LLVMIntegerTypeKind:
    {
      uint32_t width = LLVMGetIntTypeWidth(impl_type);

      if(width < 64)
      {
        return LLVMBuildZExt(c->builder, value, c->intptr, "");
      } else if(width == 64) {
        return gen_digestof_int64(c, value);
      } else if(width == 128) {
        LLVMValueRef shift = LLVMConstInt(c->i128, 64, false);
        LLVMValueRef high = LLVMBuildLShr(c->builder, value, shift, "");
        high = LLVMBuildTrunc(c->builder, high, c->i64, "");
        value = LLVMBuildTrunc(c->builder, value, c->i64, "");
        high = gen_digestof_int64(c, high);
        value = gen_digestof_int64(c, value);
        return LLVMBuildXor(c->builder, value, high, "");
      }
      break;
    }

    case LLVMStructTypeKind:
    {
      uint32_t count = LLVMCountStructElementTypes(impl_type);
      LLVMValueRef result = LLVMConstInt(c->intptr, 0, false);
      ast_t* child = ast_child(type);

      for(uint32_t i = 0; i < count; i++)
      {
        LLVMValueRef elem = LLVMBuildExtractValue(c->builder, value, i, "");
        elem = gen_digestof_value(c, child, elem);
        result = LLVMBuildXor(c->builder, result, elem, "");
        child = ast_sibling(child);
      }

      pony_assert(child == NULL);

      return result;
    }

    case LLVMPointerTypeKind:
      if(!is_known(type))
      {
        reach_type_t* t = reach_type(c->reach, type);
        int sub_kind = subtype_kind(t);

        if((sub_kind & SUBTYPE_KIND_BOXED) != 0)
          return gen_digestof_box(c, t, value, sub_kind);
      }

      return LLVMBuildPtrToInt(c->builder, value, c->intptr, "");

    default: {}
  }

  pony_assert(0);
  return NULL;
}
Esempio n. 28
0
static bool can_inline_message_send(reach_type_t* t, reach_method_t* m,
  const char* method_name)
{
  switch(t->underlying)
  {
    case TK_CLASS:
    case TK_STRUCT:
    case TK_PRIMITIVE:
      return false;

    case TK_ACTOR:
      return true;

    default: {}
  }

  size_t i = HASHMAP_BEGIN;
  reach_type_t* sub;
  while((sub = reach_type_cache_next(&t->subtypes, &i)) != NULL)
  {
    reach_method_t* m_sub = reach_method(sub, m->cap, method_name, m->typeargs);

    if(m_sub == NULL)
      continue;

    switch(sub->underlying)
    {
      case TK_CLASS:
      case TK_PRIMITIVE:
        return false;

      case TK_ACTOR:
        if(ast_id(m_sub->r_fun) == TK_FUN)
          return false;
        break;

      default: {}
    }

    pony_assert(m->param_count == m_sub->param_count);
    for(size_t i = 0; i < m->param_count; i++)
    {
      // If the param is a boxable type for us and an unboxable type for one of
      // our subtypes, that subtype will take that param as boxed through an
      // interface. In order to correctly box the value the actual function to
      // call must be resolved through name mangling, therefore we can't inline
      // the message send.
      reach_type_t* param = m->params[i].type;
      reach_type_t* sub_param = m_sub->params[i].type;
      if(param->can_be_boxed)
      {
        if(!sub_param->can_be_boxed)
          return false;

        if(param->underlying == TK_TUPLETYPE)
        {
          ast_t* child = ast_child(param->ast);
          while(child != NULL)
          {
            if(contains_boxable(child))
              return false;
          }
        }
      }
    }
  }

  return true;
}
Esempio n. 29
0
bool errorframe_has_errors(errorframe_t* frame)
{
  pony_assert(frame != NULL);
  return frame != NULL;
}
Esempio n. 30
0
LLVMValueRef gen_call(compile_t* c, ast_t* ast)
{
  // Special case calls.
  LLVMValueRef special;

  if(special_case_call(c, ast, &special))
    return special;

  AST_GET_CHILDREN(ast, positional, named, postfix);
  AST_GET_CHILDREN(postfix, receiver, method);
  ast_t* typeargs = NULL;

  // Dig through function qualification.
  switch(ast_id(receiver))
  {
    case TK_NEWREF:
    case TK_NEWBEREF:
    case TK_BEREF:
    case TK_FUNREF:
    case TK_BECHAIN:
    case TK_FUNCHAIN:
      typeargs = method;
      AST_GET_CHILDREN_NO_DECL(receiver, receiver, method);
      break;

    default: {}
  }

  // Get the receiver type.
  const char* method_name = ast_name(method);
  ast_t* type = ast_type(receiver);
  reach_type_t* t = reach_type(c->reach, type);
  pony_assert(t != NULL);

  // Generate the arguments.
  size_t count = ast_childcount(positional) + 1;
  size_t buf_size = count * sizeof(void*);

  LLVMValueRef* args = (LLVMValueRef*)ponyint_pool_alloc_size(buf_size);
  ast_t* arg = ast_child(positional);
  int i = 1;

  while(arg != NULL)
  {
    LLVMValueRef value = gen_expr(c, arg);

    if(value == NULL)
    {
      ponyint_pool_free_size(buf_size, args);
      return NULL;
    }

    args[i] = value;
    arg = ast_sibling(arg);
    i++;
  }

  bool is_new_call = false;

  // Generate the receiver. Must be done after the arguments because the args
  // could change things in the receiver expression that must be accounted for.
  if(call_needs_receiver(postfix, t))
  {
    switch(ast_id(postfix))
    {
      case TK_NEWREF:
      case TK_NEWBEREF:
      {
        call_tuple_indices_t tuple_indices = {NULL, 0, 4};
        tuple_indices.data =
          (size_t*)ponyint_pool_alloc_size(4 * sizeof(size_t));

        ast_t* current = ast;
        ast_t* parent = ast_parent(current);
        while((parent != NULL) && (ast_id(parent) != TK_ASSIGN) &&
          (ast_id(parent) != TK_CALL))
        {
          if(ast_id(parent) == TK_TUPLE)
          {
            size_t index = 0;
            ast_t* child = ast_child(parent);
            while(current != child)
            {
              ++index;
              child = ast_sibling(child);
            }
            tuple_indices_push(&tuple_indices, index);
          }
          current = parent;
          parent = ast_parent(current);
        }

        // If we're constructing an embed field, pass a pointer to the field
        // as the receiver. Otherwise, allocate an object.
        if((parent != NULL) && (ast_id(parent) == TK_ASSIGN))
        {
          size_t index = 1;
          current = ast_childidx(parent, 1);
          while((ast_id(current) == TK_TUPLE) || (ast_id(current) == TK_SEQ))
          {
            parent = current;
            if(ast_id(current) == TK_TUPLE)
            {
              // If there are no indices left, we're destructuring a tuple.
              // Errors in those cases have already been catched by the expr
              // pass.
              if(tuple_indices.count == 0)
                break;
              index = tuple_indices_pop(&tuple_indices);
              current = ast_childidx(parent, index);
            } else {
              current = ast_childlast(parent);
            }
          }
          if(ast_id(current) == TK_EMBEDREF)
          {
            args[0] = gen_fieldptr(c, current);
            set_descriptor(c, t, args[0]);
          } else {
            args[0] = gencall_alloc(c, t);
          }
        } else {
          args[0] = gencall_alloc(c, t);
        }
        is_new_call = true;
        ponyint_pool_free_size(tuple_indices.alloc * sizeof(size_t),
          tuple_indices.data);
        break;
      }

      case TK_BEREF:
      case TK_FUNREF:
      case TK_BECHAIN:
      case TK_FUNCHAIN:
        args[0] = gen_expr(c, receiver);
        break;

      default:
        pony_assert(0);
        return NULL;
    }
  } else {
    // Use a null for the receiver type.
    args[0] = LLVMConstNull(t->use_type);
  }

  // Static or virtual dispatch.
  token_id cap = cap_dispatch(type);
  reach_method_t* m = reach_method(t, cap, method_name, typeargs);
  LLVMValueRef func = dispatch_function(c, t, m, args[0]);

  bool is_message = false;

  if((ast_id(postfix) == TK_NEWBEREF) || (ast_id(postfix) == TK_BEREF) ||
    (ast_id(postfix) == TK_BECHAIN))
  {
    switch(t->underlying)
    {
      case TK_ACTOR:
        is_message = true;
        break;

      case TK_UNIONTYPE:
      case TK_ISECTTYPE:
      case TK_INTERFACE:
      case TK_TRAIT:
        if(m->cap == TK_TAG)
          is_message = can_inline_message_send(t, m, method_name);
        break;

      default: {}
    }
  }

  // Cast the arguments to the parameter types.
  LLVMTypeRef f_type = LLVMGetElementType(LLVMTypeOf(func));
  LLVMTypeRef* params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
  LLVMGetParamTypes(f_type, params);

  arg = ast_child(positional);
  i = 1;

  LLVMValueRef r = NULL;

  if(is_message)
  {
    // If we're sending a message, trace and send here instead of calling the
    // sender to trace the most specific types possible.
    LLVMValueRef* cast_args = (LLVMValueRef*)ponyint_pool_alloc_size(buf_size);
    cast_args[0] = args[0];
    while(arg != NULL)
    {
      cast_args[i] = gen_assign_cast(c, params[i], args[i], ast_type(arg));
      arg = ast_sibling(arg);
      i++;
    }

    token_id cap = cap_dispatch(type);
    reach_method_t* m = reach_method(t, cap, method_name, typeargs);

    codegen_debugloc(c, ast);
    gen_send_message(c, m, args, cast_args, positional);
    codegen_debugloc(c, NULL);
    switch(ast_id(postfix))
    {
      case TK_NEWREF:
      case TK_NEWBEREF:
        r = args[0];
        break;

      default:
        r = c->none_instance;
        break;
    }
    ponyint_pool_free_size(buf_size, cast_args);
  } else {
    while(arg != NULL)
    {
      args[i] = gen_assign_cast(c, params[i], args[i], ast_type(arg));
      arg = ast_sibling(arg);
      i++;
    }

    if(func != NULL)
    {
      // If we can error out and we have an invoke target, generate an invoke
      // instead of a call.
      codegen_debugloc(c, ast);

      if(ast_canerror(ast) && (c->frame->invoke_target != NULL))
        r = invoke_fun(c, func, args, i, "", true);
      else
        r = codegen_call(c, func, args, i);

      if(is_new_call)
      {
        LLVMValueRef md = LLVMMDNodeInContext(c->context, NULL, 0);
        LLVMSetMetadataStr(r, "pony.newcall", md);
      }

      codegen_debugloc(c, NULL);
    }
  }

  // Class constructors return void, expression result is the receiver.
  if(((ast_id(postfix) == TK_NEWREF) || (ast_id(postfix) == TK_NEWBEREF)) &&
     (t->underlying == TK_CLASS))
    r = args[0];

  // Chained methods forward their receiver.
  if((ast_id(postfix) == TK_BECHAIN) || (ast_id(postfix) == TK_FUNCHAIN))
    r = args[0];

  ponyint_pool_free_size(buf_size, args);
  ponyint_pool_free_size(buf_size, params);
  return r;
}