Exemple #1
0
bool expr_lambda(pass_opt_t* opt, ast_t** astp)
{
  assert(astp != NULL);
  ast_t* ast = *astp;
  assert(ast != NULL);

  AST_GET_CHILDREN(ast, cap, t_params, params, captures, ret_type, raises,
    body);

  ast_t* members = ast_from(ast, TK_MEMBERS);
  ast_t* last_member = NULL;
  bool failed = false;

  // Process captures
  for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p))
  {
    ast_t* field = make_capture_field(p);

    if(field != NULL)
      ast_list_append(members, &last_member, field);
    else  // An error occurred, just keep going to potentially find more errors
      failed = true;
  }

  if(failed)
  {
    ast_free(members);
    return false;
  }

  // Stop the various elements being marked as preserve
  ast_clearflag(t_params, AST_FLAG_PRESERVE);
  ast_clearflag(params, AST_FLAG_PRESERVE);
  ast_clearflag(ret_type, AST_FLAG_PRESERVE);
  ast_clearflag(body, AST_FLAG_PRESERVE);

  // Make the apply function
  BUILD(apply, ast,
    NODE(TK_FUN, AST_SCOPE
      NONE  // Capability
      ID("apply")
      TREE(t_params)
      TREE(params)
      TREE(ret_type)
      TREE(raises)
      TREE(body)
      NONE));  // Doc string

  ast_list_append(members, &last_member, apply);

  // Replace lambda with object literal
  REPLACE(astp,
    NODE(TK_OBJECT,
      TREE(cap);
      NONE  // Provides list
      TREE(members)));

  // Catch up passes
  return ast_passes_subtree(astp, opt, PASS_EXPR);
}
Exemple #2
0
// Check that the given method is compatible with the existing definition.
// Return the method that ends up being in the entity or NULL on error.
// Stages 2A, 2B and 2C.
static ast_t* add_method(ast_t* entity, ast_t* existing_method,
  ast_t* new_method, ast_t** last_method)
{
  assert(entity != NULL);
  assert(new_method != NULL);
  assert(last_method != NULL);

  const char* name = ast_name(ast_childidx(new_method, 1));
  const char* entity_name = ast_name(ast_child(entity));

  if(existing_method == NULL)
  {
    // This method is new to the entity.
    // Stage 2C.
    ast_t* case_clash = ast_get_case(entity, name, NULL);

    if(case_clash != NULL)
    {
      ast_error(case_clash, "in %s method name %s differs only in case",
        entity_name, name);
      ast_error(new_method, "clashing method is here");
      return NULL;
    }

    attach_method_t(new_method, NULL, false);
    ast_list_append(ast_childidx(entity, 4), last_method, new_method);
    ast_set(entity, name, new_method, SYM_DEFINED);
    return *last_method;
  }

  method_t* info = (method_t*)ast_data(existing_method);
  assert(info != NULL);

  if(info->local_def)
  {
    // Existing method is a local definition, new method must be a subtype
    // Stage 2A
    if(is_subtype(existing_method, new_method, true))
      return existing_method;

    ast_error(existing_method,
      "local method %s is not compatible with provided version", name);
    ast_error(new_method, "clashing method is here");

    return NULL;
  }

  // Both method versions came from the provides list, their signatures must
  // match exactly
  // Stage 2B
  if(compare_signatures(existing_method, new_method))
    return existing_method;

  ast_error(entity, "clashing definitions of method %s provided, local "
    "disambiguation required", name);
  ast_error(existing_method, "provided here");
  ast_error(new_method, "and here");

  return NULL;
}
Exemple #3
0
// Sugar any case methods in the given entity.
ast_result_t sugar_case_methods(typecheck_t* t, ast_t* entity)
{
  assert(entity != NULL);

  ast_result_t result = AST_OK;
  ast_t* members = ast_childidx(entity, 4);
  bool cases_found = false;

  // Check each method to see if its name is repeated, which indicates a case
  // method.
  for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p))
  {
    if(ast_id(p) == TK_FUN || ast_id(p) == TK_BE)
    {
      const char* p_name = ast_name(ast_childidx(p, 1));

      // Check if any subsequent methods share p's name.
      for(ast_t* q = ast_sibling(p); q != NULL; q = ast_sibling(q))
      {
        if(ast_id(q) == TK_FUN || ast_id(q) == TK_BE)
        {
          const char* q_name = ast_name(ast_childidx(q, 1));

          if(q_name == p_name)
          {
            // p's name is repeated, it's a case method, get a match method.
            if(!sugar_case_method(p, members, p_name, t))
              result = AST_ERROR;

            cases_found = true;
            break;
          }
        }
      }
    }
  }

  if(cases_found)
  {
    // Remove nodes marked during processing.
    ast_t* filtered_members = ast_from(members, TK_MEMBERS);
    ast_t* list = NULL;
    ast_t* member;

    while((member = ast_pop(members)) != NULL)
    {
      if(ast_id(member) == TK_NONE) // Marked for removal.
        ast_free(member);
      else  // Put back in filtered list.
        ast_list_append(filtered_members, &list, member);
    }

    ast_replace(&members, filtered_members);
  }

  return result;
}
Exemple #4
0
// Convert the given method into a delegation indirection to the specified
// field.
static void make_delegation(ast_t* method, ast_t* field, ast_t* delegate_ref,
  ast_t* body_donor)
{
  assert(method != NULL);
  assert(field != NULL);
  assert(delegate_ref != NULL);

  // Make a redirection method body.
  ast_t* args = ast_from(delegate_ref, TK_NONE);
  ast_t* last_arg = NULL;

  AST_GET_CHILDREN(method, cap, id, t_params, params, result, error, old_body);

  for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p))
  {
    const char* param_name = ast_name(ast_child(p));

    BUILD(arg, delegate_ref,
      NODE(TK_SEQ,
        NODE(TK_CONSUME,
          NONE
          NODE(TK_REFERENCE, ID(param_name)))));

    ast_list_append(args, &last_arg, arg);
    ast_setid(args, TK_POSITIONALARGS);
  }

  BUILD(body, delegate_ref,
    NODE(TK_SEQ,
      NODE(TK_CALL,
        TREE(args)    // Positional args.
        NODE(TK_NONE) // Named args.
        NODE(TK_DOT,  // Receiver.
          NODE(TK_REFERENCE, ID(ast_name(ast_child(field))))
          ID(ast_name(ast_childidx(method, 1)))))));

  if(is_none(result))
  {
    // Add None to end of body. Whilst the call generated above will return
    // None anyway in this case, without this extra None testing is very hard
    // since a directly written version of this body will have the None.
    BUILD(none, delegate_ref, NODE(TK_REFERENCE, ID("None")));
    ast_append(body, none);
  }

  ast_replace(&old_body, body);

  // Setup method info.
  method_t* info = (method_t*)ast_data(method);
  assert(info != NULL);
  info->body_donor = body_donor;
  info->delegated_field = field;
}
Exemple #5
0
// Process the given provides type
static bool flatten_provided_type(pass_opt_t* opt, ast_t* provides_type,
  ast_t* error_at, ast_t* list_parent, ast_t** list_end)
{
  pony_assert(error_at != NULL);
  pony_assert(provides_type != NULL);
  pony_assert(list_parent != NULL);
  pony_assert(list_end != NULL);

  switch(ast_id(provides_type))
  {
    case TK_PROVIDES:
    case TK_ISECTTYPE:
      // Flatten all children
      for(ast_t* p = ast_child(provides_type); p != NULL; p = ast_sibling(p))
      {
        if(!flatten_provided_type(opt, p, error_at, list_parent, list_end))
          return false;
      }

      return true;

    case TK_NOMINAL:
    {
      // Check type is a trait or interface
      ast_t* def = (ast_t*)ast_data(provides_type);
      pony_assert(def != NULL);

      if(ast_id(def) != TK_TRAIT && ast_id(def) != TK_INTERFACE)
      {
        ast_error(opt->check.errors, error_at,
          "can only provide traits and interfaces");
        ast_error_continue(opt->check.errors, provides_type,
          "invalid type here");
        return false;
      }

      // Add type to new provides list
      ast_list_append(list_parent, list_end, provides_type);
      ast_setdata(*list_end, ast_data(provides_type));

      return true;
    }

    default:
      ast_error(opt->check.errors, error_at,
        "provides type may only be an intersect of traits and interfaces");
      ast_error_continue(opt->check.errors, provides_type, "invalid type here");
      return false;
  }
}
Exemple #6
0
/* ast_decl_new:
 */
