Esempio n. 1
0
// Write the TQFN for the given type to a new buffer.
// By default the type name is taken from the given AST, however this can be
// overridden by the type_name parameter. Pass NULL to use the default.
// The returned buffer must be freed using pool_free_size when no longer
// needed. Note that the size reported is the size of the buffer and includes a
// terminator.
static char* write_tqfn(ast_t* type, const char* type_name, size_t* out_size)
{
  assert(type != NULL);
  assert(out_size != NULL);

  ast_t* package = ast_nearest(type, TK_PACKAGE);
  assert(package != NULL);

  // We need the qualified package name and the type name
  const char* pkg_qual_name = package_qualified_name(package);

  if(type_name == NULL)
    type_name = ast_name(ast_child(type));

  assert(pkg_qual_name != NULL);
  assert(type_name != NULL);

  char* buffer = doc_cat(pkg_qual_name, "-", type_name, "", "", out_size);

  // Change slashes to dashes
  for(char* p = buffer; *p != '\0'; p++)
  {
    if(*p == '/')
      *p = '-';
  }

  return buffer;
}
Esempio n. 2
0
static bool is_receiver_safe(typecheck_t* t, ast_t* ast)
{
  switch(ast_id(ast))
  {
     case TK_THIS:
     case TK_FLETREF:
     case TK_FVARREF:
     case TK_EMBEDREF:
     case TK_PARAMREF:
     case TK_TUPLEELEMREF:
     {
       ast_t* type = ast_type(ast);
       return sendable(type);
     }

     case TK_LETREF:
     case TK_VARREF:
     {
       ast_t* def = (ast_t*)ast_data(ast);
       pony_assert(def != NULL);
       ast_t* def_recover = ast_nearest(def, TK_RECOVER);
       if(t->frame->recover == def_recover)
         return true;
       ast_t* type = ast_type(ast);
       return sendable(type);
     }

     default:
       // Unsafe receivers inside expressions are catched before we get there.
       return true;
  }
}
Esempio n. 3
0
static bool is_receiver_safe(typecheck_t* t, ast_t* ast)
{
  switch(ast_id(ast))
  {
     case TK_THIS:
     case TK_FLETREF:
     case TK_FVARREF:
     case TK_EMBEDREF:
     case TK_PARAMREF:
     {
       ast_t* type = ast_type(ast);
       return sendable(type);
     }

     case TK_LETREF:
     case TK_VARREF:
     {
       const char* name = ast_name(ast_child(ast));
       sym_status_t status;
       ast_t* def = ast_get(ast, name, &status);
       ast_t* def_recover = ast_nearest(def, TK_RECOVER);
       if(t->frame->recover == def_recover)
         return true;
       ast_t* type = ast_type(ast);
       return sendable(type);
     }

     default:
       // Unsafe receivers inside expressions are catched before we get there.
       return true;
  }
}
Esempio n. 4
0
// Process the given provided method for the given entity.
// The method passed should be reified already and will be freed by this
// function.
static bool provided_method(ast_t* entity, ast_t* reified_method,
  ast_t* raw_method, ast_t** last_method)
{
  assert(entity != NULL);
  assert(reified_method != NULL);
  assert(last_method != NULL);

  const char* entity_name = ast_name(ast_child(entity));

  if(ast_id(reified_method) == TK_BE || ast_id(reified_method) == TK_NEW)
  {
    // Modify return type to the inheritting type
    ast_t* ret_type = ast_childidx(reified_method, 4);
    assert(ast_id(ret_type) == TK_NOMINAL);

    const char* pkg_name = package_name(ast_nearest(entity, TK_PACKAGE));
    ast_set_name(ast_childidx(ret_type, 0), pkg_name);
    ast_set_name(ast_childidx(ret_type, 1), entity_name);
  }

  // Ignore docstring
  ast_t* doc = ast_childidx(reified_method, 7);

  if(ast_id(doc) == TK_STRING)
  {
    ast_set_name(doc, "");
    ast_setid(doc, TK_NONE);
  }

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

  ast_t* existing = ast_get(entity, name, NULL);

  if(existing != NULL && is_field(existing))
  {
    ast_error(existing, "field '%s' clashes with provided method", name);
    ast_error(raw_method, "method is defined here");
    ast_free_unattached(reified_method);
    return false;
  }

  existing = add_method(entity, existing, reified_method, last_method);

  if(existing == NULL)
  {
    ast_free_unattached(reified_method);
    return false;
  }

  method_t* info = (method_t*)ast_data(existing);
  assert(info != NULL);

  if(!record_default_body(reified_method, raw_method, info))
    ast_free_unattached(reified_method);

  return true;
}
Esempio n. 5
0
static const char* nominal_name(ast_t* ast)
{
  AST_GET_CHILDREN(ast, package, name, typeargs);

  ast_t* def = (ast_t*)ast_data(ast);
  ast_t* pkg = ast_nearest(def, TK_PACKAGE);
  const char* s = package_symbol(pkg);

  return build_name(s, ast_name(name), typeargs, false);
}
Esempio n. 6
0
static void type_append(printbuf_t* buf, ast_t* type, bool first)
{
  switch(ast_id(type))
  {
    case TK_UNIONTYPE:
    {
      // u3_Arg1_Arg2_Arg3
      printbuf(buf, "u%d", ast_childcount(type));
      types_append(buf, type);
      return;
    }

    case TK_ISECTTYPE:
    {
      // i3_Arg1_Arg2_Arg3
      printbuf(buf, "i%d", ast_childcount(type));
      types_append(buf, type);
      return;
    }

    case TK_TUPLETYPE:
    {
      // t3_Arg1_Arg2_Arg3
      printbuf(buf, "t%d", ast_childcount(type));
      types_append(buf, type);
      return;
    }

    case TK_NOMINAL:
    {
      // pkg_Type[_Arg1_Arg2]_cap
      AST_GET_CHILDREN(type, package, name, typeargs, cap, eph);

      ast_t* def = (ast_t*)ast_data(type);
      ast_t* pkg = ast_nearest(def, TK_PACKAGE);
      const char* pkg_name = package_symbol(pkg);

      if(pkg_name != NULL)
        printbuf(buf, "%s_", pkg_name);

      printbuf(buf, "%s", ast_name(name));
      types_append(buf, typeargs);

      if(!first)
        printbuf(buf, "_%s", ast_get_print(cap));

      return;
    }

    default: {}
  }

  assert(0);
}
Esempio n. 7
0
ast_t* package_load(ast_t* from, const char* path, pass_opt_t* options)
{
  const char* magic = find_magic_package(path);
  const char* name = path;

  if(magic == NULL)
  {
    // Lookup (and hence normalise) path
    name = find_path(from, path);

    if(name == NULL)
      return NULL;
  }

  ast_t* program = ast_nearest(from, TK_PROGRAM);
  ast_t* package = ast_get(program, name, NULL);

  if(package != NULL) // Package already loaded
    return package;

  package = create_package(program, name);

  if(report_build)
    printf("Building %s\n", path);

  if(magic != NULL)
  {
    if(!parse_source_code(package, magic, options))
      return NULL;
  }
  else
  {
    if(!parse_files_in_dir(package, name, options))
      return NULL;
  }

  if(ast_child(package) == NULL)
  {
    ast_error(package, "no source files in package '%s'", path);
    return NULL;
  }

  if(!package_passes(package, options))
    return NULL;

  return package;
}
Esempio n. 8
0
/// Process a "path:" scheme use command.
bool use_path(ast_t* use, const char* locator, ast_t* name,
  pass_opt_t* options)
{
  (void)name;

  const char* libpath = quoted_locator(options, use, locator);

  if(libpath == NULL)
    return false;

  ast_t* p = ast_nearest(use, TK_PROGRAM);
  program_t* prog = (program_t*)ast_data(p);
  pony_assert(prog->lib_args == NULL); // Not yet built args

  if(strlist_find(prog->libpaths, libpath) != NULL) // Ignore duplicate
    return true;

  prog->libpaths = strlist_append(prog->libpaths, libpath);
  return true;
}
Esempio n. 9
0
ast_result_t ast_visit_scope(ast_t** ast, ast_visit_t pre, ast_visit_t post,
  pass_opt_t* options, pass_id pass)
{
  typecheck_t* t = &options->check;
  ast_t* module = ast_nearest(*ast, TK_MODULE);
  ast_t* package = ast_parent(module);
  assert(module != NULL);
  assert(package != NULL);

  frame_push(t, NULL);
  frame_push(t, package);
  frame_push(t, module);

  ast_result_t ret = ast_visit(ast, pre, post, options, pass);

  frame_pop(t);
  frame_pop(t);
  frame_pop(t);

  return ret;
}
Esempio n. 10
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)
{
  // First check for an absolute path
  if(is_path_absolute(path))
    return try_path(NULL, path);

  const char* result;

  if((from == NULL) || (ast_id(from) == TK_PROGRAM))
  {
    // Try a path relative to the current working directory
    result = try_path(NULL, path);

    if(result != NULL)
      return result;
  }
  else
  {
    // Try a path relative to the importing package
    from = ast_nearest(from, TK_PACKAGE);
    package_t* pkg = (package_t*)ast_data(from);
    result = try_path(pkg->path, 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;
}
Esempio n. 11
0
static ast_t* get_package_scope(ast_t* scope, ast_t* ast)
{
  assert(ast_id(ast) == TK_NOMINAL);
  ast_t* package_id = ast_child(ast);

  // Find our actual package.
  if(ast_id(package_id) != TK_NONE)
  {
    const char* name = ast_name(package_id);

    if(name[0] == '$')
      scope = ast_get(ast_nearest(scope, TK_PROGRAM), name, NULL);
    else
      scope = ast_get(scope, name, NULL);

    if((scope == NULL) || (ast_id(scope) != TK_PACKAGE))
    {
      ast_error(package_id, "can't find package '%s'", name);
      return NULL;
    }
  }

  return scope;
}
Esempio n. 12
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_STRUCT:
    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(t->frame->def_arg != NULL)
      {
        ast_error(ast, "can't reference a parameter in a default argument");
        return false;
      }

      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(!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", ast_nice_name(def));
        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:
        case TK_MATCH_CAPTURE:
          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;
}
Esempio n. 13
0
static ast_result_t sugar_object(pass_opt_t* opt, ast_t** astp)
{
  typecheck_t* t = &opt->check;
  ast_t* ast = *astp;
  ast_result_t r = AST_OK;

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

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

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

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

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

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

  ast_free_unattached(t_args);

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

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

  bool has_fields = false;
  bool has_behaviours = false;

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

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

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

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

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

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

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

        has_fields = true;
        break;
      }

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

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

    member = ast_sibling(member);
  }

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

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

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

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

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

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

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

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

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

  return r;
}
Esempio n. 14
0
// Add all methods from the provides list of the given entity into lists in the
// given symbol table.
static bool collate_provided(ast_t* entity, methods_t* method_info)
{
  assert(entity != NULL);

  bool r = true;
  ast_t* traits = ast_childidx(entity, 3);

  for(ast_t* t = ast_child(traits); t != NULL; t = ast_sibling(t))
  {
    assert(ast_id(t) == TK_NOMINAL);
    ast_t* trait_def = (ast_t*)ast_data(t);
    assert(trait_def != NULL);

    // TODO: Check whether we need an error here
    if((ast_id(trait_def) != TK_TRAIT) && (ast_id(trait_def) != TK_INTERFACE))
      return false;

    // Check for duplicates in our provides list
    // This is just simple compare of each entry against all the other. This is
    // clearly O(n^2), but since provides lists are likely to be small that
    // should be OK. If it turns out to be a problem it can be changed later.
    for(ast_t* p = ast_child(traits); p != t; p = ast_sibling(p))
    {
      if(trait_def == (ast_t*)ast_data(p))
      {
        ast_error(t, "duplicate entry in provides list");
        ast_error(p, "previous entry here");
      }
    }

    if(!build_entity_def(trait_def))
      return false;

    ast_t* type_params = ast_childidx(trait_def, 1);
    ast_t* type_args = ast_childidx(t, 2);
    ast_t* trait_methods = ast_childidx(trait_def, 4);

    for(ast_t* m = ast_child(trait_methods); m != NULL; m = ast_sibling(m))
    {
      // Reify the method with the type parameters from trait definition and
      // the reified type arguments from trait reference
      ast_t* r_method = reify(m, type_params, type_args);
      const char* entity_name = ast_name(ast_child(entity));

      if(ast_id(r_method) == TK_BE || ast_id(r_method) == TK_NEW)
      {
        // Modify return type to the inheritting type
        ast_t* ret_type = ast_childidx(r_method, 4);
        assert(ast_id(ret_type) == TK_NOMINAL);

        const char* pkg_name = package_name(ast_nearest(entity, TK_PACKAGE));
        ast_set_name(ast_childidx(ret_type, 0), pkg_name);
        ast_set_name(ast_childidx(ret_type, 1), entity_name);
      }

      if(!add_method_to_list(r_method, method_info, entity_name))
        r = false;
    }
  }

  return r;
}
Esempio n. 15
0
bool names_nominal(pass_opt_t* opt, ast_t* scope, ast_t** astp)
{
  typecheck_t* t = &opt->check;
  ast_t* ast = *astp;

  if(ast_data(ast) != NULL)
    return true;

  AST_GET_CHILDREN(ast, package_id, type_id, typeparams, cap, eph);

  // Keep some stats.
  t->stats.names_count++;

  if(ast_id(cap) == TK_NONE)
    t->stats.default_caps_count++;

  ast_t* r_scope = get_package_scope(scope, ast);

  if(r_scope == NULL)
    return false;

  bool local_package =
    (r_scope == scope) || (r_scope == ast_nearest(ast, TK_PACKAGE));

  // Find our definition.
  const char* name = ast_name(type_id);
  ast_t* def = ast_get(r_scope, name, NULL);
  bool r = true;

  if(def == NULL)
  {
    ast_error(type_id, "can't find definition of '%s'", name);
    r = false;
  } else {
    // Check for a private type.
    if(!local_package && (name[0] == '_'))
    {
      ast_error(type_id, "can't access a private type from another package");
      r = false;
    }

    switch(ast_id(def))
    {
      case TK_TYPE:
        r = names_typealias(opt, astp, def);
        break;

      case TK_TYPEPARAM:
        r = names_typeparam(astp, def);
        break;

      case TK_INTERFACE:
      case TK_TRAIT:
      case TK_PRIMITIVE:
      case TK_CLASS:
      case TK_ACTOR:
        r = names_type(t, astp, def);
        break;

      default:
        ast_error(type_id, "definition of '%s' is not a type", name);
        r = false;
        break;
    }
  }

  return r;
}
Esempio n. 16
0
static ast_t* lookup_nominal(typecheck_t* t, ast_t* from, ast_t* orig,
  ast_t* type, const char* name, bool errors)
{
  assert(ast_id(type) == TK_NOMINAL);
  ast_t* def = (ast_t*)ast_data(type);
  ast_t* type_name = ast_child(def);
  ast_t* find = ast_get(def, name, NULL);

  if(find != NULL)
  {
    switch(ast_id(find))
    {
      case TK_FVAR:
      case TK_FLET:
      case TK_NEW:
      case TK_BE:
      case TK_FUN:
        break;

      default:
        find = NULL;
    }
  }

  if(find == NULL)
  {
    if(errors)
      ast_error(from, "couldn't find '%s' in '%s'", name, ast_name(type_name));

    return NULL;
  }

  if((name[0] == '_') && (from != NULL) && (t != NULL))
  {
    switch(ast_id(find))
    {
      case TK_FVAR:
      case TK_FLET:
        if(t->frame->type != def)
        {
          if(errors)
          {
            ast_error(from,
              "can't lookup private fields from outside the type");
          }

          return NULL;
        }
        break;

      case TK_NEW:
      case TK_BE:
      case TK_FUN:
      {
        if(ast_nearest(def, TK_PACKAGE) != t->frame->package)
        {
          if(errors)
          {
            ast_error(from,
              "can't lookup private methods from outside the package");
          }

          return NULL;
        }
        break;
      }

      default:
        assert(0);
        return NULL;
    }

    if(!strcmp(name, "_final"))
    {
      switch(ast_id(find))
      {
        case TK_NEW:
        case TK_BE:
        case TK_FUN:
          if(errors)
            ast_error(from, "can't lookup a _final function");

          return NULL;

        default: {}
      }
    }
  }

  ast_t* typeparams = ast_sibling(type_name);
  ast_t* typeargs = ast_childidx(type, 2);

  find = ast_dup(find);
  orig = ast_dup(orig);
  replace_thistype(&find, orig);
  ast_free_unattached(orig);

  ast_t* r_find = reify(from, find, typeparams, typeargs);

  if(r_find != find)
  {
    ast_free_unattached(find);
    find = r_find;
  }

  if((find != NULL) && !flatten_arrows(&find, errors))
  {
    if(errors)
      ast_error(from, "can't look this up on a tag");

    ast_free_unattached(find);
    return NULL;
  }

  return find;
}
Esempio n. 17
0
static ast_t* lookup_nominal(pass_opt_t* opt, ast_t* from, ast_t* orig,
                             ast_t* type, const char* name, bool errors)
{
    assert(ast_id(type) == TK_NOMINAL);
    typecheck_t* t = &opt->check;

    ast_t* def = (ast_t*)ast_data(type);
    AST_GET_CHILDREN(def, type_id, typeparams);
    const char* type_name = ast_name(type_id);

    if((type_name[0] == '_') && (from != NULL) && (opt != NULL))
    {
        if(ast_nearest(def, TK_PACKAGE) != t->frame->package)
        {
            if(errors)
            {
                ast_error(from,
                          "can't lookup fields or methods on private types from other packages"
                         );
            }

            return NULL;
        }
    }

    ast_t* find = ast_get(def, name, NULL);

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

        case TK_NEW:
        case TK_BE:
        case TK_FUN:
        {
            // Typecheck default args immediately.
            if(opt != NULL)
            {
                AST_GET_CHILDREN(find, cap, id, typeparams, params);
                ast_t* param = ast_child(params);

                while(param != NULL)
                {
                    AST_GET_CHILDREN(param, name, type, def_arg);

                    if((ast_id(def_arg) != TK_NONE) && (ast_type(def_arg) == NULL))
                    {
                        ast_settype(def_arg, ast_from(def_arg, TK_INFERTYPE));

                        if(ast_visit_scope(&def_arg, NULL, pass_expr, opt,
                                           PASS_EXPR) != AST_OK)
                            return false;

                        ast_visit_scope(&def_arg, NULL, pass_nodebug, opt, PASS_ALL);
                    }

                    param = ast_sibling(param);
                }
            }
            break;
        }

        default:
            find = NULL;
        }
    }

    if(find == NULL)
    {
        if(errors)
            ast_error(from, "couldn't find '%s' in '%s'", name, type_name);

        return NULL;
    }

    if((name[0] == '_') && (from != NULL) && (opt != NULL))
    {
        switch(ast_id(find))
        {
        case TK_FVAR:
        case TK_FLET:
        case TK_EMBED:
            if(t->frame->type != def)
            {
                if(errors)
                {
                    ast_error(from,
                              "can't lookup private fields from outside the type");
                }

                return NULL;
            }
            break;

        case TK_NEW:
        case TK_BE:
        case TK_FUN:
        {
            if(ast_nearest(def, TK_PACKAGE) != t->frame->package)
            {
                if(errors)
                {
                    ast_error(from,
                              "can't lookup private methods from outside the package");
                }

                return NULL;
            }
            break;
        }

        default:
            assert(0);
            return NULL;
        }

        if(!strcmp(name, "_final"))
        {
            switch(ast_id(find))
            {
            case TK_NEW:
            case TK_BE:
            case TK_FUN:
                if(errors)
                    ast_error(from, "can't lookup a _final function");

                return NULL;

            default:
            {}
            }
        }
    }

    ast_t* typeargs = ast_childidx(type, 2);
    ast_t* r_find = viewpoint_replacethis(find, orig);
    ast_t* rr_find = reify(r_find, typeparams, typeargs);
    ast_free_unattached(r_find);
    return rr_find;
}
Esempio n. 18
0
bool expr_object(pass_opt_t* opt, ast_t** astp)
{
  ast_t* ast = *astp;
  bool ok = true;

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

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

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

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

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

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

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

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

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

  ast_free_unattached(t_args);

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

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

  bool has_fields = false;
  bool has_behaviours = false;

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

        has_fields = true;
        break;
      }

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

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

    member = ast_sibling(member);
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return ok;
}
Esempio n. 19
0
ast_t* package_load(ast_t* from, const char* path, pass_opt_t* options)
{
  assert(from != NULL);

  const char* magic = find_magic_package(path);
  const char* full_path = path;
  const char* qualified_name = path;
  ast_t* program = ast_nearest(from, TK_PROGRAM);

  if(magic == NULL)
  {
    // Lookup (and hence normalise) path
    bool is_relative = false;
    full_path = find_path(from, path, &is_relative);

    if(full_path == NULL)
      return NULL;

    if((from != program) && is_relative)
    {
      // Package to load is relative to from, build the qualified name
      // The qualified name should be relative to the program being built
      package_t* from_pkg = (package_t*)ast_data(ast_child(program));

      if(from_pkg != NULL)
      {
        const char* base_name = from_pkg->qualified_name;
        size_t base_name_len = strlen(base_name);
        size_t path_len = strlen(path);
        size_t len = base_name_len + path_len + 2;
        char* q_name = (char*)pool_alloc_size(len);
        memcpy(q_name, base_name, base_name_len);
        q_name[base_name_len] = '/';
        memcpy(q_name + base_name_len + 1, path, path_len);
        q_name[len - 1] = '\0';
        qualified_name = stringtab_consume(q_name, len);
      }
    }
  }

  ast_t* package = ast_get(program, full_path, NULL);

  // Package already loaded
  if(package != NULL)
    return package;

  package = create_package(program, full_path, qualified_name);

  if(report_build)
    printf("Building %s -> %s\n", path, full_path);

  if(magic != NULL)
  {
    if(!parse_source_code(package, magic, options))
      return NULL;
  }
  else
  {
    if(!parse_files_in_dir(package, full_path, options))
      return NULL;
  }

  if(ast_child(package) == NULL)
  {
    ast_error(package, "no source files in package '%s'", path);
    return NULL;
  }

  if(!ast_passes_subtree(&package, options, options->program_pass))
  {
    // If these passes failed, don't run future passes.
    ast_setflag(package, AST_FLAG_PRESERVE);
    return NULL;
  }

  return package;
}
Esempio n. 20
0
ast_t* package_load(ast_t* from, const char* path, pass_opt_t* opt)
{
  pony_assert(from != NULL);

  magic_package_t* magic = find_magic_package(path, opt);
  const char* full_path = path;
  const char* qualified_name = path;
  ast_t* program = ast_nearest(from, TK_PROGRAM);

  if(magic == NULL)
  {
    // Lookup (and hence normalise) path
    bool is_relative = false;
    bool found_notdir = false;
    full_path = find_path(from, path, &is_relative, &found_notdir, opt);

    if(full_path == NULL)
    {
      errorf(opt->check.errors, path, "couldn't locate this path");

      if(found_notdir)
        errorf_continue(opt->check.errors, path, "note that a compiler "
          "invocation or a 'use' directive must refer to a directory");

      return NULL;
    }

    if((from != program) && is_relative)
    {
      // Package to load is relative to from, build the qualified name
      // The qualified name should be relative to the program being built
      package_t* from_pkg = (package_t*)ast_data(ast_child(program));

      if(from_pkg != NULL)
      {
        const char* base_name = from_pkg->qualified_name;
        size_t base_name_len = strlen(base_name);
        size_t path_len = strlen(path);
        size_t len = base_name_len + path_len + 2;
        char* q_name = (char*)ponyint_pool_alloc_size(len);
        memcpy(q_name, base_name, base_name_len);
        q_name[base_name_len] = '/';
        memcpy(q_name + base_name_len + 1, path, path_len);
        q_name[len - 1] = '\0';
        qualified_name = stringtab_consume(q_name, len);
      }
    }

    // we are loading the package specified as program dir
    if(from == program)
    {
      // construct the qualified name from the basename of the full path
      const char* basepath = strrchr(full_path, '/');
      if(basepath == NULL)
      {
        basepath = full_path;
      } else {
        basepath = basepath + 1;
      }
      qualified_name = basepath;
    }
  }

  ast_t* package = ast_get(program, full_path, NULL);

  // Package already loaded
  if(package != NULL)
    return package;

  package = create_package(program, full_path, qualified_name, opt);

  if(opt->verbosity >= VERBOSITY_INFO)
    fprintf(stderr, "Building %s -> %s\n", path, full_path);

  if(magic != NULL)
  {
    if(magic->src != NULL)
    {
      if(!parse_source_code(package, magic->src, opt))
        return NULL;
    } else if(magic->mapped_path != NULL) {
      if(!parse_files_in_dir(package, magic->mapped_path, opt))
        return NULL;
    } else {
      return NULL;
    }
  }
  else
  {
    if(!parse_files_in_dir(package, full_path, opt))
      return NULL;
  }

  if(ast_child(package) == NULL)
  {
    ast_error(opt->check.errors, package,
      "no source files in package '%s'", path);
    return NULL;
  }

  if(!ast_passes_subtree(&package, opt, opt->program_pass))
  {
    // If these passes failed, don't run future passes.
    ast_setflag(package, AST_FLAG_PRESERVE);
    return NULL;
  }

  return package;
}
Esempio n. 21
0
// Sugar for partial application, which we convert to a lambda.
static bool partial_application(pass_opt_t* opt, ast_t** astp)
{
  /* Example that we refer to throughout this function.
   * ```pony
   * class C
   *   fun f[T](a: A, b: B = b_default): R
   *
   * let recv: T = ...
   * recv~f[T2](foo)
   * ```
   *
   * Partial call is converted to:
   * ```pony
   * {(b: B = b_default)($0 = recv, a = foo): R => $0.f[T2](a, consume b) }
   * ```
   */

  ast_t* ast = *astp;
  typecheck_t* t = &opt->check;

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

  AST_GET_CHILDREN(ast, positional, namedargs, question, lhs);

  // LHS must be an application, possibly wrapped in another application
  // if the method had type parameters for qualification.
  pony_assert(ast_id(lhs) == TK_FUNAPP || ast_id(lhs) == TK_BEAPP ||
    ast_id(lhs) == TK_NEWAPP);
  AST_GET_CHILDREN(lhs, receiver, method);
  ast_t* type_args = NULL;

  if(ast_id(receiver) == ast_id(lhs))
  {
    type_args = method;
    AST_GET_CHILDREN_NO_DECL(receiver, receiver, method);
  }

  // Look up the original method definition for this method call.
  ast_t* method_def = lookup(opt, lhs, ast_type(receiver), ast_name(method));
  pony_assert(ast_id(method_def) == TK_FUN || ast_id(method_def) == TK_BE ||
    ast_id(method_def) == TK_NEW);

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

  if(is_typecheck_error(type))
    return false;

  AST_GET_CHILDREN(type, cap, type_params, target_params, result);

  bool bare = ast_id(cap) == TK_AT;

  token_id apply_cap = TK_AT;
  if(!bare)
    apply_cap = partial_application_cap(opt, type, receiver, positional);

  token_id can_error = ast_id(ast_childidx(method_def, 5));
  const char* recv_name = package_hygienic_id(t);

  // Build lambda expression.
  ast_t* call_receiver = NULL;
  if(bare)
  {
    ast_t* arg = ast_child(positional);
    while(arg != NULL)
    {
      if(ast_id(arg) != TK_NONE)
      {
        ast_error(opt->check.errors, arg, "the partial application of a bare "
          "method cannot take arguments");
        return false;
      }

      arg = ast_sibling(arg);
    }

    ast_t* receiver_type = ast_type(receiver);
    if(is_bare(receiver_type))
    {
      // Partial application on a bare object, simply return the object itself.
      ast_replace(astp, receiver);
      return true;
    }

    AST_GET_CHILDREN(receiver_type, recv_type_package, recv_type_name);

    const char* recv_package_str = ast_name(recv_type_package);
    const char* recv_name_str = ast_name(recv_type_name);

    ast_t* module = ast_nearest(ast, TK_MODULE);
    ast_t* package = ast_parent(module);
    ast_t* pkg_id = package_id(package);
    const char* pkg_str = ast_name(pkg_id);

    const char* pkg_alias = NULL;

    if(recv_package_str != pkg_str)
      pkg_alias = package_alias_from_id(module, recv_package_str);

    ast_free_unattached(pkg_id);

    if(pkg_alias != NULL)
    {
      // `package.Type.f`
      BUILD_NO_DECL(call_receiver, ast,
        NODE(TK_DOT,
          NODE(TK_DOT,
            NODE(TK_REFERENCE, ID(pkg_alias))
            ID(recv_name_str))
          TREE(method)));
    } else {
      // `Type.f`
      BUILD_NO_DECL(call_receiver, ast,
        NODE(TK_DOT,
          NODE(TK_REFERENCE, ID(recv_name_str))
          TREE(method)));
    }
  } else {
    // `$0.f`
    BUILD_NO_DECL(call_receiver, ast,
      NODE(TK_DOT,
        NODE(TK_REFERENCE, ID(recv_name))
        TREE(method)));
  }

  ast_t* captures = NULL;
  if(bare)
  {
    captures = ast_from(receiver, TK_NONE);
  } else {
    // Build captures. We always have at least one capture, for receiver.
    // Capture: `$0 = recv`
    BUILD_NO_DECL(captures, receiver,
      NODE(TK_LAMBDACAPTURES,
        NODE(TK_LAMBDACAPTURE,
          ID(recv_name)
          NONE  // Infer type.
          TREE(receiver))));
  }

  // Process arguments.
  ast_t* target_param = ast_child(target_params);
  ast_t* lambda_params = ast_from(target_params, TK_NONE);
  ast_t* lambda_call_args = ast_from(positional, TK_NONE);
  ast_t* given_arg = ast_child(positional);

  while(given_arg != NULL)
  {
    pony_assert(target_param != NULL);
    const char* target_p_name = ast_name(ast_child(target_param));

    if(ast_id(given_arg) == TK_NONE)
    {
      // This argument is not supplied already, must be a lambda parameter.
      // Like `b` in example above.
      // Build a new a new TK_PARAM node rather than copying the target one,
      // since the target has already been processed to expr pass, and we need
      // a clean one.
      AST_GET_CHILDREN(target_param, p_id, p_type, p_default);

      // Parameter: `b: B = b_default`
      BUILD(lambda_param, target_param,
        NODE(TK_PARAM,
          TREE(p_id)
          TREE(sanitise_type(p_type))
          TREE(p_default)));

      ast_append(lambda_params, lambda_param);
      ast_setid(lambda_params, TK_PARAMS);

      // Argument: `consume b`
      BUILD(target_arg, lambda_param,
        NODE(TK_SEQ,
          NODE(TK_CONSUME,
            NONE
            NODE(TK_REFERENCE, ID(target_p_name)))));

      ast_append(lambda_call_args, target_arg);
      ast_setid(lambda_call_args, TK_POSITIONALARGS);
    }
    else
    {
      // This argument is supplied to the partial, capture it.
      // Like `a` in example above.
      // Capture: `a = foo`
      BUILD(capture, given_arg,
        NODE(TK_LAMBDACAPTURE,
          ID(target_p_name)
          NONE
          TREE(given_arg)));

      ast_append(captures, capture);

      // Argument: `a`
      BUILD(target_arg, given_arg,
        NODE(TK_SEQ,
          NODE(TK_REFERENCE, ID(target_p_name))));

      ast_append(lambda_call_args, target_arg);
      ast_setid(lambda_call_args, TK_POSITIONALARGS);
    }

    given_arg = ast_sibling(given_arg);
    target_param = ast_sibling(target_param);
  }

  pony_assert(target_param == NULL);

  if(type_args != NULL)
  {
    // The partial call has type args, add them to the actual call in apply().
    // `$0.f[T2]`
    BUILD(qualified, type_args,
      NODE(TK_QUALIFY,
        TREE(call_receiver)
        TREE(type_args)));
    call_receiver = qualified;
  }

  REPLACE(astp,
    NODE((bare ? TK_BARELAMBDA : TK_LAMBDA),
      NODE(apply_cap)
      NONE  // Lambda function name.
      NONE  // Lambda type params.
      TREE(lambda_params)
      TREE(captures)
      TREE(sanitise_type(result))
      NODE(can_error)
      NODE(TK_SEQ,
        NODE(TK_CALL,
          TREE(lambda_call_args)
          NONE  // Named args.
          NODE(can_error)
          TREE(call_receiver)))
      NONE)); // Lambda reference capability.

  // Need to preserve various lambda children.
  ast_setflag(ast_childidx(*astp, 2), AST_FLAG_PRESERVE); // Type params.
  ast_setflag(ast_childidx(*astp, 3), AST_FLAG_PRESERVE); // Parameters.
  ast_setflag(ast_childidx(*astp, 5), AST_FLAG_PRESERVE); // Return type.
  ast_setflag(ast_childidx(*astp, 7), AST_FLAG_PRESERVE); // Body.

  // Catch up to this pass.
  return ast_passes_subtree(astp, opt, PASS_EXPR);
}
Esempio n. 22
0
bool expr_assign(pass_opt_t* opt, ast_t* ast)
{
  // Left and right are swapped in the AST to make sure we type check the
  // right side before the left. Fetch them in the opposite order.
  assert(ast != NULL);

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

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

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

  ast_t* r_type = ast_type(right);

  if(is_typecheck_error(r_type))
    return false;

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

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

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

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

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

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

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

      default:
        assert(0);
        break;
    }

    ast_free_unattached(a_type);
    return false;
  }

  bool ok_safe = safe_to_write(left, a_type);

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

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

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

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

  ast_free_unattached(a_type);

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

  ast_settype(ast, consume_type(l_type, TK_NONE));
  return true;
}
Esempio n. 23
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;
}
Esempio n. 24
0
bool expr_assign(pass_opt_t* opt, ast_t* ast)
{
  // Left and right are swapped in the AST to make sure we type check the
  // right side before the left. Fetch them in the opposite order.
  assert(ast != NULL);

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

  if(!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))
  {
    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",
            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;
}
Esempio n. 25
0
const char* package_name(ast_t* ast)
{
  package_t* pkg = (package_t*)ast_data(ast_nearest(ast, TK_PACKAGE));
  return (pkg == NULL) ? NULL : pkg->id;
}
Esempio n. 26
0
ast_t* package_load(ast_t* from, const char* path, pass_opt_t* options)
{
  const char* magic = find_magic_package(path);
  const char* full_path = path;
  const char* qualified_name = path;
  ast_t* program = ast_nearest(from, TK_PROGRAM);

  if(magic == NULL)
  {
    // Lookup (and hence normalise) path
    bool is_relative = false;
    full_path = find_path(from, path, &is_relative);

    if(full_path == NULL)
      return NULL;

    if((from != NULL) && is_relative)
    {
      // Package to load is relative to from, build the qualified name
      // The qualified name should be relative to the program being built
      package_t* from_pkg = (package_t*)ast_data(ast_child(program));

      if(from_pkg != NULL)
      {
        const char* base_name = from_pkg->qualified_name;
        size_t base_name_len = strlen(base_name);
        size_t path_len = strlen(path);
        size_t len = base_name_len + path_len + 2;
        char* q_name = (char*)pool_alloc_size(len);
        memcpy(q_name, base_name, base_name_len);
        q_name[base_name_len] = '/';
        memcpy(q_name + base_name_len + 1, path, path_len);
        q_name[len - 1] = '\0';
        qualified_name = stringtab_consume(q_name, len);
      }
    }
  }

  ast_t* package = ast_get(program, full_path, NULL);

  // Package already loaded
  if(package != NULL)
    return package;

  package = create_package(program, full_path, qualified_name);

  if(report_build)
    printf("Building %s -> %s\n", path, full_path);

  if(magic != NULL)
  {
    if(!parse_source_code(package, magic, options))
      return NULL;
  }
  else
  {
    if(!parse_files_in_dir(package, full_path, options))
      return NULL;
  }

  if(ast_child(package) == NULL)
  {
    ast_error(package, "no source files in package '%s'", path);
    return NULL;
  }

  // We add new packages to the end of the program, so they will be reached by
  // the current pass processing. This means we need to catch up the new
  // package to the previous pass
  if(!ast_passes_subtree(&package, options,
    pass_prev(options->type_catchup_pass)))
    return NULL;

  return package;
}