static ast_t* lookup_base(typecheck_t* t, ast_t* from, ast_t* orig, ast_t* type, const char* name, bool errors) { switch(ast_id(type)) { case TK_UNIONTYPE: if(errors) ast_error(from, "can't lookup by name on a union type"); return NULL; case TK_ISECTTYPE: { ast_t* child = ast_child(type); while(child != NULL) { ast_t* result = lookup_base(t, from, orig, child, name, false); if(result != NULL) return result; child = ast_sibling(child); } ast_error(from, "couldn't find '%s'", name); return NULL; } case TK_TUPLETYPE: if(errors) ast_error(from, "can't lookup by name on a tuple"); return NULL; case TK_NOMINAL: return lookup_nominal(t, from, orig, type, name, errors); case TK_ARROW: return lookup_base(t, from, orig, ast_childidx(type, 1), name, errors); case TK_TYPEPARAMREF: return lookup_typeparam(t, from, orig, type, name, errors); case TK_FUNTYPE: if(errors) ast_error(from, "can't lookup by name on a function type"); return NULL; case TK_INFERTYPE: // Can only happen due to a local inference fail earlier return NULL; default: {} } assert(0); return NULL; }
static ast_t* lookup_base(pass_opt_t* opt, ast_t* from, ast_t* orig, ast_t* type, const char* name, bool errors) { switch(ast_id(type)) { case TK_UNIONTYPE: { ast_t* child = ast_child(type); ast_t* result = NULL; bool ok = true; while(child != NULL) { ast_t* r = lookup_base(opt, from, child, child, name, errors); if(r == NULL) { // All possible types in the union must have this. if(errors) { ast_error(from, "couldn't find %s in %s", name, ast_print_type(child)); } ok = false; } else { switch(ast_id(r)) { case TK_FVAR: case TK_FLET: case TK_EMBED: if(errors) { ast_error(from, "can't lookup field %s in %s in a union type", name, ast_print_type(child)); } ok = false; break; default: if(result == NULL) { // If we don't have a result yet, use this one. result = r; } else if(!is_subtype(r, result, false)) { if(is_subtype(result, r, false)) { // Use the supertype function. Require the most specific // arguments and return the least specific result. // TODO: union the signatures, to handle arg names and // default arguments. ast_free_unattached(result); result = r; } else { if(errors) { ast_error(from, "a member of the union type has an incompatible method " "signature"); ast_error(result, "first implementation is here"); ast_error(r, "second implementation is here"); } ast_free_unattached(r); ok = false; } } break; } } child = ast_sibling(child); } if(!ok) { ast_free_unattached(result); result = NULL; } return result; } case TK_ISECTTYPE: { ast_t* child = ast_child(type); ast_t* result = NULL; bool ok = true; while(child != NULL) { ast_t* r = lookup_base(opt, from, child, child, name, false); if(r != NULL) { switch(ast_id(r)) { case TK_FVAR: case TK_FLET: case TK_EMBED: // Ignore fields. break; default: if(result == NULL) { // If we don't have a result yet, use this one. result = r; } else if(!is_subtype(result, r, false)) { if(is_subtype(r, result, false)) { // Use the subtype function. Require the least specific // arguments and return the most specific result. ast_free_unattached(result); result = r; } // TODO: isect the signatures, to handle arg names and // default arguments. This is done even when the functions have // no subtype relationship. } break; } } child = ast_sibling(child); } if(errors && (result == NULL)) ast_error(from, "couldn't find '%s'", name); if(!ok) { ast_free_unattached(result); result = NULL; } return result; } case TK_TUPLETYPE: if(errors) ast_error(from, "can't lookup by name on a tuple"); return NULL; case TK_NOMINAL: return lookup_nominal(opt, from, orig, type, name, errors); case TK_ARROW: return lookup_base(opt, from, orig, ast_childidx(type, 1), name, errors); case TK_TYPEPARAMREF: return lookup_typeparam(opt, from, orig, type, name, errors); case TK_FUNTYPE: if(errors) ast_error(from, "can't lookup by name on a function type"); return NULL; case TK_INFERTYPE: case TK_ERRORTYPE: // Can only happen due to a local inference fail earlier return NULL; default: {} } assert(0); return NULL; }