Example #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;
}
Example #2
0
File: merge_keys.c Project: vab/cks
int merge_binding_signatures(struct openPGP_subkey *new_subkey, struct openPGP_subkey *old_subkey)
{
	/* Don't know what the RFC says, but GnuPG says we gotta Merge 'em. */
	struct key_signature *source_walk_sig = NULL;
	struct key_signature *target_walk_sig = NULL;
	struct key_signature *next_walk_sig = NULL;
	struct key_signature *last_sig = NULL;
	struct key_signature *first_sig = NULL;

	unsigned int sig_found = 0;
	unsigned int new_stuff = 0;


	#ifdef DEBUG
	fprintf(stderr,"merge_binding_signatures called.\n");
	#endif
	source_walk_sig = (struct key_signature *)get_first_sig(new_subkey->binding_signatures);
	first_sig = source_walk_sig;
	new_subkey->binding_signatures = first_sig;

	while(source_walk_sig != NULL)
	{
		sig_found = 0;
		if(old_subkey == NULL)
		{
			return 0;
		}
		if(old_subkey->binding_signatures == NULL)
		{
			return 0;
		}
		target_walk_sig = (struct key_signature *)get_first_sig(old_subkey->binding_signatures);
		while(target_walk_sig != NULL)
		{
			sig_found = compare_signatures(source_walk_sig,target_walk_sig);
			if(sig_found)
			{
				break;
			}
			target_walk_sig = target_walk_sig->next;
		}
		if(!sig_found)
		{
			#ifdef DEBUG
			fprintf(stderr,"New subkey binding signature Found\n");
			#endif
			new_stuff = 1;
			last_sig = (struct key_signature *)get_last_sig(old_subkey->binding_signatures);
			last_sig->next = source_walk_sig;
			next_walk_sig = source_walk_sig->next;
			extract_sig(source_walk_sig);
			source_walk_sig->prev = last_sig;
			last_sig = source_walk_sig;
			last_sig->next = NULL;
			source_walk_sig = next_walk_sig;
		}
		else
		{
			sig_found = 0;
			source_walk_sig = source_walk_sig->next;
		}
	}

	return new_stuff;
}
Example #3
0
File: merge_keys.c Project: vab/cks
int merge_signatures(struct user_id *id_new, struct user_id *id_old)
{
	struct key_signature *source_walk_sig = NULL;
	struct key_signature *target_walk_sig = NULL;
	struct key_signature *next_walk_sig = NULL;
	struct key_signature *last_sig = NULL;
	struct key_signature *first_sig = NULL;

	unsigned int sig_found = 0;
	unsigned int new_stuff = 0;


	#ifdef DEBUG
	fprintf(stderr,"merge_signatures called.\n");
	#endif
        source_walk_sig = (struct key_signature *)get_first_sig(id_new->signatures);
	if(source_walk_sig == NULL)
	{
		return 0;
	}
	
	first_sig = source_walk_sig;
	id_new->signatures = first_sig;

	while(source_walk_sig != NULL)
	{
		sig_found = 0;
		if(id_old == NULL)
		{
			return 0;
		}
		if(id_old->signatures == NULL)
		{
			return 0;
		}
		target_walk_sig = (struct key_signature *)get_first_sig(id_old->signatures);
		while(target_walk_sig != NULL)
		{
			sig_found = compare_signatures(source_walk_sig,target_walk_sig);
			if(sig_found)
			{
				break;
			}
			target_walk_sig = target_walk_sig->next;
		}
		if(!sig_found)
		{
			#ifdef DEBUG
			fprintf(stderr,"New sig Found\n");
			#endif
			new_stuff = 1;
			last_sig = (struct key_signature *)get_last_sig(id_old->signatures);
			last_sig->next = source_walk_sig;
			next_walk_sig = source_walk_sig->next;
			extract_sig(source_walk_sig);
			source_walk_sig->prev = last_sig;
			last_sig = source_walk_sig;
			last_sig->next = NULL;
			source_walk_sig = next_walk_sig;
		}
		else
		{
			sig_found = 0;
			source_walk_sig = source_walk_sig->next;
		}
	}

	return new_stuff;
}
Example #4
0
// 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 lexint_cmp(ast_int(sig_a), ast_int(sig_b)) == 0;
    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;
}
Example #5
0
// Combine the given inherited method with the existing one, if any, in the
// given entity.
// The provided method must already be reified.
// The trait_ref is the entry in the provides list that causes this method
// inclusion. Needed for error reporting.
// Returns true on success, false on failure in which case an error will have
// been reported.
static bool add_method_from_trait(ast_t* entity, ast_t* method,
  ast_t* trait_ref, pass_opt_t* opt)
{
  assert(entity != NULL);
  assert(method != NULL);
  assert(trait_ref != NULL);

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

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

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

    if(m == NULL)
      return false;

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

    return true;
  }

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

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

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

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

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

    info->failed = true;
    return false;
  }

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

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

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

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

    info->body_donor = NULL;
    return true;
  }

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

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

  info->body_donor = (ast_t*)ast_data(method);
  info->trait_ref = trait_ref;
  return true;
}