} END_TEST START_TEST(test_array_type4) { static const struct RFstring s = RF_STRING_STATIC_INIT( "{\n" "a:u64[] = [1, 2, 3]\n" "}" ); front_testdriver_new_ast_main_source(&s); ck_assert_typecheck_ok(); int64_t dims[] = {3}; struct type *t_u64 = testsupport_analyzer_type_create_elemarray( ELEMENTARY_TYPE_UINT_64, dims ); struct ast_node *block = ast_node_get_child(front_testdriver_module()->node, 0); struct ast_node *blist = ast_binaryop_right(ast_node_get_child(block, 0)); const struct type *blist_type = ast_node_get_type(blist); ck_assert_msg(blist_type, "bracket type should not be NULL"); ck_assert_msg( type_compare(blist_type, t_u64, TYPECMP_GENERIC), "Bracket type comparison failure" ); struct ast_node *vardecl = ast_binaryop_left(ast_node_get_child(block, 0)); const struct type *vardecl_type = ast_node_get_type(vardecl); ck_assert_msg(vardecl_type, "bracket type should not be NULL"); ck_assert_msg( type_compare(vardecl_type, t_u64, TYPECMP_IDENTICAL), "Bracket type comparison failure" ); } END_TEST
} END_TEST START_TEST(test_determine_block_type2) { struct ast_node *block; static const struct RFstring s = RF_STRING_STATIC_INIT( "type foo {a:i8, b:string}\n" "{\n" "a:foo\n" "}" ); front_testdriver_new_ast_main_source(&s); ck_assert_typecheck_ok(); struct type *t_i8 = testsupport_analyzer_type_create_simple_elementary(ELEMENTARY_TYPE_INT_8); struct type *t_string = testsupport_analyzer_type_create_simple_elementary(ELEMENTARY_TYPE_STRING); struct type *t_prod_1 = testsupport_analyzer_type_create_operator(TYPEOP_PRODUCT, t_i8, t_string); static const struct RFstring id_foo = RF_STRING_STATIC_INIT("foo"); struct type *t_foo = testsupport_analyzer_type_create_defined(&id_foo, t_prod_1); block = ast_node_get_child(front_testdriver_module()->node, 1); ck_assert_msg(block, "Block should be the second child of the root"); const struct type *block_type = ast_node_get_type(block); ck_assert_msg(block_type, "Block should have a type"); ck_assert_msg(type_compare(block_type, t_foo, TYPECMP_IDENTICAL), "Expected the block's type to be an f64"); } END_TEST
bool rir_process_convertcall(const struct ast_node *n, struct rir_ctx *ctx) { struct ast_node *args = ast_fncall_args(n); RF_ASSERT(ast_node_get_type(args)->category != TYPE_CATEGORY_OPERATOR, "A conversion call should only have a single argument"); // process that argument const struct rir_value *argexprval = rir_process_ast_node_getreadval(args, ctx); if (!argexprval) { RF_ERROR("Could not create rir expression from conversion call argument"); RIRCTX_RETURN_EXPR(ctx, false, NULL); } // create the conversion struct rir_object *obj = rir_convert_create_obj_maybeadd( argexprval, rir_type_create_from_type(ast_node_get_type(n), ctx), ctx ); if (!obj) { RIRCTX_RETURN_EXPR(ctx, false, NULL); } RIRCTX_RETURN_EXPR(ctx, true, obj); }
} END_TEST START_TEST(test_determine_block_type1) { struct ast_node *block; static const struct RFstring s = RF_STRING_STATIC_INIT( "{\n" "d:f64 = 3.14 * 0.14\n" "}" ); front_testdriver_new_ast_main_source(&s); ck_assert_typecheck_ok(); struct type *t_f64 = testsupport_analyzer_type_create_simple_elementary(ELEMENTARY_TYPE_FLOAT_64); block = ast_node_get_child(front_testdriver_module()->node, 0); ck_assert_msg(block, "Block should be the first child of the root"); const struct type *block_type = ast_node_get_type(block); ck_assert_msg(block_type, "Block should have a type"); ck_assert_msg(type_compare(block_type, t_f64, TYPECMP_IDENTICAL), "Expected the block's type to be an f64"); } END_TEST
} END_TEST START_TEST(test_array_type3) { static const struct RFstring s = RF_STRING_STATIC_INIT( "a:u64[42][13][24]\n" ); front_testdriver_new_ast_main_source(&s); ck_assert_typecheck_ok(); int64_t dims[] = {42, 13, 24}; struct type *t_u64 = testsupport_analyzer_type_create_elemarray( ELEMENTARY_TYPE_UINT_64, dims ); struct ast_node *typedesc = ast_node_get_child(front_testdriver_module()->node, 0); const struct type *n_type = ast_node_get_type(typedesc); ck_assert_msg(n_type, "Type should not be NULL"); ck_assert_msg( type_compare(n_type, t_u64, TYPECMP_IDENTICAL), "Node type comparison failure" ); } 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; }