Example #1
0
bool reify_defaults(ast_t* typeparams, ast_t* typeargs, bool errors,
  pass_opt_t* opt)
{
  assert(
    (ast_id(typeparams) == TK_TYPEPARAMS) ||
    (ast_id(typeparams) == TK_NONE)
    );
  assert(
    (ast_id(typeargs) == TK_TYPEARGS) ||
    (ast_id(typeargs) == TK_NONE)
    );

  size_t param_count = ast_childcount(typeparams);
  size_t arg_count = ast_childcount(typeargs);

  if(param_count == arg_count)
    return true;

  if(param_count < arg_count)
  {
    if(errors)
    {
      ast_error(opt->check.errors, typeargs, "too many type arguments");
      ast_error_continue(opt->check.errors, typeparams, "definition is here");
    }

    return false;
  }

  // Pick up default type arguments if they exist.
  ast_setid(typeargs, TK_TYPEARGS);
  ast_t* typeparam = ast_childidx(typeparams, arg_count);

  while(typeparam != NULL)
  {
    ast_t* defarg = ast_childidx(typeparam, 2);

    if(ast_id(defarg) == TK_NONE)
      break;

    ast_append(typeargs, defarg);
    typeparam = ast_sibling(typeparam);
  }

  if(typeparam != NULL)
  {
    if(errors)
    {
      ast_error(opt->check.errors, typeargs, "not enough type arguments");
      ast_error_continue(opt->check.errors, typeparams, "definition is here");
    }

    return false;
  }

  return true;
}
Example #2
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;
  }
}
Example #3
0
static bool check_partial_ffi_call(pass_opt_t* opt, ast_t* ast)
{
  pony_assert(ast_id(ast) == TK_FFICALL);
  AST_GET_CHILDREN(ast, call_name, call_ret_typeargs, args, named_args,
    call_error);

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

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

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

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

  return true;
}
Example #4
0
static ast_result_t syntax_lambda(pass_opt_t* opt, ast_t* ast)
{
  assert(ast_id(ast) == TK_LAMBDA);
  AST_GET_CHILDREN(ast, cap, name, typeparams, params, captures, type,
    can_error, body);
  switch(ast_id(type))
  {
    case TK_ISO:
    case TK_TRN:
    case TK_REF:
    case TK_VAL:
    case TK_BOX:
    case TK_TAG:
    {
      ast_error(opt->check.errors, type, "lambda return type: %s",
        ast_print_type(type));
      ast_error_continue(opt->check.errors, type, "lambda return type "
        "cannot be capability");
      return AST_ERROR;
    }
    default: {}
  }

  return AST_OK;
}
Example #5
0
bool expr_recover(pass_opt_t* opt, ast_t* ast)
{
  AST_GET_CHILDREN(ast, cap, expr);
  ast_t* type = ast_type(expr);

  if(is_typecheck_error(type))
    return false;

  if(is_type_literal(type))
  {
    make_literal_type(ast);
    return true;
  }

  ast_t* r_type = recover_type(type, ast_id(cap));

  if(r_type == NULL)
  {
    ast_error(opt->check.errors, ast, "can't recover to this capability");
    ast_error_continue(opt->check.errors, expr, "expression type is %s",
      ast_print_type(type));
    return false;
  }

  ast_settype(ast, r_type);

  // Push our symbol status to our parent scope.
  ast_inheritstatus(ast_parent(ast), expr);
  return true;
}
Example #6
0
static bool apply_default_arg(pass_opt_t* opt, ast_t* param, ast_t* arg)
{
  // Pick up a default argument.
  AST_GET_CHILDREN(param, id, type, def_arg);

  if(ast_id(def_arg) == TK_NONE)
  {
    ast_error(opt->check.errors, arg, "not enough arguments");
    ast_error_continue(opt->check.errors, param, "definition is here");
    return false;
  }

  if(ast_id(def_arg) == TK_LOCATION)
  {
    // Default argument is __loc. Expand call location.
    ast_t* location = expand_location(arg);
    ast_add(arg, location);

    if(!ast_passes_subtree(&location, opt, PASS_EXPR))
      return false;
  }
  else
  {
    // Just use default argument.
    ast_add(arg, def_arg);
  }

  ast_setid(arg, TK_SEQ);

  if(!expr_seq(opt, arg))
    return false;

  return true;
}
Example #7
0
static bool apply_named_args(pass_opt_t* opt, ast_t* params, ast_t* positional,
  ast_t* namedargs)
{
  ast_t* namedarg = ast_pop(namedargs);

  while(namedarg != NULL)
  {
    AST_GET_CHILDREN(namedarg, arg_id, arg);

    ast_t* param = ast_child(params);
    size_t param_index = 0;

    while(param != NULL)
    {
      AST_GET_CHILDREN(param, param_id);

      if(ast_name(arg_id) == ast_name(param_id))
        break;

      param = ast_sibling(param);
      param_index++;
    }

    if(param == NULL)
    {
      if(ast_id(namedarg) == TK_UPDATEARG)
      {
        ast_error(opt->check.errors, arg_id,
          "cannot use sugar, update() has no parameter named \"value\"");
        return false;
      }

      ast_error(opt->check.errors, arg_id, "not a parameter name");
      return false;
    }

    ast_t* arg_replace = ast_childidx(positional, param_index);

    if(ast_id(arg_replace) != TK_NONE)
    {
      ast_error(opt->check.errors, arg_id,
        "named argument is already supplied");
      ast_error_continue(opt->check.errors, arg_replace,
        "supplied argument is here");
      return false;
    }

    // Extract named argument expression to avoid copying it
    ast_free(ast_pop(namedarg));  // ID
    arg = ast_pop(namedarg);  // Expression

    ast_replace(&arg_replace, arg);
    namedarg = ast_pop(namedargs);
  }

  ast_setid(namedargs, TK_NONE);
  return true;
}
Example #8
0
bool expr_consume(pass_opt_t* opt, ast_t* ast)
{
  AST_GET_CHILDREN(ast, cap, term);
  ast_t* type = ast_type(term);

  if(is_typecheck_error(type))
    return false;

  const char* name = NULL;

  switch(ast_id(term))
  {
    case TK_VARREF:
    case TK_LETREF:
    case TK_PARAMREF:
    {
      ast_t* id = ast_child(term);
      name = ast_name(id);
      break;
    }

    case TK_THIS:
    {
      name = stringtab("this");
      break;
    }

    default:
      ast_error(opt->check.errors, ast,
        "consume must take 'this', a local, or a parameter");
      return false;
  }

  // Can't consume from an outer scope while in a loop condition.
  if((opt->check.frame->loop_cond != NULL) &&
    !ast_within_scope(opt->check.frame->loop_cond, ast, name))
  {
    ast_error(opt->check.errors, ast,
      "can't consume from an outer scope in a loop condition");
    return false;
  }

  ast_setstatus(ast, name, SYM_CONSUMED);

  token_id tcap = ast_id(cap);
  ast_t* c_type = consume_type(type, tcap);

  if(c_type == NULL)
  {
    ast_error(opt->check.errors, ast, "can't consume to this capability");
    ast_error_continue(opt->check.errors, term, "expression type is %s",
      ast_print_type(type));
    return false;
  }

  ast_settype(ast, c_type);
  return true;
}
Example #9
0
static void report_ffi_type_err(compile_t* c, ffi_decl_t* decl, ast_t* ast,
  const char* name)
{
  ast_error(c->opt->check.errors, ast,
    "conflicting declarations for FFI function: %s have incompatible types",
    name);

  if(decl != NULL)
    ast_error_continue(c->opt->check.errors, decl->decl, "first declaration is "
      "here");
}
Example #10
0
static bool check_embed_construction(pass_opt_t* opt, ast_t* left, ast_t* right)
{
  bool result = true;
  if(ast_id(left) == TK_EMBEDREF)
  {
    if(!is_expr_constructor(right))
    {
      ast_error(opt->check.errors, left,
        "an embedded field must be assigned using a constructor");
      ast_error_continue(opt->check.errors, right,
        "the assigned expression is here");
      return false;
    }
  } else if(ast_id(left) == TK_TUPLE) {
    if(ast_id(right) == TK_TUPLE)
    {
      ast_t* l_child = ast_child(left);
      ast_t* r_child = ast_child(right);
      while(l_child != NULL)
      {
        if(ast_id(l_child) != TK_DONTCARE)
        {
          assert((ast_id(l_child) == TK_SEQ) && (ast_id(r_child) == TK_SEQ));
          ast_t* l_member = ast_childlast(l_child);
          ast_t* r_member = ast_childlast(r_child);
          if(!check_embed_construction(opt, l_member, r_member))
            result = false;
        }
        l_child = ast_sibling(l_child);
        r_child = ast_sibling(r_child);
      }
      assert(r_child == NULL);
    } else if(tuple_contains_embed(left)) {
      ast_error(opt->check.errors, left,
        "an embedded field must be assigned using a constructor");
      ast_error_continue(opt->check.errors, right,
        "the assigned expression isn't a tuple literal");
    }
  }
  return result;
}
Example #11
0
static void show_send(pass_opt_t* opt, ast_t* ast)
{
  ast_t* child = ast_child(ast);

  while(child != NULL)
  {
    if(ast_cansend(child) || ast_mightsend(child))
      show_send(opt, child);

    child = ast_sibling(child);
  }

  if(ast_id(ast) == TK_CALL)
  {
    if(ast_cansend(ast))
      ast_error_continue(opt->check.errors, ast, "a message can be sent here");
    else if(ast_mightsend(ast))
      ast_error_continue(opt->check.errors, ast,
        "a message might be sent here");
  }
}
Example #12
0
static bool check_nonsendable_recover(pass_opt_t* opt, ast_t* ast)
{
  if(opt->check.frame->recover != NULL)
  {
    AST_GET_CHILDREN(ast, positional, namedargs, question, lhs);

    ast_t* type = ast_type(lhs);

    AST_GET_CHILDREN(type, cap, typeparams, params, result);

    // If the method is tag, the call is always safe.
    if(ast_id(cap) == TK_TAG)
      return true;

    ast_t* receiver = ast_child(lhs);

    // Dig through function qualification.
    if((ast_id(receiver) == TK_FUNREF) || (ast_id(receiver) == TK_FUNAPP) ||
       (ast_id(receiver) == TK_FUNCHAIN))
      receiver = ast_child(receiver);

    if(!is_receiver_safe(&opt->check, receiver))
    {
      ast_t* arg = ast_child(positional);
      bool args_sendable = true;
      while(arg != NULL)
      {
        if(ast_id(arg) != TK_NONE)
        {
          // Don't typecheck arg_type, this was already done in
          // auto_recover_call.
          ast_t* arg_type = ast_type(arg);
          if(!sendable(arg_type))
          {
            args_sendable = false;
            break;
          }
        }
        arg = ast_sibling(arg);
      }
      if(!args_sendable || !sendable(result))
      {
        ast_error(opt->check.errors, ast, "can't call method on non-sendable "
          "object inside of a recover expression");
        ast_error_continue(opt->check.errors, ast, "this would be possible if "
          "the arguments and return value were all sendable");
        return false;
      }
    }
  }
  return true;
}
Example #13
0
ast_result_t flatten_typeparamref(pass_opt_t* opt, ast_t* ast)
{
  ast_t* cap_ast = cap_fetch(ast);
  token_id cap = ast_id(cap_ast);

  typeparam_set_cap(ast);

  token_id set_cap = ast_id(cap_ast);

  if((cap != TK_NONE) && (cap != set_cap))
  {
    ast_t* def = (ast_t*)ast_data(ast);
    ast_t* constraint = typeparam_constraint(ast);

    if(constraint != NULL)
    {
      ast_error(opt->check.errors, cap_ast, "can't specify a capability on a "
        "type parameter that differs from the constraint");
      ast_error_continue(opt->check.errors, constraint,
        "constraint definition is here");

      if(ast_parent(constraint) != def)
      {
        ast_error_continue(opt->check.errors, def,
          "type parameter definition is here");
      }
    } else {
      ast_error(opt->check.errors, cap_ast, "a type parameter with no "
        "constraint can only have #any as its capability");
      ast_error_continue(opt->check.errors, def,
        "type parameter definition is here");
    }

    return AST_ERROR;
  }

  return AST_OK;
}
Example #14
0
/**
 * Make sure the definition of something occurs before its use. This is for
 * both fields and local variable.
 */
