// 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; }
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; }
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; }
// 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; }
// 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; }