Пример #1
0
// Check that the given method is compatible with the existing definition.
// Return the method that ends up being in the entity or NULL on error.
// Stages 2A, 2B and 2C.
static ast_t* add_method(ast_t* entity, ast_t* existing_method,
  ast_t* new_method, ast_t** last_method)
{
  assert(entity != NULL);
  assert(new_method != NULL);
  assert(last_method != NULL);

  const char* name = ast_name(ast_childidx(new_method, 1));
  const char* entity_name = ast_name(ast_child(entity));

  if(existing_method == NULL)
  {
    // This method is new to the entity.
    // Stage 2C.
    ast_t* case_clash = ast_get_case(entity, name, NULL);

    if(case_clash != NULL)
    {
      ast_error(case_clash, "in %s method name %s differs only in case",
        entity_name, name);
      ast_error(new_method, "clashing method is here");
      return NULL;
    }

    attach_method_t(new_method, NULL, false);
    ast_list_append(ast_childidx(entity, 4), last_method, new_method);
    ast_set(entity, name, new_method, SYM_DEFINED);
    return *last_method;
  }

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

  if(info->local_def)
  {
    // Existing method is a local definition, new method must be a subtype
    // Stage 2A
    if(is_subtype(existing_method, new_method, true))
      return existing_method;

    ast_error(existing_method,
      "local method %s is not compatible with provided version", name);
    ast_error(new_method, "clashing method is here");

    return NULL;
  }

  // Both method versions came from the provides list, their signatures must
  // match exactly
  // Stage 2B
  if(compare_signatures(existing_method, new_method))
    return existing_method;

  ast_error(entity, "clashing definitions of method %s provided, local "
    "disambiguation required", name);
  ast_error(existing_method, "provided here");
  ast_error(new_method, "and here");

  return NULL;
}
Пример #2
0
// Setup a method_t structure for each method in the given type
static void setup_local_methods(ast_t* ast)
{
  assert(ast != NULL);

  ast_t* members = ast_childidx(ast, 4);
  assert(members != NULL);

  for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p))
  {
    if(is_method(p))
      attach_method_t(p, ast, true);
  }
}
Пример #3
0
// Setup a method_t structure for each method in the given type.
static void setup_local_methods(ast_t* ast)
{
  assert(ast != NULL);

  ast_t* members = ast_childidx(ast, 4);
  assert(members != NULL);

  for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p))
  {
    if(is_method(p))
    {
      method_t* info = attach_method_t(p);
      info->local_define = true;

      if(ast_id(ast_childidx(p, 6)) != TK_NONE)
        info->body_donor = ast;
    }
  }
}
Пример #4
0
// Add a new method to the given entity, based on the specified method from
// the specified type.
// The trait_ref is the entry in the provides / delegates list that causes this
// method inclusion. Needed for error reporting.
// The basis_method is the reified method in the trait to add.
// The adjective parameter is used for error reporting and should be "provided"
// or similar.
// Return the newly added method or NULL on error.
static ast_t* add_method(ast_t* entity, ast_t* trait_ref, ast_t* basis_method,
  const char* adjective, pass_opt_t* opt)
{
  assert(entity != NULL);
  assert(trait_ref != NULL);
  assert(basis_method != NULL);
  assert(adjective != NULL);

  const char* name = ast_name(ast_childidx(basis_method, 1));

  // Check behaviour compatability.
  if(ast_id(basis_method) == TK_BE)
  {
    switch(ast_id(entity))
    {
      case TK_PRIMITIVE:
        ast_error(opt->check.errors, trait_ref,
          "cannot add a behaviour (%s) to a primitive", name);
        return NULL;

      case TK_STRUCT:
        ast_error(opt->check.errors, trait_ref,
          "cannot add a behaviour (%s) to a struct", name);
        return NULL;

      case TK_CLASS:
        ast_error(opt->check.errors, trait_ref,
          "cannot add a behaviour (%s) to a class", name);
        return NULL;

      default:
        break;
    }
  }

  // Check for existing method of the same name.
  ast_t* existing = ast_get(entity, name, NULL);

  if(existing != NULL)
  {
    assert(is_field(existing)); // Should already have checked for methods.

    ast_error(opt->check.errors, trait_ref,
      "%s method '%s' clashes with field", adjective, name);
    ast_error_continue(opt->check.errors, basis_method,
      "method is defined here");
    return NULL;
  }

  // Check for clash with existing method.
  ast_t* case_clash = ast_get_case(entity, name, NULL);

  if(case_clash != NULL)
  {
    const char* clash_name = "";

    switch(ast_id(case_clash))
    {
      case TK_FUN:
      case TK_BE:
      case TK_NEW:
        clash_name = ast_name(ast_childidx(case_clash, 1));
        break;

      case TK_LET:
      case TK_VAR:
      case TK_EMBED:
        clash_name = ast_name(ast_child(case_clash));
        break;

      default:
        assert(0);
        break;
    }

    ast_error(opt->check.errors, trait_ref,
      "%s method '%s' differs only in case from '%s'",
      adjective, name, clash_name);
    ast_error_continue(opt->check.errors, basis_method,
      "clashing method is defined here");
    return NULL;
  }

  AST_GET_CHILDREN(basis_method, cap, id, typeparams, params, result,
    can_error, body, doc);

  // Ignore docstring.
  if(ast_id(doc) == TK_STRING)
  {
    ast_set_name(doc, "");
    ast_setid(doc, TK_NONE);
  }

  ast_t* local = ast_append(ast_childidx(entity, 4), basis_method);
  ast_set(entity, name, local, SYM_DEFINED, false);
  ast_t* body_donor = (ast_t*)ast_data(basis_method);
  method_t* info = attach_method_t(local);
  info->trait_ref = trait_ref;

  if(ast_id(body) != TK_NONE)
    info->body_donor = body_donor;

  return local;
}