Esempio n. 1
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
   * lambda(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, lhs);
  assert(ast_id(lhs) == TK_FUNAPP || ast_id(lhs) == TK_BEAPP ||
    ast_id(lhs) == TK_NEWAPP);

  // LHS must be a TK_TILDE, possibly contained in a TK_QUALIFY.
  AST_GET_CHILDREN(lhs, receiver, method);
  ast_t* type_args = NULL;

  switch(ast_id(receiver))
  {
    case TK_NEWAPP:
    case TK_BEAPP:
    case TK_FUNAPP:
      type_args = method;
      AST_GET_CHILDREN_NO_DECL(receiver, receiver, method);
      break;

    default: {}
  }

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

  if(is_typecheck_error(type))
    return false;

  token_id apply_cap = partial_application_cap(opt, type, receiver,
    positional);
  AST_GET_CHILDREN(type, cap, type_params, target_params, result);

  token_id can_error = ast_canerror(lhs) ? TK_QUESTION : TK_NONE;
  const char* recv_name = package_hygienic_id(t);

  // Build captures. We always have at least one capture, for receiver.
  // Capture: `$0 = recv`
  BUILD(captures, receiver,
    NODE(TK_LAMBDACAPTURES,
      NODE(TK_LAMBDACAPTURE,
        ID(recv_name)
        NONE  // Infer type.
        TREE(receiver))));

  // Process arguments.
  ast_t* given_arg = ast_child(positional);
  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);

  while(given_arg != NULL)
  {
    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);
  }

  assert(target_param == NULL);

  // Build lambda expression.
  // `$0.f`
  BUILD(call_receiver, ast,
    NODE(TK_DOT,
      NODE(TK_REFERENCE, ID(recv_name))
      TREE(method)));

  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(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.
          TREE(call_receiver)))));

  // 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. 2
