示例#1
0
文件: flatten.c 项目: volth/ponyc
ast_result_t pass_flatten(ast_t** astp, pass_opt_t* options)
{
  typecheck_t* t = &options->check;
  ast_t* ast = *astp;

  switch(ast_id(ast))
  {
    case TK_NEW:
    {
      switch(ast_id(t->frame->type))
      {
        case TK_CLASS:
          return flatten_constructor(ast);

        case TK_ACTOR:
          return flatten_async(ast);

        default: {}
      }
      break;
    }

    case TK_BE:
      return flatten_async(ast);

    case TK_UNIONTYPE:
      if(!flatten_union(astp))
        return AST_ERROR;
      break;

    case TK_ISECTTYPE:
      if(!flatten_isect(astp))
        return AST_ERROR;
      break;

    case TK_TUPLETYPE:
    case TK_ARROW:
      return flatten_noconstraint(t, ast);

    case TK_TYPEPARAMREF:
      return flatten_typeparamref(ast);

    case TK_FVAR:
    case TK_FLET:
    case TK_EMBED:
      return flatten_provides_list(ast, 3);

    case TK_ACTOR:
    case TK_CLASS:
    case TK_PRIMITIVE:
    case TK_TRAIT:
    case TK_INTERFACE:
      return flatten_provides_list(ast, 3);

    case TK_OBJECT:
      return flatten_provides_list(ast, 0);

    default: {}
  }

  return AST_OK;
}
示例#2
0
文件: match.c 项目: mgist/ponyc
static bool is_valid_pattern(pass_opt_t* opt, ast_t* pattern)
{
  if(ast_id(pattern) == TK_NONE)
  {
    ast_settype(pattern, ast_from(pattern, TK_DONTCARE));
    return true;
  }

  ast_t* pattern_type = ast_type(pattern);

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

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

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

        return false;
      }

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

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

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

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

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

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

      ast_settype(pattern, pattern_type);
      return ok;
    }

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

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

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

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

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

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

        return false;
      }

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

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

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

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

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

      ast_t* param = ast_child(params);

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

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

      ast_free_unattached(fun);
      return ok;
    }
  }

  assert(0);
  return false;
}
示例#3
0
文件: match.c 项目: mgist/ponyc
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;
}
示例#4
0
文件: control.c 项目: Sendence/ponyc
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(opt->check.errors, p, "Cannot infer type of unused literal");
      ok = false;
    }
  }

  if(ok)
  {
    // 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(opt, type, last);
    ast_settype(ast, type);
  }

  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;
}
示例#5
0
文件: id.c 项目: mgist/ponyc
bool check_id(ast_t* id_node, const char* desc, int spec)
{
  assert(id_node != NULL);
  assert(ast_id(id_node) == TK_ID);
  assert(desc != NULL);

  const char* name = ast_name(id_node);
  assert(name != NULL);
  char prev = '\0';

  // Ignore leading $, handled by lexer
  if(*name == '$')
  {
    name++;
    prev = '$';
  }

  // Ignore leading _
  if(*name == '_')
  {
    name++;
    prev = '_';

    if((spec & ALLOW_LEADING_UNDERSCORE) == 0)
    {
      ast_error(id_node, "%s name \"%s\" cannot start with underscores", desc,
        ast_name(id_node));
      return false;
    }
  }

  if((spec & START_LOWER) != 0 && (*name < 'a' || *name > 'z'))
  {
    ast_error(id_node, "%s name \"%s\" must start a-z or _(a-z)", desc,
      ast_name(id_node));
    return false;
  }

  if((spec & START_UPPER) != 0 && (*name < 'A' || *name > 'Z'))
  {
    ast_error(id_node, "%s name \"%s\" must start A-Z or _(A-Z)", desc,
      ast_name(id_node));
    return false;
  }

  // Check each character looking for ticks and underscores
  for(; *name != '\0' && *name != '\''; name++)
  {
    if(*name == '_')
    {
      if((spec & ALLOW_UNDERSCORE) == 0)
      {
        ast_error(id_node, "%s name \"%s\" cannot contain underscores", desc,
          ast_name(id_node));
        return false;
      }

      if(prev == '_')
      {
        ast_error(id_node, "%s name \"%s\" cannot contain double underscores",
          desc, ast_name(id_node));
        return false;
      }
    }

    prev = *name;
  }

  // Only ticks (or nothing) left

  // Check for ending with _
  if(prev == '_')
  {
    ast_error(id_node, "%s name \"%s\" cannot end with underscores", desc,
      ast_name(id_node));
    return false;
  }

  if(*name == '\0')
    return true;

  // Should only be ticks left
  assert(*name == '\'');

  if((spec & ALLOW_TICK) == 0)
  {
    ast_error(id_node, "%s name \"%s\" cannot contain prime (')", desc,
      ast_name(id_node));
    return false;
  }

  for(; *name != '\0'; name++)
  {
    if(*name != '\'')
    {
      ast_error(id_node,
        "prime(') can only appear at the end of %s name \"%s\"", desc,
        ast_name(id_node));
      return false;
    }
  }

  return true;
}
示例#6
0
文件: gencall.c 项目: Theodus/ponyc
static bool can_inline_message_send(reach_type_t* t, reach_method_t* m,
  const char* method_name)
{
  switch(t->underlying)
  {
    case TK_UNIONTYPE:
    case TK_ISECTTYPE:
    case TK_INTERFACE:
    case TK_TRAIT:
      break;

    default:
      pony_assert(0);
      return false;
  }

  size_t i = HASHMAP_BEGIN;
  reach_type_t* sub;
  while((sub = reach_type_cache_next(&t->subtypes, &i)) != NULL)
  {
    reach_method_t* m_sub = reach_method(sub, m->cap, method_name, m->typeargs);

    if(m_sub == NULL)
      continue;

    switch(sub->underlying)
    {
      case TK_CLASS:
      case TK_PRIMITIVE:
        return false;

      case TK_ACTOR:
        if(ast_id(m_sub->fun->ast) == TK_FUN)
          return false;
        break;

      default: {}
    }

    pony_assert(m->param_count == m_sub->param_count);
    for(size_t i = 0; i < m->param_count; i++)
    {
      // If the param is a boxable type for us and an unboxable type for one of
      // our subtypes, that subtype will take that param as boxed through an
      // interface. In order to correctly box the value the actual function to
      // call must be resolved through name mangling, therefore we can't inline
      // the message send.
      reach_type_t* param = m->params[i].type;
      reach_type_t* sub_param = m_sub->params[i].type;
      if(param->can_be_boxed)
      {
        if(!sub_param->can_be_boxed)
          return false;

        if(param->underlying == TK_TUPLETYPE)
        {
          ast_t* child = ast_child(param->ast);
          while(child != NULL)
          {
            if(contains_boxable(child))
              return false;
          }
        }
      }
    }
  }

  return true;
}
示例#7
0
文件: gencall.c 项目: Theodus/ponyc
LLVMValueRef gen_pattern_eq(compile_t* c, ast_t* pattern, LLVMValueRef r_value)
{
  // This is used for structural equality in pattern matching.
  ast_t* pattern_type = deferred_reify(c->frame->reify, ast_type(pattern),
    c->opt);

  if(ast_id(pattern_type) == TK_NOMINAL)
  {
    AST_GET_CHILDREN(pattern_type, package, id);

    // Special case equality on primitive types.
    if(ast_name(package) == c->str_builtin)
    {
      const char* name = ast_name(id);

      if((name == c->str_Bool) ||
        (name == c->str_I8) ||
        (name == c->str_I16) ||
        (name == c->str_I32) ||
        (name == c->str_I64) ||
        (name == c->str_I128) ||
        (name == c->str_ILong) ||
        (name == c->str_ISize) ||
        (name == c->str_U8) ||
        (name == c->str_U16) ||
        (name == c->str_U32) ||
        (name == c->str_U64) ||
        (name == c->str_U128) ||
        (name == c->str_ULong) ||
        (name == c->str_USize) ||
        (name == c->str_F32) ||
        (name == c->str_F64)
        )
      {
        ast_free_unattached(pattern_type);
        return gen_eq_rvalue(c, pattern, r_value, true);
      }
    }
  }

  // Generate the receiver.
  LLVMValueRef l_value = gen_expr(c, pattern);
  reach_type_t* t = reach_type(c->reach, pattern_type);
  pony_assert(t != NULL);

  // Static or virtual dispatch.
  token_id cap = cap_dispatch(pattern_type);
  reach_method_t* m = reach_method(t, cap, c->str_eq, NULL);
  LLVMValueRef func = dispatch_function(c, t, m, l_value);

  ast_free_unattached(pattern_type);

  if(func == NULL)
    return NULL;

  // Call the function. We know it isn't partial.
  LLVMValueRef args[2];
  args[0] = l_value;
  args[1] = r_value;

  codegen_debugloc(c, pattern);
  LLVMValueRef result = codegen_call(c, func, args, 2, true);
  codegen_debugloc(c, NULL);

  return result;
}
示例#8
0
文件: traits.c 项目: jonas-l/ponyc
// Compare the 2 given signatures to see if they are exactly the same
static bool compare_signatures(ast_t* sig_a, ast_t* sig_b)
{
  if(sig_a == NULL && sig_b == NULL)
    return true;

  if(sig_a == NULL || sig_b == NULL)
    return false;

  token_id a_id = ast_id(sig_a);

  if(a_id != ast_id(sig_b))
    return false;

  switch(a_id)
  {
    case TK_BE:
    case TK_FUN:
    case TK_NEW:
    {
      // Check everything except body and docstring, ie first 6 children
      ast_t* a_child = ast_child(sig_a);
      ast_t* b_child = ast_child(sig_b);

      for(int i = 0; i < 6; i++)
      {
        if(a_child == NULL || b_child == NULL)
          return false;

        if(!compare_signatures(a_child, b_child))
          return false;

        a_child = ast_sibling(a_child);
        b_child = ast_sibling(b_child);
      }

      return true;
    }

    case TK_STRING:
    case TK_ID:
    {
      // Can't just use strcmp, string literals may contain \0s
      size_t a_len = ast_name_len(sig_a);
      size_t b_len = ast_name_len(sig_b);

      if(a_len != b_len)
        return false;

      const char* a_text = ast_name(sig_a);
      const char* b_text = ast_name(sig_b);

      for(size_t i = 0; i < a_len; i++)
        if(a_text[i] != b_text[i])
          return false;

      return true;
    }

    case TK_INT:     return ast_int(sig_a) == ast_int(sig_b);
    case TK_FLOAT:   return ast_float(sig_a) == ast_float(sig_b);

    case TK_NOMINAL:
      if(ast_data(sig_a) != ast_data(sig_b))
        return false;

      break;

    default:
      break;
  }

  ast_t* a_child = ast_child(sig_a);
  ast_t* b_child = ast_child(sig_b);

  while(a_child != NULL && b_child != NULL)
  {
    if(!compare_signatures(a_child, b_child))
      return false;

    a_child = ast_sibling(a_child);
    b_child = ast_sibling(b_child);
  }

  if(a_child != NULL || b_child != NULL)
    return false;

  return true;
}
示例#9
0
文件: operator.c 项目: mgist/ponyc
static bool assign_id(typecheck_t* t, ast_t* ast, bool let, bool need_value)
{
  assert(ast_id(ast) == TK_ID);
  const char* name = ast_name(ast);

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

  switch(status)
  {
    case SYM_UNDEFINED:
      if(need_value)
      {
        ast_error(ast, "the left side is undefined but its value is used");
        return false;
      }

      ast_setstatus(ast, name, SYM_DEFINED);
      return true;

    case SYM_DEFINED:
      if(let)
      {
        ast_error(ast,
          "can't assign to a let or embed definition more than once");
        return false;
      }

      return true;

    case SYM_CONSUMED:
    {
      bool ok = true;

      if(need_value)
      {
        ast_error(ast, "the left side is consumed but its value is used");
        ok = false;
      }

      if(let)
      {
        ast_error(ast,
          "can't assign to a let or embed definition more than once");
        ok = false;
      }

      if(t->frame->try_expr != NULL)
      {
        ast_error(ast,
          "can't reassign to a consumed identifier in a try expression");
        ok = false;
      }

      if(ok)
        ast_setstatus(ast, name, SYM_DEFINED);

      return ok;
    }

    default: {}
  }

  assert(0);
  return false;
}
示例#10
0
文件: viewpoint.c 项目: Theodus/ponyc
ast_t* viewpoint_reifytypeparam(ast_t* type, ast_t* typeparamref)
{
  pony_assert(ast_id(typeparamref) == TK_TYPEPARAMREF);
  AST_GET_CHILDREN(typeparamref, id, cap, eph);

  switch(ast_id(cap))
  {
    case TK_ISO:
    case TK_TRN:
    case TK_REF:
    case TK_VAL:
    case TK_BOX:
    case TK_TAG:
      return NULL;

    case TK_CAP_SEND:
    {
      ast_t* tuple = ast_from(type, TK_TUPLETYPE);
      replace_typeparam(tuple, type, typeparamref, TK_ISO, ast_id(eph));
      replace_typeparam(tuple, type, typeparamref, TK_VAL, TK_NONE);
      replace_typeparam(tuple, type, typeparamref, TK_TAG, TK_NONE);
      return tuple;
    }

    case TK_CAP_SHARE:
    {
      ast_t* tuple = ast_from(type, TK_TUPLETYPE);
      replace_typeparam(tuple, type, typeparamref, TK_VAL, TK_NONE);
      replace_typeparam(tuple, type, typeparamref, TK_TAG, TK_NONE);
      return tuple;
    }

    case TK_CAP_READ:
    {
      ast_t* tuple = ast_from(type, TK_TUPLETYPE);
      replace_typeparam(tuple, type, typeparamref, TK_REF, TK_NONE);
      replace_typeparam(tuple, type, typeparamref, TK_VAL, TK_NONE);
      replace_typeparam(tuple, type, typeparamref, TK_BOX, TK_NONE);
      return tuple;
    }

    case TK_CAP_ALIAS:
    {
      ast_t* tuple = ast_from(type, TK_TUPLETYPE);
      replace_typeparam(tuple, type, typeparamref, TK_REF, TK_NONE);
      replace_typeparam(tuple, type, typeparamref, TK_VAL, TK_NONE);
      replace_typeparam(tuple, type, typeparamref, TK_BOX, TK_NONE);
      replace_typeparam(tuple, type, typeparamref, TK_TAG, TK_NONE);
      return tuple;
    }

    case TK_CAP_ANY:
    {
      ast_t* tuple = ast_from(type, TK_TUPLETYPE);
      replace_typeparam(tuple, type, typeparamref, TK_ISO, ast_id(eph));
      replace_typeparam(tuple, type, typeparamref, TK_TRN, ast_id(eph));
      replace_typeparam(tuple, type, typeparamref, TK_REF, TK_NONE);
      replace_typeparam(tuple, type, typeparamref, TK_VAL, TK_NONE);
      replace_typeparam(tuple, type, typeparamref, TK_BOX, TK_NONE);
      replace_typeparam(tuple, type, typeparamref, TK_TAG, TK_NONE);
      return tuple;
    }

    default: {}
  }

  pony_assert(0);
  return NULL;
}
示例#11
0
static void make_prototype(compile_t* c, reachable_type_t* t,
  reachable_method_t* m)
{
  if(m->intrinsic)
    return;

  // Behaviours and actor constructors also have handler functions.
  bool handler = false;

  switch(ast_id(m->r_fun))
  {
    case TK_NEW:
      handler = t->underlying == TK_ACTOR;
      break;

    case TK_BE:
      handler = true;
      break;

    default: {}
  }

  make_signature(t, m);

  switch(t->underlying)
  {
    case TK_PRIMITIVE:
    case TK_STRUCT:
    case TK_CLASS:
    case TK_ACTOR:
      break;

    default:
      return;
  }

  if(handler)
  {
    // Generate the sender prototype.
    const char* sender_name = genname_be(m->full_name);
    m->func = codegen_addfun(c, sender_name, m->func_type);

    // Change the return type to void for the handler.
    size_t count = LLVMCountParamTypes(m->func_type);
    size_t buf_size = count * sizeof(LLVMTypeRef);
    LLVMTypeRef* tparams = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
    LLVMGetParamTypes(m->func_type, tparams);

    LLVMTypeRef handler_type = LLVMFunctionType(c->void_type, tparams,
      (int)count, false);
    ponyint_pool_free_size(buf_size, tparams);

    // Generate the handler prototype.
    m->func_handler = codegen_addfun(c, m->full_name, handler_type);
    make_function_debug(c, t, m, m->func_handler);
  } else {
    // Generate the function prototype.
    m->func = codegen_addfun(c, m->full_name, m->func_type);
    make_function_debug(c, t, m, m->func);
  }
}
示例#12
0
文件: viewpoint.c 项目: Theodus/ponyc
static void replace_type(ast_t** astp, ast_t* target, ast_t* with)
{
  ast_t* ast = *astp;
  ast_t* child = ast_child(ast);

  while(child != NULL)
  {
    replace_type(&child, target, with);
    child = ast_sibling(child);
  }

  ast_t* node_type = ast_type(ast);

  if(node_type != NULL)
    replace_type(&node_type, target, with);

  if(ast_id(ast) == ast_id(target))
  {
    switch(ast_id(target))
    {
      case TK_THISTYPE:
        // Replace `this`.
        ast_replace(astp, ast_dup(with));
        break;

      case TK_TYPEPARAMREF:
      {
        // Replace a typeparamref with a reification.
        ast_t* target_def = (ast_t*)ast_data(target);
        ast_t* left_def = (ast_t*)ast_data(ast);

        if(target_def == left_def)
        {
          AST_GET_CHILDREN(ast, id, cap, eph);
          ast_t* a_with = with;

          switch(ast_id(eph))
          {
            case TK_EPHEMERAL:
              a_with = consume_type(with, TK_NONE);
              break;

            case TK_ALIASED:
              a_with = alias(with);
              break;

            default: {}
          }

          if(a_with == with)
            a_with = ast_dup(with);

          ast_replace(astp, a_with);
        }
        break;
      }

      default:
        pony_assert(0);
    }
  } else if(ast_id(ast) == TK_ARROW) {
    // Recalculate all arrow types.
    AST_GET_CHILDREN(ast, left, right);
    ast_t* r_type = viewpoint_type(left, right);
    ast_replace(astp, r_type);
  }
}
示例#13
0
文件: viewpoint.c 项目: Theodus/ponyc
ast_t* viewpoint_lower(ast_t* type)
{
  // T = N | A
  // s = {k}
  // upper(s p'->T k p) = union[k' in s](T (k'->k) eph(s, p', p))
  // eph(s, p', p) = { unalias(p) if p' = ^, exists k in s . k in {iso, trn}
  //                 { p          otherwise
  pony_assert(ast_id(type) == TK_ARROW);
  AST_GET_CHILDREN(type, left, right);
  ast_t* r_right = right;

  switch(ast_id(right))
  {
    case TK_NOMINAL:
    case TK_TYPEPARAMREF:
      break;

    case TK_ARROW:
      // Arrow types are right associative.
      r_right = viewpoint_lower(right);

      if(r_right == NULL)
        return NULL;
      break;

    default:
      pony_assert(0);
      return NULL;
  }

  token_id l_cap = TK_NONE;
  token_id l_eph = TK_NONE;

  switch(ast_id(left))
  {
    case TK_ISO:
    case TK_TRN:
    case TK_REF:
    case TK_VAL:
    case TK_BOX:
    case TK_TAG:
      l_cap = ast_id(left);
      break;

    case TK_THISTYPE:
      l_cap = TK_CAP_READ;
      break;

    case TK_NOMINAL:
    case TK_TYPEPARAMREF:
    {
      ast_t* left_cap = cap_fetch(left);
      ast_t* left_eph = ast_sibling(left_cap);

      l_cap = ast_id(left_cap);
      l_eph = ast_id(left_eph);
      break;
    }

    default:
      pony_assert(0);
      return NULL;
  }

  ast_t* right_cap = cap_fetch(r_right);
  ast_t* right_eph = ast_sibling(right_cap);

  token_id r_cap = ast_id(right_cap);
  token_id r_eph = ast_id(right_eph);

  // No result: left side could be a tag.
  if(!cap_view_lower(l_cap, l_eph, &r_cap, &r_eph))
    return NULL;

  ast_t* rr_right = set_cap_and_ephemeral(r_right, r_cap, r_eph);

  if(r_right != right)
    ast_free_unattached(r_right);

  return rr_right;
}
示例#14
0
文件: viewpoint.c 项目: Theodus/ponyc
ast_t* viewpoint_type(ast_t* l_type, ast_t* r_type)
{
  int upper = VIEW_UPPER_NO;

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

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

      return type;
    }

    case TK_TYPEPARAMREF:
      upper = VIEW_UPPER_NO;
      break;

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

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

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

        default: {}
      }
      break;
    }

    case TK_ARROW:
      break;

    default:
      pony_assert(0);
      return NULL;
  }

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

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

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

        default: {}
      }
      break;
    }

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

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

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

    default:
      pony_assert(0);
      return NULL;
  }

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

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

    if(arrow_upper == NULL)
      return arrow;

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

  return arrow;
}
示例#15
0
文件: gencall.c 项目: Theodus/ponyc
LLVMValueRef gen_funptr(compile_t* c, ast_t* ast)
{
  pony_assert((ast_id(ast) == TK_FUNREF) || (ast_id(ast) == TK_BEREF));
  AST_GET_CHILDREN(ast, receiver, method);
  ast_t* typeargs = NULL;

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

    default: {}
  }

  // Generate the receiver.
  LLVMValueRef value = gen_expr(c, receiver);

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

  const char* name = ast_name(method);
  token_id cap = cap_dispatch(type);
  reach_method_t* m = reach_method(t, cap, name, typeargs);
  LLVMValueRef funptr = dispatch_function(c, t, m, value);

  ast_free_unattached(type);

  if((m->cap != TK_AT) && (c->linkage != LLVMExternalLinkage))
  {
    // We must reset the function linkage and calling convention since we're
    // passing a function pointer to a FFI call. Bare methods always use the
    // external linkage and the C calling convention so we don't need to process
    // them.
    switch(t->underlying)
    {
      case TK_PRIMITIVE:
      case TK_STRUCT:
      case TK_CLASS:
      case TK_ACTOR:
      {
        compile_method_t* c_m = (compile_method_t*)m->c_method;
        LLVMSetFunctionCallConv(c_m->func, LLVMCCallConv);
        LLVMSetLinkage(c_m->func, LLVMExternalLinkage);
        break;
      }
      case TK_UNIONTYPE:
      case TK_ISECTTYPE:
      case TK_INTERFACE:
      case TK_TRAIT:
        set_method_external_interface(t, name, m->vtable_index);
        break;
      default:
        pony_assert(0);
        break;
    }
  }

  return funptr;
}
示例#16
0
文件: operator.c 项目: mgist/ponyc
static ast_t* find_infer_type(ast_t* type, infer_path_t* path)
{
  assert(type != NULL);

  switch(ast_id(type))
  {
    case TK_TUPLETYPE:
      if(path == NULL)  // End of path, infer the whole tuple
        return type;

      if(path->index >= ast_childcount(type)) // Cardinality mismatch
        return NULL;

      return find_infer_type(ast_childidx(type, path->index), path->next);

    case TK_UNIONTYPE:
    {
      // Infer all children
      ast_t* u_type = NULL;

      for(ast_t* p = ast_child(type); p != NULL; p = ast_sibling(p))
      {
        ast_t* t = find_infer_type(p, path);

        if(t == NULL)
        {
          // Propogate error
          ast_free_unattached(u_type);
          return NULL;
        }

        u_type = type_union(u_type, t);
      }

      return u_type;
    }

    case TK_ISECTTYPE:
    {
      // Infer all children
      ast_t* i_type = NULL;

      for(ast_t* p = ast_child(type); p != NULL; p = ast_sibling(p))
      {
        ast_t* t = find_infer_type(p, path);

        if(t == NULL)
        {
          // Propogate error
          ast_free_unattached(i_type);
          return NULL;
        }

        i_type = type_isect(i_type, t);
      }

      return i_type;
    }

    default:
      if(path != NULL)  // Type doesn't match path
        return NULL;

      // Just return whatever this type is
      return type;
  }
}
示例#17
0
文件: gencall.c 项目: Theodus/ponyc
void gen_send_message(compile_t* c, reach_method_t* m, LLVMValueRef args[],
  ast_t* args_ast)
{
  // Allocate the message, setting its size and ID.
  compile_method_t* c_m = (compile_method_t*)m->c_method;
  size_t msg_size = (size_t)LLVMABISizeOfType(c->target_data, c_m->msg_type);
  LLVMTypeRef msg_type_ptr = LLVMPointerType(c_m->msg_type, 0);

  size_t params_buf_size = (m->param_count + 3) * sizeof(LLVMTypeRef);
  LLVMTypeRef* param_types =
    (LLVMTypeRef*)ponyint_pool_alloc_size(params_buf_size);
  LLVMGetStructElementTypes(c_m->msg_type, param_types);
  size_t args_buf_size = (m->param_count + 1) * sizeof(LLVMValueRef);
  LLVMValueRef* cast_args =
    (LLVMValueRef*)ponyint_pool_alloc_size(args_buf_size);
  size_t arg_types_buf_size = m->param_count * sizeof(ast_t*);
  ast_t** arg_types = (ast_t**)ponyint_pool_alloc_size(arg_types_buf_size);

  ast_t* arg_ast = ast_child(args_ast);

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

  for(size_t i = 0; i < m->param_count; i++)
  {
    arg_types[i] = deferred_reify(reify, ast_type(arg_ast), c->opt);
    cast_args[i+1] = gen_assign_cast(c, param_types[i+3], args[i+1],
      arg_types[i]);
    arg_ast = ast_sibling(arg_ast);
  }

  LLVMValueRef msg_args[5];

  msg_args[0] = LLVMConstInt(c->i32, ponyint_pool_index(msg_size), false);
  msg_args[1] = LLVMConstInt(c->i32, m->vtable_index, false);
  LLVMValueRef msg = gencall_runtime(c, "pony_alloc_msg", msg_args, 2, "");
  LLVMValueRef md = LLVMMDNodeInContext(c->context, NULL, 0);
  LLVMSetMetadataStr(msg, "pony.msgsend", md);
  LLVMValueRef msg_ptr = LLVMBuildBitCast(c->builder, msg, msg_type_ptr, "");

  for(unsigned int i = 0; i < m->param_count; i++)
  {
    LLVMValueRef arg_ptr = LLVMBuildStructGEP(c->builder, msg_ptr, i + 3, "");
    LLVMBuildStore(c->builder, cast_args[i+1], arg_ptr);
  }

  // Trace while populating the message contents.
  bool need_trace = false;

  for(size_t i = 0; i < m->param_count; i++)
  {
    if(gentrace_needed(c, arg_types[i], m->params[i].ast))
    {
      need_trace = true;
      break;
    }
  }

  LLVMValueRef ctx = codegen_ctx(c);

  if(need_trace)
  {
    LLVMValueRef gc = gencall_runtime(c, "pony_gc_send", &ctx, 1, "");
    LLVMSetMetadataStr(gc, "pony.msgsend", md);

    for(size_t i = 0; i < m->param_count; i++)
    {
      gentrace(c, ctx, args[i+1], cast_args[i+1], arg_types[i],
        m->params[i].ast);
    }

    gc = gencall_runtime(c, "pony_send_done", &ctx, 1, "");
    LLVMSetMetadataStr(gc, "pony.msgsend", md);
  }

  // Send the message.
  msg_args[0] = ctx;
  msg_args[1] = LLVMBuildBitCast(c->builder, args[0], c->object_ptr, "");
  msg_args[2] = msg;
  msg_args[3] = msg;
  msg_args[4] = LLVMConstInt(c->i1, 1, false);
  LLVMValueRef send;

  if(ast_id(m->fun->ast) == TK_NEW)
    send = gencall_runtime(c, "pony_sendv_single", msg_args, 5, "");
  else
    send = gencall_runtime(c, "pony_sendv", msg_args, 5, "");

  LLVMSetMetadataStr(send, "pony.msgsend", md);

  ponyint_pool_free_size(params_buf_size, param_types);
  ponyint_pool_free_size(args_buf_size, cast_args);

  for(size_t i = 0; i < m->param_count; i++)
    ast_free_unattached(arg_types[i]);

  ponyint_pool_free_size(arg_types_buf_size, arg_types);
}
示例#18
0
文件: operator.c 项目: mgist/ponyc
static infer_ret_t infer_local_inner(ast_t* left, ast_t* r_type,
  infer_path_t* path)
{
  assert(left != NULL);
  assert(r_type != NULL);
  assert(path != NULL);
  assert(path->root != NULL);

  infer_ret_t ret_val = INFER_NOP;

  switch(ast_id(left))
  {
    case TK_SEQ:
    {
      assert(ast_childcount(left) == 1);
      infer_ret_t r = infer_local_inner(ast_child(left), r_type, path);

      if(r == INFER_OK) // Update seq type
        ast_settype(left, ast_type(ast_child(left)));

      return r;
    }

    case TK_TUPLE:
    {
      // Add a new node to the end of the path
      infer_path_t path_node = { 0, NULL, path->root };
      path->next = &path_node;

      for(ast_t* p = ast_child(left); p != NULL; p = ast_sibling(p))
      {
        infer_ret_t r = infer_local_inner(p, r_type, &path_node);

        if(r == INFER_ERROR)
          return INFER_ERROR;

        if(r == INFER_OK)
        {
          // Update tuple type element to remove infer type
          ast_t* old_ele = ast_childidx(ast_type(left), path_node.index);
          ast_replace(&old_ele, ast_type(p));
          ret_val = INFER_OK;
        }

        path_node.index++;
      }

      // Pop our node off the path
      path->next = NULL;
      return ret_val;
    }

    case TK_VAR:
    case TK_LET:
    {
      ast_t* var_type = ast_type(left);
      assert(var_type != NULL);

      if(ast_id(var_type) != TK_INFERTYPE)  // No inferring needed
        return INFER_NOP;

      ast_t* infer_type = find_infer_type(r_type, path->root->next);

      if(infer_type == NULL)
      {
        ast_error(left, "could not infer type of local");
        ast_settype(left, ast_from(left, TK_ERRORTYPE));
        return INFER_ERROR;
      }

      // Variable type is the alias of the inferred type
      ast_t* a_type = alias(infer_type);
      ast_settype(left, a_type);
      ast_settype(ast_child(left), a_type);

      // Add the type to the var / let AST as if it had been specified by the
      // user. Not really needed, but makes testing easier
      ast_t* speced_type = ast_childidx(left, 1);
      assert(ast_id(speced_type) == TK_NONE);
      ast_replace(&speced_type, a_type);

      ast_free_unattached(infer_type);
      return INFER_OK;
    }

    default:
      // No locals to infer here
      return INFER_NOP;
  }
}
示例#19
0
文件: gencall.c 项目: Theodus/ponyc
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, postfix, positional, named, question);
  AST_GET_CHILDREN(postfix, receiver, method);
  ast_t* typeargs = NULL;

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

  // 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 = deferred_reify(reify, method, c->opt);
      AST_GET_CHILDREN_NO_DECL(receiver, receiver, method);
      break;

    default: {}
  }

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

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

  ast_free_unattached(type);
  ast_free_unattached(typeargs);

  // Generate the arguments.
  size_t count = m->param_count + 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:
        args[0] = gen_constructor_receiver(c, t, ast);
        is_new_call = true;
        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(((compile_type_t*)t->c_type)->use_type);
  }

  // Static or virtual dispatch.
  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: {}
    }
  }

  bool bare = m->cap == TK_AT;
  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.
    codegen_debugloc(c, ast);
    gen_send_message(c, m, 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;
    }
  } else {
    LLVMTypeRef f_type = LLVMGetElementType(LLVMTypeOf(func));
    LLVMTypeRef* params = (LLVMTypeRef*)ponyint_pool_alloc_size(buf_size);
    LLVMGetParamTypes(f_type, params + (bare ? 1 : 0));

    arg = ast_child(positional);
    i = 1;

    while(arg != NULL)
    {
      ast_t* arg_type = deferred_reify(reify, ast_type(arg), c->opt);
      args[i] = gen_assign_cast(c, params[i], args[i], arg_type);
      ast_free_unattached(arg_type);
      arg = ast_sibling(arg);
      i++;
    }

    uintptr_t arg_offset = 0;
    if(bare)
    {
      arg_offset = 1;
      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 + arg_offset, i, "", !bare);
      else
        r = codegen_call(c, func, args + arg_offset, i, !bare);

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

      codegen_debugloc(c, NULL);
      ponyint_pool_free_size(buf_size, params);
    }
  }

  // Bare methods with None return type return void, special case a None return
  // value.
  if(bare && is_none(m->result->ast))
    r = c->none_instance;

  // 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);
  return r;
}
示例#20
0
文件: operator.c 项目: mgist/ponyc
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(!is_lvalue(&opt->check, left, is_result_needed(ast)))
  {
    ast_error(ast, "left side must be something that can be assigned to");
    return false;
  }

  assert(l_type != NULL);

  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(!infer_locals(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);

  if(!is_subtype(a_type, l_type, true))
  {
    ast_error(ast, "right side must be a subtype of left side");
    ast_error(a_type, "right side type: %s", ast_print_type(a_type));
    ast_error(l_type, "left side type: %s", ast_print_type(l_type));
    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(ast,
          "can't destructure a union using assignment, use pattern matching "
          "instead");
        break;

      case TK_ISECTTYPE:
        ast_error(ast,
          "can't destructure an intersection using assignment, use pattern "
          "matching instead");
        break;

      default:
        assert(0);
        break;
    }

    ast_error(a_type, "right side type: %s", ast_print_type(a_type));
    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(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(ast, "not safe to write right side to left side");
    ast_error(a_type, "right side type: %s", ast_print_type(a_type));
    ast_free_unattached(a_type);
    return false;
  }

  ast_free_unattached(a_type);

  // If it's an embedded field, check for a constructor result.
  if(ast_id(left) == TK_EMBEDREF)
  {
    if((ast_id(right) != TK_CALL) ||
      (ast_id(ast_childidx(right, 2)) != TK_NEWREF))
    {
      ast_error(ast, "an embedded field must be assigned using a constructor");
      return false;
    }
  }

  ast_settype(ast, consume_type(l_type, TK_NONE));
  ast_inheritflags(ast);
  return true;
}
示例#21
0
文件: control.c 项目: Sendence/ponyc
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(opt->check.errors, 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(opt, type, left);
    ast_inheritbranch(ast, left);
    branch_count++;
  }

  if(!is_control_type(r_type))
  {
    type = control_type_add_branch(opt, 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(opt->check.errors, ast_sibling(ast), "unreachable code");
      return false;
    }

    type = ast_from(ast, TK_IF);
  }

  ast_settype(ast, type);
  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;
}
示例#22
0
文件: operator.c 项目: mgist/ponyc
static bool is_lvalue(typecheck_t* t, ast_t* ast, bool need_value)
{
  switch(ast_id(ast))
  {
    case TK_DONTCARE:
      // Can only assign to it if we don't need the value.
      return !need_value;

    case TK_VAR:
    case TK_LET:
      return assign_id(t, ast_child(ast), ast_id(ast) == TK_LET, need_value);

    case TK_VARREF:
    {
      ast_t* id = ast_child(ast);
      return assign_id(t, id, false, need_value);
    }

    case TK_LETREF:
    {
      ast_error(ast, "can't assign to a let local");
      return false;
    }

    case TK_FVARREF:
    {
      AST_GET_CHILDREN(ast, left, right);

      if(ast_id(left) == TK_THIS)
        return assign_id(t, right, false, need_value);

      return true;
    }

    case TK_FLETREF:
    {
      AST_GET_CHILDREN(ast, left, right);

      if(ast_id(left) != TK_THIS)
      {
        ast_error(ast, "can't assign to a let field");
        return false;
      }

      if(t->frame->loop_body != NULL)
      {
        ast_error(ast, "can't assign to a let field in a loop");
        return false;
      }

      return assign_id(t, right, true, need_value);
    }

    case TK_EMBEDREF:
    {
      AST_GET_CHILDREN(ast, left, right);

      if(ast_id(left) != TK_THIS)
      {
        ast_error(ast, "can't assign to an embed field");
        return false;
      }

      if(t->frame->loop_body != NULL)
      {
        ast_error(ast, "can't assign to an embed field in a loop");
        return false;
      }

      return assign_id(t, right, true, need_value);
    }

    case TK_TUPLE:
    {
      // A tuple is an lvalue if every component expression is an lvalue.
      ast_t* child = ast_child(ast);

      while(child != NULL)
      {
        if(!is_lvalue(t, child, need_value))
          return false;

        child = ast_sibling(child);
      }

      return true;
    }

    case TK_SEQ:
    {
      // A sequence is an lvalue if it has a single child that is an lvalue.
      // This is used because the components of a tuple are sequences.
      ast_t* child = ast_child(ast);

      if(ast_sibling(child) != NULL)
        return false;

      return is_lvalue(t, child, need_value);
    }

    default: {}
  }

  return false;
}
示例#23
0
文件: control.c 项目: Sendence/ponyc
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);

  ast_t* parent = ast_parent(ast);
  ast_t* current = ast;

  while(ast_id(parent) == TK_SEQ)
  {
    assert(ast_childlast(parent) == current);
    current = parent;
    parent = ast_parent(parent);
  }

  if(current == t->frame->method_body)
  {
    ast_error(opt->check.errors, 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(opt->check.errors, 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(opt->check.errors, 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, opt) ||
        !is_subtype(a_body_type, a_type, &info, opt))
      {
        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, opt->check.errors);
        ok = false;
      }

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

  ast_settype(ast, ast_from(ast, TK_RETURN));
  return ok;
}
示例#24
0
文件: viewpoint.c 项目: volth/ponyc
bool safe_to_write(ast_t* ast, ast_t* type)
{
  switch(ast_id(ast))
  {
    case TK_VAR:
    case TK_LET:
    case TK_VARREF:
    case TK_DONTCARE:
      return true;

    case TK_FVARREF:
    case TK_FLETREF:
    case TK_EMBEDREF:
    {
      // If the ast is x.f, we need the type of x, which will be a nominal
      // type or an arrow type, since we were able to lookup a field on it.
      AST_GET_CHILDREN(ast, left, right);
      ast_t* l_type = ast_type(left);

      // Any viewpoint adapted type will not be safe to write to.
      if(ast_id(l_type) != TK_NOMINAL)
        return false;

      token_id l_cap = cap_single(l_type);

      // If the RHS is safe to write, we're done.
      if(safe_field_write(l_cap, type))
        return true;

      // If the field type (without adaptation) is safe, then it's ok as
      // well. So iso.tag = ref should be allowed.
      ast_t* r_type = ast_type(right);
      return safe_field_write(l_cap, r_type);
    }

    case TK_TUPLE:
    {
      // At this point, we know these will be the same length.
      assert(ast_id(type) == TK_TUPLETYPE);
      ast_t* child = ast_child(ast);
      ast_t* type_child = ast_child(type);

      while(child != NULL)
      {
        if(!safe_to_write(child, type_child))
          return false;

        child = ast_sibling(child);
        type_child = ast_sibling(type_child);
      }

      assert(type_child == NULL);
      return true;
    }

    case TK_SEQ:
    {
      // Occurs when there is a tuple on the left. Each child of the tuple will
      // be a sequence, but only sequences with a single writeable child are
      // valid. Other types won't appear here.
      return safe_to_write(ast_child(ast), type);
    }

    default: {}
  }

  assert(0);
  return false;
}
示例#25
0
文件: ast.c 项目: federicosan/ponyc
static void print_type(printbuf_t* buffer, ast_t* type)
{
  switch(ast_id(type))
  {
    case TK_NOMINAL:
    {
      AST_GET_CHILDREN(type, package, id, typeargs, cap, ephemeral);
      ast_t* origpkg = ast_sibling(ephemeral);

      if(origpkg != NULL && ast_id(origpkg) != TK_NONE)
        printbuf(buffer, "%s.", ast_name(origpkg));

      ast_t* def = (ast_t*)ast_data(type);
      if(def != NULL)
        id = ast_child(def);

      printbuf(buffer, "%s", ast_nice_name(id));

      if(ast_id(typeargs) != TK_NONE)
        print_typeexpr(buffer, typeargs, ", ", true);

      if(ast_id(cap) != TK_NONE)
        printbuf(buffer, " %s", token_print(cap->t));

      if(ast_id(ephemeral) != TK_NONE)
        printbuf(buffer, "%s", token_print(ephemeral->t));

      break;
    }

    case TK_UNIONTYPE:
      print_typeexpr(buffer, type, " | ", false);
      break;

    case TK_ISECTTYPE:
      print_typeexpr(buffer, type, " & ", false);
      break;

    case TK_TUPLETYPE:
      print_typeexpr(buffer, type, ", ", false);
      break;

    case TK_TYPEPARAMREF:
    {
      AST_GET_CHILDREN(type, id, cap, ephemeral);
      printbuf(buffer, "%s", ast_nice_name(id));

      if(ast_id(cap) != TK_NONE)
        printbuf(buffer, " %s", token_print(cap->t));

      if(ast_id(ephemeral) != TK_NONE)
        printbuf(buffer, " %s", token_print(ephemeral->t));

      break;
    }

    case TK_ARROW:
    {
      AST_GET_CHILDREN(type, left, right);
      print_type(buffer, left);
      printbuf(buffer, "->");
      print_type(buffer, right);
      break;
    }

    case TK_THISTYPE:
      printbuf(buffer, "this");
      break;

    case TK_DONTCARE:
      printbuf(buffer, "_");
      break;

    case TK_FUNTYPE:
      printbuf(buffer, "function");
      break;

    case TK_INFERTYPE:
      printbuf(buffer, "to_infer");
      break;

    case TK_ERRORTYPE:
      printbuf(buffer, "<type error>");
      break;

    case TK_NONE:
      break;

    default:
      printbuf(buffer, "%s", token_print(type->t));
  }
}
示例#26
0
文件: gencall.c 项目: Theodus/ponyc
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;
}
示例#27
0
文件: match.c 项目: mgist/ponyc
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;
}
示例#28
0
文件: gencall.c 项目: Theodus/ponyc
static bool special_case_call(compile_t* c, ast_t* ast, LLVMValueRef* value)
{
  AST_GET_CHILDREN(ast, postfix, positional, named, question);

  if((ast_id(postfix) != TK_FUNREF) || (ast_id(named) != TK_NONE))
    return false;

  AST_GET_CHILDREN(postfix, receiver, method);
  ast_t* receiver_type = deferred_reify(c->frame->reify, ast_type(receiver),
    c->opt);

  const char* name = NULL;

  if(ast_id(receiver_type) == TK_NOMINAL)
  {
    AST_GET_CHILDREN(receiver_type, package, id);

    if(ast_name(package) == c->str_builtin)
      name = ast_name(id);
  }

  ast_free_unattached(receiver_type);

  if(name == NULL)
    return false;

  if(name == c->str_Bool)
    return special_case_operator(c, ast, value, true, true);

  if((name == c->str_I8) ||
    (name == c->str_I16) ||
    (name == c->str_I32) ||
    (name == c->str_I64) ||
    (name == c->str_ILong) ||
    (name == c->str_ISize) ||
    (name == c->str_U8) ||
    (name == c->str_U16) ||
    (name == c->str_U32) ||
    (name == c->str_U64) ||
    (name == c->str_ULong) ||
    (name == c->str_USize) ||
    (name == c->str_F32) ||
    (name == c->str_F64)
    )
  {
    return special_case_operator(c, ast, value, false, true);
  }

  if((name == c->str_I128) || (name == c->str_U128))
  {
    bool native128 = target_is_native128(c->opt->triple);
    return special_case_operator(c, ast, value, false, native128);
  }

  if(name == c->str_Platform)
  {
    *value = special_case_platform(c, ast);
    return true;
  }

  return false;
}
示例#29
0
文件: genexpr.c 项目: DevL/ponyc
LLVMValueRef gen_expr(compile_t* c, ast_t* ast)
{
  LLVMValueRef ret;
  bool has_scope = ast_has_scope(ast);
  bool has_source = codegen_hassource(c);

  if(has_scope)
  {
    codegen_pushscope(c);

    // Dwarf a new lexical scope, if necessary.
    if(has_source)
      dwarf_lexicalscope(&c->dwarf, ast);
  }

  switch(ast_id(ast))
  {
    case TK_SEQ:
      ret = gen_seq(c, ast);
      break;

    case TK_FVARREF:
    case TK_FLETREF:
      ret = gen_fieldload(c, ast);
      break;

    case TK_EMBEDREF:
      ret = gen_fieldptr(c, ast);
      break;

    case TK_PARAMREF:
      ret = gen_param(c, ast);
      break;

    case TK_VAR:
    case TK_LET:
    case TK_MATCH_CAPTURE:
      ret = gen_localdecl(c, ast);
      break;

    case TK_VARREF:
    case TK_LETREF:
      ret = gen_localload(c, ast);
      break;

    case TK_IF:
      ret = gen_if(c, ast);
      break;

    case TK_WHILE:
      ret = gen_while(c, ast);
      break;

    case TK_REPEAT:
      ret = gen_repeat(c, ast);
      break;

    case TK_TRY:
    case TK_TRY_NO_CHECK:
      ret = gen_try(c, ast);
      break;

    case TK_MATCH:
      ret = gen_match(c, ast);
      break;

    case TK_CALL:
      ret = gen_call(c, ast);
      break;

    case TK_CONSUME:
      ret = gen_expr(c, ast_childidx(ast, 1));
      break;

    case TK_RECOVER:
      ret = gen_expr(c, ast_childidx(ast, 1));
      break;

    case TK_BREAK:
      ret = gen_break(c, ast);
      break;

    case TK_CONTINUE:
      ret = gen_continue(c, ast);
      break;

    case TK_RETURN:
      ret = gen_return(c, ast);
      break;

    case TK_ERROR:
      ret = gen_error(c, ast);
      break;

    case TK_IS:
      ret = gen_is(c, ast);
      break;

    case TK_ISNT:
      ret = gen_isnt(c, ast);
      break;

    case TK_ASSIGN:
      ret = gen_assign(c, ast);
      break;

    case TK_THIS:
      ret = gen_this(c, ast);
      break;

    case TK_TRUE:
      ret = LLVMConstInt(c->i1, 1, false);
      break;

    case TK_FALSE:
      ret = LLVMConstInt(c->i1, 0, false);
      break;

    case TK_INT:
      ret = gen_int(c, ast);
      break;

    case TK_FLOAT:
      ret = gen_float(c, ast);
      break;

    case TK_STRING:
      ret = gen_string(c, ast);
      break;

    case TK_TUPLE:
      ret = gen_tuple(c, ast);
      break;

    case TK_FFICALL:
      ret = gen_ffi(c, ast);
      break;

    case TK_ADDRESS:
      ret = gen_addressof(c, ast);
      break;

    case TK_IDENTITY:
      ret = gen_identity(c, ast);
      break;

    case TK_DONTCARE:
      ret = GEN_NOVALUE;
      break;

    case TK_COMPILE_INTRINSIC:
      ast_error(ast, "unimplemented compile intrinsic");
      return NULL;

    case TK_COMPILE_ERROR:
    {
      ast_t* reason_seq = ast_child(ast);
      ast_t* reason = ast_child(reason_seq);
      ast_error(ast, "compile error: %s", ast_name(reason));
      return NULL;
    }

    default:
      ast_error(ast, "not implemented (codegen unknown)");
      return NULL;
  }

  if(has_scope)
  {
    codegen_popscope(c);

    if(has_source)
      dwarf_finish(&c->dwarf);
  }

  return ret;
}
示例#30
0
文件: program.c 项目: enigma/ponyc
void program_lib_build_args(ast_t* program, pass_opt_t* opt,
  const char* path_preamble, const char* rpath_preamble,
  const char* global_preamble, const char* global_postamble,
  const char* lib_premable, const char* lib_postamble)
{
  assert(program != NULL);
  assert(ast_id(program) == TK_PROGRAM);
  assert(global_preamble != NULL);
  assert(global_postamble != NULL);
  assert(lib_premable != NULL);
  assert(lib_postamble != NULL);

  program_t* data = (program_t*)ast_data(program);
  assert(data != NULL);
  assert(data->lib_args == NULL); // Not yet built args

  // Start with an arbitrary amount of space
  data->lib_args_alloced = 256;
  data->lib_args = (char*)malloc(data->lib_args_alloced);
  data->lib_args[0] = '\0';
  data->lib_args_size = 0;

  // Library paths defined in the source code.
  for(strlist_t* p = data->libpaths; p != NULL; p = strlist_next(p))
  {
    const char* libpath = strlist_data(p);
    append_to_args(data, path_preamble);
    append_to_args(data, libpath);
    append_to_args(data, " ");

    if(rpath_preamble != NULL)
    {
      append_to_args(data, rpath_preamble);
      append_to_args(data, libpath);
      append_to_args(data, " ");
    }
  }

  // Library paths from the command line and environment variable.
  for(strlist_t* p = package_paths(); p != NULL; p = strlist_next(p))
  {
    const char* libpath = quoted_locator(opt, NULL, strlist_data(p));

    if(libpath == NULL)
      continue;

    append_to_args(data, path_preamble);
    append_to_args(data, libpath);
    append_to_args(data, " ");

    if(rpath_preamble != NULL)
    {
      append_to_args(data, rpath_preamble);
      append_to_args(data, libpath);
      append_to_args(data, " ");
    }
  }

  // Library names.
  append_to_args(data, global_preamble);

  for(strlist_t* p = data->libs; p != NULL; p = strlist_next(p))
  {
    const char* lib = strlist_data(p);
    bool amble = !is_path_absolute(&lib[1]);

    if(amble)
      append_to_args(data, lib_premable);

    append_to_args(data, lib);

    if(amble)
      append_to_args(data, lib_postamble);

    append_to_args(data, " ");
  }

  append_to_args(data, global_postamble);
}