void deferred_reify_add_method_typeparams(deferred_reification_t* deferred, ast_t* typeparams, ast_t* typeargs, pass_opt_t* opt) { pony_assert((deferred->method_typeparams == NULL) && (deferred->method_typeargs == NULL)); pony_assert(((typeparams != NULL) && (typeargs != NULL)) || ((typeparams == NULL) && (typeargs == NULL))); if(typeparams == NULL) return; ast_t* r_typeparams = ast_dup(typeparams); deferred->method_typeargs = ast_dup(typeargs); // Must replace `this` before typeparam reification. if(deferred->thistype != NULL) r_typeparams = viewpoint_replacethis(r_typeparams, deferred->thistype, false); if(deferred->type_typeparams != NULL) r_typeparams = reify(r_typeparams, deferred->type_typeparams, deferred->type_typeargs, opt, false); deferred->method_typeparams = r_typeparams; }
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 void add_rmethod(reachable_method_stack_t** s, reachable_type_t* t, reachable_method_name_t* n, ast_t* typeargs) { const char* name = genname_fun(NULL, n->name, typeargs); reachable_method_t* m = reach_method(n, name); if(m == NULL) { m = POOL_ALLOC(reachable_method_t); m->name = name; m->typeargs = ast_dup(typeargs); m->vtable_index = (uint32_t)-1; ast_t* fun = lookup(NULL, NULL, t->type, n->name); if(typeargs != NULL) { // Reify the method with its typeargs, if it has any. AST_GET_CHILDREN(fun, cap, id, typeparams, params, result, can_error, body); ast_t* r_fun = reify(fun, typeparams, typeargs); ast_free_unattached(fun); fun = r_fun; } m->r_fun = ast_dup(fun); ast_free_unattached(fun); reachable_methods_put(&n->r_methods, m); // Put on a stack of reachable methods to trace. *s = reachable_method_stack_push(*s, m); } }
static void add_rmethod_to_subtype(reach_t* r, reach_type_t* t, reach_method_name_t* n, reach_method_t* m, pass_opt_t* opt) { // Add the method to the type if it isn't already there. reach_method_name_t* n2 = add_method_name(t, n->name); add_rmethod(r, t, n2, m->cap, m->typeargs, opt); // Add this mangling to the type if it isn't already there. reach_method_t* mangled = reach_mangled_get(&n2->r_mangled, m); if(mangled != NULL) return; mangled = POOL_ALLOC(reach_method_t); memset(mangled, 0, sizeof(reach_method_t)); mangled->name = m->name; mangled->mangled_name = m->mangled_name; mangled->full_name = make_full_name(t, mangled); mangled->cap = m->cap; mangled->r_fun = ast_dup(m->r_fun); mangled->typeargs = ast_dup(m->typeargs); mangled->forwarding = true; mangled->param_count = m->param_count; mangled->params = (reach_param_t*)ponyint_pool_alloc_size( mangled->param_count * sizeof(reach_param_t)); memcpy(mangled->params, m->params, m->param_count * sizeof(reach_param_t)); mangled->result = m->result; // Add to the mangled table only. reach_mangled_put(&n2->r_mangled, mangled); }
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; }
// Process the given list of provided methods (all of the same name) for the // given entity static bool process_method_name(ast_t* list, ast_t* entity) { assert(list != NULL); assert(entity != NULL); const char* name = ast_name(list); assert(name != NULL); ast_t* existing = ast_get(entity, name, NULL); if(existing != NULL) { // Method is explicitly defined in entity if(!methods_compatible(list, existing, name, entity)) return false; } else { // Method is not defined in entity existing = most_general_method(list, entity, name); if(existing == NULL) return false; // Add the most general method from the list to the entity existing = ast_dup(existing); ast_append(ast_childidx(entity, 4), existing); ast_set(entity, name, existing, SYM_NONE); } // Current body (if any) is not provided by this entity, get one from the // trait methods if(ast_data(existing) != (void*)entity) attach_body_from_list(list, existing); return true; }
ast_t* ast_append(ast_t* parent, ast_t* child) { assert(parent != NULL); assert(child != NULL); assert(parent != child); if(hasparent(child)) child = ast_dup(child); set_scope_and_parent(child, parent); if(parent->child == NULL) { parent->child = child; return child; } ast_t* ast = parent->child; while(ast->sibling != NULL) ast = ast->sibling; ast->sibling = child; return child; }
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; }
static ast_t* viewpoint_for_type(token_id view, token_id eph, ast_t* type, int cap_index) { ast_t* cap = ast_childidx(type, cap_index); token_id tcap = ast_id(cap); token_id rcap = cap_viewpoint(view, tcap); if(rcap == TK_NONE) return NULL; if((tcap != rcap) || (eph == TK_EPHEMERAL)) { type = ast_dup(type); cap = ast_childidx(type, cap_index); ast_setid(cap, rcap); if(eph == TK_EPHEMERAL && ((view == TK_ISO) || (view == TK_TRN))) { // If we're adapting from an ephemeral type, make this type ephemeral. // iso^->iso = iso^, previous views were iso, alias(iso^) ~ alias(iso) // iso^->trn = trn^, previous views were tag, alias(trn^) ~ alias(tag) // trn^->iso = iso^, previous views were iso, alias(iso^) ~ alias(iso) // trn^->trn = trn^, previous views were trn, alias(trn^) ~ alias(trn) // alias(iso)->x isn't alllowed // alias(trn)->x = box->x // alias(iso^) ~ alias(box->iso) // alias(trn^) ~ alias(box->trn) // Doesn't work for ref, isn't needed for val, box or tag. ast_t* ephemeral = ast_sibling(cap); ast_setid(ephemeral, eph); } } return type; }
void ast_swap(ast_t* prev, ast_t* next) { assert(prev != NULL); assert(prev != next); ast_t* parent = ast_parent(prev); assert(parent != NULL); if(hasparent(next)) next = ast_dup(next); set_scope_and_parent(next, parent); if(parent->type == prev) { parent->type = next; } else { ast_t* last = ast_previous(prev); if(last != NULL) last->sibling = next; else parent->child = next; next->sibling = prev->sibling; } prev->sibling = NULL; make_orphan_leave_scope(prev); }
static reachable_type_t* add_tuple(reachable_method_stack_t** s, reachable_types_t* r, uint32_t* next_type_id, ast_t* type) { if(contains_dontcare(type)) return NULL; reachable_type_t* t = reach_type(r, type); if(t != NULL) return t; t = add_reachable_type(r, type); t->type_id = ++(*next_type_id); t->field_count = (uint32_t)ast_childcount(t->ast); t->fields = (reachable_field_t*)calloc(t->field_count, sizeof(reachable_field_t)); size_t index = 0; ast_t* child = ast_child(type); while(child != NULL) { t->fields[index].ast = ast_dup(child); t->fields[index].type = add_type(s, r, next_type_id, child);; index++; child = ast_sibling(child); } return t; }
static ast_t* reify_without_defaults(ast_t* ast, ast_t* typeparams, ast_t* typeargs, ast_t** lastparam, ast_t** lastarg) { // Duplicate the node. ast_t* r_ast = ast_dup(ast); // Iterate pairwise through the params and the args. ast_t* typeparam = ast_child(typeparams); ast_t* typearg = ast_child(typeargs); while((typeparam != NULL) && (typearg != NULL)) { // Reify the typeparam with the typearg. reify_one(&r_ast, typeparam, typearg); typeparam = ast_sibling(typeparam); typearg = ast_sibling(typearg); } if(lastparam != NULL) *lastparam = typeparam; if(lastarg != NULL) *lastarg = typearg; return r_ast; }
ast_t* reify(ast_t* ast, ast_t* typeparams, ast_t* typeargs, pass_opt_t* opt) { (void)opt; assert( (ast_id(typeparams) == TK_TYPEPARAMS) || (ast_id(typeparams) == TK_NONE) ); assert( (ast_id(typeargs) == TK_TYPEARGS) || (ast_id(typeargs) == TK_NONE) ); // Duplicate the node. ast_t* r_ast = ast_dup(ast); // Iterate pairwise through the typeparams and typeargs. ast_t* typeparam = ast_child(typeparams); ast_t* typearg = ast_child(typeargs); while((typeparam != NULL) && (typearg != NULL)) { reify_one(&r_ast, typeparam, typearg); typeparam = ast_sibling(typeparam); typearg = ast_sibling(typearg); } assert(typeparam == NULL); assert(typearg == NULL); return r_ast; }
deferred_reification_t* deferred_reify_dup(deferred_reification_t* deferred) { if(deferred == NULL) return NULL; deferred_reification_t* copy = POOL_ALLOC(deferred_reification_t); copy->ast = deferred->ast; copy->type_typeparams = ast_dup(deferred->type_typeparams); copy->type_typeargs = ast_dup(deferred->type_typeargs); copy->method_typeparams = ast_dup(deferred->method_typeparams); copy->method_typeargs = ast_dup(deferred->method_typeargs); copy->thistype = ast_dup(deferred->thistype); return copy; }
deferred_reification_t* deferred_reify_new(ast_t* ast, ast_t* typeparams, ast_t* typeargs, ast_t* thistype) { pony_assert(((typeparams != NULL) && (typeargs != NULL)) || ((typeparams == NULL) && (typeargs == NULL))); deferred_reification_t* deferred = POOL_ALLOC(deferred_reification_t); deferred->ast = ast; deferred->type_typeparams = ast_dup(typeparams); deferred->type_typeargs = ast_dup(typeargs); deferred->thistype = ast_dup(thistype); deferred->method_typeparams = NULL; deferred->method_typeargs = NULL; return deferred; }
ast_t* sanitise_type(ast_t* type) { assert(type != NULL); ast_t* new_type = ast_dup(type); sanitise(&new_type); return new_type; }
ast_t* viewpoint_replace(ast_t* ast, ast_t* target, ast_t* with) { // Target is thistype or a typeparamref. With is a type (when replacing // `this` in a reified method signature) or a single capability (when // typechecking arrow types). assert( (ast_id(target) == TK_THISTYPE) || (ast_id(target) == TK_TYPEPARAMREF)); ast_t* r_ast = ast_dup(ast); replace_type(&r_ast, target, with); return r_ast; }
ast_t* ast_add(ast_t* parent, ast_t* child) { assert(parent != NULL); assert(parent != child); assert(parent->child != child); if(hasparent(child)) child = ast_dup(child); set_scope_and_parent(child, parent); child->sibling = parent->child; parent->child = child; return child; }
void ast_replace(ast_t** prev, ast_t* next) { if(*prev == next) return; if(hasparent(next)) next = ast_dup(next); if(hasparent(*prev)) ast_swap(*prev, next); ast_free(*prev); *prev = next; }
static ast_t* viewpoint_lower_for_type(ast_t* type, int cap_index) { ast_t* cap = ast_childidx(type, cap_index); token_id tcap = ast_id(cap); // For any chain of arrows, return a capability that is a subtype of the // resulting capability. // ref->iso = iso, val->iso = val, box->iso = tag => iso // ref->trn = trn, val->trn = val, box->trn = box => trn // ref->ref = ref, val->ref = val, box->ref = box => trn // ref->val = val, val->val = val, box->val = val => val // ref->box = box, val->box = val, box->box = box => val // ref->tag = tag, val->tag = tag, box->tag = tag => tag // #read: ref = trn, val = val, box = val => trn // #send: iso = iso, val = val, tag = tag => iso // #share: val = val, tag = tag => val // #any: iso = iso => iso switch(tcap) { case TK_ISO: case TK_TRN: case TK_VAL: case TK_TAG: return type; case TK_REF: case TK_CAP_READ: tcap = TK_TRN; break; case TK_BOX: case TK_CAP_SHARE: tcap = TK_VAL; break; case TK_CAP_SEND: case TK_CAP_ANY: tcap = TK_ISO; break; default: assert(0); return NULL; } type = ast_dup(type); cap = ast_childidx(type, cap_index); ast_setid(cap, tcap); return type; }
static reach_method_t* add_rmethod(reach_t* r, reach_type_t* t, reach_method_name_t* n, token_id cap, ast_t* typeargs, pass_opt_t* opt) { const char* name = genname_fun(cap, n->name, typeargs); reach_method_t* m = reach_rmethod(n, name); if(m != NULL) return m; m = POOL_ALLOC(reach_method_t); memset(m, 0, sizeof(reach_method_t)); m->name = name; m->cap = cap; m->typeargs = ast_dup(typeargs); m->vtable_index = (uint32_t)-1; ast_t* r_ast = set_cap_and_ephemeral(t->ast, cap, TK_NONE); ast_t* fun = lookup(NULL, NULL, r_ast, n->name); ast_free_unattached(r_ast); if(typeargs != NULL) { // Reify the method with its typeargs, if it has any. AST_GET_CHILDREN(fun, cap, id, typeparams, params, result, can_error, body); fun = reify(fun, typeparams, typeargs, opt, false); } m->r_fun = fun; set_method_types(r, m, opt); m->mangled_name = make_mangled_name(m); m->full_name = make_full_name(t, m); // Add to both tables. reach_methods_put(&n->r_methods, m); reach_mangled_put(&n->r_mangled, m); // Put on a stack of reachable methods to trace. r->stack = reach_method_stack_push(r->stack, m); // Add the method to any subtypes. add_rmethod_to_subtypes(r, t, n, m, opt); return m; }
static bool reify_one(ast_t** astp, ast_t* typeparam, ast_t* typearg) { ast_t* ast = *astp; ast_t* type = ast_type(ast); if(type != NULL) reify_one(&type, typeparam, typearg); if(ast_id(ast) == TK_TYPEPARAMREF) return reify_typeparamref(astp, typeparam, typearg); ast_t* child = ast_child(ast); bool flatten = false; while(child != NULL) { flatten |= reify_one(&child, typeparam, typearg); child = ast_sibling(child); } // Flatten type expressions after reifying them. if(flatten) { switch(ast_id(ast)) { case TK_ARROW: { AST_GET_CHILDREN(ast, left, right); ast = viewpoint_type(left, right); if(ast == NULL) return false; if(ast == right) ast = ast_dup(ast); ast_replace(astp, ast); return true; } default: {} } } return false; }
ast_t* ast_add_sibling(ast_t* older_sibling, ast_t* new_sibling) { assert(older_sibling != NULL); assert(new_sibling != NULL); assert(older_sibling != new_sibling); assert(hasparent(older_sibling)); if(hasparent(new_sibling)) new_sibling = ast_dup(new_sibling); assert(new_sibling->sibling == NULL); set_scope_and_parent(new_sibling, older_sibling->parent); new_sibling->sibling = older_sibling->sibling; older_sibling->sibling = new_sibling; return new_sibling; }
ast_t* deferred_reify(deferred_reification_t* deferred, ast_t* ast, pass_opt_t* opt) { ast_t* r_ast = ast_dup(ast); // Must replace `this` before typeparam reification. if(deferred->thistype != NULL) r_ast = viewpoint_replacethis(r_ast, deferred->thistype, false); if(deferred->type_typeparams != NULL) r_ast = reify(r_ast, deferred->type_typeparams, deferred->type_typeargs, opt, false); if(deferred->method_typeparams != NULL) r_ast = reify(r_ast, deferred->method_typeparams, deferred->method_typeargs, opt, false); return r_ast; }
static ast_t* type_typeexpr(token_id t, ast_t* l_type, ast_t* r_type) { bool is_union = t == TK_UNIONTYPE; if(l_type == NULL) return r_type; if(r_type == NULL) return l_type; if(is_subtype(l_type, r_type)) { if(is_union) return r_type; else return l_type; } if(is_subtype(r_type, l_type)) { if(is_union) return l_type; else return r_type; } ast_t* type = ast_from(l_type, t); append_to_typeexpr(type, l_type, is_union); append_to_typeexpr(type, r_type, is_union); // If there's only one element, remove the type expression node. ast_t* child = ast_child(type); if(ast_sibling(child) == NULL) { child = ast_dup(child); ast_free_unattached(type); type = child; } return type; }
// Coerce a literal control block to be the specified target type static bool coerce_control_block(ast_t** astp, ast_t* target_type, lit_chain_t* chain, pass_opt_t* options, bool report_errors) { assert(astp != NULL); ast_t* literal_expr = *astp; assert(literal_expr != NULL); ast_t* lit_type = ast_type(literal_expr); assert(lit_type != NULL); assert(ast_id(lit_type) == TK_LITERAL); ast_t* block_type = ast_type(lit_type); for(ast_t* p = ast_child(lit_type); p != NULL; p = ast_sibling(p)) { assert(ast_id(p) == TK_LITERALBRANCH); ast_t* branch = (ast_t*)ast_data(p); assert(branch != NULL); if(!coerce_literal_to_type(&branch, target_type, chain, options, report_errors)) { ast_free_unattached(block_type); return false; } block_type = type_union(block_type, ast_type(branch)); } if(is_typecheck_error(block_type)) return false; // block_type may be a sub-tree of the current type of literal_expr. // This means we must copy it before setting it as the type since ast_settype // will first free the existing type of literal_expr, which may include // block_type. if(ast_parent(block_type) != NULL) block_type = ast_dup(block_type); ast_settype(literal_expr, block_type); return true; }
static ast_t* viewpoint_lower_for_typeparam(ast_t* type) { AST_GET_CHILDREN(type, id, cap, eph); token_id tcap = ast_id(cap); // ref->boxgen = trn, val->boxgen = val, box->boxgen = val => trn // ref->taggen = val, val->taggen = val, box->taggen = val => val // ref->anygen = iso, val->anygen = val, box->anygen = val => iso switch(tcap) { case TK_ISO: case TK_TRN: case TK_REF: case TK_VAL: case TK_TAG: return type; case TK_BOX_GENERIC: tcap = TK_TRN; break; case TK_TAG_GENERIC: tcap = TK_VAL; break; case TK_ANY_GENERIC: tcap = TK_ISO; break; default: assert(0); return NULL; } type = ast_dup(type); cap = ast_childidx(type, 1); ast_setid(cap, tcap); return type; }
static ast_t* viewpoint_lower_for_nominal(ast_t* type) { ast_t* cap = ast_childidx(type, 3); token_id tcap = ast_id(cap); // For any chain of arrows, return a capability that is a subtype of the // resulting capability. // ref->iso = iso, val->iso = val, box->iso = tag => iso // ref->trn = trn, val->trn = val, box->trn = box => trn // ref->ref = ref, val->ref = val, box->ref = box => trn // ref->val = val, val->val = val, box->val = val => val // ref->box = box, val->box = val, box->box = box => val // ref->tag = tag, val->tag = tag, box->tag = tag => tag switch(tcap) { case TK_ISO: case TK_TRN: case TK_VAL: case TK_TAG: return type; case TK_REF: tcap = TK_TRN; break; case TK_BOX: tcap = TK_VAL; break; default: assert(0); return NULL; } type = ast_dup(type); cap = ast_childidx(type, 3); ast_setid(cap, tcap); return type; }
ast_t* type_for_fun(ast_t* ast) { AST_GET_CHILDREN(ast, cap, name, typeparams, params, result); token_id fcap = ast_id(cap); if(fcap == TK_NONE) fcap = TK_TAG; // The params may already have types attached. If we build the function type // directly from those we'll get nested types which can mess things up. To // avoid this make a clean version of the params without types. ast_t* clean_params = ast_dup(params); for(ast_t* p = ast_child(clean_params); p != NULL; p = ast_sibling(p)) ast_settype(p, NULL); BUILD(fun, ast, NODE(TK_FUNTYPE, NODE(fcap) TREE(typeparams) TREE(clean_params) TREE(result))); return fun; }
static reach_type_t* add_tuple(reach_t* r, ast_t* type, pass_opt_t* opt) { if(contains_dontcare(type)) return NULL; reach_type_t* t = reach_type(r, type); if(t != NULL) return t; t = add_reach_type(r, type); t->underlying = TK_TUPLETYPE; t->type_id = r->next_type_id++; t->field_count = (uint32_t)ast_childcount(t->ast); t->fields = (reach_field_t*)calloc(t->field_count, sizeof(reach_field_t)); printbuf_t* mangle = printbuf_new(); printbuf(mangle, "%d", t->field_count); ast_t* child = ast_child(type); size_t index = 0; while(child != NULL) { t->fields[index].ast = ast_dup(child); t->fields[index].type = add_type(r, child, opt); printbuf(mangle, "%s", t->fields[index].type->mangle); index++; child = ast_sibling(child); } t->mangle = stringtab(mangle->m); printbuf_free(mangle); return t; }