} END_TEST START_TEST (test_type_comparison_for_sum_fncall) { // test for a bug concerning subtypes of sum types // make type a:i64 | b:u64 | c:f64 | d:string // just like the typechecking for function calls // check that comparing a subtype gives the correct matched type struct type *t_i64 = testsupport_analyzer_type_create_simple_elementary(ELEMENTARY_TYPE_INT_64); struct type *t_u64 = testsupport_analyzer_type_create_simple_elementary(ELEMENTARY_TYPE_UINT_64); struct type *t_f64 = testsupport_analyzer_type_create_simple_elementary(ELEMENTARY_TYPE_FLOAT_64); struct type *t_string = testsupport_analyzer_type_create_simple_elementary(ELEMENTARY_TYPE_STRING); struct type *t_sum = testsupport_analyzer_type_create_operator(TYPEOP_SUM, t_i64, t_u64, t_f64, t_string); typecmp_ctx_set_flags(TYPECMP_FLAG_FUNCTION_CALL); ck_assert(type_compare(t_u64, t_sum, TYPECMP_PATTERN_MATCHING)); const struct type *matched_type = typemp_ctx_get_matched_type(); ck_assert_msg( matched_type == t_u64, "Unexpected match type "RFS_PF" found", RFS_PA(type_str_or_die(matched_type, TSTR_DEFAULT)) ); typecmp_ctx_set_flags(TYPECMP_FLAG_FUNCTION_CALL); ck_assert(type_compare(t_i64, t_sum, TYPECMP_PATTERN_MATCHING)); matched_type = typemp_ctx_get_matched_type(); ck_assert_msg( matched_type == t_i64, "Unexpected match type "RFS_PF" found", RFS_PA(type_str_or_die(matched_type, TSTR_DEFAULT)) ); typecmp_ctx_set_flags(TYPECMP_FLAG_FUNCTION_CALL); ck_assert(type_compare(t_f64, t_sum, TYPECMP_PATTERN_MATCHING)); matched_type = typemp_ctx_get_matched_type(); ck_assert_msg( matched_type == t_f64, "Unexpected match type "RFS_PF" found", RFS_PA(type_str_or_die(matched_type, TSTR_DEFAULT)) ); typecmp_ctx_set_flags(TYPECMP_FLAG_FUNCTION_CALL); ck_assert(type_compare(t_string, t_sum, TYPECMP_PATTERN_MATCHING)); matched_type = typemp_ctx_get_matched_type(); ck_assert_msg( matched_type == t_string, "Unexpected match type "RFS_PF" found", RFS_PA(type_str_or_die(matched_type, TSTR_DEFAULT)) ); } END_TEST
} END_TEST START_TEST (test_type_comparison_for_sum_fncall_with_conversion) { struct type *t_i8 = testsupport_analyzer_type_create_elementary(ELEMENTARY_TYPE_INT_8, false); struct type *t_i64 = testsupport_analyzer_type_create_elementary(ELEMENTARY_TYPE_INT_64, false); struct type *t_f64 = testsupport_analyzer_type_create_elementary(ELEMENTARY_TYPE_FLOAT_64, false); struct type *t_string = testsupport_analyzer_type_create_elementary(ELEMENTARY_TYPE_STRING, false); struct type *t_sum = testsupport_analyzer_type_create_operator(TYPEOP_SUM, t_i64, t_f64, t_string); typecmp_ctx_set_flags(TYPECMP_FLAG_FUNCTION_CALL); ck_assert(type_compare(t_i8, t_sum, TYPECMP_PATTERN_MATCHING)); const struct type *matched_type = typemp_ctx_get_matched_type(); ck_assert_msg(matched_type == t_i64, "Unexpected match type "RF_STR_PF_FMT" found", RF_STR_PF_ARG(type_str_or_die(matched_type, TSTR_DEFAULT))); } END_TEST
enum traversal_cb_res typecheck_function_call( struct ast_node *n, struct analyzer_traversal_ctx *ctx) { const struct RFstring *fn_name; const struct type *fn_type; const struct type *fn_declared_args_type; const struct type *fn_found_args_type; struct ast_node *fn_call_args = ast_fncall_args(n); // check for existence of function fn_name = ast_fncall_name(n); fn_type = type_lookup_identifier_string(fn_name, ctx->current_st); if (!fn_type || !type_is_callable(fn_type)) { analyzer_err(ctx->m, ast_node_startmark(n), ast_node_endmark(n), "Undefined function call \""RFS_PF"\" detected", RFS_PA(fn_name)); goto fail; } // create the arguments ast node array ast_fncall_arguments(n); // also check the ast node of the function declaration to get more // information if it's not a conversion if (!type_is_explicitly_convertable_elementary(fn_type)) { const struct ast_node *fndecl = symbol_table_lookup_node( ctx->current_st, fn_name, NULL ); RF_ASSERT(fndecl, "Since fn_type was found so should fndecl be found here"); if (fndecl->fndecl.position == FNDECL_PARTOF_FOREIGN_IMPORT) { n->fncall.type = AST_FNCALL_FOREIGN; } } fn_found_args_type = (fn_call_args) ? ast_node_get_type(fn_call_args) : type_elementary_get_type(ELEMENTARY_TYPE_NIL); if (!fn_found_args_type) { // argument typechecking failed goto fail; } if (type_is_explicitly_convertable_elementary(fn_type)) { // silly way to check if it's only 1 argument. Maybe figure out safer way? if (!fn_call_args || fn_found_args_type->category == TYPE_CATEGORY_OPERATOR) { analyzer_err( ctx->m, ast_node_startmark(n), ast_node_endmark(n), "Invalid arguments for explicit conversion to \"" RFS_PF"\".", RFS_PA(fn_name) ); goto fail; } // check if the explicit conversion is valid if (!type_compare(fn_found_args_type, fn_type, TYPECMP_EXPLICIT_CONVERSION)) { analyzer_err(ctx->m, ast_node_startmark(n), ast_node_endmark(n), "Invalid explicit conversion. "RFS_PF".", RFS_PA(typecmp_ctx_get_error())); goto fail; } n->fncall.type = AST_FNCALL_EXPLICIT_CONVERSION; } else { //check that the types of its arguments do indeed match fn_declared_args_type = type_callable_get_argtype(fn_type); n->fncall.declared_type = fn_declared_args_type; if (type_is_sumop(fn_declared_args_type)) { n->fncall.type = AST_FNCALL_SUM; } typecmp_ctx_set_flags(TYPECMP_FLAG_FUNCTION_CALL); if (!type_compare( fn_found_args_type, fn_declared_args_type, n->fncall.type == AST_FNCALL_SUM ? TYPECMP_PATTERN_MATCHING : TYPECMP_IMPLICIT_CONVERSION)) { RFS_PUSH(); analyzer_err( ctx->m, ast_node_startmark(n), ast_node_endmark(n), RFS_PF" "RFS_PF"() is called with argument type of \""RFS_PF "\" which does not match the expected " "type of \""RFS_PF"\"%s"RFS_PF".", RFS_PA(type_callable_category_str(fn_type)), RFS_PA(fn_name), RFS_PA(type_str_or_die(fn_found_args_type, TSTR_DEFAULT)), RFS_PA(type_str_or_die(fn_declared_args_type, TSTR_DEFINED_ONLY_CONTENTS)), typecmp_ctx_have_error() ? ". " : "", RFS_PA(typecmp_ctx_get_error()) ); RFS_POP(); goto fail; } // the function call's matched type should be either a specific sum type // that may have matched or the entirety of the called arguments if (!(n->fncall.params_type = typemp_ctx_get_matched_type())) { n->fncall.params_type = fn_found_args_type; } } traversal_node_set_type(n, type_callable_get_rettype(fn_type), ctx); return TRAVERSAL_CB_OK; fail: traversal_node_set_type(n, NULL, ctx); return TRAVERSAL_CB_ERROR; }