static void set_method_types(reach_t* r, reach_method_t* m, pass_opt_t* opt) { AST_GET_CHILDREN(m->r_fun, cap, id, typeparams, params, result, can_error, body); m->param_count = ast_childcount(params); m->params = (reach_param_t*)ponyint_pool_alloc_size( m->param_count * sizeof(reach_param_t)); ast_t* param = ast_child(params); size_t i = 0; while(param != NULL) { AST_GET_CHILDREN(param, p_id, p_type); m->params[i].type = add_type(r, p_type, opt); if(ast_id(p_type) != TK_NOMINAL && ast_id(p_type) != TK_TYPEPARAMREF) m->params[i].cap = TK_REF; else m->params[i].cap = ast_id(cap_fetch(p_type)); ++i; param = ast_sibling(param); } m->result = add_type(r, result, opt); }
static bool is_typeparam_sub_typeparam(ast_t* sub, ast_t* super, errorframe_t* errors) { // k <: k' // --- // A k <: A k' ast_t* sub_def = (ast_t*)ast_data(sub); ast_t* super_def = (ast_t*)ast_data(super); if(sub_def == super_def) { // We know the bounds on the rcap is the same, so we have different // subtyping rules here. 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_bound(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; } if(errors != NULL) { ast_error_frame(errors, sub, "%s is not a subtype of %s: they are different type parameters", ast_print_type(sub), ast_print_type(super)); } return false; }
token_id cap_single(ast_t* type) { ast_t* cap = cap_fetch(type); ast_t* eph = ast_sibling(cap); token_id tcap = ast_id(cap); token_id teph = ast_id(eph); cap_aliasing(&tcap, &teph); return tcap; }
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; }
ast_result_t flatten_typeparamref(pass_opt_t* opt, ast_t* ast) { ast_t* cap_ast = cap_fetch(ast); token_id cap = ast_id(cap_ast); typeparam_set_cap(ast); token_id set_cap = ast_id(cap_ast); if((cap != TK_NONE) && (cap != set_cap)) { ast_t* def = (ast_t*)ast_data(ast); ast_t* constraint = typeparam_constraint(ast); if(constraint != NULL) { ast_error(opt->check.errors, cap_ast, "can't specify a capability on a " "type parameter that differs from the constraint"); ast_error_continue(opt->check.errors, constraint, "constraint definition is here"); if(ast_parent(constraint) != def) { ast_error_continue(opt->check.errors, def, "type parameter definition is here"); } } else { ast_error(opt->check.errors, cap_ast, "a type parameter with no " "constraint can only have #any as its capability"); ast_error_continue(opt->check.errors, def, "type parameter definition is here"); } return AST_ERROR; } return AST_OK; }
static void trace_dynamic_nominal(compile_t* c, LLVMValueRef ctx, LLVMValueRef object, ast_t* type, ast_t* orig, ast_t* tuple, LLVMBasicBlockRef next_block) { pony_assert(ast_id(type) == TK_NOMINAL); // Skip if a primitive. ast_t* def = (ast_t*)ast_data(type); if(ast_id(def) == TK_PRIMITIVE) return; int mutability = trace_cap_nominal(c->opt, type, orig, tuple); // If we can't extract the element from the original type, there is no need to // trace the element. if(mutability == -1) return; token_id dst_cap = TK_TAG; switch(mutability) { case PONY_TRACE_MUTABLE: dst_cap = TK_ISO; break; case PONY_TRACE_IMMUTABLE: dst_cap = TK_VAL; break; default: {} } ast_t* dst_type = ast_dup(type); ast_t* dst_cap_ast = cap_fetch(dst_type); ast_setid(dst_cap_ast, dst_cap); ast_t* dst_eph = ast_sibling(dst_cap_ast); if(ast_id(dst_eph) == TK_EPHEMERAL) ast_setid(dst_eph, TK_NONE); // We aren't always this type. We need to check dynamically. LLVMValueRef desc = gendesc_fetch(c, object); LLVMValueRef test = gendesc_isnominal(c, desc, type); LLVMBasicBlockRef is_true = codegen_block(c, ""); LLVMBasicBlockRef is_false = codegen_block(c, ""); LLVMBuildCondBr(c->builder, test, is_true, is_false); // Trace as this type. LLVMPositionBuilderAtEnd(c->builder, is_true); gentrace(c, ctx, object, object, type, dst_type); ast_free_unattached(dst_type); // If we have traced as mut or val, we're done with this element. Otherwise, // continue tracing this as if the match had been unsuccessful. if(mutability != PONY_TRACE_OPAQUE) LLVMBuildBr(c->builder, next_block); else LLVMBuildBr(c->builder, is_false); // Carry on, whether we have traced or not. LLVMPositionBuilderAtEnd(c->builder, is_false); }
static int trace_cap_nominal(pass_opt_t* opt, ast_t* type, ast_t* orig, ast_t* tuple) { pony_assert(ast_id(type) == TK_NOMINAL); ast_t* cap = cap_fetch(type); if(tuple != NULL) { // We are a tuple element. Our type is in the correct position in the // tuple, everything else is TK_DONTCARETYPE. type = tuple; } token_id orig_cap = ast_id(cap); // We can have a non-sendable rcap if we're tracing a field in a type's trace // function. In this case we must always recurse and we have to trace the // field as mutable. switch(orig_cap) { case TK_TRN: case TK_REF: case TK_BOX: return PONY_TRACE_MUTABLE; default: {} } // If it's possible to use match or to extract the source type from the // destination type with a given cap, then we must trace as this cap. Try iso, // val and tag in that order. if(orig_cap == TK_ISO) { if(is_matchtype(orig, type, opt) == MATCHTYPE_ACCEPT) { return PONY_TRACE_MUTABLE; } else { ast_setid(cap, TK_VAL); } } if(ast_id(cap) == TK_VAL) { if(is_matchtype(orig, type, opt) == MATCHTYPE_ACCEPT) { ast_setid(cap, orig_cap); return PONY_TRACE_IMMUTABLE; } else { ast_setid(cap, TK_TAG); } } pony_assert(ast_id(cap) == TK_TAG); int ret = -1; if(is_matchtype(orig, type, opt) == MATCHTYPE_ACCEPT) ret = PONY_TRACE_OPAQUE; ast_setid(cap, orig_cap); return ret; }
ast_t* viewpoint_lower(ast_t* type) { // T = N | A // s = {k} // upper(s p'->T k p) = union[k' in s](T (k'->k) eph(s, p', p)) // eph(s, p', p) = { unalias(p) if p' = ^, exists k in s . k in {iso, trn} // { p otherwise assert(ast_id(type) == TK_ARROW); AST_GET_CHILDREN(type, left, right); ast_t* r_right = right; switch(ast_id(right)) { case TK_NOMINAL: case TK_TYPEPARAMREF: break; case TK_ARROW: // Arrow types are right associative. r_right = viewpoint_lower(right); if(r_right == NULL) return NULL; break; default: assert(0); return NULL; } token_id l_cap = TK_NONE; token_id l_eph = TK_NONE; switch(ast_id(left)) { case TK_ISO: case TK_TRN: case TK_REF: case TK_VAL: case TK_BOX: case TK_TAG: l_cap = ast_id(left); break; case TK_THISTYPE: l_cap = TK_CAP_READ; break; case TK_NOMINAL: case TK_TYPEPARAMREF: { ast_t* left_cap = cap_fetch(left); ast_t* left_eph = ast_sibling(left_cap); l_cap = ast_id(left_cap); l_eph = ast_id(left_eph); break; } default: assert(0); return NULL; } ast_t* right_cap = cap_fetch(r_right); ast_t* right_eph = ast_sibling(right_cap); token_id r_cap = ast_id(right_cap); token_id r_eph = ast_id(right_eph); // No result: left side could be a tag. if(!cap_view_lower(l_cap, l_eph, &r_cap, &r_eph)) return NULL; ast_t* rr_right = set_cap_and_ephemeral(r_right, r_cap, r_eph); if(r_right != right) ast_free_unattached(r_right); return rr_right; }
ast_t* viewpoint_type(ast_t* l_type, ast_t* r_type) { int upper = VIEW_UPPER_NO; switch(ast_id(r_type)) { case TK_UNIONTYPE: case TK_ISECTTYPE: case TK_TUPLETYPE: { // Adapt each element. ast_t* type = ast_from(r_type, ast_id(r_type)); ast_t* child = ast_child(r_type); while(child != NULL) { ast_append(type, viewpoint_type(l_type, child)); child = ast_sibling(child); } return type; } case TK_NOMINAL: case TK_TYPEPARAMREF: { ast_t* cap = cap_fetch(r_type); switch(ast_id(cap)) { case TK_ISO: case TK_TRN: case TK_REF: case TK_BOX: case TK_ISO_BIND: case TK_TRN_BIND: case TK_REF_BIND: case TK_BOX_BIND: // A known refcap on the right can be compacted. upper = VIEW_UPPER_YES; break; case TK_VAL: case TK_TAG: case TK_CAP_SHARE: case TK_VAL_BIND: case TK_TAG_BIND: case TK_CAP_SHARE_BIND: // No refcap on the left modifies these. upper = VIEW_UPPER_FORCE; break; default: {} } break; } case TK_ARROW: break; default: assert(0); return NULL; } switch(ast_id(l_type)) { case TK_NOMINAL: case TK_TYPEPARAMREF: { ast_t* cap = cap_fetch(l_type); switch(ast_id(cap)) { case TK_REF: case TK_REF_BIND: // ref->T = T return ast_dup(r_type); case TK_CAP_SEND: case TK_CAP_SHARE: case TK_CAP_READ: case TK_CAP_ALIAS: case TK_CAP_ANY: case TK_CAP_SEND_BIND: case TK_CAP_SHARE_BIND: case TK_CAP_READ_BIND: case TK_CAP_ALIAS_BIND: case TK_CAP_ANY_BIND: // Don't compact through an unknown refcap. if(upper == VIEW_UPPER_YES) upper = VIEW_UPPER_NO; break; default: {} } break; } case TK_THISTYPE: if(upper == VIEW_UPPER_YES) upper = VIEW_UPPER_NO; break; case TK_ISO: case TK_TRN: case TK_REF: case TK_VAL: case TK_BOX: case TK_TAG: break; case TK_ARROW: { // (T1->T2)->T3 --> T1->(T2->T3) AST_GET_CHILDREN(l_type, left, right); ast_t* r_right = viewpoint_type(right, r_type); return viewpoint_type(left, r_right); } default: assert(0); return NULL; } BUILD(arrow, l_type, NODE(TK_ARROW, TREE(ast_dup(l_type)) TREE(ast_dup(r_type)))); if(upper != VIEW_UPPER_NO) { ast_t* arrow_upper = viewpoint_upper(arrow); if(arrow_upper == NULL) return arrow; if(arrow != arrow_upper) { ast_free_unattached(arrow); arrow = arrow_upper; } } return arrow; }