0
// Add eq() and ne() functions to the given entity.
static bool add_comparable(ast_t* ast, pass_opt_t* options)
{
  assert(ast != NULL);

  AST_GET_CHILDREN(ast, id, typeparams, defcap, traits, members);
  ast_t* typeargs = ast_from(typeparams, TK_NONE);
  bool r = true;

  for(ast_t* p = ast_child(typeparams); p != NULL; p = ast_sibling(p))
  {
    ast_t* p_id = ast_child(p);

    BUILD_NO_DEBUG(type, p_id,
      NODE(TK_NOMINAL, NONE TREE(p_id) NONE NONE NONE));

    ast_append(typeargs, type);
    ast_setid(typeargs, TK_TYPEARGS);
  }

  if(!has_member(members, "eq"))
  {
    BUILD_NO_DEBUG(eq, members,
      NODE(TK_FUN, AST_SCOPE
        NODE(TK_BOX)
        ID("eq")
        NONE
        NODE(TK_PARAMS,
          NODE(TK_PARAM,
            ID("that")
            NODE(TK_NOMINAL, NONE TREE(id) TREE(typeargs) NONE NONE)
            NONE))
        NODE(TK_NOMINAL, NONE ID("Bool") NONE NONE NONE)
        NONE
        NODE(TK_SEQ,
          NODE(TK_IS,
            NODE(TK_THIS)
            NODE(TK_REFERENCE, ID("that"))))
        NONE
        NONE));

    // Need to set function data field to point to originating type, ie ast.
    // This won't be done when we catch up the passes since we've already
    // processed that type.
    ast_setdata(eq, ast);
    ast_append(members, eq);
    ast_set(ast, stringtab("eq"), eq, SYM_DEFINED);

    if(!ast_passes_subtree(&eq, options, PASS_TRAITS))
      r = false;
  }

  if(!has_member(members, "ne"))
  {
    BUILD_NO_DEBUG(ne, members,
      NODE(TK_FUN, AST_SCOPE
        NODE(TK_BOX)
        ID("ne")
        NONE
        NODE(TK_PARAMS,
          NODE(TK_PARAM,
            ID("that")
            NODE(TK_NOMINAL, NONE TREE(id) TREE(typeargs) NONE NONE)
            NONE))
        NODE(TK_NOMINAL, NONE ID("Bool") NONE NONE NONE)
        NONE
        NODE(TK_SEQ,
          NODE(TK_ISNT,
            NODE(TK_THIS)
            NODE(TK_REFERENCE, ID("that"))))
        NONE
        NONE));

    // Need to set function data field to point to originating type, ie ast.
    // This won't be done when we catch up the passes since we've already
    // processed that type.
    ast_setdata(ne, ast);
    ast_append(members, ne);
    ast_set(ast, stringtab("ne"), ne, SYM_DEFINED);

    if(!ast_passes_subtree(&ne, options, PASS_TRAITS))
      r = false;
  }

  ast_free_unattached(typeargs);
  return r;
}
Esempio n. 3
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;
}
Esempio n. 4
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. 5
0
// Add a new method to the given entity, based on the specified method from
// the specified type.
// The trait_ref is the entry in the provides / delegates list that causes this
// method inclusion. Needed for error reporting.
// The basis_method is the reified method in the trait to add.
// The adjective parameter is used for error reporting and should be "provided"
// or similar.
// Return the newly added method or NULL on error.
static ast_t* add_method(ast_t* entity, ast_t* trait_ref, ast_t* basis_method,
  const char* adjective, pass_opt_t* opt)
{
  assert(entity != NULL);
  assert(trait_ref != NULL);
  assert(basis_method != NULL);
  assert(adjective != NULL);

  const char* name = ast_name(ast_childidx(basis_method, 1));

  // Check behaviour compatability.
  if(ast_id(basis_method) == TK_BE)
  {
    switch(ast_id(entity))
    {
      case TK_PRIMITIVE:
        ast_error(opt->check.errors, trait_ref,
          "cannot add a behaviour (%s) to a primitive", name);
        return NULL;

      case TK_STRUCT:
        ast_error(opt->check.errors, trait_ref,
          "cannot add a behaviour (%s) to a struct", name);
        return NULL;

      case TK_CLASS:
        ast_error(opt->check.errors, trait_ref,
          "cannot add a behaviour (%s) to a class", name);
        return NULL;

      default:
        break;
    }
  }

  // Check for existing method of the same name.
  ast_t* existing = ast_get(entity, name, NULL);

  if(existing != NULL)
  {
    assert(is_field(existing)); // Should already have checked for methods.

    ast_error(opt->check.errors, trait_ref,
      "%s method '%s' clashes with field", adjective, name);
    ast_error_continue(opt->check.errors, basis_method,
      "method is defined here");
    return NULL;
  }

  // Check for clash with existing method.
  ast_t* case_clash = ast_get_case(entity, name, NULL);

  if(case_clash != NULL)
  {
    const char* clash_name = "";

    switch(ast_id(case_clash))
    {
      case TK_FUN:
      case TK_BE:
      case TK_NEW:
        clash_name = ast_name(ast_childidx(case_clash, 1));
        break;

      case TK_LET:
      case TK_VAR:
      case TK_EMBED:
        clash_name = ast_name(ast_child(case_clash));
        break;

      default:
        assert(0);
        break;
    }

    ast_error(opt->check.errors, trait_ref,
      "%s method '%s' differs only in case from '%s'",
      adjective, name, clash_name);
    ast_error_continue(opt->check.errors, basis_method,
      "clashing method is defined here");
    return NULL;
  }

  AST_GET_CHILDREN(basis_method, cap, id, typeparams, params, result,
    can_error, body, doc);

  // Ignore docstring.
  if(ast_id(doc) == TK_STRING)
  {
    ast_set_name(doc, "");
    ast_setid(doc, TK_NONE);
  }

  ast_t* local = ast_append(ast_childidx(entity, 4), basis_method);
  ast_set(entity, name, local, SYM_DEFINED, false);
  ast_t* body_donor = (ast_t*)ast_data(basis_method);
  method_t* info = attach_method_t(local);
  info->trait_ref = trait_ref;

  if(ast_id(body) != TK_NONE)
    info->body_donor = body_donor;

  return local;
}
Esempio n. 6
0
static bool partial_application(pass_opt_t* opt, ast_t** astp)
{
  ast_t* ast = *astp;
  typecheck_t* t = &opt->check;

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

  AST_GET_CHILDREN(ast, positional, namedargs, lhs);

  // LHS must be a TK_TILDE, possibly contained in a TK_QUALIFY.
  AST_GET_CHILDREN(lhs, receiver, method);

  switch(ast_id(receiver))
  {
    case TK_NEWAPP:
    case TK_BEAPP:
    case TK_FUNAPP:
      AST_GET_CHILDREN_NO_DECL(receiver, receiver, method);
      break;

    default: {}
  }

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

  if(is_typecheck_error(type))
    return false;

  token_id apply_cap = partial_application_cap(type, receiver, positional);
  AST_GET_CHILDREN(type, cap, typeparams, params, result);

  // Create a new anonymous type.
  ast_t* c_id = ast_from_string(ast, package_hygienic_id(t));

  BUILD(def, ast,
    NODE(TK_CLASS, AST_SCOPE
      TREE(c_id)
      NONE
      NONE
      NONE
      NODE(TK_MEMBERS)
      NONE
      NONE));

  // We will have a create method in the type.
  BUILD(create, ast,
    NODE(TK_NEW, AST_SCOPE
      NONE
      ID("create")
      NONE
      NODE(TK_PARAMS)
      NONE
      NONE
      NODE(TK_SEQ)
      NONE));

  // We will have an apply method in the type.
  token_id can_error = ast_canerror(lhs) ? TK_QUESTION : TK_NONE;

  BUILD(apply, ast,
    NODE(TK_FUN, AST_SCOPE
      NODE(apply_cap)
      ID("apply")
      NONE
      NODE(TK_PARAMS)
      TREE(result)
      NODE(can_error)
      NODE(TK_SEQ)
      NONE));

  // We will replace partial application with $0.create(...)
  BUILD(call_receiver, ast, NODE(TK_REFERENCE, TREE(c_id)));
  BUILD(call_dot, ast, NODE(TK_DOT, TREE(call_receiver) ID("create")));

  BUILD(call, ast,
    NODE(TK_CALL,
      NONE
      NODE(TK_NAMEDARGS)
      TREE(call_dot)));

  ast_t* class_members = ast_childidx(def, 4);
  ast_t* create_params = ast_childidx(create, 3);
  ast_t* create_body = ast_childidx(create, 6);
  ast_t* apply_params = ast_childidx(apply, 3);
  ast_t* apply_body = ast_childidx(apply, 6);
  ast_t* call_namedargs = ast_childidx(call, 1);

  // Add the receiver to the anonymous type.
  ast_t* r_id = ast_from_string(receiver, package_hygienic_id(t));
  ast_t* r_field_id = ast_from_string(receiver, package_hygienic_id(t));
  ast_t* r_type = ast_type(receiver);

  if(is_typecheck_error(r_type))
    return false;

  // A field in the type.
  BUILD(r_field, receiver, NODE(TK_FLET, TREE(r_field_id) TREE(r_type) NONE));

  // A parameter of the constructor.
  BUILD(r_ctor_param, receiver, NODE(TK_PARAM, TREE(r_id) TREE(r_type) NONE));

  // An assignment in the constructor body.
  BUILD(r_assign, receiver,
    NODE(TK_ASSIGN,
      NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(r_id)))
      NODE(TK_REFERENCE, TREE(r_field_id))));

  // A named argument at the call site.
  BUILD(r_call_seq, receiver, NODE(TK_SEQ, TREE(receiver)));
  BUILD(r_call_arg, receiver, NODE(TK_NAMEDARG, TREE(r_id) TREE(r_call_seq)));
  ast_settype(r_call_seq, r_type);

  ast_append(class_members, r_field);
  ast_append(create_params, r_ctor_param);
  ast_append(create_body, r_assign);
  ast_append(call_namedargs, r_call_arg);

  // Add a call to the original method to the apply body.
  BUILD(apply_call, ast,
    NODE(TK_CALL,
      NODE(TK_POSITIONALARGS)
      NONE
      NODE(TK_DOT, NODE(TK_REFERENCE, TREE(r_field_id)) TREE(method))));

  ast_append(apply_body, apply_call);
  ast_t* apply_args = ast_child(apply_call);

  // Add the arguments to the anonymous type.
  ast_t* arg = ast_child(positional);
  ast_t* param = ast_child(params);

  while(arg != NULL)
  {
    AST_GET_CHILDREN(param, id, p_type);

    if(ast_id(arg) == TK_NONE)
    {
      // A parameter of the apply method, using the same name, type and default
      // argument.
      ast_append(apply_params, param);

      // An arg in the call to the original method.
      BUILD(apply_arg, param,
        NODE(TK_SEQ,
          NODE(TK_CONSUME,
            NODE(TK_NONE)
            NODE(TK_REFERENCE, TREE(id)))));

      ast_append(apply_args, apply_arg);
    } else {
      ast_t* p_id = ast_from_string(id, package_hygienic_id(t));

      // A field in the type.
      BUILD(field, arg, NODE(TK_FLET, TREE(id) TREE(p_type) NONE));

      // A parameter of the constructor.
      BUILD(ctor_param, arg, NODE(TK_PARAM, TREE(p_id) TREE(p_type) NONE));

      // An assignment in the constructor body.
      BUILD(assign, arg,
        NODE(TK_ASSIGN,
          NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id)))
          NODE(TK_REFERENCE, TREE(id))));

      // A named argument at the call site.
      BUILD(call_arg, arg, NODE(TK_NAMEDARG, TREE(p_id) TREE(arg)));

      // An arg in the call to the original method.
      BUILD(apply_arg, arg, NODE(TK_SEQ, NODE(TK_REFERENCE, TREE(id))));

      ast_append(class_members, field);
      ast_append(create_params, ctor_param);
      ast_append(create_body, assign);
      ast_append(call_namedargs, call_arg);
      ast_append(apply_args, apply_arg);
    }

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

  // Add create and apply to the anonymous type.
  ast_append(class_members, create);
  ast_append(class_members, apply);

  // Typecheck the anonymous type.
  ast_add(t->frame->module, def);

  if(!type_passes(def, opt))
    return false;

  // Typecheck the create call.
  if(!expr_reference(opt, &call_receiver))
    return false;

  if(!expr_dot(opt, &call_dot))
    return false;

  if(!expr_call(opt, &call))
    return false;

  // Replace the partial application with the create call.
  ast_replace(astp, call);
  return true;
}
Esempio n. 7
0
bool expr_object(pass_opt_t* opt, ast_t** astp)
{
  ast_t* ast = *astp;
  bool ok = true;

  AST_GET_CHILDREN(ast, cap, provides, members);
  ast_clearflag(cap, AST_FLAG_PRESERVE);
  ast_clearflag(provides, AST_FLAG_PRESERVE);
  ast_clearflag(members, AST_FLAG_PRESERVE);

  ast_t* annotation = ast_consumeannotation(ast);
  const char* c_id = package_hygienic_id(&opt->check);

  ast_t* t_params;
  ast_t* t_args;
  collect_type_params(ast, &t_params, &t_args);

  const char* nice_id = (const char*)ast_data(ast);

  if(nice_id == NULL)
    nice_id = "object literal";

  // Create a new anonymous type.
  BUILD(def, ast,
    NODE(TK_CLASS, AST_SCOPE
      ANNOTATE(annotation)
      NICE_ID(c_id, nice_id)
      TREE(t_params)
      NONE
      TREE(provides)
      NODE(TK_MEMBERS)
      NONE
      NONE));

  // We will have a create method in the type.
  BUILD(create, members,
    NODE(TK_NEW, AST_SCOPE
      NONE
      ID("create")
      NONE
      NODE(TK_PARAMS)
      NONE
      NONE
      NODE(TK_SEQ,
        NODE(TK_TRUE))
      NONE
      NONE));

  BUILD(type_ref, ast, NODE(TK_REFERENCE, ID(c_id)));

  if(ast_id(t_args) != TK_NONE)
  {
    // Need to add type args to our type reference
    BUILD(t, ast, NODE(TK_QUALIFY, TREE(type_ref) TREE(t_args)));
    type_ref = t;
  }

  ast_free_unattached(t_args);

  // We will replace object..end with $0.create(...)
  BUILD(call, ast,
    NODE(TK_CALL,
      NODE(TK_POSITIONALARGS)
      NONE
      NONE
      NODE(TK_DOT,
        TREE(type_ref)
        ID("create"))));

  ast_t* create_params = ast_childidx(create, 3);
  ast_t* create_body = ast_childidx(create, 6);
  ast_t* call_args = ast_child(call);
  ast_t* class_members = ast_childidx(def, 4);
  ast_t* member = ast_child(members);

  bool has_fields = false;
  bool has_behaviours = false;

  while(member != NULL)
  {
    switch(ast_id(member))
    {
      case TK_FVAR:
      case TK_FLET:
      case TK_EMBED:
      {
        add_field_to_object(opt, member, class_members, create_params,
          create_body, call_args);

        has_fields = true;
        break;
      }

      case TK_BE:
        // If we have behaviours, we must be an actor.
        ast_append(class_members, member);
        has_behaviours = true;
        break;

      default:
        // Keep all the methods as they are.
        ast_append(class_members, member);
        break;
    }

    member = ast_sibling(member);
  }

  // Add the create function at the end.
  ast_append(class_members, create);

  // Add new type to current module and bring it up to date with passes.
  ast_t* module = ast_nearest(ast, TK_MODULE);
  ast_append(module, def);

  // Turn any free variables into fields.
  ast_t* captures = ast_from(ast, TK_MEMBERS);
  ast_t* last_capture = NULL;

  if(!capture_from_type(opt, *astp, &def, captures, &last_capture))
    ok = false;

  for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p))
  {
    add_field_to_object(opt, p, class_members, create_params, create_body,
      call_args);
    has_fields = true;
  }

  ast_free_unattached(captures);
  ast_resetpass(def, PASS_SUGAR);

  // Handle capability and whether the anonymous type is a class, primitive or
  // actor.
  token_id cap_id = ast_id(cap);

  if(has_behaviours)
  {
    // Change the type to an actor.
    ast_setid(def, TK_ACTOR);

    if(cap_id != TK_NONE && cap_id != TK_TAG)
    {
      ast_error(opt->check.errors, cap, "object literals with behaviours are "
        "actors and so must have tag capability");
      ok = false;
    }

    cap_id = TK_TAG;
  }
  else if(!has_fields && (cap_id == TK_NONE || cap_id == TK_TAG ||
    cap_id == TK_BOX || cap_id == TK_VAL))
  {
    // Change the type from a class to a primitive.
    ast_setid(def, TK_PRIMITIVE);
    cap_id = TK_VAL;
  }

  if(ast_id(def) != TK_PRIMITIVE)
    pony_assert(!ast_has_annotation(def, "ponyint_bare"));

  // Reset constructor to pick up the correct defaults.
  ast_setid(ast_child(create), cap_id);
  ast_t* result = ast_childidx(create, 4);
  ast_replace(&result,
    type_for_class(opt, def, result, cap_id, TK_EPHEMERAL, false));

  // Catch up provides before catching up the entire type.
  if(!catch_up_provides(opt, provides))
    return false;

  // Type check the anonymous type.
  if(!ast_passes_type(&def, opt, PASS_EXPR))
    return false;

  // Replace object..end with $0.create(...)
  ast_replace(astp, call);

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

  if(!ast_passes_subtree(astp, opt, PASS_EXPR))
    return false;

  return ok;
}
Esempio n. 8
0
// Handle the given case method parameter, which does have a type specified.
// Combine and check the case parameter against the existing match parameter
// and generate the pattern element.
// Returns: true on success, false on error.
static bool param_with_type(ast_t* case_param, ast_t* match_param,
  ast_t* pattern)
{
  assert(case_param != NULL);
  assert(match_param != NULL);
  assert(pattern != NULL);

  AST_GET_CHILDREN(case_param, case_id, case_type, case_def_arg);
  assert(ast_id(case_type) != TK_NONE);

  if(ast_id(case_id) != TK_ID)
  {
    ast_error(case_id, "expected parameter name for case method. If this is "
      "meant to be a value to match on, do not specify parameter type");
    return false;
  }

  AST_GET_CHILDREN(match_param, match_id, match_type, match_def_arg);
  assert(ast_id(match_id) == TK_ID || ast_id(match_id) == TK_NONE);

  if(ast_id(match_id) == TK_NONE)
  {
    // This is the first case method to give this parameter, just use it as
    // given by this case.
    ast_replace(&match_id, case_id);
    ast_replace(&match_type, case_type);
    ast_replace(&match_def_arg, case_def_arg);
  }
  else
  {
    // Combine new case param with existing match param.
    if(ast_name(case_id) != ast_name(match_id))
    {
      ast_error(case_id, "parameter name differs between case methods");
      ast_error(match_id, "clashing name here");
      return false;
    }

    if(ast_id(case_def_arg) != TK_NONE)
    {
      // This case provides a default argument.
      if(ast_id(match_def_arg) != TK_NONE)
      {
        ast_error(case_def_arg,
          "multiple defaults provided for case method parameter");
        ast_error(match_def_arg, "another default defined here");
        return false;
      }

      // Move default arg to match parameter.
      ast_replace(&match_def_arg, case_def_arg);
    }

    // Union param types.
    REPLACE(&match_type,
      NODE(TK_UNIONTYPE,
        TREE(match_type)
        TREE(case_type)));
  }

  // Add parameter to match pattern.
  BUILD(pattern_elem, case_param,
    NODE(TK_SEQ,
      NODE(TK_LET, TREE(case_id) TREE(case_type))));

  ast_append(pattern, pattern_elem);
  return true;
}
Esempio n. 9
0
// Check parameters and build the all parameter structures for a complete set
// of case methods with the given name.
// Generate match operand, worker parameters and worker call arguments.
// Returns: match operand, NULL on failure.
static ast_t* build_params(ast_t* match_params, ast_t* worker_params,
  ast_t* call_args, const char* name, typecheck_t* t)
{
  assert(match_params != NULL);
  assert(worker_params != NULL);
  assert(call_args != NULL);
  assert(name != NULL);
  assert(t != NULL);

  ast_t* match_operand = ast_from(match_params, TK_TUPLE);
  size_t count = 0;
  bool ok = true;

  for(ast_t* p = ast_child(match_params); p != NULL; p = ast_sibling(p))
  {
    AST_GET_CHILDREN(p, id, type, def_arg);
    count++;

    if(ast_id(id) == TK_NONE)
    {
      assert(ast_id(type) == TK_NONE);
      ast_error(p,
        "name and type not specified for parameter " __zu
        " of case function %s",
        count, name);
      ok = false;
    }
    else
    {
      const char* worker_param_name = package_hygienic_id(t);

      // Add parameter to match operand.
      BUILD(element, p,
        NODE(TK_SEQ,
        NODE(TK_CONSUME,
        NONE
        NODE(TK_REFERENCE, ID(worker_param_name)))));

      ast_append(match_operand, element);

      // Add parameter to worker function.
      BUILD(param, p,
        NODE(TK_PARAM,
        ID(worker_param_name)
        TREE(type)
        NONE)); // Default argument.

      ast_append(worker_params, param);

      // Add argument to worker call.
      BUILD(arg, p,
        NODE(TK_SEQ,
        NODE(TK_CONSUME,
        NONE
        NODE(TK_REFERENCE, TREE(id)))));

      ast_append(call_args, arg);
    }
  }

  if(!ok)
  {
    ast_free(match_operand);
    return NULL;
  }

  assert(count > 0);
  if(count == 1)
  {
    // Only one parameter, don't need tuple in operand.
    ast_t* tmp = ast_pop(ast_child(match_operand));
    ast_free(match_operand);
    match_operand = tmp;
  }

  return match_operand;
}
Esempio n. 10
0
File: subtype.c Progetto: ozra/ponyc
static bool is_fun_sub_fun(ast_t* sub, ast_t* super,
  ast_t* isub, ast_t* isuper)
{
  token_id tsub = ast_id(sub);
  token_id tsuper = ast_id(super);

  switch(tsub)
  {
    case TK_NEW:
    case TK_BE:
    case TK_FUN:
      break;

    default:
      return false;
  }

  switch(tsuper)
  {
    case TK_NEW:
    case TK_BE:
    case TK_FUN:
      break;

    default:
      return false;
  }

  // A constructor can only be a subtype of a constructor.
  if(((tsub == TK_NEW) || (tsuper == TK_NEW)) && (tsub != tsuper))
    return false;

  AST_GET_CHILDREN(sub, sub_cap, sub_id, sub_typeparams, sub_params);
  AST_GET_CHILDREN(super, super_cap, super_id, super_typeparams, super_params);

  // Must have the same name.
  if(ast_name(sub_id) != ast_name(super_id))
    return false;

  // Must have the same number of type parameters and parameters.
  if((ast_childcount(sub_typeparams) != ast_childcount(super_typeparams)) ||
    (ast_childcount(sub_params) != ast_childcount(super_params)))
    return false;

  ast_t* r_sub = sub;

  if(ast_id(super_typeparams) != TK_NONE)
  {
    // Reify sub with the type parameters of super.
    BUILD(typeargs, super_typeparams, NODE(TK_TYPEARGS));
    ast_t* super_typeparam = ast_child(super_typeparams);

    while(super_typeparam != NULL)
    {
      AST_GET_CHILDREN(super_typeparam, super_id, super_constraint);
      token_id cap = cap_from_constraint(super_constraint);

      BUILD(typearg, super_typeparam,
        NODE(TK_TYPEPARAMREF, TREE(super_id) NODE(cap) NONE));

      ast_t* def = ast_get(super_typeparam, ast_name(super_id), NULL);
      ast_setdata(typearg, def);
      ast_append(typeargs, typearg);

      super_typeparam = ast_sibling(super_typeparam);
    }

    r_sub = reify(sub, sub, sub_typeparams, typeargs);
    ast_free_unattached(typeargs);
  }

  bool ok = is_reified_fun_sub_fun(r_sub, super, isub, isuper);

  if(r_sub != sub)
    ast_free_unattached(r_sub);

  return ok;
}
Esempio n. 11
0
static bool is_fun_sub_fun(ast_t* sub, ast_t* super, errorframe_t* errors)
{
  token_id tsub = ast_id(sub);
  token_id tsuper = ast_id(super);

  switch(tsub)
  {
    case TK_NEW:
    case TK_BE:
    case TK_FUN:
      break;

    default:
      return false;
  }

  switch(tsuper)
  {
    case TK_NEW:
    case TK_BE:
    case TK_FUN:
      break;

    default:
      return false;
  }

  AST_GET_CHILDREN(sub, sub_cap, sub_id, sub_typeparams, sub_params);
  AST_GET_CHILDREN(super, super_cap, super_id, super_typeparams, super_params);

  // Must have the same name.
  if(ast_name(sub_id) != ast_name(super_id))
  {
    if(errors != NULL)
    {
      ast_error_frame(errors, sub,
        "method %s is not a subtype of method %s: they have different names",
        ast_name(sub_id), ast_name(super_id));
    }

    return false;
  }

  // A constructor can only be a subtype of a constructor.
  if(((tsub == TK_NEW) || (tsuper == TK_NEW)) && (tsub != tsuper))
  {
    if(errors != NULL)
      ast_error_frame(errors, sub,
        "only a constructor can be a subtype of a constructor");

    return false;
  }

  // Must have the same number of type parameters.
  if(ast_childcount(sub_typeparams) != ast_childcount(super_typeparams))
  {
    if(errors != NULL)
      ast_error_frame(errors, sub,
        "methods have a different number of type parameters");

    return false;
  }

  // Must have the same number of parameters.
  if(ast_childcount(sub_params) != ast_childcount(super_params))
  {
    if(errors != NULL)
      ast_error_frame(errors, sub,
        "methods have a different number of parameters");

    return false;
  }

  ast_t* r_sub = sub;

  if(ast_id(super_typeparams) != TK_NONE)
  {
    // Reify sub with the type parameters of super.
    BUILD(typeargs, super_typeparams, NODE(TK_TYPEARGS));
    ast_t* super_typeparam = ast_child(super_typeparams);

    while(super_typeparam != NULL)
    {
      AST_GET_CHILDREN(super_typeparam, super_id, super_constraint);

      BUILD(typearg, super_typeparam,
        NODE(TK_TYPEPARAMREF, TREE(super_id) NONE NONE));

      ast_t* def = ast_get(super_typeparam, ast_name(super_id), NULL);
      ast_setdata(typearg, def);
      typeparam_set_cap(typearg);
      ast_append(typeargs, typearg);

      super_typeparam = ast_sibling(super_typeparam);
    }

    r_sub = reify(sub, sub_typeparams, typeargs);
    ast_free_unattached(typeargs);
  }

  bool ok = is_reified_fun_sub_fun(r_sub, super, errors);

  if(r_sub != sub)
    ast_free_unattached(r_sub);

  return ok;
}
Esempio n. 12
0
static ast_result_t sugar_object(pass_opt_t* opt, ast_t** astp)
{
  typecheck_t* t = &opt->check;
  ast_t* ast = *astp;
  ast_result_t r = AST_OK;

  AST_GET_CHILDREN(ast, cap, provides, members);
  ast_t* c_id = ast_from_string(ast, package_hygienic_id(t));

  ast_t* t_params;
  ast_t* t_args;
  collect_type_params(ast, &t_params, &t_args);

  // Create a new anonymous type.
  BUILD(def, ast,
    NODE(TK_CLASS, AST_SCOPE
      TREE(c_id)
      TREE(t_params)
      NONE
      TREE(provides)
      NODE(TK_MEMBERS)
      NONE
      NONE));

  // We will have a create method in the type.
  BUILD_NO_DEBUG(create, members,
    NODE(TK_NEW, AST_SCOPE
      NONE
      ID("create")
      NONE
      NONE
      NONE
      NONE
      NODE(TK_SEQ)
      NONE
      NONE));

  BUILD(type_ref, ast, NODE(TK_REFERENCE, TREE(c_id)));

  if(ast_id(t_args) != TK_NONE)
  {
    // Need to add type args to our type reference
    BUILD(t, ast, NODE(TK_QUALIFY, TREE(type_ref) TREE(t_args)));
    type_ref = t;
  }

  ast_free_unattached(t_args);

  // We will replace object..end with $0.create(...)
  BUILD(call, ast,
    NODE(TK_CALL,
      NONE
      NONE
      NODE(TK_DOT,
        TREE(type_ref)
        ID("create"))));

  ast_t* create_params = ast_childidx(create, 3);
  ast_t* create_body = ast_childidx(create, 6);
  ast_t* call_args = ast_child(call);
  ast_t* class_members = ast_childidx(def, 4);
  ast_t* member = ast_child(members);

  bool has_fields = false;
  bool has_behaviours = false;

  while(member != NULL)
  {
    switch(ast_id(member))
    {
      case TK_FVAR:
      case TK_FLET:
      case TK_EMBED:
      {
        AST_GET_CHILDREN(member, id, type, init);
        ast_t* p_id = ast_from_string(id, package_hygienic_id(t));

        // The field is: var/let/embed id: type
        BUILD(field, member,
          NODE(ast_id(member),
            TREE(id)
            TREE(type)
            NONE
            NONE));

        // The param is: $0: type
        BUILD(param, member,
          NODE(TK_PARAM,
            TREE(p_id)
            TREE(type)
            NONE));

        // The arg is: $seq init
        BUILD(arg, init,
          NODE(TK_SEQ,
            TREE(init)));

        // The body of create contains: id = consume $0
        BUILD_NO_DEBUG(assign, init,
          NODE(TK_ASSIGN,
            NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id)))
            NODE(TK_REFERENCE, TREE(id))));

        ast_setid(create_params, TK_PARAMS);
        ast_setid(call_args, TK_POSITIONALARGS);

        ast_append(class_members, field);
        ast_append(create_params, param);
        ast_append(create_body, assign);
        ast_append(call_args, arg);

        has_fields = true;
        break;
      }

      case TK_BE:
        // If we have behaviours, we must be an actor.
        ast_append(class_members, member);
        has_behaviours = true;
        break;

      default:
        // Keep all the methods as they are.
        ast_append(class_members, member);
        break;
    }

    member = ast_sibling(member);
  }

  if(!has_fields)
  {
    // End the constructor with 'true', since it has no parameters.
    BUILD_NO_DEBUG(true_node, ast, NODE(TK_TRUE));
    ast_append(create_body, true_node);
  }

  // Handle capability and whether the anonymous type is a class, primitive or
  // actor.
  token_id cap_id = ast_id(cap);

  if(has_behaviours)
  {
    // Change the type to an actor.
    ast_setid(def, TK_ACTOR);

    if(cap_id != TK_NONE && cap_id != TK_TAG)
    {
      ast_error(cap, "object literals with behaviours are actors and so must "
        "have tag capability");
      r = AST_ERROR;
    }
  }
  else if(!has_fields && (cap_id == TK_NONE || cap_id == TK_TAG ||
    cap_id == TK_BOX || cap_id == TK_VAL))
  {
    // Change the type from a class to a primitive.
    ast_setid(def, TK_PRIMITIVE);
  }
  else
  {
    // Type is a class, set the create capability as specified
    ast_setid(ast_child(create), cap_id);
  }

  // Add the create function at the end.
  ast_append(class_members, create);

  // Replace object..end with $0.create(...)
  ast_replace(astp, call);

  // Add new type to current module and bring it up to date with passes.
  ast_t* module = ast_nearest(ast, TK_MODULE);
  ast_append(module, def);

  if(!ast_passes_type(&def, opt))
    return AST_FATAL;

  // Sugar the call.
  if(!ast_passes_subtree(astp, opt, PASS_SUGAR))
    return AST_FATAL;

  return r;
}
Esempio n. 13
0
ast_t* viewpoint_type(ast_t* l_type, ast_t* r_type)
{
  int upper = VIEW_UPPER_NO;

  switch(ast_id(r_type))
  {
    case TK_UNIONTYPE:
    case TK_ISECTTYPE:
    case TK_TUPLETYPE:
    {
      // Adapt each element.
      ast_t* type = ast_from(r_type, ast_id(r_type));
      ast_t* child = ast_child(r_type);

      while(child != NULL)
      {
        ast_append(type, viewpoint_type(l_type, child));
        child = ast_sibling(child);
      }

      return type;
    }

    case TK_NOMINAL:
    case TK_TYPEPARAMREF:
    {
      ast_t* cap = cap_fetch(r_type);

      switch(ast_id(cap))
      {
        case TK_ISO:
        case TK_TRN:
        case TK_REF:
        case TK_BOX:
        case TK_ISO_BIND:
        case TK_TRN_BIND:
        case TK_REF_BIND:
        case TK_BOX_BIND:
          // A known refcap on the right can be compacted.
          upper = VIEW_UPPER_YES;
          break;

        case TK_VAL:
        case TK_TAG:
        case TK_CAP_SHARE:
        case TK_VAL_BIND:
        case TK_TAG_BIND:
        case TK_CAP_SHARE_BIND:
          // No refcap on the left modifies these.
          upper = VIEW_UPPER_FORCE;
          break;

        default: {}
      }
      break;
    }

    case TK_ARROW:
      break;

    default:
      assert(0);
      return NULL;
  }

  switch(ast_id(l_type))
  {
    case TK_NOMINAL:
    case TK_TYPEPARAMREF:
    {
      ast_t* cap = cap_fetch(l_type);

      switch(ast_id(cap))
      {
        case TK_REF:
        case TK_REF_BIND:
          // ref->T = T
          return ast_dup(r_type);

        case TK_CAP_SEND:
        case TK_CAP_SHARE:
        case TK_CAP_READ:
        case TK_CAP_ALIAS:
        case TK_CAP_ANY:
        case TK_CAP_SEND_BIND:
        case TK_CAP_SHARE_BIND:
        case TK_CAP_READ_BIND:
        case TK_CAP_ALIAS_BIND:
        case TK_CAP_ANY_BIND:
          // Don't compact through an unknown refcap.
          if(upper == VIEW_UPPER_YES)
            upper = VIEW_UPPER_NO;
          break;

        default: {}
      }
      break;
    }

    case TK_THISTYPE:
      if(upper == VIEW_UPPER_YES)
        upper = VIEW_UPPER_NO;
      break;

    case TK_ISO:
    case TK_TRN:
    case TK_REF:
    case TK_VAL:
    case TK_BOX:
    case TK_TAG:
      break;

    case TK_ARROW:
    {
      // (T1->T2)->T3 --> T1->(T2->T3)
      AST_GET_CHILDREN(l_type, left, right);
      ast_t* r_right = viewpoint_type(right, r_type);
      return viewpoint_type(left, r_right);
    }

    default:
      assert(0);
      return NULL;
  }

  BUILD(arrow, l_type,
    NODE(TK_ARROW, TREE(ast_dup(l_type)) TREE(ast_dup(r_type))));

  if(upper != VIEW_UPPER_NO)
  {
    ast_t* arrow_upper = viewpoint_upper(arrow);

    if(arrow_upper == NULL)
      return arrow;

    if(arrow != arrow_upper)
    {
      ast_free_unattached(arrow);
      arrow = arrow_upper;
    }
  }

  return arrow;
}
Esempio n. 14
0
File: match.c Progetto: mgist/ponyc
static bool is_valid_pattern(pass_opt_t* opt, ast_t* pattern)
{
  if(ast_id(pattern) == TK_NONE)
  {
    ast_settype(pattern, ast_from(pattern, TK_DONTCARE));
    return true;
  }

  ast_t* pattern_type = ast_type(pattern);

  if(is_control_type(pattern_type))
  {
    ast_error(pattern, "not a matchable pattern");
    return false;
  }

  switch(ast_id(pattern))
  {
    case TK_VAR:
    case TK_LET:
    {
      // Disallow capturing tuples.
      AST_GET_CHILDREN(pattern, id, capture_type);

      if(ast_id(capture_type) == TK_TUPLETYPE)
      {
        ast_error(capture_type,
          "can't capture a tuple, change this into a tuple of capture "
          "expressions");

        return false;
      }

      // Set the pattern type to be the capture type.
      ast_settype(pattern, capture_type);
      return true;
    }

    case TK_TUPLE:
    {
      ast_t* pattern_child = ast_child(pattern);

      // Treat a one element tuple as a normal expression.
      if(ast_sibling(pattern_child) == NULL)
      {
        bool ok = is_valid_pattern(opt, pattern_child);
        ast_settype(pattern, ast_type(pattern_child));
        return ok;
      }

      // Check every element pairwise.
      ast_t* pattern_type = ast_from(pattern, TK_TUPLETYPE);
      bool ok = true;

      while(pattern_child != NULL)
      {
        if(!is_valid_pattern(opt, pattern_child))
          ok = false;

        ast_append(pattern_type, ast_type(pattern_child));
        pattern_child = ast_sibling(pattern_child);
      }

      ast_settype(pattern, pattern_type);
      return ok;
    }

    case TK_SEQ:
    {
      // Patterns cannot contain sequences.
      ast_t* child = ast_child(pattern);
      ast_t* next = ast_sibling(child);

      if(next != NULL)
      {
        ast_error(next, "expression in patterns cannot be sequences");
        return false;
      }

      bool ok = is_valid_pattern(opt, child);
      ast_settype(pattern, ast_type(child));
      return ok;
    }

    case TK_DONTCARE:
      // It's always ok not to care.
      return true;

    default:
    {
      // Structural equality, pattern.eq(match).
      ast_t* fun = lookup(opt, pattern, pattern_type, stringtab("eq"));

      if(fun == NULL)
      {
        ast_error(pattern,
          "this pattern element doesn't support structural equality");

        return false;
      }

      if(ast_id(fun) != TK_FUN)
      {
        ast_error(pattern, "eq is not a function on this pattern element");
        ast_error(fun, "definition of eq is here");
        ast_free_unattached(fun);
        return false;
      }

      AST_GET_CHILDREN(fun, cap, id, typeparams, params, result, partial);
      bool ok = true;

      if(ast_id(typeparams) != TK_NONE)
      {
        ast_error(pattern, "polymorphic eq not supported in pattern matching");
        ok = false;
      }

      if(!is_bool(result))
      {
        ast_error(pattern, "eq must return Bool when pattern matching");
        ok = false;
      }

      if(ast_id(partial) != TK_NONE)
      {
        ast_error(pattern, "eq cannot be partial when pattern matching");
        ok = false;
      }

      ast_t* param = ast_child(params);

      if(param == NULL || ast_sibling(param) != NULL)
      {
        ast_error(pattern,
          "eq must take a single argument when pattern matching");

        ok = false;
      } else {
        AST_GET_CHILDREN(param, param_id, param_type);
        ast_settype(pattern, param_type);
      }

      ast_free_unattached(fun);
      return ok;
    }
  }

  assert(0);
  return false;
}
Esempio n. 15
0
static void trace_dynamic_tuple(compile_t* c, LLVMValueRef ctx,
  LLVMValueRef ptr, LLVMValueRef desc, ast_t* type, ast_t* orig, ast_t* tuple)
{
  // Build a "don't care" type of our cardinality.
  size_t cardinality = ast_childcount(type);
  ast_t* dontcare = ast_from(type, TK_TUPLETYPE);

  for(size_t i = 0; i < cardinality; i++)
    ast_append(dontcare, ast_from(type, TK_DONTCARE));

  // Replace our type in the tuple type with the "don't care" type.
  bool in_tuple = (tuple != NULL);

  if(in_tuple)
    ast_swap(type, dontcare);
  else
    tuple = dontcare;

  // If the original type is a subtype of the test type, then we are always
  // the correct cardinality. Otherwise, we need to dynamically check
  // cardinality.
  LLVMBasicBlockRef is_true = codegen_block(c, "");
  LLVMBasicBlockRef is_false = codegen_block(c, "");

  if(!is_subtype(orig, tuple, NULL))
  {
    LLVMValueRef dynamic_count = gendesc_fieldcount(c, desc);
    LLVMValueRef static_count = LLVMConstInt(c->i32, cardinality, false);
    LLVMValueRef test = LLVMBuildICmp(c->builder, LLVMIntEQ, static_count,
      dynamic_count, "");

    // Skip if not the right cardinality.
    LLVMBuildCondBr(c->builder, test, is_true, is_false);
  } else {
    LLVMBuildBr(c->builder, is_true);
  }

  LLVMPositionBuilderAtEnd(c->builder, is_true);

  size_t index = 0;
  ast_t* child = ast_child(type);
  ast_t* dc_child = ast_child(dontcare);

  while(child != NULL)
  {
    switch(trace_type(child))
    {
      case TRACE_PRIMITIVE:
        // Skip this element.
        break;

      case TRACE_ACTOR:
      case TRACE_KNOWN:
      case TRACE_UNKNOWN:
      case TRACE_KNOWN_VAL:
      case TRACE_UNKNOWN_VAL:
      case TRACE_TAG:
      case TRACE_TAG_OR_ACTOR:
      case TRACE_DYNAMIC:
      {
        // If we are (A, B), turn (_, _) into (A, _).
        ast_t* swap = ast_dup(child);
        ast_swap(dc_child, swap);

        // Create a next block.
        LLVMBasicBlockRef next_block = codegen_block(c, "");

        // Load the object from the tuple field.
        LLVMValueRef field_info = gendesc_fieldinfo(c, desc, index);
        LLVMValueRef object = gendesc_fieldload(c, ptr, field_info);

        // Trace dynamic, even if the tuple thinks the field isn't dynamic.
        trace_dynamic(c, ctx, object, swap, orig, tuple, next_block);

        // Continue into the next block.
        LLVMBuildBr(c->builder, next_block);
        LLVMPositionBuilderAtEnd(c->builder, next_block);

        // Restore (A, _) to (_, _).
        ast_swap(swap, dc_child);
        ast_free_unattached(swap);
        break;
      }

      case TRACE_TUPLE:
      {
        // If we are (A, B), turn (_, _) into (A, _).
        ast_t* swap = ast_dup(child);
        ast_swap(dc_child, swap);

        // Get a pointer to the unboxed tuple and it's descriptor.
        LLVMValueRef field_info = gendesc_fieldinfo(c, desc, index);
        LLVMValueRef field_ptr = gendesc_fieldptr(c, ptr, field_info);
        LLVMValueRef field_desc = gendesc_fielddesc(c, field_info);

        // Trace the tuple dynamically.
        trace_dynamic_tuple(c, ctx, field_ptr, field_desc, swap, orig, tuple);

        // Restore (A, _) to (_, _).
        ast_swap(swap, dc_child);
        ast_free_unattached(swap);
        break;
      }

      default: {}
    }

    index++;
    child = ast_sibling(child);
    dc_child = ast_sibling(dc_child);
  }

  // Restore the tuple type.
  if(in_tuple)
    ast_swap(dontcare, type);

  ast_free_unattached(dontcare);

  // Continue with other possible tracings.
  LLVMBuildBr(c->builder, is_false);
  LLVMPositionBuilderAtEnd(c->builder, is_false);
}
Esempio n. 16
0
// Handle a set of case methods, starting at the given method.
// Generate wrapper and worker methods and append them to the given list.
// Returns: true on success, false on failure.
static bool sugar_case_method(ast_t* first_case_method, ast_t* members,
  const char* name, typecheck_t* t)
{
  assert(first_case_method != NULL);
  assert(members != NULL);
  assert(name != NULL);
  assert(t != NULL);

  ast_t* wrapper = make_match_wrapper(first_case_method);

  if(wrapper == NULL)
    return false;

  ast_t* match_cases = ast_from(first_case_method, TK_CASES);
  ast_scope(match_cases);

  // Process all methods with the same name.
  // We need to remove processed methods. However, removing nodes from a list
  // we're iterating over gets rather complex. Instead we mark nodes and remove
  // them all in one sweep later.
  for(ast_t* p = first_case_method; p != NULL; p = ast_sibling(p))
  {
    const char* p_name = ast_name(ast_childidx(p, 1));

    if(p_name == name)
    {
      // This method is in the case set.
      ast_t* case_ast = add_case_method(wrapper, p);

      if(case_ast == NULL)
      {
        ast_free(wrapper);
        ast_free(match_cases);
        return false;
      }

      ast_append(match_cases, case_ast);
      ast_setid(p, TK_NONE);  // Mark for removal.
    }
  }

  // Check params and build match operand, worker parameters and worker call
  // arguments.
  ast_t* match_params = ast_childidx(wrapper, 3);
  ast_t* worker_params = ast_from(match_params, TK_PARAMS);
  ast_t* call_args = ast_from(match_params, TK_POSITIONALARGS);
  ast_t* match_operand = build_params(match_params, worker_params, call_args,
    name, t);

  if(match_operand == NULL)
  {
    ast_free(wrapper);
    ast_free(match_cases);
    ast_free(worker_params);
    ast_free(call_args);
    return false;
  }

  // Complete wrapper function and add to containing entity.
  const char* worker_name = package_hygienic_id(t);
  ast_t* wrapper_body = ast_childidx(wrapper, 6);
  ast_t* wrapper_call;
  ast_t* call_t_args = ast_from(wrapper, TK_NONE);
  assert(ast_id(wrapper_body) == TK_SEQ);
  assert(ast_childcount(wrapper_body) == 0);

  build_t_params(ast_childidx(wrapper, 2), call_t_args);

  if(ast_id(call_t_args) == TK_NONE)
  {
    // No type args needed.
    ast_free(call_t_args);
    BUILD(tmp, wrapper_body,
      NODE(TK_CALL,
        TREE(call_args)
        NONE
        NODE(TK_REFERENCE, ID(worker_name))));

    wrapper_call = tmp;
  }
  else
  {
    // Type args needed on call.
    BUILD(tmp, wrapper_body,
      NODE(TK_CALL,
        TREE(call_args)
        NONE
        NODE(TK_QUALIFY,
          NODE(TK_REFERENCE, ID(worker_name))
          TREE(call_t_args))));

    wrapper_call = tmp;
  }

  ast_append(wrapper_body, wrapper_call);
  ast_append(members, wrapper);

  // Make worker function and add to containing entity.
  AST_GET_CHILDREN(wrapper, cap, wrapper_id, t_params, wrapper_params,
    ret_type, error);

  if(ast_id(wrapper) == TK_BE)
    cap = ast_from(cap, TK_REF);

  BUILD(worker, wrapper,
    NODE(TK_FUN, AST_SCOPE
      TREE(cap)
      ID(worker_name)
      TREE(t_params)
      TREE(worker_params)
      TREE(ret_type)
      TREE(error)
      NODE(TK_SEQ,
        NODE(TK_MATCH, AST_SCOPE
          NODE(TK_SEQ, TREE(match_operand))
          TREE(match_cases)
          NONE)) // Else clause.
      NONE    // Doc string.
      NONE)); // Guard.

  ast_append(members, worker);
  return true;
}
Esempio n. 17
0
bool expr_typeref(pass_opt_t* opt, ast_t** astp)
{
  ast_t* ast = *astp;
  assert(ast_id(ast) == TK_TYPEREF);
  ast_t* type = ast_type(ast);

  if(is_typecheck_error(type))
    return false;

  switch(ast_id(ast_parent(ast)))
  {
    case TK_QUALIFY:
      // Doesn't have to be valid yet.
      break;

    case TK_DOT:
      // Has to be valid.
      if(!expr_nominal(opt, &type))
      {
        ast_settype(ast, ast_from(type, TK_ERRORTYPE));
        ast_free_unattached(type);
        return false;
      }
      break;

    case TK_CALL:
    {
      // Has to be valid.
      if(!expr_nominal(opt, &type))
      {
        ast_settype(ast, ast_from(type, TK_ERRORTYPE));
        ast_free_unattached(type);
        return false;
      }

      // Transform to a default constructor.
      ast_t* dot = ast_from(ast, TK_DOT);
      ast_add(dot, ast_from_string(ast, "create"));
      ast_swap(ast, dot);
      *astp = dot;
      ast_add(dot, ast);

      if(!expr_dot(opt, astp))
      {
        ast_settype(ast, ast_from(type, TK_ERRORTYPE));
        ast_free_unattached(type);
        return false;
      }

      ast_t* ast = *astp;

      // If the default constructor has no parameters, transform to an apply
      // call.
      if((ast_id(ast) == TK_NEWREF) || (ast_id(ast) == TK_NEWBEREF))
      {
        type = ast_type(ast);

        if(is_typecheck_error(type))
          return false;

        assert(ast_id(type) == TK_FUNTYPE);
        AST_GET_CHILDREN(type, cap, typeparams, params, result);

        if(ast_id(params) == TK_NONE)
        {
          // Add a call node.
          ast_t* call = ast_from(ast, TK_CALL);
          ast_add(call, ast_from(call, TK_NONE)); // Named
          ast_add(call, ast_from(call, TK_NONE)); // Positional
          ast_swap(ast, call);
          ast_append(call, ast);

          if(!expr_call(opt, &call))
          {
            ast_settype(ast, ast_from(type, TK_ERRORTYPE));
            ast_free_unattached(type);
            return false;
          }

          // Add a dot node.
          ast_t* apply = ast_from(call, TK_DOT);
          ast_add(apply, ast_from_string(call, "apply"));
          ast_swap(call, apply);
          ast_add(apply, call);

          if(!expr_dot(opt, &apply))
          {
            ast_settype(ast, ast_from(type, TK_ERRORTYPE));
            ast_free_unattached(type);
            return false;
          }
        }
      }

      return true;
    }

    default:
    {
      // Has to be valid.
      if(!expr_nominal(opt, &type))
      {
        ast_settype(ast, ast_from(type, TK_ERRORTYPE));
        ast_free_unattached(type);
        return false;
      }

      // Transform to a default constructor.
      ast_t* dot = ast_from(ast, TK_DOT);
      ast_add(dot, ast_from_string(ast, "create"));
      ast_swap(ast, dot);
      ast_add(dot, ast);

      // Call the default constructor with no arguments.
      ast_t* call = ast_from(ast, TK_CALL);
      ast_swap(dot, call);
      ast_add(call, dot); // Receiver comes last.
      ast_add(call, ast_from(ast, TK_NONE)); // Named args.
      ast_add(call, ast_from(ast, TK_NONE)); // Positional args.

      *astp = call;

      if(!expr_dot(opt, &dot))
      {
        ast_settype(ast, ast_from(type, TK_ERRORTYPE));
        ast_free_unattached(type);
        return false;
      }

      if(!expr_call(opt, astp))
      {
        ast_settype(ast, ast_from(type, TK_ERRORTYPE));
        ast_free_unattached(type);
        return false;
      }
      break;
    }
  }

  return true;
}
Esempio n. 18
0
static bool is_valid_pattern(pass_opt_t* opt, ast_t* pattern)
{
  if(ast_id(pattern) == TK_NONE)
  {
    ast_settype(pattern, ast_from(pattern, TK_DONTCARE));
    return true;
  }

  ast_t* pattern_type = ast_type(pattern);

  if(is_control_type(pattern_type))
  {
    ast_error(opt->check.errors, pattern, "not a matchable pattern");
    return false;
  }

  switch(ast_id(pattern))
  {
    case TK_MATCH_CAPTURE:
      // Captures are always OK.
      return true;

    case TK_TUPLE:
    {
      ast_t* pattern_child = ast_child(pattern);

      // Treat a one element tuple as a normal expression.
      if(ast_sibling(pattern_child) == NULL)
      {
        bool ok = is_valid_pattern(opt, pattern_child);
        ast_settype(pattern, ast_type(pattern_child));
        return ok;
      }

      // Check every element pairwise.
      ast_t* pattern_type = ast_from(pattern, TK_TUPLETYPE);
      bool ok = true;

      while(pattern_child != NULL)
      {
        if(!is_valid_pattern(opt, pattern_child))
          ok = false;

        ast_append(pattern_type, ast_type(pattern_child));
        pattern_child = ast_sibling(pattern_child);
      }

      ast_settype(pattern, pattern_type);
      return ok;
    }

    case TK_SEQ:
      if(ast_childcount(pattern) == 1)  // This may be a just a capture.
        return is_valid_pattern(opt, ast_child(pattern));

      // Treat this like other nodes.
      break;

    case TK_DONTCARE:
      // It's always ok not to care.
      return true;

    default:
      break;
  }

  // Structural equality, pattern.eq(match).
  ast_t* fun = lookup(opt, pattern, pattern_type, stringtab("eq"));

  if(fun == NULL)
  {
    ast_error(opt->check.errors, pattern,
      "this pattern element doesn't support structural equality");

    return false;
  }

  if(ast_id(fun) != TK_FUN)
  {
    ast_error(opt->check.errors, pattern,
      "eq is not a function on this pattern element");
    ast_error_continue(opt->check.errors, fun,
      "definition of eq is here");
    ast_free_unattached(fun);
    return false;
  }

  AST_GET_CHILDREN(fun, cap, id, typeparams, params, result, partial);
  bool ok = true;

  if(ast_id(typeparams) != TK_NONE)
  {
    ast_error(opt->check.errors, pattern,
      "polymorphic eq not supported in pattern matching");
    ok = false;
  }

  if(!is_bool(result))
  {
    ast_error(opt->check.errors, pattern,
      "eq must return Bool when pattern matching");
    ok = false;
  }

  if(ast_id(partial) != TK_NONE)
  {
    ast_error(opt->check.errors, pattern,
      "eq cannot be partial when pattern matching");
    ok = false;
  }

  ast_t* r_type = set_cap_and_ephemeral(pattern_type, ast_id(cap), TK_NONE);
  ast_t* a_type = alias(pattern_type);
  errorframe_t info = NULL;

  if(!is_subtype(a_type, r_type, &info, opt))
  {
    errorframe_t frame = NULL;
    ast_error_frame(&frame, pattern, "eq cannot be called on this pattern");
    errorframe_append(&frame, &info);
    errorframe_report(&frame, opt->check.errors);
    ok = false;
  }

  ast_t* param = ast_child(params);

  if(param == NULL || ast_sibling(param) != NULL)
  {
    ast_error(opt->check.errors, pattern,
      "eq must take a single argument when pattern matching");

    ok = false;
  } else {
    AST_GET_CHILDREN(param, param_id, param_type);
    ast_settype(pattern, param_type);
  }

  ast_free_unattached(r_type);
  ast_free_unattached(a_type);
  ast_free_unattached(fun);
  return ok;
}