Beispiel #1
0
// Check whether the given entity members are legal in their entity
static bool check_members(pass_opt_t* opt, ast_t* members, int entity_def_index)
{
  assert(members != NULL);
  assert(entity_def_index >= 0 && entity_def_index < DEF_ENTITY_COUNT);
  bool r = true;

  const permission_def_t* def = &_entity_def[entity_def_index];
  ast_t* member = ast_child(members);

  while(member != NULL)
  {
    switch(ast_id(member))
    {
      case TK_FLET:
      case TK_FVAR:
      case TK_EMBED:
      {
        if(def->permissions[ENTITY_FIELD] == 'N')
        {
          ast_error(opt->check.errors, member,
            "Can't have fields in %s", def->desc);
          r = false;
        }

        if((ast_id(ast_parent(members)) == TK_OBJECT) && \
          (ast_id(ast_childidx(member, 2)) == TK_NONE))
        {
          ast_error(opt->check.errors, member,
            "object literal fields must be initialized");
          r = false;
        }

        if(!check_id_field(opt, ast_child(member)))
          r = false;

        ast_t* delegate_type = ast_childidx(member, 3);
        if(ast_id(delegate_type) != TK_NONE &&
          !check_provides_type(opt, delegate_type, "delegate"))
          r = false;
        break;
      }

      case TK_NEW:
        if(!check_method(opt, member, entity_def_index + DEF_NEW))
          r = false;
        break;

      case TK_BE:
      {
        if(!check_method(opt, member, entity_def_index + DEF_BE))
          r = false;
        break;
      }

      case TK_FUN:
      {
        if(!check_method(opt, member, entity_def_index + DEF_FUN))
          r = false;
        break;
      }

      default:
        ast_print(members);
        assert(0);
        return false;
    }

    member = ast_sibling(member);
  }

  return r;
}
Beispiel #2
0
LLVMValueRef gen_call(compile_t* c, ast_t* ast)
{
  // Special case calls.
  LLVMValueRef special;

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

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

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

    default: {}
  }

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

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

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

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

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

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

  bool is_new_call = false;

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

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

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

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

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

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

  bool is_message = false;

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

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

      default: {}
    }
  }

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

  arg = ast_child(positional);
  i = 1;

  LLVMValueRef r = NULL;

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

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

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

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

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

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

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

      codegen_debugloc(c, NULL);
    }
  }

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

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

  ponyint_pool_free_size(buf_size, args);
  ponyint_pool_free_size(buf_size, params);
  return r;
}
Beispiel #3
0
bool expr_fieldref(pass_opt_t* opt, ast_t* ast, ast_t* find, token_id tid)
{
  AST_GET_CHILDREN(ast, left, right);
  ast_t* l_type = ast_type(left);

  if(is_typecheck_error(l_type))
    return false;

  AST_GET_CHILDREN(find, id, f_type, init);

  // Viewpoint adapted type of the field.
  ast_t* type = viewpoint_type(l_type, f_type);

  if(ast_id(type) == TK_ARROW)
  {
    ast_t* upper = viewpoint_upper(type);

    if(upper == NULL)
    {
      ast_error(opt->check.errors, ast, "can't read a field through %s",
        ast_print_type(l_type));
      return false;
    }

    ast_free_unattached(upper);
  }

  // In a recover expression, we can access obj.field if field is sendable
  // and not being assigned to, even if obj isn't sendable.

  typecheck_t* t = &opt->check;

  if(t->frame->recover != NULL)
  {
    if(!sendable(type))
    {
      if(!sendable(l_type))
      {
        errorframe_t frame = NULL;
        ast_error_frame(&frame, ast, "can't access field of non-sendable "
          "object inside of a recover expression");
        ast_error_frame(&frame, find, "this would be possible if the field was "
          "sendable");
        errorframe_report(&frame, opt->check.errors);
        return false;
      }
    }
    else
    {
      ast_t* parent = ast_parent(ast);
      ast_t* current = ast;
      while(ast_id(parent) != TK_RECOVER && ast_id(parent) != TK_ASSIGN)
      {
        current = parent;
        parent = ast_parent(parent);
      }
      if(ast_id(parent) == TK_ASSIGN && ast_child(parent) != current)
      {
        errorframe_t frame = NULL;
        ast_error_frame(&frame, ast, "can't access field of non-sendable "
          "object inside of a recover expression");
        ast_error_frame(&frame, parent, "this would be possible if the field "
          "wasn't assigned to");
        errorframe_report(&frame, opt->check.errors);
        return false;
      }
    }
  }

  // Set the unadapted field type.
  ast_settype(right, f_type);

  // Set the type so that it isn't free'd as unattached.
  ast_setid(ast, tid);
  ast_settype(ast, type);

  if(ast_id(left) == TK_THIS)
  {
    // Handle symbol status if the left side is 'this'.
    const char* name = ast_name(id);

    sym_status_t status;
    ast_get(ast, name, &status);

    if(!valid_reference(opt, ast, type, status))
      return false;
  }

  return true;
}
Beispiel #4
0
bool expr_this(pass_opt_t* opt, ast_t* ast)
{
  typecheck_t* t = &opt->check;

  if(t->frame->def_arg != NULL)
  {
    ast_error(opt->check.errors, ast,
      "can't reference 'this' in a default argument");
    return false;
  }

  sym_status_t status;
  ast_get(ast, stringtab("this"), &status);

  if(status == SYM_CONSUMED)
  {
    ast_error(opt->check.errors, ast,
      "can't use a consumed 'this' in an expression");
    return false;
  }

  assert(status == SYM_NONE);
  token_id cap = cap_for_this(t);

  if(!cap_sendable(cap) && (t->frame->recover != NULL))
  {
    ast_t* parent = ast_parent(ast);
    if(ast_id(parent) != TK_DOT)
      cap = TK_TAG;
  }

  bool make_arrow = false;

  if(cap == TK_BOX)
  {
    cap = TK_REF;
    make_arrow = true;
  }

  ast_t* type = type_for_this(opt, ast, cap, TK_NONE, false);

  if(make_arrow)
  {
    BUILD(arrow, ast, NODE(TK_ARROW, NODE(TK_THISTYPE) TREE(type)));
    type = arrow;
  }

  // Get the nominal type, which may be the right side of an arrow type.
  ast_t* nominal;
  bool arrow;

  if(ast_id(type) == TK_NOMINAL)
  {
    nominal = type;
    arrow = false;
  } else {
    nominal = ast_childidx(type, 1);
    arrow = true;
  }

  ast_t* typeargs = ast_childidx(nominal, 2);
  ast_t* typearg = ast_child(typeargs);

  while(typearg != NULL)
  {
    if(!expr_nominal(opt, &typearg))
    {
      ast_error(opt->check.errors, ast, "couldn't create a type for 'this'");
      ast_free(type);
      return false;
    }

    typearg = ast_sibling(typearg);
  }

  if(!expr_nominal(opt, &nominal))
  {
    ast_error(opt->check.errors, ast, "couldn't create a type for 'this'");
    ast_free(type);
    return false;
  }

  // Unless this is a field lookup, treat an incomplete `this` as a tag.
  ast_t* parent = ast_parent(ast);
  bool incomplete_ok = false;

  if((ast_id(parent) == TK_DOT) && (ast_child(parent) == ast))
  {
    ast_t* right = ast_sibling(ast);
    assert(ast_id(right) == TK_ID);
    ast_t* find = lookup_try(opt, ast, nominal, ast_name(right));

    if(find != NULL)
    {
      switch(ast_id(find))
      {
        case TK_FVAR:
        case TK_FLET:
        case TK_EMBED:
          incomplete_ok = true;
          break;

        default: {}
      }
    }
  }

  if(!incomplete_ok && is_this_incomplete(t, ast))
  {
    ast_t* tag_type = set_cap_and_ephemeral(nominal, TK_TAG, TK_NONE);
    ast_replace(&nominal, tag_type);
  }

  if(arrow)
    type = ast_parent(nominal);
  else
    type = nominal;

  ast_settype(ast, type);
  return true;
}
Beispiel #5
0
bool expr_addressof(pass_opt_t* opt, ast_t* ast)
{
  // Check if we're in an FFI call.
  ast_t* parent = ast_parent(ast);
  bool ok = false;

  if(ast_id(parent) == TK_SEQ)
  {
    parent = ast_parent(parent);

    if(ast_id(parent) == TK_POSITIONALARGS)
    {
      parent = ast_parent(parent);

      if(ast_id(parent) == TK_FFICALL)
        ok = true;
    }
  }

  if(!ok)
  {
    ast_error(ast, "the & operator can only be used for FFI arguments");
    return false;
  }

  ast_t* expr = ast_child(ast);

  switch(ast_id(expr))
  {
    case TK_FVARREF:
    case TK_VARREF:
    case TK_FUNREF:
    case TK_BEREF:
      break;

    case TK_FLETREF:
      ast_error(ast, "can't take the address of a let field");
      return false;

    case TK_EMBEDREF:
      ast_error(ast, "can't take the address of an embed field");
      return false;

    case TK_LETREF:
      ast_error(ast, "can't take the address of a let local");
      return false;

    case TK_PARAMREF:
      ast_error(ast, "can't take the address of a function parameter");
      return false;

    default:
      ast_error(ast, "can only take the address of a local, field or method");
      return false;
  }

  // Set the type to Pointer[ast_type(expr)].
  ast_t* expr_type = ast_type(expr);

  if(is_typecheck_error(expr_type))
    return false;

  ast_t* type = type_pointer_to(opt, expr_type);
  ast_settype(ast, type);
  return true;
}
Beispiel #6
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(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))
  {
    case MATCHTYPE_ACCEPT:
      break;

    case MATCHTYPE_REJECT:
      ast_error(pattern, "this pattern can never match");
      ast_error(match_type, "match type: %s", ast_print_type(operand_type));
      ast_error(pattern, "pattern type: %s", ast_print_type(pattern_type));
      ok = false;
      break;

    case MATCHTYPE_DENY:
      ast_error(pattern, "this capture violates capabilities");
      ast_error(match_type, "match type: %s", ast_print_type(operand_type));
      ast_error(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(guard, "guard must be a boolean expression");
      ok = false;
    }
  }

  ast_free_unattached(operand_type);
  ast_inheritflags(ast);
  return ok;
}
Beispiel #7
0
bool expr_seq(pass_opt_t* opt, ast_t* ast)
{
    bool ok = true;

    // Any expression other than the last that is still literal is an error
    for(ast_t* p = ast_child(ast); ast_sibling(p) != NULL; p = ast_sibling(p))
    {
        ast_t* p_type = ast_type(p);

        if(is_typecheck_error(p_type))
        {
            ok = false;
        } else if(is_type_literal(p_type)) {
            ast_error(p, "Cannot infer type of unused literal");
            ok = false;
        }
    }

    // We might already have a type due to a return expression.
    ast_t* type = ast_type(ast);
    ast_t* last = ast_childlast(ast);

    if((type != NULL) && !coerce_literals(&last, type, opt))
        return false;

    // Type is unioned with the type of the last child.
    type = control_type_add_branch(type, last);
    ast_settype(ast, type);
    ast_inheritflags(ast);

    if(!ast_has_scope(ast))
        return ok;

    ast_t* parent = ast_parent(ast);

    switch(ast_id(parent))
    {
    case TK_TRY:
    case TK_TRY_NO_CHECK:
    {
        // Propagate consumes forward in a try expression.
        AST_GET_CHILDREN(parent, body, else_clause, then_clause);

        if(body == ast)
        {
            // Push our consumes, but not defines, to the else clause.
            ast_inheritbranch(else_clause, body);
            ast_consolidate_branches(else_clause, 2);
        } else if(else_clause == ast) {
            // Push our consumes, but not defines, to the then clause. This
            // includes the consumes from the body.
            ast_inheritbranch(then_clause, else_clause);
            ast_consolidate_branches(then_clause, 2);
        }
    }

    default:
    {}
    }

    return ok;
}
Beispiel #8
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);
      }
      break;
    }
  }

  return true;
}
Beispiel #9
0
bool expr_return(pass_opt_t* opt, ast_t* ast)
{
  typecheck_t* t = &opt->check;

  // return is always the last expression in a sequence
  assert(ast_sibling(ast) == NULL);

  if(ast_parent(ast) == t->frame->method_body)
  {
    ast_error(ast,
      "use return only to exit early from a method, not at the end");
    return false;
  }

  ast_t* type = ast_childidx(t->frame->method, 4);
  ast_t* body = ast_child(ast);

  if(!coerce_literals(&body, type, opt))
    return false;

  ast_t* body_type = ast_type(body);

  if(is_typecheck_error(body_type))
    return false;

  if(is_control_type(body_type))
  {
    ast_error(body, "return value cannot be a control statement");
    return false;
  }

  bool ok = true;

  switch(ast_id(t->frame->method))
  {
    case TK_NEW:
      if(is_this_incomplete(t, ast))
      {
        ast_error(ast,
          "all fields must be defined before constructor returns");
        ok = false;
      }
      break;

    case TK_BE:
      assert(is_none(body_type));
      break;

    default:
    {
      // The body type must be a subtype of the return type, and an alias of
      // the body type must be a subtype of an alias of the return type.
      ast_t* a_type = alias(type);
      ast_t* a_body_type = alias(body_type);

      errorframe_t info = NULL;
      if(!is_subtype(body_type, type, &info) ||
        !is_subtype(a_body_type, a_type, &info))
      {
        errorframe_t frame = NULL;
        ast_t* last = ast_childlast(body);
        ast_error_frame(&frame, last, "returned value isn't the return type");
        ast_error_frame(&frame, type, "function return type: %s",
          ast_print_type(type));
        ast_error_frame(&frame, body_type, "returned value type: %s",
          ast_print_type(body_type));
        errorframe_append(&frame, &info);
        errorframe_report(&frame);
        ok = false;
      }

      ast_free_unattached(a_type);
      ast_free_unattached(a_body_type);
    }
  }

  ast_settype(ast, ast_from(ast, TK_RETURN));
  ast_inheritflags(ast);
  return ok;
}
Beispiel #10
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);
}
Beispiel #11
0
bool expr_if(pass_opt_t* opt, ast_t* ast)
{
  ast_t* cond = ast_child(ast);
  ast_t* left = ast_sibling(cond);
  ast_t* right = ast_sibling(left);

  if(ast_id(ast) == TK_IF)
  {
    ast_t* cond_type = ast_type(cond);

    if(is_typecheck_error(cond_type))
      return false;

    if(!is_bool(cond_type))
    {
      ast_error(cond, "condition must be a Bool");
      return false;
    }
  }

  ast_t* l_type = ast_type(left);
  ast_t* r_type = ast_type(right);

  if(is_typecheck_error(l_type) || is_typecheck_error(r_type))
    return false;

  ast_t* type = NULL;
  size_t branch_count = 0;

  if(!is_control_type(l_type))
  {
    type = control_type_add_branch(type, left);
    ast_inheritbranch(ast, left);
    branch_count++;
  }

  if(!is_control_type(r_type))
  {
    type = control_type_add_branch(type, right);
    ast_inheritbranch(ast, right);
    branch_count++;
  }

  if(type == NULL)
  {
    if((ast_id(ast_parent(ast)) == TK_SEQ) && ast_sibling(ast) != NULL)
    {
      ast_error(ast_sibling(ast), "unreachable code");
      return false;
    }

    type = ast_from(ast, TK_IF);
  }

  ast_settype(ast, type);
  ast_inheritflags(ast);
  ast_consolidate_branches(ast, branch_count);
  literal_unify_control(ast, opt);

  // Push our symbol status to our parent scope.
  ast_inheritstatus(ast_parent(ast), ast);

  if(ast_id(ast) == TK_IFDEF)
    return resolve_ifdef(opt, ast);

  return true;
}
Beispiel #12
0
LLVMValueRef gen_call(compile_t* c, ast_t* ast)
{
  // Special case calls.
  LLVMValueRef special;

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

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

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

    default: {}
  }

  // Generate the receiver type.
  const char* method_name = ast_name(method);
  ast_t* type = ast_type(receiver);
  gentype_t g;

  if(!gentype(c, type, &g))
    return NULL;

  // Generate the arguments.
  LLVMTypeRef f_type = genfun_sig(c, &g, method_name, typeargs);

  if(f_type == NULL)
  {
    ast_error(ast, "couldn't create a signature for '%s'", method_name);
    return NULL;
  }

  size_t count = ast_childcount(positional) + 1;
  size_t buf_size = count * sizeof(void*);

  LLVMValueRef* args = (LLVMValueRef*)ponyint_pool_alloc_size(buf_size);
  LLVMTypeRef* params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
  LLVMGetParamTypes(f_type, params);

  ast_t* arg = ast_child(positional);
  int i = 1;

  while(arg != NULL)
  {
    LLVMValueRef value = make_arg(c, params[i], arg);

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

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

  // Generate the receiver. Must be done after the arguments because the args
  // could change things in the receiver expression that must be accounted for.
  if(call_needs_receiver(postfix, &g))
  {
    switch(ast_id(postfix))
    {
      case TK_NEWREF:
      case TK_NEWBEREF:
      {
        ast_t* parent = ast_parent(ast);
        ast_t* sibling = ast_sibling(ast);

        // If we're constructing an embed field, pass a pointer to the field
        // as the receiver. Otherwise, allocate an object.
        if((ast_id(parent) == TK_ASSIGN) && (ast_id(sibling) == TK_EMBEDREF))
          args[0] = gen_fieldptr(c, sibling);
        else
          args[0] = gencall_alloc(c, &g);
        break;
      }

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

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

  // Always emit location info for a call, to prevent inlining errors. This may
  // be disabled in dispatch_function, if the target function has no debug
  // info set.
  ast_setdebug(ast, true);
  dwarf_location(&c->dwarf, ast);

  // Static or virtual dispatch.
  LLVMValueRef func = dispatch_function(c, ast, &g, args[0], method_name,
    typeargs);

  LLVMValueRef r = NULL;

  if(func != NULL)
  {
    // If we can error out and we have an invoke target, generate an invoke
    // instead of a call.
    if(ast_canerror(ast) && (c->frame->invoke_target != NULL))
      r = invoke_fun(c, func, args, i, "", true);
    else
      r = codegen_call(c, func, args, i);
  }

  ponyint_pool_free_size(buf_size, args);
  ponyint_pool_free_size(buf_size, params);
  return r;
}
Beispiel #13
0
// Assign a UIF type from the given target type to the given AST
static bool uif_type_from_chain(pass_opt_t* opt, ast_t* literal,
  ast_t* target_type, lit_chain_t* chain, bool require_float,
  bool report_errors)
{
  pony_assert(literal != NULL);
  pony_assert(chain != NULL);

  lit_chain_t* chain_head = chain;
  while(chain_head->cardinality != CHAIN_CARD_BASE)
    chain_head = chain_head->next;

  if(chain_head->cached_type == NULL)
  {
    // This is the first time we've needed this type, find it
    if(!uif_type(opt, literal, target_type, chain_head, report_errors))
      return false;
  }

  if(require_float && !chain_head->valid_for_float)
  {
    if(report_errors)
      ast_error(opt->check.errors, literal, "Inferred possibly integer type %s for float literal",
        chain_head->name);

    return false;
  }

  if(ast_id(literal) == TK_INT && chain_head->cached_uif_index >= 0)
  {
    // Check for literals that are outside the range of their type.
    // Note we don't check for types bound to type parameters.
    int i = chain_head->cached_uif_index;

    if(_str_uif_types[i].limit.low != 0 || _str_uif_types[i].limit.high != 0)
    {
      // There is a limit specified for this type, the literal must be smaller
      // than that.
      bool neg_plus_one = false;

      if(_str_uif_types[i].neg_plus_one)
      {
        // If the literal is immediately negated it can be equal to the given
        // limit. This is because of how the world chooses to encode negative
        // integers.
        // For example, the maximum value in an I8 is 127. But the minimum
        // value is -128.
        // We don't actually calculate the negative value here, but we have a
        // looser test if the literal is immediately negated.
        // We do not do this if the negation is not immediate, eg "-(128)".
        ast_t* parent = ast_parent(literal);
        pony_assert(parent != NULL);
        ast_t* parent_type = ast_type(parent);

        if(parent_type != NULL && ast_id(parent_type) == TK_OPERATORLITERAL &&
          ast_child(parent) == literal &&
          ((lit_op_info_t const*)ast_data(parent_type))->neg_plus_one)
          neg_plus_one = true;
      }

      lexint_t* actual = ast_int(literal);
      int test = lexint_cmp(actual, &_str_uif_types[i].limit);

      if((test > 0) || (!neg_plus_one && (test == 0)))
      {
        // Illegal value.
        ast_error(opt->check.errors, literal, "Literal value is out of range for type (%s)",
          chain_head->name);
        return false;
      }
    }
  }

  ast_settype(literal, chain_head->cached_type);
  return true;
}
Beispiel #14
0
bool frame_push(typecheck_t* t, ast_t* ast)
{
  bool pop = false;

  if(ast == NULL)
  {
    typecheck_frame_t* f = POOL_ALLOC(typecheck_frame_t);
    memset(f, 0, sizeof(typecheck_frame_t));
    f->prev = t->frame;
    t->frame = f;

    return true;
  }

  switch(ast_id(ast))
  {
    case TK_PROGRAM:
      pop = push_frame(t);
      t->frame->package = NULL;
      t->frame->module = NULL;
      break;

    case TK_PACKAGE:
      // Can occur in a module as a result of a use expression.
      pop = push_frame(t);
      t->frame->package = ast;
      t->frame->module = NULL;
      break;

    case TK_MODULE:
      pop = push_frame(t);
      t->frame->module = ast;
      t->frame->ifdef_cond = NULL;
      t->frame->ifdef_clause = ast;
      break;

    case TK_INTERFACE:
    case TK_TRAIT:
    case TK_PRIMITIVE:
    case TK_STRUCT:
    case TK_CLASS:
    case TK_ACTOR:
      pop = push_frame(t);
      t->frame->type = ast;
      break;

    case TK_PROVIDES:
      pop = push_frame(t);
      t->frame->provides = ast;
      break;

    case TK_NEW:
    case TK_BE:
    case TK_FUN:
      pop = push_frame(t);
      t->frame->method = ast;
      break;

    case TK_TYPEARGS:
      pop = push_frame(t);
      t->frame->method_type = NULL;
      t->frame->ffi_type = NULL;
      t->frame->local_type = NULL;
      t->frame->constraint = NULL;
      t->frame->iftype_constraint = NULL;
      break;

    case TK_CASE:
      pop = push_frame(t);
      t->frame->the_case = ast;
      break;

    case TK_WHILE:
    case TK_REPEAT:
      pop = push_frame(t);
      t->frame->loop = ast;
      break;

    case TK_TRY:
    case TK_TRY_NO_CHECK:
      pop = push_frame(t);
      t->frame->try_expr = ast;
      break;

    case TK_RECOVER:
      pop = push_frame(t);
      t->frame->recover = ast;
      break;

    default:
    {
      ast_t* parent = ast_parent(ast);

      if(parent == NULL)
        return pop;

      switch(ast_id(parent))
      {
        case TK_TYPEPARAM:
        {
          AST_GET_CHILDREN(parent, id, constraint, def_type);

          if(constraint == ast)
          {
            pop = push_frame(t);
            t->frame->constraint = ast;
          }
          break;
        }

        case TK_NEW:
        case TK_BE:
        case TK_FUN:
        {
          AST_GET_CHILDREN(parent,
            cap, id, typeparams, params, result, error, body);

          if(params == ast)
          {
            pop = push_frame(t);
            t->frame->method_params = ast;
          } else if(result == ast) {
            pop = push_frame(t);
            t->frame->method_type = ast;
          } else if(body == ast) {
            pop = push_frame(t);
            t->frame->method_body = ast;
          }
          break;
        }

        case TK_TYPEARGS:
        {
          switch(ast_id(ast_parent(parent)))
          {
            case TK_FFIDECL:
            case TK_FFICALL:
              pop = push_frame(t);
              t->frame->ffi_type = ast;
              break;

            default: {}
          }
          break;
        }

        case TK_PARAM:
        {
          AST_GET_CHILDREN(parent, id, type, def_arg);

          if(type == ast)
          {
            pop = push_frame(t);
            t->frame->local_type = ast;
          } else if(def_arg == ast) {
            pop = push_frame(t);
            t->frame->def_arg = ast;
          }
          break;
        }

        case TK_VAR:
        case TK_LET:
          if(ast_childidx(parent, 1) == ast)
          {
            pop = push_frame(t);
            t->frame->local_type = ast;
          }
          break;

        case TK_CASE:
          if(ast_child(parent) == ast)
          {
            pop = push_frame(t);
            t->frame->pattern = ast;
          }
          break;

        case TK_AS:
        {
          AST_GET_CHILDREN(parent, expr, type);

          if(type == ast)
          {
            pop = push_frame(t);
            t->frame->as_type = ast;
          }
          break;
        }

        case TK_WHILE:
        {
          AST_GET_CHILDREN(parent, cond, body, else_clause);

          if(cond == ast)
          {
            pop = push_frame(t);
            t->frame->loop_cond = ast;
          } else if(body == ast) {
            pop = push_frame(t);
            t->frame->loop_body = ast;
          } else if(else_clause == ast) {
            pop = push_frame(t);
            t->frame->loop_else = ast;
          }
          break;
        }

        case TK_REPEAT:
        {
          AST_GET_CHILDREN(parent, body, cond, else_clause);

          if(body == ast)
          {
            pop = push_frame(t);
            t->frame->loop_body = ast;
          } else if(cond == ast) {
            pop = push_frame(t);
            t->frame->loop_cond = ast;
          } else if(else_clause == ast) {
            pop = push_frame(t);
            t->frame->loop_else = ast;
          }
          break;
        }

        case TK_IFDEF:
        {
          AST_GET_CHILDREN(parent, cond, body, else_clause, else_cond);

          if(body == ast)
          {
            pop = push_frame(t);
            t->frame->ifdef_cond = cond;
            t->frame->ifdef_clause = body;
          } else if(else_clause == ast) {
            pop = push_frame(t);
            t->frame->ifdef_cond = else_cond;
            t->frame->ifdef_clause = else_clause;
          }
          break;
        }

        case TK_IFTYPE:
        {
          AST_GET_CHILDREN(parent, l_type, r_type, body);

          pop = push_frame(t);
          if(r_type == ast)
          {
            t->frame->iftype_constraint = ast;
          } else if(body == ast) {
            t->frame->iftype_body = ast;
          }
          break;
        }

        default: {}
      }
      break;
    }
  }

  return pop;
}
Beispiel #15
0
bool expr_try(pass_opt_t* opt, ast_t* ast)
{
    AST_GET_CHILDREN(ast, body, else_clause, then_clause);

    // It has to be possible for the left side to result in an error.
    if((ast_id(ast) == TK_TRY) && !ast_canerror(body))
    {
        ast_error(body, "try expression never results in an error");
        return false;
    }

    ast_t* body_type = ast_type(body);
    ast_t* else_type = ast_type(else_clause);
    ast_t* then_type = ast_type(then_clause);

    if(is_typecheck_error(body_type) ||
            is_typecheck_error(else_type) ||
            is_typecheck_error(then_type))
        return false;

    ast_t* type = NULL;

    if(!is_control_type(body_type))
        type = control_type_add_branch(type, body);

    if(!is_control_type(else_type))
        type = control_type_add_branch(type, else_clause);

    if(type == NULL)
    {
        if(ast_sibling(ast) != NULL)
        {
            ast_error(ast_sibling(ast), "unreachable code");
            return false;
        }

        type = ast_from(ast, TK_TRY);
    }

    // The then clause does not affect the type of the expression.
    if(is_control_type(then_type))
    {
        ast_error(then_clause, "then clause always terminates the function");
        return false;
    }

    if(is_type_literal(then_type))
    {
        ast_error(then_clause, "Cannot infer type of unused literal");
        return false;
    }

    ast_settype(ast, type);

    // Doesn't inherit error from the body.
    if(ast_canerror(else_clause) || ast_canerror(then_clause))
        ast_seterror(ast);

    if(ast_cansend(body) || ast_cansend(else_clause) || ast_cansend(then_clause))
        ast_setsend(ast);

    if(ast_mightsend(body) || ast_mightsend(else_clause) ||
            ast_mightsend(then_clause))
        ast_setmightsend(ast);

    literal_unify_control(ast, opt);

    // Push the symbol status from the then clause to our parent scope.
    ast_inheritstatus(ast_parent(ast), then_clause);
    return true;
}
Beispiel #16
0
ast_result_t pass_expr(ast_t** astp, pass_opt_t* options)
{
  typecheck_t* t = &options->check;
  ast_t* ast = *astp;
  bool r = true;

  switch(ast_id(ast))
  {
    case TK_NOMINAL:    r = expr_nominal(options, astp); break;
    case TK_FVAR:
    case TK_FLET:
    case TK_PARAM:      r = expr_field(options, ast); break;
    case TK_NEW:
    case TK_BE:
    case TK_FUN:        r = expr_fun(options, ast); break;
    case TK_SEQ:        r = expr_seq(ast); break;
    case TK_VAR:
    case TK_LET:        r = expr_local(t, ast); break;
    case TK_BREAK:      r = expr_break(t, ast); break;
    case TK_CONTINUE:   r = expr_continue(t, ast); break;
    case TK_RETURN:     r = expr_return(options, ast); break;
    case TK_IS:
    case TK_ISNT:       r = expr_identity(options, ast); break;
    case TK_ASSIGN:     r = expr_assign(options, ast); break;
    case TK_CONSUME:    r = expr_consume(t, ast); break;
    case TK_RECOVER:    r = expr_recover(ast); break;
    case TK_DOT:        r = expr_dot(options, astp); break;
    case TK_TILDE:      r = expr_tilde(options, astp); break;
    case TK_QUALIFY:    r = expr_qualify(options, astp); break;
    case TK_CALL:       r = expr_call(options, astp); break;
    case TK_IF:         r = expr_if(options, ast); break;
    case TK_WHILE:      r = expr_while(options, ast); break;
    case TK_REPEAT:     r = expr_repeat(options, ast); break;
    case TK_TRY_NO_CHECK:
    case TK_TRY:        r = expr_try(options, ast); break;
    case TK_MATCH:      r = expr_match(options, ast); break;
    case TK_CASES:      r = expr_cases(ast); break;
    case TK_CASE:       r = expr_case(options, ast); break;
    case TK_TUPLE:      r = expr_tuple(ast); break;
    case TK_ARRAY:      r = expr_array(options, astp); break;
    case TK_REFERENCE:  r = expr_reference(options, astp); break;
    case TK_THIS:       r = expr_this(options, ast); break;
    case TK_TRUE:
    case TK_FALSE:      r = expr_literal(options, ast, "Bool"); break;
    case TK_ERROR:      r = expr_error(ast); break;
    case TK_COMPILER_INTRINSIC:
                        r = expr_compiler_intrinsic(t, ast); break;
    case TK_POSITIONALARGS:
    case TK_NAMEDARGS:
    case TK_NAMEDARG:
    case TK_UPDATEARG:  ast_inheritflags(ast); break;
    case TK_AMP:        r = expr_addressof(options, ast); break;
    case TK_DONTCARE:   r = expr_dontcare(ast); break;

    case TK_INT:
      // Integer literals can be integers or floats
      make_literal_type(ast);
      break;

    case TK_FLOAT:
      make_literal_type(ast);
      break;

    case TK_STRING:
      if(ast_id(ast_parent(ast)) == TK_PACKAGE)
        return AST_OK;

      r = expr_literal(options, ast, "String");
      break;

    case TK_FFICALL:
      return expr_ffi(options, ast);

    default: {}
  }

  if(!r)
  {
    assert(get_error_count() > 0);
    return AST_ERROR;
  }

  // Can't use ast here, it might have changed
  symtab_t* symtab = ast_get_symtab(*astp);

  if(symtab != NULL && !symtab_check_all_defined(symtab))
    return AST_ERROR;

  return AST_OK;
}
Beispiel #17
0
bool expr_return(pass_opt_t* opt, ast_t* ast)
{
    typecheck_t* t = &opt->check;

    if(t->frame->method_body == NULL)
    {
        ast_error(ast, "return must occur in a method body");
        return false;
    }

    // return is always the last expression in a sequence
    assert(ast_sibling(ast) == NULL);

    if(ast_parent(ast) == t->frame->method_body)
    {
        ast_error(ast,
                  "use return only to exit early from a method, not at the end");
        return false;
    }

    ast_t* type = ast_childidx(t->frame->method, 4);
    ast_t* body = ast_child(ast);

    if(!coerce_literals(&body, type, opt))
        return false;

    ast_t* body_type = ast_type(body);

    if(is_typecheck_error(body_type))
        return false;

    if(is_control_type(body_type))
    {
        ast_error(body, "return value cannot be a control statement");
        return false;
    }

    bool ok = true;

    switch(ast_id(t->frame->method))
    {
    case TK_NEW:
        if(!is_none(body_type))
        {
            ast_error(ast, "return in a constructor must return None");
            ok = false;
        }
        break;

    case TK_BE:
        if(!is_none(body_type))
        {
            ast_error(ast, "return in a behaviour must return None");
            ok = false;
        }
        break;

    default:
    {
        // The body type must be a subtype of the return type, and an alias of
        // the body type must be a subtype of an alias of the return type.
        ast_t* a_type = alias(type);
        ast_t* a_body_type = alias(body_type);

        if(!is_subtype(body_type, type) || !is_subtype(a_body_type, a_type))
        {
            ast_t* last = ast_childlast(body);
            ast_error(last, "returned value isn't the return type");
            ast_error(type, "function return type: %s", ast_print_type(type));
            ast_error(body_type, "returned value type: %s",
                      ast_print_type(body_type));
            ok = false;
        }

        ast_free_unattached(a_type);
        ast_free_unattached(a_body_type);
    }
    }

    ast_settype(ast, ast_from(ast, TK_RETURN));
    ast_inheritflags(ast);
    return ok;
}
Beispiel #18
0
bool expr_reference(pass_opt_t* opt, ast_t** astp)
{
  typecheck_t* t = &opt->check;
  ast_t* ast = *astp;

  // Everything we reference must be in scope.
  const char* name = ast_name(ast_child(ast));

  sym_status_t status;
  ast_t* def = ast_get(ast, name, &status);

  if(def == NULL)
  {
    const char* alt_name = suggest_alt_name(ast, name);

    if(alt_name == NULL)
      ast_error(ast, "can't find declaration of '%s'", name);
    else
      ast_error(ast, "can't find declaration of '%s', did you mean '%s'?",
        name, alt_name);

    return false;
  }

  switch(ast_id(def))
  {
    case TK_PACKAGE:
    {
      // Only allowed if in a TK_DOT with a type.
      if(ast_id(ast_parent(ast)) != TK_DOT)
      {
        ast_error(ast, "a package can only appear as a prefix to a type");
        return false;
      }

      ast_setid(ast, TK_PACKAGEREF);
      return true;
    }

    case TK_INTERFACE:
    case TK_TRAIT:
    case TK_TYPE:
    case TK_TYPEPARAM:
    case TK_PRIMITIVE:
    case TK_CLASS:
    case TK_ACTOR:
    {
      // It's a type name. This may not be a valid type, since it may need
      // type arguments.
      ast_t* id = ast_child(def);
      const char* name = ast_name(id);
      ast_t* type = type_sugar(ast, NULL, name);
      ast_settype(ast, type);
      ast_setid(ast, TK_TYPEREF);

      return expr_typeref(opt, astp);
    }

    case TK_FVAR:
    case TK_FLET:
    case TK_EMBED:
    {
      // Transform to "this.f".
      if(!def_before_use(def, ast, name))
        return false;

      ast_t* dot = ast_from(ast, TK_DOT);
      ast_add(dot, ast_child(ast));

      ast_t* self = ast_from(ast, TK_THIS);
      ast_add(dot, self);

      ast_replace(astp, dot);

      if(!expr_this(opt, self))
        return false;

      return expr_dot(opt, astp);
    }

    case TK_PARAM:
    {
      if(!def_before_use(def, ast, name))
        return false;

      ast_t* type = ast_type(def);

      if(is_typecheck_error(type))
        return false;

      if(!valid_reference(opt, ast, type, status))
        return false;

      if(t->frame->def_arg != NULL)
      {
        ast_error(ast, "can't reference a parameter in a default argument");
        return false;
      }

      if(!sendable(type) && (t->frame->recover != NULL))
      {
        ast_error(ast,
          "can't access a non-sendable parameter from inside a recover "
          "expression");
        return false;
      }

      // Get the type of the parameter and attach it to our reference.
      // Automatically consume a parameter if the function is done.
      ast_t* r_type = type;

      if(is_method_return(t, ast))
        r_type = consume_type(type, TK_NONE);

      ast_settype(ast, r_type);
      ast_setid(ast, TK_PARAMREF);
      return true;
    }

    case TK_NEW:
    case TK_BE:
    case TK_FUN:
    {
      // Transform to "this.f".
      ast_t* dot = ast_from(ast, TK_DOT);
      ast_add(dot, ast_child(ast));

      ast_t* self = ast_from(ast, TK_THIS);
      ast_add(dot, self);

      ast_replace(astp, dot);

      if(!expr_this(opt, self))
        return false;

      return expr_dot(opt, astp);
    }

    case TK_ID:
    {
      if(!def_before_use(def, ast, name))
        return false;

      ast_t* type = ast_type(def);

      if(type != NULL && ast_id(type) == TK_INFERTYPE)
      {
        ast_error(ast, "cannot infer type of %s\n", name);
        ast_settype(def, ast_from(def, TK_ERRORTYPE));
        ast_settype(ast, ast_from(ast, TK_ERRORTYPE));
        return false;
      }

      if(is_typecheck_error(type))
        return false;

      if(!valid_reference(opt, ast, type, status))
        return false;

      ast_t* var = ast_parent(def);

      switch(ast_id(var))
      {
        case TK_VAR:
          ast_setid(ast, TK_VARREF);
          break;

        case TK_LET:
          ast_setid(ast, TK_LETREF);
          break;

        default:
          assert(0);
          return false;
      }

      if(!sendable(type))
      {
        if(t->frame->recover != NULL)
        {
          ast_t* def_recover = ast_nearest(def, TK_RECOVER);

          if(t->frame->recover != def_recover)
          {
            ast_error(ast, "can't access a non-sendable local defined outside "
              "of a recover expression from within that recover expression");
            return false;
          }
        }
      }

      // Get the type of the local and attach it to our reference.
      // Automatically consume a local if the function is done.
      ast_t* r_type = type;

      if(is_method_return(t, ast))
        r_type = consume_type(type, TK_NONE);

      ast_settype(ast, r_type);
      return true;
    }

    default: {}
  }

  assert(0);
  return false;
}
Beispiel #19
0
// Attempt to find the specified package directory in our search path
// @return The resulting directory path, which should not be deleted and is
// valid indefinitely. NULL is directory cannot be found.
static const char* find_path(ast_t* from, const char* path,
  bool* out_is_relative)
{
  if(out_is_relative != NULL)
    *out_is_relative = false;

  // First check for an absolute path
  if(is_path_absolute(path))
    return try_path(NULL, path);

  // Get the base directory
  const char* base;

  if((from == NULL) || (ast_id(from) == TK_PROGRAM))
  {
    base = NULL;
  } else {
    from = ast_nearest(from, TK_PACKAGE);
    package_t* pkg = (package_t*)ast_data(from);
    base = pkg->path;
  }

  // Try a path relative to the base
  const char* result = try_path(base, path);

  if(result != NULL)
  {
    if(out_is_relative != NULL)
      *out_is_relative = true;

    return result;
  }

  // If it's a relative path, don't try elsewhere
  if(!is_path_relative(path))
  {
    // Check ../pony_packages and further up the tree
    if(base != NULL)
    {
      result = try_package_path(base, path);

      if(result != NULL)
        return result;

      // Check ../pony_packages from the compiler target
      if((from != NULL) && (ast_id(from) == TK_PACKAGE))
      {
        ast_t* target = ast_child(ast_parent(from));
        package_t* pkg = (package_t*)ast_data(target);
        base = pkg->path;

        result = try_package_path(base, path);

        if(result != NULL)
          return result;
      }
    }

    // Try the search paths
    for(strlist_t* p = search; p != NULL; p = strlist_next(p))
    {
      result = try_path(strlist_data(p), path);

      if(result != NULL)
        return result;
    }
  }

  errorf(path, "couldn't locate this path");
  return NULL;
}
Beispiel #20
0
bool expr_this(pass_opt_t* opt, ast_t* ast)
{
  typecheck_t* t = &opt->check;

  sym_status_t status;
  ast_get(ast, stringtab("this"), &status);

  if(status == SYM_CONSUMED)
  {
    ast_error(ast, "can't use a consumed 'this' in an expression");
    return false;
  }

  assert(status == SYM_NONE);
  token_id cap = cap_for_this(t);

  if(!cap_sendable(cap) && (t->frame->recover != NULL))
    cap = TK_TAG;

  ast_t* type = type_for_this(t, ast, cap, TK_NONE);

  if(t->frame->def_arg != NULL)
  {
    ast_error(ast, "can't reference 'this' in a default argument");
    return false;
  }

  // Get the nominal type, which may be the right side of an arrow type.
  ast_t* nominal;
  bool arrow;

  if(ast_id(type) == TK_NOMINAL)
  {
    nominal = type;
    arrow = false;
  } else {
    nominal = ast_childidx(type, 1);
    arrow = true;
  }

  ast_t* typeargs = ast_childidx(nominal, 2);
  ast_t* typearg = ast_child(typeargs);

  while(typearg != NULL)
  {
    if(!expr_nominal(opt, &typearg))
    {
      ast_error(ast, "couldn't create a type for 'this'");
      ast_free(type);
      return false;
    }

    typearg = ast_sibling(typearg);
  }

  if(!expr_nominal(opt, &nominal))
  {
    ast_error(ast, "couldn't create a type for 'this'");
    ast_free(type);
    return false;
  }

  if(arrow)
    type = ast_parent(nominal);
  else
    type = nominal;

  ast_settype(ast, type);
  return true;
}
Beispiel #21
0
bool expr_match(pass_opt_t* opt, ast_t* ast)
{
  assert(ast_id(ast) == TK_MATCH);
  AST_GET_CHILDREN(ast, expr, cases, else_clause);

  // A literal match expression should have been caught by the cases, but check
  // again to avoid an assert if we've missed a case
  ast_t* expr_type = ast_type(expr);

  if(is_typecheck_error(expr_type))
    return false;

  if(is_type_literal(expr_type))
  {
    ast_error(expr, "cannot infer type for literal match expression");
    return false;
  }

  ast_t* cases_type = ast_type(cases);
  ast_t* else_type = ast_type(else_clause);

  if(is_typecheck_error(cases_type) || is_typecheck_error(else_type))
    return false;

  ast_t* type = NULL;
  size_t branch_count = 0;

  if(!is_control_type(cases_type))
  {
    type = control_type_add_branch(type, cases);
    ast_inheritbranch(ast, cases);
    branch_count++;
  }

  if(!is_control_type(else_type))
  {
    type = control_type_add_branch(type, else_clause);
    ast_inheritbranch(ast, else_clause);
    branch_count++;
  }

  if(type == NULL)
  {
    if(ast_sibling(ast) != NULL)
    {
      ast_error(ast_sibling(ast), "unreachable code");
      return false;
    }

    type = ast_from(ast, TK_MATCH);
  }

  ast_settype(ast, type);
  ast_inheritflags(ast);
  ast_consolidate_branches(ast, branch_count);
  literal_unify_control(ast, opt);

  // Push our symbol status to our parent scope.
  ast_inheritstatus(ast_parent(ast), ast);
  return true;
}