Пример #1
0
ast_t* type_isect_fun(ast_t* a, ast_t* b)
{
  token_id ta = ast_id(a);
  token_id tb = ast_id(b);

  if(((ta == TK_NEW) || (tb == TK_NEW)) && (ta != tb))
    return NULL;

  AST_GET_CHILDREN(a, a_cap, a_id, a_typeparams, a_params, a_result, a_throw);
  AST_GET_CHILDREN(b, b_cap, b_id, b_typeparams, b_params, b_result, b_throw);

  // Must have the same name.
  if(ast_name(a_id) != ast_name(b_id))
    return NULL;

  // Must have the same number of type parameters and parameters.
  if((ast_childcount(a_typeparams) != ast_childcount(b_typeparams)) ||
    (ast_childcount(a_params) != ast_childcount(b_params)))
    return NULL;

  // Contravariant receiver cap.
  token_id tcap;
  token_id a_tcap = ast_id(a_cap);
  token_id b_tcap = ast_id(b_cap);

  if(is_cap_sub_cap(b_tcap, TK_NONE, a_tcap, TK_NONE))
    tcap = a_tcap;
  else if(is_cap_sub_cap(a_tcap, TK_NONE, b_tcap, TK_NONE))
    tcap = b_tcap;
  else
    tcap = TK_BOX;

  // Result is the intersection of the results.
  ast_t* result = type_isect(a_result, b_result);

  // Covariant throws.
  token_id throws;

  if((ast_id(a_throw) == TK_NONE) || (ast_id(b_throw) == TK_NONE))
    throws = TK_NONE;
  else
    throws = TK_QUESTION;

  BUILD(fun, a,
    NODE(tcap)
    TREE(a_id)
    NODE(TK_TYPEPARAMS)
    NODE(TK_PARAMS)
    TREE(result)
    NODE(throws)
    );

  // TODO: union typeparams and params
  // handling typeparam names is tricky
  return fun;
}
Пример #2
0
bool is_sub_cap_and_ephemeral(ast_t* sub, ast_t* super)
{
  ast_t* sub_cap = fetch_cap(sub);
  ast_t* sub_eph = ast_sibling(sub_cap);
  ast_t* super_cap = fetch_cap(super);
  ast_t* super_eph = ast_sibling(super_cap);

  token_id t_sub_cap = ast_id(sub_cap);
  token_id t_sub_eph = ast_id(sub_eph);
  token_id t_super_cap = ast_id(super_cap);
  token_id t_super_eph = ast_id(super_eph);
  token_id t_alt_cap = t_sub_cap;

  // Adjusted for borrowing.
  if(t_sub_eph == TK_BORROWED)
  {
    switch(t_alt_cap)
    {
      case TK_ISO: t_alt_cap = TK_TAG; break;
      case TK_TRN: t_alt_cap = TK_BOX; break;
      case TK_ANY_GENERIC: t_alt_cap = TK_TAG; break;
      default: {}
    }
  }

  switch(t_super_eph)
  {
    case TK_EPHEMERAL:
      // Sub must be ephemeral if t_sub_cap != t_alt_cap. Otherwise, we don't
      // have to be ephemeral, since, for example, a ref can be a subtype of
      // a ref^, but an iso is not a subtype of an iso^.
      if((t_sub_cap != t_alt_cap) && (t_sub_eph != TK_EPHEMERAL))
        return false;

      // Capability must be a sub-capability.
      return is_cap_sub_cap(t_sub_cap, t_super_cap);

    case TK_NONE:
      // Check the adjusted capability.
      return is_cap_sub_cap(t_alt_cap, t_super_cap);

    case TK_BORROWED:
      // Borrow a capability.
      if(t_sub_cap == t_super_cap)
        return true;

      // Or alias a capability.
      return is_cap_sub_cap(t_alt_cap, t_super_cap);

    default: {}
  }

  assert(0);
  return false;
}
Пример #3
0
static matchtype_t is_trait_match_trait(ast_t* operand, ast_t* pattern,
  errorframe_t* errorf, bool report_reject, pass_opt_t* opt)
{
  (void)report_reject;
  (void)opt;
  AST_GET_CHILDREN(operand, o_pkg, o_id, o_typeargs, o_cap, o_eph);
  AST_GET_CHILDREN(pattern, p_pkg, p_id, p_typeargs, p_cap, p_eph);

  // If the operand refcap can't match the pattern refcap, deny the match.
  if(!is_cap_sub_cap(ast_id(o_cap), ast_id(o_eph),
    ast_id(p_cap), ast_id(p_eph)))
  {
    if(errorf != NULL)
    {
      ast_error_frame(errorf, pattern,
        "matching %s with %s could violate capabilities: "
        "%s%s isn't a subcap of %s%s",
        ast_print_type(operand), ast_print_type(pattern),
        ast_print_type(o_cap), ast_print_type(o_eph),
        ast_print_type(p_cap), ast_print_type(p_eph));
    }

    return MATCHTYPE_DENY;
  }

  // Otherwise, accept the match.
  return MATCHTYPE_ACCEPT;
}
Пример #4
0
bool is_sub_cap_and_ephemeral(ast_t* sub, ast_t* super)
{
  ast_t* sub_cap = fetch_cap(sub);
  ast_t* sub_eph = ast_sibling(sub_cap);
  ast_t* super_cap = fetch_cap(super);
  ast_t* super_eph = ast_sibling(super_cap);

  return is_cap_sub_cap(ast_id(sub_cap), ast_id(sub_eph), ast_id(super_cap),
    ast_id(super_eph));
}
Пример #5
0
static matchtype_t is_nominal_match_entity(ast_t* operand, ast_t* pattern,
  errorframe_t* errorf, bool report_reject, pass_opt_t* opt)
{
  AST_GET_CHILDREN(operand, o_pkg, o_id, o_typeargs, o_cap, o_eph);
  AST_GET_CHILDREN(pattern, p_pkg, p_id, p_typeargs, p_cap, p_eph);

  // We say the pattern provides the operand if it is a subtype without taking
  // capabilities into account.
  bool provides = is_subtype_ignore_cap(pattern, operand, NULL, opt);

  // If the pattern doesn't provide the operand, reject the match.
  if(!provides)
  {
    if((errorf != NULL) && report_reject)
    {
      ast_error_frame(errorf, pattern,
        "%s cannot match %s: %s isn't a subtype of %s",
        ast_print_type(operand), ast_print_type(pattern),
        ast_print_type_no_cap(pattern), ast_print_type_no_cap(operand));
    }

    return MATCHTYPE_REJECT;
  }

  // If the operand does provide the pattern, but the operand refcap can't
  // match the pattern refcap, deny the match.
  if(!is_cap_sub_cap(ast_id(o_cap), ast_id(o_eph),
    ast_id(p_cap), ast_id(p_eph)))
  {
    if(errorf != NULL)
    {
      ast_error_frame(errorf, pattern,
        "matching %s with %s could violate capabilities: "
        "%s%s isn't a subcap of %s%s",
        ast_print_type(operand), ast_print_type(pattern),
        ast_print_type(o_cap), ast_print_type(o_eph),
        ast_print_type(p_cap), ast_print_type(p_eph));
    }

    return MATCHTYPE_DENY;
  }

  // Otherwise, accept the match.
  return MATCHTYPE_ACCEPT;
}
Пример #6
0
static bool is_sub_cap_and_eph(ast_t* sub, ast_t* super, errorframe_t* errors)
{
  ast_t* sub_cap = cap_fetch(sub);
  ast_t* sub_eph = ast_sibling(sub_cap);
  ast_t* super_cap = cap_fetch(super);
  ast_t* super_eph = ast_sibling(super_cap);

  if(!is_cap_sub_cap(ast_id(sub_cap), ast_id(sub_eph),
    ast_id(super_cap), ast_id(super_eph)))
  {
    if(errors != NULL)
    {
      ast_error_frame(errors, sub,
        "%s is not a subtype of %s: %s%s is not a subtype of %s%s",
        ast_print_type(sub), ast_print_type(super),
        ast_print_type(sub_cap), ast_print_type(sub_eph),
        ast_print_type(super_cap), ast_print_type(super_eph));
    }

    return false;
  }

  return true;
}
Пример #7
0
static bool is_reified_fun_sub_fun(ast_t* sub, ast_t* super,
  ast_t* isub, ast_t* isuper)
{
  AST_GET_CHILDREN(sub, sub_cap, sub_id, sub_typeparams, sub_params,
    sub_result, sub_throws);

  AST_GET_CHILDREN(super, super_cap, super_id, super_typeparams, super_params,
    super_result, super_throws);

  switch(ast_id(sub))
  {
    case TK_NEW:
    {
      // Covariant receiver.
      if(!is_cap_sub_cap(ast_id(sub_cap), TK_NONE, ast_id(super_cap), TK_NONE))
        return false;

      // Covariant result. Don't check this for interfaces, as it produces
      // an infinite loop. It will be true if the whole interface is provided.
      if(isuper == NULL)
      {
        if(!is_subtype(sub_result, super_result))
         return false;

        // If either result type is a machine word, the other must be as well.
        if(is_machine_word(sub_result) && !is_machine_word(super_result))
          return false;

        if(is_machine_word(super_result) && !is_machine_word(sub_result))
          return false;
      }

      break;
    }

    case TK_FUN:
    case TK_BE:
    {
      // Contravariant receiver.
      if(!is_cap_sub_cap(ast_id(super_cap), TK_NONE, ast_id(sub_cap), TK_NONE))
        return false;

      // Covariant result.
      if(!is_recursive_interface(sub_result, super_result, isub, isuper))
      {
        if(!is_subtype(sub_result, super_result))
          return false;

        // If either result type is a machine word, the other must be as well.
        if(is_machine_word(sub_result) && !is_machine_word(super_result))
          return false;

        if(is_machine_word(super_result) && !is_machine_word(sub_result))
          return false;
      }

      break;
    }

    default: {}
  }

  // Contravariant type parameter constraints.
  ast_t* sub_typeparam = ast_child(sub_typeparams);
  ast_t* super_typeparam = ast_child(super_typeparams);

  while((sub_typeparam != NULL) && (super_typeparam != NULL))
  {
    ast_t* sub_constraint = ast_childidx(sub_typeparam, 1);
    ast_t* super_constraint = ast_childidx(super_typeparam, 1);

    if(!is_recursive_interface(super_constraint, sub_constraint, isub, isuper)
      && !is_subtype(super_constraint, sub_constraint))
      return false;

    sub_typeparam = ast_sibling(sub_typeparam);
    super_typeparam = ast_sibling(super_typeparam);
  }

  // Contravariant parameters.
  ast_t* sub_param = ast_child(sub_params);
  ast_t* super_param = ast_child(super_params);

  while((sub_param != NULL) && (super_param != NULL))
  {
    ast_t* sub_type = ast_childidx(sub_param, 1);
    ast_t* super_type = ast_childidx(super_param, 1);

    // If either parameter type is a machine word, the other must be as well.
    if(is_machine_word(sub_type) && !is_machine_word(super_type))
      return false;

    if(is_machine_word(super_type) && !is_machine_word(sub_type))
      return false;

    // Contravariant: the super type must be a subtype of the sub type.
    if(!is_recursive_interface(super_type, sub_type, isub, isuper) &&
      !is_subtype(super_type, sub_type))
      return false;

    sub_param = ast_sibling(sub_param);
    super_param = ast_sibling(super_param);
  }

  if((sub_param != NULL) || (super_param != NULL))
    return false;

  // Covariant throws.
  if((ast_id(sub_throws) == TK_QUESTION) &&
    (ast_id(super_throws) != TK_QUESTION))
    return false;

  return true;
}
Пример #8
0
static bool is_reified_fun_sub_fun(ast_t* sub, ast_t* super,
  errorframe_t* errors)
{
  AST_GET_CHILDREN(sub, sub_cap, sub_id, sub_typeparams, sub_params,
    sub_result, sub_throws);

  AST_GET_CHILDREN(super, super_cap, super_id, super_typeparams, super_params,
    super_result, super_throws);

  switch(ast_id(sub))
  {
    case TK_NEW:
    {
      // Covariant receiver.
      if(!is_cap_sub_cap(ast_id(sub_cap), TK_NONE, ast_id(super_cap), TK_NONE))
      {
        if(errors != NULL)
        {
          ast_error_frame(errors, sub,
            "%s constructor is not a subtype of %s constructor",
            ast_print_type(sub_cap), ast_print_type(super_cap));
        }

        return false;
      }

      // Covariant result.
      if(!is_subtype(sub_result, super_result, errors))
      {
        if(errors != NULL)
        {
          ast_error_frame(errors, sub,
            "constructor result %s is not a subtype of %s",
            ast_print_type(sub_result), ast_print_type(super_result));
        }

        return false;
      }

      if(!check_machine_words(sub_result, super_result, errors))
        return false;

      break;
    }

    case TK_FUN:
    case TK_BE:
    {
      // Contravariant receiver.
      if(!is_cap_sub_cap(ast_id(super_cap), TK_NONE, ast_id(sub_cap), TK_NONE))
      {
        if(errors != NULL)
        {
          ast_error_frame(errors, sub,
            "%s method is not a subtype of %s method",
            ast_print_type(sub_cap), ast_print_type(super_cap));
        }

        return false;
      }

      // Covariant result.
      if(!is_subtype(sub_result, super_result, errors))
      {
        if(errors != NULL)
        {
          ast_error_frame(errors, sub,
            "method result %s is not a subtype of %s",
            ast_print_type(sub_result), ast_print_type(super_result));
        }

        return false;
      }

      if(!check_machine_words(sub_result, super_result, errors))
        return false;

      break;
    }

    default: {}
  }

  // Contravariant type parameter constraints.
  ast_t* sub_typeparam = ast_child(sub_typeparams);
  ast_t* super_typeparam = ast_child(super_typeparams);

  while((sub_typeparam != NULL) && (super_typeparam != NULL))
  {
    ast_t* sub_constraint = ast_childidx(sub_typeparam, 1);
    ast_t* super_constraint = ast_childidx(super_typeparam, 1);

    if(!is_subtype(super_constraint, sub_constraint, errors))
    {
      if(errors != NULL)
      {
        ast_error_frame(errors, sub,
          "type parameter constraint %s is not a supertype of %s",
          ast_print_type(sub_constraint), ast_print_type(super_constraint));
      }

      return false;
    }

    sub_typeparam = ast_sibling(sub_typeparam);
    super_typeparam = ast_sibling(super_typeparam);
  }

  // Contravariant parameters.
  ast_t* sub_param = ast_child(sub_params);
  ast_t* super_param = ast_child(super_params);

  while((sub_param != NULL) && (super_param != NULL))
  {
    ast_t* sub_type = ast_childidx(sub_param, 1);
    ast_t* super_type = ast_childidx(super_param, 1);

    // Contravariant: the super type must be a subtype of the sub type.
    if(!is_subtype(super_type, sub_type, errors))
    {
      if(errors != NULL)
      {
        ast_error_frame(errors, sub, "parameter %s is not a supertype of %s",
          ast_print_type(sub_type), ast_print_type(super_type));
      }

      return false;
    }

    if(!check_machine_words(sub_type, super_type, errors))
      return false;

    sub_param = ast_sibling(sub_param);
    super_param = ast_sibling(super_param);
  }

  // Covariant throws.
  if((ast_id(sub_throws) == TK_QUESTION) &&
    (ast_id(super_throws) != TK_QUESTION))
  {
    if(errors != NULL)
    {
      ast_error_frame(errors, sub,
        "a partial function is not a subtype of a total function");
    }

    return false;
  }

  return true;
}
Пример #9
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);
}