ast_t* type_isect_fun(ast_t* a, ast_t* b) { token_id ta = ast_id(a); token_id tb = ast_id(b); if(((ta == TK_NEW) || (tb == TK_NEW)) && (ta != tb)) return NULL; AST_GET_CHILDREN(a, a_cap, a_id, a_typeparams, a_params, a_result, a_throw); AST_GET_CHILDREN(b, b_cap, b_id, b_typeparams, b_params, b_result, b_throw); // Must have the same name. if(ast_name(a_id) != ast_name(b_id)) return NULL; // Must have the same number of type parameters and parameters. if((ast_childcount(a_typeparams) != ast_childcount(b_typeparams)) || (ast_childcount(a_params) != ast_childcount(b_params))) return NULL; // Contravariant receiver cap. token_id tcap; token_id a_tcap = ast_id(a_cap); token_id b_tcap = ast_id(b_cap); if(is_cap_sub_cap(b_tcap, TK_NONE, a_tcap, TK_NONE)) tcap = a_tcap; else if(is_cap_sub_cap(a_tcap, TK_NONE, b_tcap, TK_NONE)) tcap = b_tcap; else tcap = TK_BOX; // Result is the intersection of the results. ast_t* result = type_isect(a_result, b_result); // Covariant throws. token_id throws; if((ast_id(a_throw) == TK_NONE) || (ast_id(b_throw) == TK_NONE)) throws = TK_NONE; else throws = TK_QUESTION; BUILD(fun, a, NODE(tcap) TREE(a_id) NODE(TK_TYPEPARAMS) NODE(TK_PARAMS) TREE(result) NODE(throws) ); // TODO: union typeparams and params // handling typeparam names is tricky return fun; }
bool is_sub_cap_and_ephemeral(ast_t* sub, ast_t* super) { ast_t* sub_cap = fetch_cap(sub); ast_t* sub_eph = ast_sibling(sub_cap); ast_t* super_cap = fetch_cap(super); ast_t* super_eph = ast_sibling(super_cap); token_id t_sub_cap = ast_id(sub_cap); token_id t_sub_eph = ast_id(sub_eph); token_id t_super_cap = ast_id(super_cap); token_id t_super_eph = ast_id(super_eph); token_id t_alt_cap = t_sub_cap; // Adjusted for borrowing. if(t_sub_eph == TK_BORROWED) { switch(t_alt_cap) { case TK_ISO: t_alt_cap = TK_TAG; break; case TK_TRN: t_alt_cap = TK_BOX; break; case TK_ANY_GENERIC: t_alt_cap = TK_TAG; break; default: {} } } switch(t_super_eph) { case TK_EPHEMERAL: // Sub must be ephemeral if t_sub_cap != t_alt_cap. Otherwise, we don't // have to be ephemeral, since, for example, a ref can be a subtype of // a ref^, but an iso is not a subtype of an iso^. if((t_sub_cap != t_alt_cap) && (t_sub_eph != TK_EPHEMERAL)) return false; // Capability must be a sub-capability. return is_cap_sub_cap(t_sub_cap, t_super_cap); case TK_NONE: // Check the adjusted capability. return is_cap_sub_cap(t_alt_cap, t_super_cap); case TK_BORROWED: // Borrow a capability. if(t_sub_cap == t_super_cap) return true; // Or alias a capability. return is_cap_sub_cap(t_alt_cap, t_super_cap); default: {} } assert(0); return false; }
static matchtype_t is_trait_match_trait(ast_t* operand, ast_t* pattern, errorframe_t* errorf, bool report_reject, pass_opt_t* opt) { (void)report_reject; (void)opt; AST_GET_CHILDREN(operand, o_pkg, o_id, o_typeargs, o_cap, o_eph); AST_GET_CHILDREN(pattern, p_pkg, p_id, p_typeargs, p_cap, p_eph); // If the operand refcap can't match the pattern refcap, deny the match. if(!is_cap_sub_cap(ast_id(o_cap), ast_id(o_eph), ast_id(p_cap), ast_id(p_eph))) { if(errorf != NULL) { ast_error_frame(errorf, pattern, "matching %s with %s could violate capabilities: " "%s%s isn't a subcap of %s%s", ast_print_type(operand), ast_print_type(pattern), ast_print_type(o_cap), ast_print_type(o_eph), ast_print_type(p_cap), ast_print_type(p_eph)); } return MATCHTYPE_DENY; } // Otherwise, accept the match. return MATCHTYPE_ACCEPT; }
bool is_sub_cap_and_ephemeral(ast_t* sub, ast_t* super) { ast_t* sub_cap = fetch_cap(sub); ast_t* sub_eph = ast_sibling(sub_cap); ast_t* super_cap = fetch_cap(super); ast_t* super_eph = ast_sibling(super_cap); return is_cap_sub_cap(ast_id(sub_cap), ast_id(sub_eph), ast_id(super_cap), ast_id(super_eph)); }
static matchtype_t is_nominal_match_entity(ast_t* operand, ast_t* pattern, errorframe_t* errorf, bool report_reject, pass_opt_t* opt) { AST_GET_CHILDREN(operand, o_pkg, o_id, o_typeargs, o_cap, o_eph); AST_GET_CHILDREN(pattern, p_pkg, p_id, p_typeargs, p_cap, p_eph); // We say the pattern provides the operand if it is a subtype without taking // capabilities into account. bool provides = is_subtype_ignore_cap(pattern, operand, NULL, opt); // If the pattern doesn't provide the operand, reject the match. if(!provides) { if((errorf != NULL) && report_reject) { ast_error_frame(errorf, pattern, "%s cannot match %s: %s isn't a subtype of %s", ast_print_type(operand), ast_print_type(pattern), ast_print_type_no_cap(pattern), ast_print_type_no_cap(operand)); } return MATCHTYPE_REJECT; } // If the operand does provide the pattern, but the operand refcap can't // match the pattern refcap, deny the match. if(!is_cap_sub_cap(ast_id(o_cap), ast_id(o_eph), ast_id(p_cap), ast_id(p_eph))) { if(errorf != NULL) { ast_error_frame(errorf, pattern, "matching %s with %s could violate capabilities: " "%s%s isn't a subcap of %s%s", ast_print_type(operand), ast_print_type(pattern), ast_print_type(o_cap), ast_print_type(o_eph), ast_print_type(p_cap), ast_print_type(p_eph)); } return MATCHTYPE_DENY; } // Otherwise, accept the match. return MATCHTYPE_ACCEPT; }
static bool is_sub_cap_and_eph(ast_t* sub, ast_t* super, errorframe_t* errors) { ast_t* sub_cap = cap_fetch(sub); ast_t* sub_eph = ast_sibling(sub_cap); ast_t* super_cap = cap_fetch(super); ast_t* super_eph = ast_sibling(super_cap); if(!is_cap_sub_cap(ast_id(sub_cap), ast_id(sub_eph), ast_id(super_cap), ast_id(super_eph))) { if(errors != NULL) { ast_error_frame(errors, sub, "%s is not a subtype of %s: %s%s is not a subtype of %s%s", ast_print_type(sub), ast_print_type(super), ast_print_type(sub_cap), ast_print_type(sub_eph), ast_print_type(super_cap), ast_print_type(super_eph)); } return false; } return true; }
static bool is_reified_fun_sub_fun(ast_t* sub, ast_t* super, ast_t* isub, ast_t* isuper) { AST_GET_CHILDREN(sub, sub_cap, sub_id, sub_typeparams, sub_params, sub_result, sub_throws); AST_GET_CHILDREN(super, super_cap, super_id, super_typeparams, super_params, super_result, super_throws); switch(ast_id(sub)) { case TK_NEW: { // Covariant receiver. if(!is_cap_sub_cap(ast_id(sub_cap), TK_NONE, ast_id(super_cap), TK_NONE)) return false; // Covariant result. Don't check this for interfaces, as it produces // an infinite loop. It will be true if the whole interface is provided. if(isuper == NULL) { if(!is_subtype(sub_result, super_result)) return false; // If either result type is a machine word, the other must be as well. if(is_machine_word(sub_result) && !is_machine_word(super_result)) return false; if(is_machine_word(super_result) && !is_machine_word(sub_result)) return false; } break; } case TK_FUN: case TK_BE: { // Contravariant receiver. if(!is_cap_sub_cap(ast_id(super_cap), TK_NONE, ast_id(sub_cap), TK_NONE)) return false; // Covariant result. if(!is_recursive_interface(sub_result, super_result, isub, isuper)) { if(!is_subtype(sub_result, super_result)) return false; // If either result type is a machine word, the other must be as well. if(is_machine_word(sub_result) && !is_machine_word(super_result)) return false; if(is_machine_word(super_result) && !is_machine_word(sub_result)) return false; } break; } default: {} } // Contravariant type parameter constraints. ast_t* sub_typeparam = ast_child(sub_typeparams); ast_t* super_typeparam = ast_child(super_typeparams); while((sub_typeparam != NULL) && (super_typeparam != NULL)) { ast_t* sub_constraint = ast_childidx(sub_typeparam, 1); ast_t* super_constraint = ast_childidx(super_typeparam, 1); if(!is_recursive_interface(super_constraint, sub_constraint, isub, isuper) && !is_subtype(super_constraint, sub_constraint)) return false; sub_typeparam = ast_sibling(sub_typeparam); super_typeparam = ast_sibling(super_typeparam); } // Contravariant parameters. ast_t* sub_param = ast_child(sub_params); ast_t* super_param = ast_child(super_params); while((sub_param != NULL) && (super_param != NULL)) { ast_t* sub_type = ast_childidx(sub_param, 1); ast_t* super_type = ast_childidx(super_param, 1); // If either parameter type is a machine word, the other must be as well. if(is_machine_word(sub_type) && !is_machine_word(super_type)) return false; if(is_machine_word(super_type) && !is_machine_word(sub_type)) return false; // Contravariant: the super type must be a subtype of the sub type. if(!is_recursive_interface(super_type, sub_type, isub, isuper) && !is_subtype(super_type, sub_type)) return false; sub_param = ast_sibling(sub_param); super_param = ast_sibling(super_param); } if((sub_param != NULL) || (super_param != NULL)) return false; // Covariant throws. if((ast_id(sub_throws) == TK_QUESTION) && (ast_id(super_throws) != TK_QUESTION)) return false; return true; }
static bool is_reified_fun_sub_fun(ast_t* sub, ast_t* super, errorframe_t* errors) { AST_GET_CHILDREN(sub, sub_cap, sub_id, sub_typeparams, sub_params, sub_result, sub_throws); AST_GET_CHILDREN(super, super_cap, super_id, super_typeparams, super_params, super_result, super_throws); switch(ast_id(sub)) { case TK_NEW: { // Covariant receiver. if(!is_cap_sub_cap(ast_id(sub_cap), TK_NONE, ast_id(super_cap), TK_NONE)) { if(errors != NULL) { ast_error_frame(errors, sub, "%s constructor is not a subtype of %s constructor", ast_print_type(sub_cap), ast_print_type(super_cap)); } return false; } // Covariant result. if(!is_subtype(sub_result, super_result, errors)) { if(errors != NULL) { ast_error_frame(errors, sub, "constructor result %s is not a subtype of %s", ast_print_type(sub_result), ast_print_type(super_result)); } return false; } if(!check_machine_words(sub_result, super_result, errors)) return false; break; } case TK_FUN: case TK_BE: { // Contravariant receiver. if(!is_cap_sub_cap(ast_id(super_cap), TK_NONE, ast_id(sub_cap), TK_NONE)) { if(errors != NULL) { ast_error_frame(errors, sub, "%s method is not a subtype of %s method", ast_print_type(sub_cap), ast_print_type(super_cap)); } return false; } // Covariant result. if(!is_subtype(sub_result, super_result, errors)) { if(errors != NULL) { ast_error_frame(errors, sub, "method result %s is not a subtype of %s", ast_print_type(sub_result), ast_print_type(super_result)); } return false; } if(!check_machine_words(sub_result, super_result, errors)) return false; break; } default: {} } // Contravariant type parameter constraints. ast_t* sub_typeparam = ast_child(sub_typeparams); ast_t* super_typeparam = ast_child(super_typeparams); while((sub_typeparam != NULL) && (super_typeparam != NULL)) { ast_t* sub_constraint = ast_childidx(sub_typeparam, 1); ast_t* super_constraint = ast_childidx(super_typeparam, 1); if(!is_subtype(super_constraint, sub_constraint, errors)) { if(errors != NULL) { ast_error_frame(errors, sub, "type parameter constraint %s is not a supertype of %s", ast_print_type(sub_constraint), ast_print_type(super_constraint)); } return false; } sub_typeparam = ast_sibling(sub_typeparam); super_typeparam = ast_sibling(super_typeparam); } // Contravariant parameters. ast_t* sub_param = ast_child(sub_params); ast_t* super_param = ast_child(super_params); while((sub_param != NULL) && (super_param != NULL)) { ast_t* sub_type = ast_childidx(sub_param, 1); ast_t* super_type = ast_childidx(super_param, 1); // Contravariant: the super type must be a subtype of the sub type. if(!is_subtype(super_type, sub_type, errors)) { if(errors != NULL) { ast_error_frame(errors, sub, "parameter %s is not a supertype of %s", ast_print_type(sub_type), ast_print_type(super_type)); } return false; } if(!check_machine_words(sub_type, super_type, errors)) return false; sub_param = ast_sibling(sub_param); super_param = ast_sibling(super_param); } // Covariant throws. if((ast_id(sub_throws) == TK_QUESTION) && (ast_id(super_throws) != TK_QUESTION)) { if(errors != NULL) { ast_error_frame(errors, sub, "a partial function is not a subtype of a total function"); } return false; } return true; }
bool expr_lambda(pass_opt_t* opt, ast_t** astp) { pony_assert(astp != NULL); ast_t* ast = *astp; pony_assert(ast != NULL); AST_GET_CHILDREN(ast, receiver_cap, name, t_params, params, captures, ret_type, raises, body, obj_cap); ast_t* annotation = ast_consumeannotation(ast); // Try to find an antecedent type, and find possible lambda interfaces in it. ast_t* antecedent_type = find_antecedent_type(opt, ast, NULL); astlist_t* possible_fun_defs = NULL; astlist_t* possible_obj_caps = NULL; if(!is_typecheck_error(antecedent_type)) find_possible_fun_defs(opt, antecedent_type, &possible_fun_defs, &possible_obj_caps); // If there's more than one possible fun defs, rule out impossible ones by // comparing each fun def by some basic criteria against the lambda, // creating a new list containing only the remaining possibilities. if(astlist_length(possible_fun_defs) > 1) { astlist_t* new_fun_defs = NULL; astlist_t* new_obj_caps = NULL; astlist_t* fun_def_cursor = possible_fun_defs; astlist_t* obj_cap_cursor = possible_obj_caps; for(; (fun_def_cursor != NULL) && (obj_cap_cursor != NULL); fun_def_cursor = astlist_next(fun_def_cursor), obj_cap_cursor = astlist_next(obj_cap_cursor)) { ast_t* fun_def = astlist_data(fun_def_cursor); ast_t* def_obj_cap = astlist_data(obj_cap_cursor); if(is_typecheck_error(fun_def)) continue; AST_GET_CHILDREN(fun_def, def_receiver_cap, def_name, def_t_params, def_params, def_ret_type, def_raises); // Must have the same number of parameters. if(ast_childcount(params) != ast_childcount(def_params)) continue; // Must have a supercap of the def's receiver cap (if present). if((ast_id(receiver_cap) != TK_NONE) && !is_cap_sub_cap( ast_id(def_receiver_cap), TK_NONE, ast_id(receiver_cap), TK_NONE) ) continue; // Must have a supercap of the def's object cap (if present). if((ast_id(obj_cap) != TK_NONE) && !is_cap_sub_cap(ast_id(obj_cap), TK_NONE, ast_id(def_obj_cap), TK_NONE)) continue; // TODO: This logic could potentially be expanded to do deeper // compatibility checks, but checks involving subtyping here would be // difficult, because the lambda's AST is not caught up yet in the passes. new_fun_defs = astlist_push(new_fun_defs, fun_def); new_obj_caps = astlist_push(new_obj_caps, def_obj_cap); } astlist_free(possible_fun_defs); astlist_free(possible_obj_caps); possible_fun_defs = new_fun_defs; possible_obj_caps = new_obj_caps; } if(astlist_length(possible_fun_defs) == 1) { ast_t* fun_def = astlist_data(possible_fun_defs); ast_t* def_obj_cap = astlist_data(possible_obj_caps); // Try to complete the lambda's type info by inferring from the lambda type. if(!is_typecheck_error(fun_def)) { // Infer the object cap, receiver cap, and return type if unspecified. if(ast_id(obj_cap) == TK_NONE) ast_replace(&obj_cap, def_obj_cap); if(ast_id(receiver_cap) == TK_NONE) ast_replace(&receiver_cap, ast_child(fun_def)); if(ast_id(ret_type) == TK_NONE) ast_replace(&ret_type, ast_childidx(fun_def, 4)); // Infer the type of any parameters that were left unspecified. ast_t* param = ast_child(params); ast_t* def_param = ast_child(ast_childidx(fun_def, 3)); while((param != NULL) && (def_param != NULL)) { ast_t* param_id = ast_child(param); ast_t* param_type = ast_sibling(param_id); // Convert a "_" parameter to whatever the expected parameter is. if(is_name_dontcare(ast_name(param_id))) { ast_replace(¶m_id, ast_child(def_param)); ast_replace(¶m_type, ast_childidx(def_param, 1)); } // Give a type-unspecified parameter the type of the expected parameter. else if(ast_id(param_type) == TK_NONE) { ast_replace(¶m_type, ast_childidx(def_param, 1)); } param = ast_sibling(param); def_param = ast_sibling(def_param); } } ast_free_unattached(fun_def); } astlist_free(possible_obj_caps); // If any parameters still have no type specified, it's an error. ast_t* param = ast_child(params); while(param != NULL) { if(ast_id(ast_childidx(param, 1)) == TK_NONE) { ast_error(opt->check.errors, param, "a lambda parameter must specify a type or be inferable from context"); if(astlist_length(possible_fun_defs) > 1) { for(astlist_t* fun_def_cursor = possible_fun_defs; fun_def_cursor != NULL; fun_def_cursor = astlist_next(fun_def_cursor)) { ast_error_continue(opt->check.errors, astlist_data(fun_def_cursor), "this lambda interface is inferred, but it's not the only one"); } } astlist_free(possible_fun_defs); return false; } param = ast_sibling(param); } astlist_free(possible_fun_defs); bool bare = ast_id(ast) == TK_BARELAMBDA; ast_t* members = ast_from(ast, TK_MEMBERS); ast_t* last_member = NULL; bool failed = false; if(bare) pony_assert(ast_id(captures) == TK_NONE); // Process captures for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p)) { ast_t* field = NULL; bool ok = make_capture_field(opt, p, &field); if(field != NULL) ast_list_append(members, &last_member, field); else if(!ok) // An error occurred, just keep going to potentially find more errors failed = true; } if(failed) { ast_free(members); return false; } // Stop the various elements being marked as preserve ast_clearflag(t_params, AST_FLAG_PRESERVE); ast_clearflag(params, AST_FLAG_PRESERVE); ast_clearflag(ret_type, AST_FLAG_PRESERVE); ast_clearflag(body, AST_FLAG_PRESERVE); const char* fn_name = "apply"; if(ast_id(name) == TK_ID) fn_name = ast_name(name); // Make the apply function BUILD(apply, ast, NODE(TK_FUN, AST_SCOPE ANNOTATE(annotation) TREE(receiver_cap) ID(fn_name) TREE(t_params) TREE(params) TREE(ret_type) TREE(raises) TREE(body) NONE)); // Doc string ast_list_append(members, &last_member, apply); ast_setflag(members, AST_FLAG_PRESERVE); printbuf_t* buf = printbuf_new(); printbuf(buf, bare ? "@{(" : "{("); bool first = true; for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p)) { if(first) first = false; else printbuf(buf, ", "); printbuf(buf, "%s", ast_print_type(ast_childidx(p, 1))); } printbuf(buf, ")"); if(ast_id(ret_type) != TK_NONE) printbuf(buf, ": %s", ast_print_type(ret_type)); if(ast_id(raises) != TK_NONE) printbuf(buf, " ?"); printbuf(buf, "}"); // Replace lambda with object literal REPLACE(astp, NODE(TK_OBJECT, DATA(stringtab(buf->m)) TREE(obj_cap) NONE // Provides list TREE(members))); printbuf_free(buf); if(bare) { BUILD(bare_annotation, *astp, NODE(TK_ANNOTATION, ID("ponyint_bare"))); // Record the syntax pass as done to avoid the error about internal // annotations. ast_pass_record(bare_annotation, PASS_SYNTAX); ast_setannotation(*astp, bare_annotation); } // Catch up passes if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK) return false; return ast_passes_subtree(astp, opt, PASS_EXPR); }