bool def_before_use(pass_opt_t* opt, ast_t* def, ast_t* use, const char* name)
{
  if((ast_line(def) > ast_line(use)) ||
     ((ast_line(def) == ast_line(use)) &&
      (ast_pos(def) > ast_pos(use))))
  {
    ast_error(opt->check.errors, use,
      "declaration of '%s' appears after use", name);
    ast_error_continue(opt->check.errors, def,
      "declaration of '%s' appears here", name);
    return false;
  }

  return true;
}
Example #15
0
static bool extend_positional_args(pass_opt_t* opt, ast_t* params,
  ast_t* positional)
{
  // Fill out the positional args to be as long as the param list.
  size_t param_len = ast_childcount(params);
  size_t arg_len = ast_childcount(positional);

  if(arg_len > param_len)
  {
    ast_error(opt->check.errors, positional, "too many arguments");
    ast_error_continue(opt->check.errors, params, "definition is here");
    return false;
  }

  while(arg_len < param_len)
  {
    ast_setid(positional, TK_POSITIONALARGS);
    ast_append(positional, ast_from(positional, TK_NONE));
    arg_len++;
  }

  return true;
}
Example #16
0
static bool show_partiality(pass_opt_t* opt, ast_t* ast)
{
  ast_t* child = ast_child(ast);
  bool found = false;

  while(child != NULL)
  {
    if(ast_canerror(child))
      found |= show_partiality(opt, child);

    child = ast_sibling(child);
  }

  if(found)
    return true;

  if(ast_canerror(ast))
  {
    ast_error_continue(opt->check.errors, ast, "an error can be raised here");
    return true;
  }

  return false;
}
Example #17
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;
}
Example #18
0
LLVMValueRef gen_ffi(compile_t* c, ast_t* ast)
{
  AST_GET_CHILDREN(ast, id, typeargs, args, named_args, can_err);
  bool err = (ast_id(can_err) == TK_QUESTION);

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

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

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

  // Get the function. First check if the name is in use by a global and error
  // if it's the case.
  ffi_decl_t* ffi_decl;
  bool is_func = false;
  LLVMValueRef func = LLVMGetNamedGlobal(c->module, f_name);

  if(func == NULL)
  {
    func = LLVMGetNamedFunction(c->module, f_name);
    is_func = true;
  }

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

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

    size_t index = HASHMAP_UNKNOWN;

#ifndef PONY_NDEBUG
    ffi_decl_t k;
    k.func = func;

    ffi_decl = ffi_decls_get(&c->ffi_decls, &k, &index);
    pony_assert(ffi_decl == NULL);
#endif

    ffi_decl = POOL_ALLOC(ffi_decl_t);
    ffi_decl->func = func;
    ffi_decl->decl = (decl != NULL) ? decl : ast;

    ffi_decls_putindex(&c->ffi_decls, ffi_decl, index);
  } else {
    ffi_decl_t k;
    k.func = func;
    size_t index = HASHMAP_UNKNOWN;

    ffi_decl = ffi_decls_get(&c->ffi_decls, &k, &index);

    if((ffi_decl == NULL) && (!is_func || LLVMHasMetadataStr(func, "pony.abi")))
    {
      ast_error(c->opt->check.errors, ast, "cannot use '%s' as an FFI name: "
        "name is already in use by the internal ABI", f_name);
      return NULL;
    }

    pony_assert(is_func);
  }

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

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

  if(!vararg)
  {
    if(count != (int)LLVMCountParamTypes(f_type))
    {
      ast_error(c->opt->check.errors, ast,
        "conflicting declarations for FFI function: declarations have an "
        "incompatible number of parameters");

      if(ffi_decl != NULL)
        ast_error_continue(c->opt->check.errors, ffi_decl->decl, "first "
          "declaration is here");

      return NULL;
    }

    f_params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
    LLVMGetParamTypes(f_type, f_params);
  }

  ast_t* arg = ast_child(args);

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

    if(!vararg)
      f_args[i] = cast_ffi_arg(c, ffi_decl, ast, f_args[i], f_params[i],
        "parameters");

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

    arg = ast_sibling(arg);
  }

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

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

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

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

  compile_type_t* c_t = (compile_type_t*)t->c_type;

  // Special case a None return value, which is used for void functions.
  bool isnone = is_none(t->ast);
  bool isvoid = LLVMGetReturnType(f_type) == c->void_type;

  if(isnone && isvoid)
  {
    result = c_t->instance;
  } else if(isnone != isvoid) {
    report_ffi_type_err(c, ffi_decl, ast, "return values");
    return NULL;
  }

  result = cast_ffi_arg(c, ffi_decl, ast, result, c_t->use_type,
    "return values");
  result = gen_assign_cast(c, c_t->use_type, result, t->ast_cap);

  return result;
}
Example #19
0
bool check_constraints(ast_t* orig, ast_t* typeparams, ast_t* typeargs,
  bool report_errors, pass_opt_t* opt)
{
  ast_t* typeparam = ast_child(typeparams);
  ast_t* typearg = ast_child(typeargs);

  while(typeparam != NULL)
  {
    if(ast_id(typearg) == TK_TYPEPARAMREF)
    {
      ast_t* def = (ast_t*)ast_data(typearg);

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

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

    if(bind_constraint != r_constraint)
      ast_free_unattached(bind_constraint);

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

      ast_free_unattached(r_constraint);
      return false;
    }

    ast_free_unattached(r_constraint);

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

      return false;
    }

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

  assert(typeparam == NULL);
  assert(typearg == NULL);
  return true;
}
Example #20
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);
}
Example #21
0
// Find the declaration for the specified FFI name that is valid for the given
// build config.
// The declaration found is stored in the given decl info argument.
// There must be exactly one valid declaration.
// If a declaration is already specified in the given decl info this must be
// the same as the one found.
// All other cases are errors, which will be reported by this function.
// Returns: true on success, false on failure.
static bool find_decl_for_config(ast_t* call, ast_t* package,
  const char* ffi_name, buildflagset_t* config, ffi_decl_t* decl_info,
  pass_opt_t* opt)
{
  pony_assert(call != NULL);
  pony_assert(package != NULL);
  pony_assert(ffi_name != NULL);
  pony_assert(config != NULL);
  pony_assert(decl_info != NULL);

  bool had_valid_decl = false;

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

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

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

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

  return true;
}
Example #22
0
bool check_constraints(ast_t* orig, ast_t* typeparams, ast_t* typeargs,
  bool report_errors, pass_opt_t* opt)
{
  ast_t* typeparam = ast_child(typeparams);
  ast_t* typearg = ast_child(typeargs);

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

      return false;
    }

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

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

          return false;
        }
        break;
      }

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

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

      default: {}
    }

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

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

      ast_free_unattached(r_constraint);
      return false;
    }

    ast_free_unattached(r_constraint);

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

      return false;
    }

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

  pony_assert(typeparam == NULL);
  pony_assert(typearg == NULL);
  return true;
}
Example #23
0
// Combine the given delegated method with the existing one, if any, in the
// given entity.
// The trait_ref is the entry in the delegates list that causes this method
// inclusion. Needed for error reporting.
// Returns true on success, false on failure in which case an error will have
// been reported.
static bool delegated_method(ast_t* entity, ast_t* method, ast_t* field,
  ast_t* trait_ref, pass_opt_t* opt)
{
  assert(entity != NULL);
  assert(method != NULL);
  assert(is_method(method));
  assert(field != NULL);
  assert(trait_ref != NULL);

  if(ast_id(method) == TK_NEW)  // Don't delegate constructors.
    return true;

  // Check for existing method of the same name.
  const char* name = ast_name(ast_childidx(method, 1));
  assert(name != NULL);

  ast_t* existing = find_method(entity, name);

  if(existing != NULL)
  {
    // We already have a method with this name.
    method_t* info = (method_t*)ast_data(existing);
    assert(info != NULL);

    // Nothing new to add.
    if(info->local_define || info->delegated_field == field)
      return true;

    assert(info->trait_ref != NULL);

    ast_error(opt->check.errors, trait_ref,
      "clashing delegates for method %s, local disambiguation required", name);
    ast_error_continue(opt->check.errors, trait_ref,
      "field %s delegates to %s here", ast_name(ast_child(field)), name);
    ast_error_continue(opt->check.errors, info->trait_ref,
      "field %s delegates to %s here",
      ast_name(ast_child(info->delegated_field)), name);
    info->failed = true;
    info->delegated_field = NULL;
    return false;
  }

  // This is a new method, reify and add it.
  ast_t* reified = reify_provides_type(method, trait_ref, opt);

  // Reification error, already reported
  if(reified == NULL)
    return false;

  ast_t* new_method = add_method(entity, trait_ref, reified, "delegate", opt);

  if(new_method == NULL)
  {
    ast_free_unattached(reified);
    return false;
  }

  // Convert the newly added method into a delegation redirection.
  make_delegation(new_method, field, trait_ref, entity);
  return true;
}
Example #24
0
// Combine the given inherited method with the existing one, if any, in the
// given entity.
// The provided method must already be reified.
// The trait_ref is the entry in the provides list that causes this method
// inclusion. Needed for error reporting.
// Returns true on success, false on failure in which case an error will have
// been reported.
static bool add_method_from_trait(ast_t* entity, ast_t* method,
  ast_t* trait_ref, pass_opt_t* opt)
{
  assert(entity != NULL);
  assert(method != NULL);
  assert(trait_ref != NULL);

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

  const char* method_name = ast_name(id);
  ast_t* existing_method = find_method(entity, method_name);

  if(existing_method == NULL)
  {
    // We don't have a method yet with this name, add the one from this trait.
    ast_t* m = add_method(entity, trait_ref, method, "provided", opt);

    if(m == NULL)
      return false;

    if(ast_id(ast_childidx(m, 6)) != TK_NONE)
      ast_visit(&m, rescope, NULL, opt, PASS_ALL);

    return true;
  }

  // A method with this name already exists.
  method_t* info = (method_t*)ast_data(existing_method);
  assert(info != NULL);

  // Method has already caused an error, do nothing.
  if(info->failed)
    return false;

  if(info->local_define || info->delegated_field != NULL)
    return true;

  // Existing method is also provided, signatures must match exactly.
  if(!compare_signatures(existing_method, method))
  {
    assert(info->trait_ref != NULL);

    ast_error(opt->check.errors, trait_ref,
      "clashing definitions for method '%s' provided, local disambiguation "
      "required",
      method_name);
    ast_error_continue(opt->check.errors, trait_ref,
      "provided here, type: %s",
      ast_print_type(method));
    ast_error_continue(opt->check.errors, info->trait_ref,
      "and here, type: %s",
      ast_print_type(existing_method));

    info->failed = true;
    return false;
  }

  // Resolve bodies, if any.
  ast_t* existing_body = ast_childidx(existing_method, 6);

  bool multiple_bodies =
    (info->body_donor != NULL) &&
    (ast_id(method_body) != TK_NONE) &&
    (info->body_donor != (ast_t*)ast_data(method));

  if(multiple_bodies ||
    ast_checkflag(existing_method, AST_FLAG_AMBIGUOUS) ||
    ast_checkflag(method, AST_FLAG_AMBIGUOUS))
  {
    // This method body ambiguous, which is not necessarily an error.
    ast_setflag(existing_method, AST_FLAG_AMBIGUOUS);

    if(ast_id(existing_body) != TK_NONE) // Ditch existing body.
      ast_replace(&existing_body, ast_from(existing_method, TK_NONE));

    info->body_donor = NULL;
    return true;
  }

  // No new body to resolve.
  if((ast_id(method_body) == TK_NONE) ||
    (info->body_donor == (ast_t*)ast_data(method)))
    return true;

  // Trait provides default body. Use it and patch up symbol tables.
  assert(ast_id(existing_body) == TK_NONE);
  ast_replace(&existing_body, method_body);
  ast_visit(&method_body, rescope, NULL, opt, PASS_ALL);

  info->body_donor = (ast_t*)ast_data(method);
  info->trait_ref = trait_ref;
  return true;
}
Example #25
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;
}
Example #26
0
bool expr_assign(pass_opt_t* opt, ast_t* ast)
{
  // Left and right are swapped in the AST to make sure we type check the
  // right side before the left. Fetch them in the opposite order.
  assert(ast != NULL);

  AST_GET_CHILDREN(ast, right, left);
  ast_t* l_type = ast_type(left);

  if(l_type == NULL || !is_lvalue(opt, left, is_result_needed(ast)))
  {
    ast_error(opt->check.errors, ast,
      "left side must be something that can be assigned to");
    return false;
  }

  if(!coerce_literals(&right, l_type, opt))
    return false;

  ast_t* r_type = ast_type(right);

  if(is_typecheck_error(r_type))
    return false;

  if(is_control_type(r_type))
  {
    ast_error(opt->check.errors, ast,
      "the right hand side does not return a value");
    return false;
  }

  if(!infer_locals(opt, left, r_type))
    return false;

  // Inferring locals may have changed the left type.
  l_type = ast_type(left);

  // Assignment is based on the alias of the right hand side.
  ast_t* a_type = alias(r_type);

  errorframe_t info = NULL;
  if(!is_subtype(a_type, l_type, &info, opt))
  {
    errorframe_t frame = NULL;
    ast_error_frame(&frame, ast, "right side must be a subtype of left side");
    errorframe_append(&frame, &info);
    errorframe_report(&frame, opt->check.errors);
    ast_free_unattached(a_type);
    return false;
  }

  if((ast_id(left) == TK_TUPLE) && (ast_id(a_type) != TK_TUPLETYPE))
  {
    switch(ast_id(a_type))
    {
      case TK_UNIONTYPE:
        ast_error(opt->check.errors, ast,
          "can't destructure a union using assignment, use pattern matching "
          "instead");
        break;

      case TK_ISECTTYPE:
        ast_error(opt->check.errors, ast,
          "can't destructure an intersection using assignment, use pattern "
          "matching instead");
        break;

      default:
        assert(0);
        break;
    }

    ast_free_unattached(a_type);
    return false;
  }

  bool ok_safe = safe_to_write(left, a_type);

  if(!ok_safe)
  {
    if(ast_id(left) == TK_FVARREF && ast_child(left) != NULL &&
      ast_id(ast_child(left)) == TK_THIS)
    {
      // We are writing to a field in this
      ast_t* fn = ast_nearest(left, TK_FUN);

      if(fn != NULL)
      {
        ast_t* iso = ast_child(fn);
        assert(iso != NULL);
        token_id iso_id = ast_id(iso);

        if(iso_id == TK_BOX || iso_id == TK_VAL || iso_id == TK_TAG)
        {
          ast_error(opt->check.errors, ast,
            "cannot write to a field in a %s function. If you are trying to "
            "change state in a function use fun ref",
            lexer_print(iso_id));
          ast_free_unattached(a_type);
          return false;
        }
      }
    }

    ast_error(opt->check.errors, ast,
      "not safe to write right side to left side");
    ast_error_continue(opt->check.errors, a_type, "right side type: %s",
      ast_print_type(a_type));
    ast_free_unattached(a_type);
    return false;
  }

  ast_free_unattached(a_type);

  if(!check_embed_construction(opt, left, right))
    return false;

  ast_settype(ast, consume_type(l_type, TK_NONE));
  return true;
}
Example #27
0
bool expr_case(pass_opt_t* opt, ast_t* ast)
{
  assert(opt != NULL);
  assert(ast_id(ast) == TK_CASE);
  AST_GET_CHILDREN(ast, pattern, guard, body);

  if((ast_id(pattern) == TK_NONE) && (ast_id(guard) == TK_NONE))
  {
    ast_error(opt->check.errors, ast,
      "can't have a case with no conditions, use an else clause");
    return false;
  }

  ast_t* cases = ast_parent(ast);
  ast_t* match = ast_parent(cases);
  ast_t* match_expr = ast_child(match);
  ast_t* match_type = ast_type(match_expr);

  if(is_control_type(match_type) || is_typecheck_error(match_type))
    return false;

  if(!infer_pattern_type(pattern, match_type, opt))
    return false;

  if(!is_valid_pattern(opt, pattern))
    return false;

  ast_t* operand_type = alias(match_type);
  ast_t* pattern_type = ast_type(pattern);
  bool ok = true;

  switch(is_matchtype(operand_type, pattern_type, opt))
  {
    case MATCHTYPE_ACCEPT:
      break;

    case MATCHTYPE_REJECT:
      ast_error(opt->check.errors, pattern, "this pattern can never match");
      ast_error_continue(opt->check.errors, match_type, "match type: %s",
        ast_print_type(operand_type));
      ast_error_continue(opt->check.errors, pattern, "pattern type: %s",
        ast_print_type(pattern_type));
      ok = false;
      break;

    case MATCHTYPE_DENY:
      ast_error(opt->check.errors, pattern, "this capture violates capabilities");
      ast_error_continue(opt->check.errors, match_type, "match type: %s",
        ast_print_type(operand_type));
      ast_error_continue(opt->check.errors, pattern, "pattern type: %s",
        ast_print_type(pattern_type));
      ok = false;
      break;
  }

  if(ast_id(guard) != TK_NONE)
  {
    ast_t* guard_type = ast_type(guard);

    if(is_typecheck_error(guard_type))
    {
      ok = false;
    }
    else if(!is_bool(guard_type))
    {
      ast_error(opt->check.errors, guard, "guard must be a boolean expression");
      ok = false;
    }
  }

  ast_free_unattached(operand_type);
  ast_inheritflags(ast);
  return ok;
}