static void trace_dynamic_nominal(compile_t* c, LLVMValueRef ctx, LLVMValueRef object, ast_t* type, ast_t* orig, ast_t* tuple, LLVMBasicBlockRef next_block) { // Skip if a primitive. ast_t* def = (ast_t*)ast_data(type); if(ast_id(def) == TK_PRIMITIVE) return; // If it's not possible to use match or as to extract this type from the // original type, there's no need to trace as this type. if(tuple != NULL) { // We are a tuple element. Our type is in the correct position in the // tuple, everything else is TK_DONTCARE. if(is_matchtype(orig, tuple) != MATCHTYPE_ACCEPT) return; } else { // We aren't a tuple element. if(is_matchtype(orig, type) != MATCHTYPE_ACCEPT) return; } // 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, type); // If we have traced as known, unknown or actor, we're done with this // element. Otherwise, continue tracing this as if the match had been // unsuccessful. switch(trace_type(type)) { case TRACE_KNOWN: case TRACE_UNKNOWN: case TRACE_KNOWN_VAL: case TRACE_UNKNOWN_VAL: case TRACE_ACTOR: LLVMBuildBr(c->builder, next_block); break; default: LLVMBuildBr(c->builder, is_false); break; } // Carry on, whether we have traced or not. LLVMPositionBuilderAtEnd(c->builder, is_false); }
static matchtype_t is_x_match_isect(ast_t* operand, ast_t* pattern) { matchtype_t ok = MATCHTYPE_ACCEPT; for(ast_t* child = ast_child(pattern); child != NULL; child = ast_sibling(child)) { switch(is_matchtype(operand, child)) { case MATCHTYPE_ACCEPT: break; case MATCHTYPE_REJECT: // If any type in the pattern isect rejects a match, the entire pattern // isect rejects a match. ok = MATCHTYPE_REJECT; break; case MATCHTYPE_DENY: // If any type in the pattern isect denies a match, the entire pattern // isect denies a match. return MATCHTYPE_DENY; } } return ok; }
static matchtype_t is_tuple_match_tuple(ast_t* operand, ast_t* pattern) { // Must be a pairwise match. if(ast_childcount(operand) != ast_childcount(pattern)) return MATCHTYPE_REJECT; ast_t* operand_child = ast_child(operand); ast_t* pattern_child = ast_child(pattern); matchtype_t ok = MATCHTYPE_ACCEPT; while(operand_child != NULL) { switch(is_matchtype(operand_child, pattern_child)) { case MATCHTYPE_ACCEPT: break; case MATCHTYPE_REJECT: ok = MATCHTYPE_REJECT; break; case MATCHTYPE_DENY: return MATCHTYPE_DENY; } operand_child = ast_sibling(operand_child); pattern_child = ast_sibling(pattern_child); } return ok; }
static matchtype_t is_union_match_x(ast_t* operand, ast_t* pattern) { matchtype_t ok = MATCHTYPE_REJECT; for(ast_t* child = ast_child(operand); child != NULL; child = ast_sibling(child)) { switch(is_matchtype(child, pattern)) { case MATCHTYPE_ACCEPT: // If any type in the operand union accepts a match, then the entire // operand union accepts a match. ok = MATCHTYPE_ACCEPT; break; case MATCHTYPE_REJECT: break; case MATCHTYPE_DENY: // If any type in the operand union denies a match, then the entire // operand union is denied a match. return MATCHTYPE_DENY; } } return ok; }
static matchtype_t is_x_match_arrow(ast_t* operand, ast_t* pattern) { // T1 match upperbound(T2->T3) // --- // T1 match T2->T3 ast_t* pattern_upper = viewpoint_upper(pattern); matchtype_t ok = is_matchtype(operand, pattern_upper); ast_free_unattached(pattern_upper); return ok; }
static matchtype_t is_arrow_match_nominal(ast_t* operand, ast_t* pattern) { // lowerbound(T1->T2) match T3 // --- // (T1->T2) match T3 ast_t* operand_lower = viewpoint_lower(operand); matchtype_t ok = is_matchtype(operand_lower, pattern); ast_free_unattached(operand_lower); return ok; }
static matchtype_t is_x_match_typeparam(ast_t* operand, ast_t* pattern) { ast_t* pattern_upper = typeparam_upper(pattern); // An unconstrained typeparam can match anything. if(pattern_upper == NULL) return MATCHTYPE_ACCEPT; // Otherwise, match the constraint. matchtype_t ok = is_matchtype(operand, pattern_upper); ast_free_unattached(pattern_upper); return ok; }
static matchtype_t is_typeparam_match_x(ast_t* operand, ast_t* pattern) { ast_t* operand_upper = typeparam_upper(operand); // An unconstrained typeparam could match anything. if(operand_upper == NULL) return MATCHTYPE_ACCEPT; // Check if the constraint can match the pattern. matchtype_t ok = is_matchtype(operand_upper, pattern); ast_free_unattached(operand_upper); return ok; }
LLVMValueRef gen_match(compile_t* c, ast_t* ast) { bool needed = is_result_needed(ast); ast_t* type = ast_type(ast); AST_GET_CHILDREN(ast, match_expr, cases, else_expr); // We will have no type if all case have control types. LLVMTypeRef phi_type = NULL; if(needed && !is_control_type(type)) { reach_type_t* t_phi = reach_type(c->reach, type); phi_type = t_phi->use_type; } ast_t* match_type = alias(ast_type(match_expr)); LLVMValueRef match_value = gen_expr(c, match_expr); LLVMBasicBlockRef pattern_block = codegen_block(c, "case_pattern"); LLVMBasicBlockRef else_block = codegen_block(c, "match_else"); LLVMBasicBlockRef post_block = NULL; LLVMBasicBlockRef next_block = NULL; // Jump to the first case. LLVMBuildBr(c->builder, pattern_block); LLVMValueRef phi = GEN_NOVALUE; if(!is_control_type(type)) { // Start the post block so that a case can modify the phi node. post_block = codegen_block(c, "match_post"); LLVMPositionBuilderAtEnd(c->builder, post_block); if(needed) phi = LLVMBuildPhi(c->builder, phi_type, ""); else phi = GEN_NOTNEEDED; } // Iterate over the cases. ast_t* the_case = ast_child(cases); while(the_case != NULL) { ast_t* next_case = ast_sibling(the_case); if(next_case != NULL) next_block = codegen_block(c, "case_pattern"); else next_block = else_block; AST_GET_CHILDREN(the_case, pattern, guard, body); LLVMPositionBuilderAtEnd(c->builder, pattern_block); codegen_pushscope(c, the_case); ast_t* pattern_type = ast_type(pattern); bool ok = true; if(is_matchtype(match_type, pattern_type, c->opt) != MATCHTYPE_ACCEPT) { // If there's no possible match, jump directly to the next block. LLVMBuildBr(c->builder, next_block); } else { // Check the pattern. ok = static_match(c, match_value, match_type, pattern, next_block); // Check the guard. ok = ok && guard_match(c, guard, next_block); // Case body. ok = ok && case_body(c, body, post_block, phi, phi_type); } codegen_popscope(c); if(!ok) { ast_free_unattached(match_type); return NULL; } the_case = next_case; pattern_block = next_block; } ast_free_unattached(match_type); // Else body. LLVMPositionBuilderAtEnd(c->builder, else_block); codegen_pushscope(c, else_expr); bool ok = case_body(c, else_expr, post_block, phi, phi_type); codegen_popscope(c); if(!ok) return NULL; if(post_block != NULL) LLVMPositionBuilderAtEnd(c->builder, post_block); return phi; }
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; }
bool expr_case(pass_opt_t* opt, ast_t* ast) { assert(opt != NULL); assert(ast_id(ast) == TK_CASE); AST_GET_CHILDREN(ast, pattern, guard, body); if((ast_id(pattern) == TK_NONE) && (ast_id(guard) == TK_NONE)) { ast_error(ast, "can't have a case with no conditions, use an else clause"); return false; } ast_t* cases = ast_parent(ast); ast_t* match = ast_parent(cases); ast_t* match_expr = ast_child(match); ast_t* match_type = ast_type(match_expr); if(is_control_type(match_type) || is_typecheck_error(match_type)) return false; if(!infer_pattern_type(pattern, match_type, opt)) return false; if(!is_valid_pattern(opt, pattern)) return false; ast_t* operand_type = alias(match_type); ast_t* pattern_type = ast_type(pattern); bool ok = true; switch(is_matchtype(operand_type, pattern_type)) { case MATCHTYPE_ACCEPT: break; case MATCHTYPE_REJECT: ast_error(pattern, "this pattern can never match"); ast_error(match_type, "match type: %s", ast_print_type(operand_type)); ast_error(pattern, "pattern type: %s", ast_print_type(pattern_type)); ok = false; break; case MATCHTYPE_DENY: ast_error(pattern, "this capture violates capabilities"); ast_error(match_type, "match type: %s", ast_print_type(operand_type)); ast_error(pattern, "pattern type: %s", ast_print_type(pattern_type)); ok = false; break; } if(ast_id(guard) != TK_NONE) { ast_t* guard_type = ast_type(guard); if(is_typecheck_error(guard_type)) { ok = false; } else if(!is_bool(guard_type)) { ast_error(guard, "guard must be a boolean expression"); ok = false; } } ast_free_unattached(operand_type); ast_inheritflags(ast); return ok; }