ast_t* control_type_add_branch(ast_t* control_type, ast_t* branch) { assert(branch != NULL); ast_t* branch_type = ast_type(branch); if(is_typecheck_error(branch_type)) return branch_type; if(is_type_literal(branch_type)) { // The new branch is a literal if(control_type == NULL) control_type = ast_from(branch, TK_LITERAL); if(ast_id(control_type) != TK_LITERAL) { // The current control type is not a literal, fix that ast_t* old_control = control_type; control_type = ast_from(branch, TK_LITERAL); ast_settype(control_type, old_control); } assert(ast_id(control_type) == TK_LITERAL); // Add a literal branch reference to the new branch ast_t* member = ast_from(branch, TK_LITERALBRANCH); ast_setdata(member, (void*)branch); ast_append(control_type, member); ast_t* branch_non_lit = ast_type(branch_type); if(branch_non_lit != NULL) { // The branch's literal type has a non-literal component ast_t* non_literal_type = ast_type(control_type); ast_settype(control_type, type_union(non_literal_type, branch_non_lit)); } return control_type; } if(control_type != NULL && ast_id(control_type) == TK_LITERAL) { // New branch is not literal, but the control structure is // Add new branch type to the control structure's non-literal aspect ast_t* non_literal_type = ast_type(control_type); non_literal_type = type_union(non_literal_type, branch_type); ast_settype(control_type, non_literal_type); return control_type; } // No literals here, just union the types return type_union(control_type, branch_type); }
void collect_type_params(ast_t* ast, ast_t** out_params, ast_t** out_args) { assert(ast != NULL); // Create params and args as TK_NONE, we'll change them if we find any type // params ast_t* params = out_params ? ast_from(ast, TK_NONE) : NULL; ast_t* args = out_args ? ast_from(ast, TK_NONE) : NULL; // Find enclosing entity, or NULL if not within an entity ast_t* entity = ast; while(entity != NULL && ast_id(entity) != TK_INTERFACE && ast_id(entity) != TK_TRAIT && ast_id(entity) != TK_PRIMITIVE && ast_id(entity) != TK_STRUCT && ast_id(entity) != TK_CLASS && ast_id(entity) != TK_ACTOR) { entity = ast_parent(entity); } // Find enclosing method, or NULL if not within a method ast_t* method = ast; while(method != NULL && ast_id(method) != TK_FUN && ast_id(method) != TK_NEW && ast_id(method) != TK_BE) { method = ast_parent(method); } // Collect type parameters defined on the entity (if within an entity) if(entity != NULL) { ast_t* entity_t_params = ast_childidx(entity, 1); for(ast_t* p = ast_child(entity_t_params); p != NULL; p = ast_sibling(p)) collect_type_param(p, params, args); } // Collect type parameters defined on the method (if within a method) if(method != NULL) { ast_t* method_t_params = ast_childidx(method, 2); for(ast_t* p = ast_child(method_t_params); p != NULL; p = ast_sibling(p)) collect_type_param(p, params, args); } if(out_params != NULL) *out_params = params; if(out_args != NULL) *out_args = args; }
ast_t* viewpoint_replacethis(ast_t* ast, ast_t* with) { ast_t* thistype = ast_from(ast, TK_THISTYPE); ast_t* r_ast = viewpoint_replace(ast, thistype, with); ast_free_unattached(thistype); return r_ast; }
bool expr_compiler_intrinsic(typecheck_t* t, ast_t* ast) { if(t->frame->method_body == NULL) { ast_error(ast, "a compiler intrinsic must be a method body"); return false; } ast_t* child = ast_child(t->frame->method_body); // Allow a docstring before the compiler_instrinsic. if(ast_id(child) == TK_STRING) child = ast_sibling(child); if((child != ast) || (ast_sibling(child) != NULL)) { ast_error(ast, "a compiler intrinsic must be the entire body"); return false; } // Disable debuglocs on calls to this method. ast_setdebug(t->frame->method, false); ast_settype(ast, ast_from(ast, TK_COMPILER_INTRINSIC)); return true; }
static bool check_type_params(ast_t** astp) { ast_t* lhs = *astp; ast_t* type = ast_type(lhs); if(is_typecheck_error(type)) return false; ast_t* typeparams = ast_childidx(type, 1); assert(ast_id(type) == TK_FUNTYPE); if(ast_id(typeparams) == TK_NONE) return true; BUILD(typeargs, typeparams, NODE(TK_TYPEARGS)); if(!check_constraints(typeparams, typeargs, true)) { ast_free_unattached(typeargs); return false; } type = reify(type, typeparams, typeargs); typeparams = ast_childidx(type, 1); ast_replace(&typeparams, ast_from(typeparams, TK_NONE)); REPLACE(astp, NODE(ast_id(lhs), TREE(lhs) TREE(typeargs))); ast_settype(*astp, type); return true; }
static void add_field_to_object(pass_opt_t* opt, ast_t* field, ast_t* class_members, ast_t* create_params, ast_t* create_body, ast_t* call_args) { AST_GET_CHILDREN(field, id, type, init); ast_t* p_id = ast_from_string(id, package_hygienic_id(&opt->check)); // The param is: $0: type BUILD(param, field, NODE(TK_PARAM, TREE(p_id) TREE(type) NONE)); // The arg is: $seq init BUILD(arg, init, NODE(TK_SEQ, TREE(init))); // The body of create contains: id = consume $0 BUILD(assign, init, NODE(TK_ASSIGN, NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id))) NODE(TK_REFERENCE, TREE(id)))); // Remove the initialiser from the field ast_replace(&init, ast_from(init, TK_NONE)); ast_add(class_members, field); ast_append(create_params, param); ast_append(create_body, assign); ast_append(call_args, arg); }
bool expr_break(typecheck_t* t, ast_t* ast) { if(t->frame->loop_body == NULL) { ast_error(ast, "must be in a loop"); return false; } if(!ast_all_consumes_in_scope(t->frame->loop_body, ast)) return false; // break is always the last expression in a sequence assert(ast_sibling(ast) == NULL); ast_settype(ast, ast_from(ast, TK_BREAK)); ast_inheritflags(ast); // Add type to loop. ast_t* body = ast_child(ast); if(is_control_type(ast_type(body))) { ast_error(body, "break value cannot be a control statement"); return false; } ast_t* loop_type = ast_type(t->frame->loop); loop_type = control_type_add_branch(loop_type, body); ast_settype(t->frame->loop, loop_type); return true; }
static bool push_assume(ast_t* sub, ast_t* super) { // Returns true if we have already assumed sub is a subtype of super. if(subtype_assume != NULL) { ast_t* assumption = ast_child(subtype_assume); while(assumption != NULL) { AST_GET_CHILDREN(assumption, assume_sub, assume_super); if(exact_nominal(sub, assume_sub) && exact_nominal(super, assume_super)) return true; assumption = ast_sibling(assumption); } } else { subtype_assume = ast_from(sub, TK_NONE); } BUILD(assume, sub, NODE(TK_NONE, TREE(ast_dup(sub)) TREE(ast_dup(super)))); ast_add(subtype_assume, assume); return false; }
static ast_result_t sugar_binop(ast_t** astp, const char* fn_name) { AST_GET_CHILDREN(*astp, left, right); ast_t* positional = ast_from(right, TK_POSITIONALARGS); if(ast_id(right) == TK_TUPLE) { ast_t* value = ast_child(right); while(value != NULL) { BUILD(arg, right, NODE(TK_SEQ, TREE(value))); ast_append(positional, arg); value = ast_sibling(value); } } else { BUILD(arg, right, NODE(TK_SEQ, TREE(right))); ast_add(positional, arg); } REPLACE(astp, NODE(TK_CALL, TREE(positional) NONE NODE(TK_DOT, TREE(left) ID(fn_name)) )); return AST_OK; }
bool expr_lambda(pass_opt_t* opt, ast_t** astp) { assert(astp != NULL); ast_t* ast = *astp; assert(ast != NULL); AST_GET_CHILDREN(ast, cap, t_params, params, captures, ret_type, raises, body); ast_t* members = ast_from(ast, TK_MEMBERS); ast_t* last_member = NULL; bool failed = false; // Process captures for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p)) { ast_t* field = make_capture_field(p); if(field != NULL) ast_list_append(members, &last_member, field); else // 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); // Make the apply function BUILD(apply, ast, NODE(TK_FUN, AST_SCOPE NONE // Capability ID("apply") TREE(t_params) TREE(params) TREE(ret_type) TREE(raises) TREE(body) NONE)); // Doc string ast_list_append(members, &last_member, apply); // Replace lambda with object literal REPLACE(astp, NODE(TK_OBJECT, TREE(cap); NONE // Provides list TREE(members))); // Catch up passes return ast_passes_subtree(astp, opt, PASS_EXPR); }
static ast_t* make_single_arrow(ast_t* left, ast_t* right) { switch(ast_id(left)) { case TK_ARROW: { ast_t* arrow = ast_dup(left); ast_t* child = ast_childidx(arrow, 1); // Arrow is right associative. while(ast_id(child) == TK_ARROW) child = ast_childidx(child, 1); ast_t* view = viewpoint_type(child, right); if(view == NULL) { ast_free_unattached(arrow); return NULL; } ast_replace(&child, view); return arrow; } default: {} } ast_t* arrow = ast_from(left, TK_ARROW); ast_add(arrow, right); ast_add(arrow, left); return arrow; }
bool expr_compile_error(ast_t* ast) { // compile_error is always the last expression in a sequence assert(ast_sibling(ast) == NULL); ast_settype(ast, ast_from(ast, TK_COMPILE_ERROR)); return true; }
bool expr_error(pass_opt_t* opt, ast_t* ast) { (void)opt; // error is always the last expression in a sequence assert(ast_sibling(ast) == NULL); ast_settype(ast, ast_from(ast, TK_ERROR)); return true; }
// Sugar any case methods in the given entity. ast_result_t sugar_case_methods(typecheck_t* t, ast_t* entity) { assert(entity != NULL); ast_result_t result = AST_OK; ast_t* members = ast_childidx(entity, 4); bool cases_found = false; // Check each method to see if its name is repeated, which indicates a case // method. for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p)) { if(ast_id(p) == TK_FUN || ast_id(p) == TK_BE) { const char* p_name = ast_name(ast_childidx(p, 1)); // Check if any subsequent methods share p's name. for(ast_t* q = ast_sibling(p); q != NULL; q = ast_sibling(q)) { if(ast_id(q) == TK_FUN || ast_id(q) == TK_BE) { const char* q_name = ast_name(ast_childidx(q, 1)); if(q_name == p_name) { // p's name is repeated, it's a case method, get a match method. if(!sugar_case_method(p, members, p_name, t)) result = AST_ERROR; cases_found = true; break; } } } } } if(cases_found) { // Remove nodes marked during processing. ast_t* filtered_members = ast_from(members, TK_MEMBERS); ast_t* list = NULL; ast_t* member; while((member = ast_pop(members)) != NULL) { if(ast_id(member) == TK_NONE) // Marked for removal. ast_free(member); else // Put back in filtered list. ast_list_append(filtered_members, &list, member); } ast_replace(&members, filtered_members); } return result; }
// Handle the given case method parameter list. // Combine and check the case parameters against the existing match parameters // and generate the match pattern to go in the worker method. // Returns: match pattern, NULL on error. static ast_t* process_params(ast_t* match_params, ast_t* case_params) { assert(match_params != NULL); assert(case_params != NULL); bool ok = true; size_t count = 0; ast_t* pattern = ast_from(case_params, TK_TUPLE); ast_t* case_param = ast_child(case_params); ast_t* match_param = ast_child(match_params); while(case_param != NULL && match_param != NULL) { AST_GET_CHILDREN(case_param, id, type, def_arg); if(ast_id(type) != TK_NONE) { // We have a parameter. if(!param_with_type(case_param, match_param, pattern)) ok = false; } else { // We have a value to match. if(!param_without_type(case_param, pattern)) ok = false; } case_param = ast_sibling(case_param); match_param = ast_sibling(match_param); count++; } if(case_param != NULL || match_param != NULL) { ast_error(case_params, "differing number of parameters to case methods"); ast_error(match_params, "clashing parameter count here"); ok = false; } if(!ok) { ast_free(pattern); return NULL; } assert(count > 0); if(count == 1) { // Only one parameter, don't need a tuple pattern. ast_t* tmp = ast_pop(ast_child(pattern)); ast_free(pattern); pattern = tmp; } return pattern; }
ast_t* viewpoint_reifythis(ast_t* type) { ast_t* tuple = ast_from(type, TK_TUPLETYPE); ast_t* this_ref = ast_from(type, TK_REF); ast_append(tuple, viewpoint_replacethis(type, this_ref)); ast_free_unattached(this_ref); ast_t* this_val = ast_from(type, TK_VAL); ast_append(tuple, viewpoint_replacethis(type, this_val)); ast_free_unattached(this_val); ast_t* this_box = ast_from(type, TK_BOX); ast_append(tuple, viewpoint_replacethis(type, this_box)); ast_free_unattached(this_box); return tuple; }
static token_id partial_application_cap(pass_opt_t* opt, ast_t* ftype, ast_t* receiver, ast_t* positional) { // Check if the apply method in the generated object literal can accept a box // receiver. If not, it must be a ref receiver. It can accept a box receiver // if box->receiver <: lhs->receiver and box->arg <: lhs->param. AST_GET_CHILDREN(ftype, cap, typeparams, params, result); ast_t* type = ast_type(receiver); ast_t* view_type = viewpoint_type(ast_from(type, TK_BOX), type); ast_t* need_type = set_cap_and_ephemeral(type, ast_id(cap), TK_NONE); bool ok = is_subtype(view_type, need_type, NULL, opt); ast_free_unattached(view_type); ast_free_unattached(need_type); if(!ok) return TK_REF; ast_t* param = ast_child(params); ast_t* arg = ast_child(positional); while(arg != NULL) { if(ast_id(arg) != TK_NONE) { type = ast_type(arg); view_type = viewpoint_type(ast_from(type, TK_BOX), type); need_type = ast_childidx(param, 1); ok = is_subtype(view_type, need_type, NULL, opt); ast_free_unattached(view_type); ast_free_unattached(need_type); if(!ok) return TK_REF; } arg = ast_sibling(arg); param = ast_sibling(param); } return TK_BOX; }
bool expr_repeat(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, body, cond, else_clause); ast_t* body_type = ast_type(body); ast_t* cond_type = ast_type(cond); ast_t* else_type = ast_type(else_clause); if(is_typecheck_error(cond_type)) return false; if(!is_bool(cond_type)) { ast_error(opt->check.errors, cond, "condition must be a Bool"); return false; } if(is_typecheck_error(body_type) || is_typecheck_error(else_type)) return false; // All consumes have to be in scope when the loop body finishes. errorframe_t errorf = NULL; if(!ast_all_consumes_in_scope(body, body, &errorf)) { errorframe_report(&errorf, opt->check.errors); return false; } // Union with any existing type due to a break expression. ast_t* type = ast_type(ast); // No symbol status is inherited from the loop body or condition. Nothing // from outside can be consumed, and definitions inside may not occur. if(!is_control_type(body_type)) type = control_type_add_branch(opt, type, body); if(!is_control_type(else_type)) { type = control_type_add_branch(opt, type, else_clause); ast_inheritbranch(ast, else_clause); // Use a branch count of two instead of one. This means we will pick up any // consumes, but not any definitions, since definitions may not occur. ast_consolidate_branches(ast, 2); } if(type == NULL) type = ast_from(ast, TK_REPEAT); ast_settype(ast, type); literal_unify_control(ast, opt); // Push our symbol status to our parent scope. ast_inheritstatus(ast_parent(ast), ast); return true; }
bool expr_if(pass_opt_t* opt, ast_t* ast) { ast_t* cond = ast_child(ast); ast_t* left = ast_sibling(cond); ast_t* right = ast_sibling(left); ast_t* cond_type = ast_type(cond); if(is_typecheck_error(cond_type)) return false; if(!is_bool(cond_type)) { ast_error(cond, "condition must be a Bool"); return false; } ast_t* l_type = ast_type(left); ast_t* r_type = ast_type(right); ast_t* type = NULL; size_t branch_count = 0; if(!is_control_type(l_type)) { type = control_type_add_branch(type, left); ast_inheritbranch(ast, left); branch_count++; } if(!is_control_type(r_type)) { type = control_type_add_branch(type, right); ast_inheritbranch(ast, right); branch_count++; } if(type == NULL) { if(ast_sibling(ast) != NULL) { ast_error(ast_sibling(ast), "unreachable code"); return false; } type = ast_from(ast, TK_IF); } ast_settype(ast, type); ast_inheritflags(ast); ast_consolidate_branches(ast, branch_count); literal_unify_control(ast, opt); // Push our symbol status to our parent scope. ast_inheritstatus(ast_parent(ast), ast); return true; }
bool expr_try(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, body, else_clause, then_clause); ast_t* body_type = ast_type(body); ast_t* else_type = ast_type(else_clause); ast_t* then_type = ast_type(then_clause); if(is_typecheck_error(body_type) || is_typecheck_error(else_type) || is_typecheck_error(then_type)) return false; ast_t* type = NULL; if(!is_control_type(body_type)) type = control_type_add_branch(opt, type, body); if(!is_control_type(else_type)) type = control_type_add_branch(opt, type, else_clause); if(type == NULL) { if(ast_sibling(ast) != NULL) { ast_error(opt->check.errors, ast_sibling(ast), "unreachable code"); return false; } type = ast_from(ast, TK_TRY); } // The then clause does not affect the type of the expression. if(is_control_type(then_type)) { ast_error(opt->check.errors, then_clause, "then clause always terminates the function"); return false; } if(is_type_literal(then_type)) { ast_error(opt->check.errors, then_clause, "Cannot infer type of unused literal"); return false; } ast_settype(ast, type); literal_unify_control(ast, opt); // Push the symbol status from the then clause to our parent scope. ast_inheritstatus(ast_parent(ast), then_clause); return true; }
ast_t* set_cap_and_ephemeral(ast_t* type, token_id cap, token_id ephemeral) { switch(ast_id(type)) { case TK_UNIONTYPE: case TK_ISECTTYPE: case TK_TUPLETYPE: { ast_t* child = ast_child(type); type = ast_from(type, ast_id(type)); while(child != NULL) { ast_append(type, set_cap_and_ephemeral(child, cap, ephemeral)); child = ast_sibling(child); } return type; } case TK_NOMINAL: { type = ast_dup(type); AST_GET_CHILDREN(type, package, id, typeargs, tcap, eph); if(cap != TK_NONE) ast_setid(tcap, cap); ast_setid(eph, ephemeral); return type; } case TK_TYPEPARAMREF: { type = ast_dup(type); AST_GET_CHILDREN(type, id, tcap, eph); if(cap != TK_NONE) ast_setid(tcap, cap); ast_setid(eph, ephemeral); return type; } case TK_ARROW: // Just use the lhs of the viewpoint type. return set_cap_and_ephemeral(ast_childidx(type, 1), cap, ephemeral); default: {} } assert(0); return NULL; }
// Convert the given method into a delegation indirection to the specified // field. static void make_delegation(ast_t* method, ast_t* field, ast_t* delegate_ref, ast_t* body_donor) { assert(method != NULL); assert(field != NULL); assert(delegate_ref != NULL); // Make a redirection method body. ast_t* args = ast_from(delegate_ref, TK_NONE); ast_t* last_arg = NULL; AST_GET_CHILDREN(method, cap, id, t_params, params, result, error, old_body); for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p)) { const char* param_name = ast_name(ast_child(p)); BUILD(arg, delegate_ref, NODE(TK_SEQ, NODE(TK_CONSUME, NONE NODE(TK_REFERENCE, ID(param_name))))); ast_list_append(args, &last_arg, arg); ast_setid(args, TK_POSITIONALARGS); } BUILD(body, delegate_ref, NODE(TK_SEQ, NODE(TK_CALL, TREE(args) // Positional args. NODE(TK_NONE) // Named args. NODE(TK_DOT, // Receiver. NODE(TK_REFERENCE, ID(ast_name(ast_child(field)))) ID(ast_name(ast_childidx(method, 1))))))); if(is_none(result)) { // Add None to end of body. Whilst the call generated above will return // None anyway in this case, without this extra None testing is very hard // since a directly written version of this body will have the None. BUILD(none, delegate_ref, NODE(TK_REFERENCE, ID("None"))); ast_append(body, none); } ast_replace(&old_body, body); // Setup method info. method_t* info = (method_t*)ast_data(method); assert(info != NULL); info->body_donor = body_donor; info->delegated_field = field; }
ast_t* ast_from_string(ast_t* ast, const char* name) { if(name == NULL) return ast_from(ast, TK_NONE); token_t* t = token_dup(ast->t); token_set_id(t, TK_ID); token_set_string(t, name, 0); ast_t* new_ast = ast_token(t); set_scope_no_parent(new_ast, ast->parent); return new_ast; }
void collect_type_params(ast_t* ast, ast_t** out_params, ast_t** out_args) { assert(ast != NULL); assert(out_params != NULL); assert(out_args != NULL); // Create params and args as TK_NONE, we'll change them if we find any type // params ast_t* params = ast_from(ast, TK_NONE); ast_t* args = ast_from(ast, TK_NONE); ast_t* method = ast; // Find enclosing method while(ast_id(method) != TK_FUN && ast_id(method) != TK_NEW && ast_id(method) != TK_BE) { method = ast_parent(method); assert(method != NULL); } // Find enclosing entity ast_t* entity = ast_parent(ast_parent(method)); ast_t* entity_t_params = ast_childidx(entity, 1); // Collect type parameters defined on the entity for(ast_t* p = ast_child(entity_t_params); p != NULL; p = ast_sibling(p)) collect_type_param(p, params, args); ast_t* method_t_params = ast_childidx(method, 1); // Collect type parameters defined on the method for(ast_t* p = ast_child(method_t_params); p != NULL; p = ast_sibling(p)) collect_type_param(p, params, args); *out_params = params; *out_args = args; }
static ast_result_t sugar_as(pass_opt_t* opt, ast_t** astp) { typecheck_t* t = &opt->check; ast_t* ast = *astp; AST_GET_CHILDREN(ast, expr, type); ast_t* pattern_root = ast_from(type, TK_LEX_ERROR); ast_t* body_root = ast_from(type, TK_LEX_ERROR); add_as_type(t, type, pattern_root, body_root); ast_t* body = ast_pop(body_root); ast_free(body_root); if(body == NULL) { // No body implies all types are "don't care" ast_error(ast, "Cannot treat value as \"don't care\""); ast_free(pattern_root); return AST_ERROR; } // Don't need top sequence in pattern assert(ast_id(ast_child(pattern_root)) == TK_SEQ); ast_t* pattern = ast_pop(ast_child(pattern_root)); ast_free(pattern_root); REPLACE(astp, NODE(TK_MATCH, AST_SCOPE NODE(TK_SEQ, TREE(expr)) NODE(TK_CASES, AST_SCOPE NODE(TK_CASE, AST_SCOPE TREE(pattern) NONE TREE(body))) NODE(TK_SEQ, AST_SCOPE NODE(TK_ERROR, NONE)))); return ast_visit(astp, pass_sugar, NULL, opt, PASS_SUGAR); }
// Setup the type, or lack thereof, for local variable declarations. // This is not really anything to do with traits, but must be done before the // expr pass (to allow initialisation references to the variable type) but // after the name pass (to get temporal capabilities). static void local_types(ast_t* ast) { assert(ast != NULL); // Setup type or mark as inferred now to allow calling create on a // non-inferred local to initialise itself AST_GET_CHILDREN(ast, id, type); assert(type != NULL); if(ast_id(type) == TK_NONE) type = ast_from(id, TK_INFERTYPE); ast_settype(id, type); ast_settype(ast, type); }
static void reify_reference(ast_t** astp, ast_t* typeparam, ast_t* typearg) { ast_t* ast = *astp; pony_assert(ast_id(ast) == TK_REFERENCE); const char* name = ast_name(ast_child(ast)); sym_status_t status; ast_t* ref_def = ast_get(ast, name, &status); if(ref_def == NULL) return; ast_t* param_def = (ast_t*)ast_data(typeparam); pony_assert(param_def != NULL); if(ref_def != param_def) return; ast_setid(ast, TK_TYPEREF); ast_add(ast, ast_from(ast, TK_NONE)); // 1st child: package reference ast_append(ast, ast_from(ast, TK_NONE)); // 3rd child: type args ast_settype(ast, typearg); }
ast_t* viewpoint_cap(token_id cap, token_id eph, ast_t* type) { if(cap == TK_TAG) return NULL; switch(ast_id(type)) { case TK_UNIONTYPE: case TK_ISECTTYPE: case TK_TUPLETYPE: { // Adapt all elements. ast_t* r_type = ast_from(type, ast_id(type)); ast_t* child = ast_child(type); while(child != NULL) { ast_append(r_type, viewpoint_cap(cap, eph, child)); child = ast_sibling(child); } return r_type; } case TK_NOMINAL: return viewpoint_for_type(cap, eph, type, 3); case TK_TYPEPARAMREF: return viewpoint_for_type(cap, eph, type, 1); case TK_ARROW: { // Adapt the lower bounds. ast_t* lower = viewpoint_lower(type); ast_t* r_type = viewpoint_cap(cap, eph, lower); if(r_type != lower) ast_free_unattached(lower); return r_type; } default: {} } assert(0); return NULL; }
static bool insert_apply(pass_opt_t* opt, ast_t** astp) { // Sugar .apply() ast_t* ast = *astp; AST_GET_CHILDREN(ast, positional, namedargs, lhs); ast_t* dot = ast_from(ast, TK_DOT); ast_add(dot, ast_from_string(ast, "apply")); ast_swap(lhs, dot); ast_add(dot, lhs); if(!expr_dot(opt, &dot)) return false; return expr_call(opt, astp); }
bool expr_continue(typecheck_t* t, ast_t* ast) { if(t->frame->loop_body == NULL) { ast_error(ast, "must be in a loop"); return false; } if(!ast_all_consumes_in_scope(t->frame->loop_body, ast)) return false; // continue is always the last expression in a sequence assert(ast_sibling(ast) == NULL); ast_settype(ast, ast_from(ast, TK_CONTINUE)); return true; }