static AST *ast_decl_new ( ASTType type,
                           AST *context,
                           CIdentType ident_type,
                           AST *ident )
{
  AST *node;
  ASSERT(ast_type_isa(type, AST_TYPE_DECL));
  ASSERT(AST_IS_IDENT(ident));
  node = ast_new(type);
  AST_DECL(node)->ident = ident;
  if (context)
    {
      ASSERT(AST_IS_DECL(context));
      AST_DECL(node)->context = context;
      AST_DECL(context)->members = ast_list_append(AST_DECL(context)->members, node);
      AST_DECL(node)->cident = c_ident_new(ident_type, AST_IDENT_NAME(ident), AST_DECL(context)->cident);
    }
  else
    {
      AST_DECL(node)->cident = c_ident_new(ident_type, AST_IDENT_NAME(ident), NULL);
    }
  return node;
}
Exemple #7
0
bool expr_lambda(pass_opt_t* opt, ast_t** astp)
{
  assert(astp != NULL);
  ast_t* ast = *astp;
  assert(ast != NULL);

  AST_GET_CHILDREN(ast, cap, name, t_params, params, captures, ret_type,
    raises, body);

  ast_t* members = ast_from(ast, TK_MEMBERS);
  ast_t* last_member = NULL;
  bool failed = false;

  // Process captures
  for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p))
  {
    ast_t* field = make_capture_field(opt, p);

    if(field != NULL)
      ast_list_append(members, &last_member, field);
    else  // An error occurred, just keep going to potentially find more errors
      failed = true;
  }

  if(failed)
  {
    ast_free(members);
    return false;
  }

  // Stop the various elements being marked as preserve
  ast_clearflag(t_params, AST_FLAG_PRESERVE);
  ast_clearflag(params, AST_FLAG_PRESERVE);
  ast_clearflag(ret_type, AST_FLAG_PRESERVE);
  ast_clearflag(body, AST_FLAG_PRESERVE);

  const char* fn_name = "apply";

  if(ast_id(name) == TK_ID)
    fn_name = ast_name(name);

  // Make the apply function
  BUILD(apply, ast,
    NODE(TK_FUN, AST_SCOPE
      TREE(cap)
      ID(fn_name)
      TREE(t_params)
      TREE(params)
      TREE(ret_type)
      TREE(raises)
      TREE(body)
      NONE    // Doc string
      NONE)); // Guard

  ast_list_append(members, &last_member, apply);

  printbuf_t* buf = printbuf_new();
  printbuf(buf, "lambda(");
  bool first = true;

  for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p))
  {
    if(first)
      first = false;
    else
      printbuf(buf, ", ");

    printbuf(buf, "%s", ast_print_type(ast_childidx(p, 1)));
  }

  printbuf(buf, ")");

  if(ast_id(ret_type) != TK_NONE)
    printbuf(buf, ": %s", ast_print_type(ret_type));

  if(ast_id(raises) != TK_NONE)
    printbuf(buf, " ?");

  printbuf(buf, " end");

  // Replace lambda with object literal
  REPLACE(astp,
    NODE(TK_OBJECT, DATA(stringtab(buf->m))
      NONE
      NONE  // Provides list
      TREE(members)));

  printbuf_free(buf);

  // Catch up passes
  if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK)
    return false;

  return ast_passes_subtree(astp, opt, PASS_EXPR);
}
Exemple #8
0
// Resolve the field delegate body to use for the given method, if any.
// Return the body to use, NULL if none found or BODY_ERROR on error.
static ast_t* resolve_delegate_body(ast_t* entity, ast_t* method,
  method_t* info, const char* name)
{
  assert(entity != NULL);
  assert(method != NULL);
  assert(info != NULL);
  assert(name != NULL);

  if(info->delegate_field_2 != NULL)
  {
    // Ambiguous delegate
    assert(info->delegate_field_1 != NULL);
    assert(info->delegate_target_1 != NULL);
    assert(info->delegate_target_2 != NULL);

    ast_error(entity,
      "clashing delegates for method %s, local disambiguation required", name);
    ast_error(info->delegate_field_1, "field %s delegates to %s via %s",
      ast_name(ast_child(info->delegate_field_1)), name,
      ast_name(ast_child(info->delegate_target_1)));
    ast_error(info->delegate_field_2, "field %s delegates to %s via %s",
      ast_name(ast_child(info->delegate_field_2)), name,
      ast_name(ast_child(info->delegate_target_2)));

    return BODY_ERROR;
  }

  if(info->delegate_field_1 == NULL)  // No delegation needed
    return NULL;

  // We have a delegation, make a redirection body
  const char* field_name = ast_name(ast_child(info->delegate_field_1));
  ast_t* args = ast_from(info->delegate_field_1, TK_NONE);
  ast_t* last_arg = NULL;

  AST_GET_CHILDREN(method, cap, id, t_params, params, result, error, old_body);

  for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p))
  {
    const char* param_name = ast_name(ast_child(p));

    BUILD(arg, info->delegate_field_1,
      NODE(TK_SEQ,
        NODE(TK_CONSUME,
          NONE
          NODE(TK_REFERENCE, ID(param_name)))));

    ast_list_append(args, &last_arg, arg);
    ast_setid(args, TK_POSITIONALARGS);
  }

  BUILD(body, info->delegate_field_1,
    NODE(TK_SEQ,
      NODE(TK_CALL,
        TREE(args)    // Positional args
        NODE(TK_NONE) // Named args
        NODE(TK_DOT,  // Receiver
          NODE(TK_REFERENCE, ID(field_name))
          ID(name)))));

  if(is_none(result))
  {
    // Add None to end of body. Whilst the call generated above will return
    // None anyway in this case, without this extra None testing is very hard
    // since a directly written version of this body will have the None.
    BUILD(none, info->delegate_field_1,
      NODE(TK_REFERENCE, ID("None")));

    ast_append(body, none);
  }

  info->body_donor = entity;
  return body;
}
Exemple #9
0
static bool capture_from_reference(pass_opt_t* opt, ast_t* ctx, ast_t* ast,
  ast_t* captures, ast_t** last_capture)
{
  const char* name = ast_name(ast_child(ast));

  if(is_name_dontcare(name))
    return true;

  ast_t* refdef = ast_get(ast, name, NULL);

  if(refdef != NULL)
    return true;

  refdef = ast_get(ctx, name, NULL);

  if(refdef == NULL)
  {
    ast_error(opt->check.errors, ast,
      "cannot capture \"%s\", variable not defined", name);
    return false;
  }

  if(!def_before_use(opt, refdef, ctx, name))
    return false;

  switch(ast_id(refdef))
  {
    case TK_VAR:
    case TK_LET:
    case TK_PARAM:
    case TK_MATCH_CAPTURE:
    case TK_FVAR:
    case TK_FLET:
    case TK_EMBED:
      break;

    default:
      ast_error(opt->check.errors, ast, "cannot capture \"%s\", can only "
        "capture fields, parameters and local variables", name);
      return NULL;
  }

  // Check if we've already captured it
  for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p))
  {
    AST_GET_CHILDREN(p, c_name, c_type);

    if(name == ast_name(c_name))
      return true;
  }

  ast_t* type = alias(ast_type(refdef));

  if(is_typecheck_error(type))
    return false;

  type = sanitise_type(type);

  BUILD(field, ast,
    NODE(TK_FVAR,
      ID(name)
      TREE(type)
      NODE(TK_REFERENCE, ID(name))));

  ast_list_append(captures, last_capture, field);
  return true;
}
Exemple #10
0
bool expr_lambda(pass_opt_t* opt, ast_t** astp)
{
  pony_assert(astp != NULL);
  ast_t* ast = *astp;
  pony_assert(ast != NULL);

  AST_GET_CHILDREN(ast, receiver_cap, name, t_params, params, captures,
    ret_type, raises, body, reference_cap);
  ast_t* annotation = ast_consumeannotation(ast);

  bool bare = ast_id(ast) == TK_BARELAMBDA;
  ast_t* members = ast_from(ast, TK_MEMBERS);
  ast_t* last_member = NULL;
  bool failed = false;

  if(bare)
    pony_assert(ast_id(captures) == TK_NONE);

  // Process captures
  for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p))
  {
    ast_t* field = NULL;
    bool ok = make_capture_field(opt, p, &field);

    if(field != NULL)
      ast_list_append(members, &last_member, field);
    else if(!ok)
      // An error occurred, just keep going to potentially find more errors
      failed = true;
  }

  if(failed)
  {
    ast_free(members);
    return false;
  }

  // Stop the various elements being marked as preserve
  ast_clearflag(t_params, AST_FLAG_PRESERVE);
  ast_clearflag(params, AST_FLAG_PRESERVE);
  ast_clearflag(ret_type, AST_FLAG_PRESERVE);
  ast_clearflag(body, AST_FLAG_PRESERVE);

  const char* fn_name = "apply";

  if(ast_id(name) == TK_ID)
    fn_name = ast_name(name);

  // Make the apply function
  BUILD(apply, ast,
    NODE(TK_FUN, AST_SCOPE
      ANNOTATE(annotation)
      TREE(receiver_cap)
      ID(fn_name)
      TREE(t_params)
      TREE(params)
      TREE(ret_type)
      TREE(raises)
      TREE(body)
      NONE    // Doc string
      NONE)); // Guard

  ast_list_append(members, &last_member, apply);
  ast_setflag(members, AST_FLAG_PRESERVE);

  printbuf_t* buf = printbuf_new();
  printbuf(buf, bare ? "@{(" : "{(");
  bool first = true;

  for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p))
  {
    if(first)
      first = false;
    else
      printbuf(buf, ", ");

    printbuf(buf, "%s", ast_print_type(ast_childidx(p, 1)));
  }

  printbuf(buf, ")");

  if(ast_id(ret_type) != TK_NONE)
    printbuf(buf, ": %s", ast_print_type(ret_type));

  if(ast_id(raises) != TK_NONE)
    printbuf(buf, " ?");

  printbuf(buf, "}");

  // Replace lambda with object literal
  REPLACE(astp,
    NODE(TK_OBJECT, DATA(stringtab(buf->m))
      TREE(reference_cap)
      NONE  // Provides list
      TREE(members)));

  printbuf_free(buf);

  if(bare)
  {
    BUILD(bare_annotation, *astp,
      NODE(TK_ANNOTATION,
        ID("ponyint_bare")));

    // Record the syntax pass as done to avoid the error about internal
    // annotations.
    ast_pass_record(bare_annotation, PASS_SYNTAX);
    ast_setannotation(*astp, bare_annotation);
  }

  // Catch up passes
  if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK)
    return false;

  return ast_passes_subtree(astp, opt, PASS_EXPR);
}
Exemple #11
0
// Make a skeleton wrapper method based on the given case method.
// Returns: created method, NULL on error.
static ast_t* make_match_wrapper(ast_t* case_method)
{
  assert(case_method != NULL);

  if(ast_id(case_method) == TK_FUN)
    fun_defaults(case_method);

  AST_GET_CHILDREN(case_method, cap, id, t_params, params, ret_type, question,
    body, docstring);

  if(ast_child(params) == NULL)
  {
    ast_error(params, "case method must have at least one parameter");
    return NULL;
  }

  ast_t* new_params = ast_from(params, TK_PARAMS);
  ast_t* param_list_end = NULL;

  for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p))
  {
    // Set all parameter info to none now and fill it in later.
    BUILD(new_param, p, NODE(TK_PARAM, NONE NONE NONE));
    ast_list_append(new_params, &param_list_end, new_param);
  }

  ast_t* new_t_params = ast_from(params, TK_NONE);
  ast_t* t_param_list_end = NULL;

  for(ast_t* p = ast_child(t_params); p != NULL; p = ast_sibling(p))
  {
    // Set all type parameter info to none now and fill it in later.
    BUILD(new_t_param, p, NODE(TK_TYPEPARAM, NONE NONE NONE));
    ast_list_append(new_t_params, &t_param_list_end, new_t_param);
    ast_setid(new_t_params, TK_TYPEPARAMS);
  }

  if(ast_id(case_method) == TK_FUN)
  {
    // Function case.

    // TODO: For now always need a `None |` in the return type to allow for
    // match else clause. We won't need that once exhaustive matching is done.
    BUILD(wrapper, case_method,
      NODE(ast_id(case_method), AST_SCOPE
        TREE(cap)
        TREE(id)
        TREE(new_t_params)
        TREE(new_params)
        NODE(TK_NOMINAL, NONE ID("None") NONE NONE NONE)  // Return value.
        NONE    // Error.
        NODE(TK_SEQ)  // Body.
        NONE    // Doc string.
        NONE)); // Guard.

    return wrapper;
  }
  else
  {
    // Behaviour case.
    BUILD(wrapper, case_method,
      NODE(ast_id(case_method), AST_SCOPE
      NONE    // Capability.
      TREE(id)
      TREE(new_t_params)
      TREE(new_params)
      NONE    // Return value.
      NONE    // Error.
      NODE(TK_SEQ)  // Body.
      NONE    // Doc string.
      NONE)); // Guard.

    return wrapper;
  }
}
Exemple #12
0
bool expr_lambda(pass_opt_t* opt, ast_t** astp)
{
  pony_assert(astp != NULL);
  ast_t* ast = *astp;
  pony_assert(ast != NULL);

  AST_GET_CHILDREN(ast, receiver_cap, name, t_params, params, captures,
    ret_type, raises, body, obj_cap);
  ast_t* annotation = ast_consumeannotation(ast);

  // Try to find an antecedent type, and find possible lambda interfaces in it.
  ast_t* antecedent_type = find_antecedent_type(opt, ast, NULL);
  astlist_t* possible_fun_defs = NULL;
  astlist_t* possible_obj_caps = NULL;
  if(!is_typecheck_error(antecedent_type))
    find_possible_fun_defs(opt, antecedent_type, &possible_fun_defs,
      &possible_obj_caps);

  // If there's more than one possible fun defs, rule out impossible ones by
  // comparing each fun def by some basic criteria against the lambda,
  // creating a new list containing only the remaining possibilities.
  if(astlist_length(possible_fun_defs) > 1)
  {
    astlist_t* new_fun_defs = NULL;
    astlist_t* new_obj_caps = NULL;

    astlist_t* fun_def_cursor = possible_fun_defs;
    astlist_t* obj_cap_cursor = possible_obj_caps;
    for(; (fun_def_cursor != NULL) && (obj_cap_cursor != NULL);
      fun_def_cursor = astlist_next(fun_def_cursor),
      obj_cap_cursor = astlist_next(obj_cap_cursor))
    {
      ast_t* fun_def = astlist_data(fun_def_cursor);
      ast_t* def_obj_cap = astlist_data(obj_cap_cursor);

      if(is_typecheck_error(fun_def))
        continue;

      AST_GET_CHILDREN(fun_def, def_receiver_cap, def_name, def_t_params,
        def_params, def_ret_type, def_raises);

      // Must have the same number of parameters.
      if(ast_childcount(params) != ast_childcount(def_params))
        continue;

      // Must have a supercap of the def's receiver cap (if present).
      if((ast_id(receiver_cap) != TK_NONE) && !is_cap_sub_cap(
        ast_id(def_receiver_cap), TK_NONE, ast_id(receiver_cap), TK_NONE)
        )
        continue;

      // Must have a supercap of the def's object cap (if present).
      if((ast_id(obj_cap) != TK_NONE) &&
        !is_cap_sub_cap(ast_id(obj_cap), TK_NONE, ast_id(def_obj_cap), TK_NONE))
        continue;

      // TODO: This logic could potentially be expanded to do deeper
      // compatibility checks, but checks involving subtyping here would be
      // difficult, because the lambda's AST is not caught up yet in the passes.

      new_fun_defs = astlist_push(new_fun_defs, fun_def);
      new_obj_caps = astlist_push(new_obj_caps, def_obj_cap);
    }

    astlist_free(possible_fun_defs);
    astlist_free(possible_obj_caps);
    possible_fun_defs = new_fun_defs;
    possible_obj_caps = new_obj_caps;
  }

  if(astlist_length(possible_fun_defs) == 1)
  {
    ast_t* fun_def = astlist_data(possible_fun_defs);
    ast_t* def_obj_cap = astlist_data(possible_obj_caps);

    // Try to complete the lambda's type info by inferring from the lambda type.
    if(!is_typecheck_error(fun_def))
    {
      // Infer the object cap, receiver cap, and return type if unspecified.
      if(ast_id(obj_cap) == TK_NONE)
        ast_replace(&obj_cap, def_obj_cap);
      if(ast_id(receiver_cap) == TK_NONE)
        ast_replace(&receiver_cap, ast_child(fun_def));
      if(ast_id(ret_type) == TK_NONE)
        ast_replace(&ret_type, ast_childidx(fun_def, 4));

      // Infer the type of any parameters that were left unspecified.
      ast_t* param = ast_child(params);
      ast_t* def_param = ast_child(ast_childidx(fun_def, 3));
      while((param != NULL) && (def_param != NULL))
      {
        ast_t* param_id = ast_child(param);
        ast_t* param_type = ast_sibling(param_id);

        // Convert a "_" parameter to whatever the expected parameter is.
        if(is_name_dontcare(ast_name(param_id)))
        {
          ast_replace(&param_id, ast_child(def_param));
          ast_replace(&param_type, ast_childidx(def_param, 1));
        }
        // Give a type-unspecified parameter the type of the expected parameter.
        else if(ast_id(param_type) == TK_NONE)
        {
          ast_replace(&param_type, ast_childidx(def_param, 1));
        }

        param = ast_sibling(param);
        def_param = ast_sibling(def_param);
      }
    }

    ast_free_unattached(fun_def);
  }

  astlist_free(possible_obj_caps);

  // If any parameters still have no type specified, it's an error.
  ast_t* param = ast_child(params);
  while(param != NULL)
  {
    if(ast_id(ast_childidx(param, 1)) == TK_NONE)
    {
      ast_error(opt->check.errors, param,
        "a lambda parameter must specify a type or be inferable from context");

      if(astlist_length(possible_fun_defs) > 1)
      {
        for(astlist_t* fun_def_cursor = possible_fun_defs;
          fun_def_cursor != NULL;
          fun_def_cursor = astlist_next(fun_def_cursor))
        {
          ast_error_continue(opt->check.errors, astlist_data(fun_def_cursor),
            "this lambda interface is inferred, but it's not the only one");
        }
      }

      astlist_free(possible_fun_defs);
      return false;
    }
    param = ast_sibling(param);
  }

  astlist_free(possible_fun_defs);

  bool bare = ast_id(ast) == TK_BARELAMBDA;
  ast_t* members = ast_from(ast, TK_MEMBERS);
  ast_t* last_member = NULL;
  bool failed = false;

  if(bare)
    pony_assert(ast_id(captures) == TK_NONE);

  // Process captures
  for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p))
  {
    ast_t* field = NULL;
    bool ok = make_capture_field(opt, p, &field);

    if(field != NULL)
      ast_list_append(members, &last_member, field);
    else if(!ok)
      // An error occurred, just keep going to potentially find more errors
      failed = true;
  }

  if(failed)
  {
    ast_free(members);
    return false;
  }

  // Stop the various elements being marked as preserve
  ast_clearflag(t_params, AST_FLAG_PRESERVE);
  ast_clearflag(params, AST_FLAG_PRESERVE);
  ast_clearflag(ret_type, AST_FLAG_PRESERVE);
  ast_clearflag(body, AST_FLAG_PRESERVE);

  const char* fn_name = "apply";

  if(ast_id(name) == TK_ID)
    fn_name = ast_name(name);

  // Make the apply function
  BUILD(apply, ast,
    NODE(TK_FUN, AST_SCOPE
      ANNOTATE(annotation)
      TREE(receiver_cap)
      ID(fn_name)
      TREE(t_params)
      TREE(params)
      TREE(ret_type)
      TREE(raises)
      TREE(body)
      NONE)); // Doc string

  ast_list_append(members, &last_member, apply);
  ast_setflag(members, AST_FLAG_PRESERVE);

  printbuf_t* buf = printbuf_new();
  printbuf(buf, bare ? "@{(" : "{(");
  bool first = true;

  for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p))
  {
    if(first)
      first = false;
    else
      printbuf(buf, ", ");

    printbuf(buf, "%s", ast_print_type(ast_childidx(p, 1)));
  }

  printbuf(buf, ")");

  if(ast_id(ret_type) != TK_NONE)
    printbuf(buf, ": %s", ast_print_type(ret_type));

  if(ast_id(raises) != TK_NONE)
    printbuf(buf, " ?");

  printbuf(buf, "}");

  // Replace lambda with object literal
  REPLACE(astp,
    NODE(TK_OBJECT, DATA(stringtab(buf->m))
      TREE(obj_cap)
      NONE  // Provides list
      TREE(members)));

  printbuf_free(buf);

  if(bare)
  {
    BUILD(bare_annotation, *astp,
      NODE(TK_ANNOTATION,
        ID("ponyint_bare")));

    // Record the syntax pass as done to avoid the error about internal
    // annotations.
    ast_pass_record(bare_annotation, PASS_SYNTAX);
    ast_setannotation(*astp, bare_annotation);
  }

  // Catch up passes
  if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK)
    return false;

  return ast_passes_subtree(astp, opt, PASS_EXPR);
}