// Process the given capture and create the AST for the corresponding field. // Returns the create field AST in out_field, which must be freed by the caller, // or NULL if there is no field to create. // Returns false on error. static bool make_capture_field(pass_opt_t* opt, ast_t* capture, ast_t** out_field) { pony_assert(capture != NULL); pony_assert(out_field != NULL); AST_GET_CHILDREN(capture, id_node, type, value); const char* name = ast_name(id_node); bool is_dontcare = is_name_dontcare(name); // There are 3 varieties of capture: // x -> capture variable x, type from defn of x // x = y -> capture expression y, type inferred from expression type // x: T = y -> capture expression y, type T if(ast_id(value) == TK_NONE) { // Variable capture pony_assert(ast_id(type) == TK_NONE); if(is_dontcare) { *out_field = NULL; return true; } ast_t* def = ast_get(capture, name, NULL); if(def == NULL) { ast_error(opt->check.errors, id_node, "cannot capture \"%s\", variable not defined", name); return false; } // lambda captures used before their declaration with their type // not defined are not legal if(!def_before_use(opt, def, capture, name)) return false; switch(ast_id(def)) { case TK_VAR: case TK_LET: case TK_PARAM: case TK_MATCH_CAPTURE: case TK_FVAR: case TK_FLET: case TK_EMBED: break; default: ast_error(opt->check.errors, id_node, "cannot capture \"%s\", can only " "capture fields, parameters and local variables", name); return false; } BUILD(capture_rhs, id_node, NODE(TK_REFERENCE, ID(name))); type = alias(ast_type(def)); value = capture_rhs; } else if(ast_id(type) == TK_NONE) { // No type specified, use type of the captured expression if(ast_type(value) == NULL) return false; type = alias(ast_type(value)); } else { // Type given, infer literals if(!coerce_literals(&value, type, opt)) return false; // Check subtyping now if we're capturing into '_', since the field will be // discarded. if(is_dontcare) { ast_t* v_type = alias(ast_type(value)); errorframe_t info = NULL; if(!is_subtype(v_type, type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, value, "argument not a subtype of parameter"); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); ast_free_unattached(v_type); return false; } ast_free_unattached(v_type); } } if(is_typecheck_error(type)) return false; if(is_dontcare) { *out_field = NULL; return true; } type = sanitise_type(type); BUILD(field, id_node, NODE(TK_FVAR, TREE(id_node) TREE(type) TREE(value))); *out_field = field; return true; }
static bool capture_from_reference(pass_opt_t* opt, ast_t* ctx, ast_t* ast, ast_t* captures, ast_t** last_capture) { const char* name = ast_name(ast_child(ast)); if(is_name_dontcare(name)) return true; ast_t* refdef = ast_get(ast, name, NULL); if(refdef != NULL) return true; refdef = ast_get(ctx, name, NULL); if(refdef == NULL) { ast_error(opt->check.errors, ast, "cannot capture \"%s\", variable not defined", name); return false; } if(!def_before_use(opt, refdef, ctx, name)) return false; switch(ast_id(refdef)) { case TK_VAR: case TK_LET: case TK_PARAM: case TK_MATCH_CAPTURE: case TK_FVAR: case TK_FLET: case TK_EMBED: break; default: ast_error(opt->check.errors, ast, "cannot capture \"%s\", can only " "capture fields, parameters and local variables", name); return NULL; } // Check if we've already captured it for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p)) { AST_GET_CHILDREN(p, c_name, c_type); if(name == ast_name(c_name)) return true; } ast_t* type = alias(ast_type(refdef)); if(is_typecheck_error(type)) return false; type = sanitise_type(type); BUILD(field, ast, NODE(TK_FVAR, ID(name) TREE(type) NODE(TK_REFERENCE, ID(name)))); ast_list_append(captures, last_capture, field); 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); }