static bool check_partial_ffi_call(pass_opt_t* opt, ast_t* ast) { pony_assert(ast_id(ast) == TK_FFICALL); AST_GET_CHILDREN(ast, call_name, call_ret_typeargs, args, named_args, call_error); // The expr pass (expr_ffi) should have stored the declaration here, if found. ast_t* decl = (ast_t*)ast_data(ast); if(decl == NULL) { if(ast_id(call_error) == TK_QUESTION) ast_seterror(ast); } else { pony_assert(ast_id(decl) == TK_FFIDECL); AST_GET_CHILDREN(decl, decl_name, decl_ret_typeargs, params, named_params, decl_error); if((ast_id(decl_error) == TK_QUESTION) || (ast_id(call_error) == TK_QUESTION)) ast_seterror(ast); if((ast_id(decl_error) == TK_NONE) && (ast_id(call_error) != TK_NONE)) { ast_error(opt->check.errors, call_error, "call is partial but the declaration is not"); ast_error_continue(opt->check.errors, decl_error, "declaration is here"); return false; } } return true; }
static bool check_partial_function_call(pass_opt_t* opt, ast_t* ast) { pony_assert((ast_id(ast) == TK_FUNREF) || (ast_id(ast) == TK_FUNCHAIN) || (ast_id(ast) == TK_NEWREF)); AST_GET_CHILDREN(ast, receiver, method); // Receiver might be wrapped in another funref/newref // if the method had type parameters for qualification. if(ast_id(receiver) == ast_id(ast)) AST_GET_CHILDREN_NO_DECL(receiver, receiver, method); // Look up the original method definition for this method call. ast_t* method_def = lookup(opt, receiver, ast_type(receiver), ast_name(method)); pony_assert(ast_id(method_def) == TK_FUN || ast_id(method_def) == TK_BE || ast_id(method_def) == TK_NEW); token_id can_error = ast_id(ast_childidx(method_def, 5)); if(can_error == TK_QUESTION) ast_seterror(ast); ast_free_unattached(method_def); return true; }
ast_result_t pass_verify(ast_t** astp, pass_opt_t* options) { ast_t* ast = *astp; bool r = true; switch(ast_id(ast)) { case TK_FUN: case TK_NEW: case TK_BE: r = verify_fun(options, ast); break; case TK_FUNREF: case TK_FUNCHAIN: case TK_NEWREF: r = verify_function_call(options, ast); break; case TK_BEREF: case TK_BECHAIN: case TK_NEWBEREF: r = verify_behaviour_call(options, ast); break; case TK_FFICALL: r = verify_ffi_call(options, ast); break; case TK_TRY: case TK_TRY_NO_CHECK: r = verify_try(options, ast); break; case TK_LETREF: case TK_VARREF: case TK_FLETREF: case TK_FVARREF: case TK_EMBEDREF: case TK_CALL: case TK_QUALIFY: case TK_TUPLE: case TK_ASSIGN: case TK_MATCH: case TK_CASES: case TK_CASE: case TK_IS: case TK_ISNT: case TK_SEQ: case TK_BREAK: case TK_RETURN: case TK_IF: case TK_IFDEF: case TK_WHILE: case TK_REPEAT: case TK_RECOVER: case TK_POSITIONALARGS: case TK_NAMEDARGS: case TK_NAMEDARG: case TK_UPDATEARG: ast_inheritflags(ast); break; case TK_ERROR: ast_seterror(ast); break; default: {} } if(!r) { assert(errors_get_count(options->check.errors) > 0); return AST_ERROR; } return AST_OK; }
bool expr_error(ast_t* ast) { // error is always the last expression in a sequence assert(ast_sibling(ast) == NULL); ast_settype(ast, ast_from(ast, TK_ERROR)); ast_seterror(ast); return true; }
ast_result_t expr_ffi(pass_opt_t* opt, ast_t* ast) { if(!package_allow_ffi(&opt->check)) { ast_error(opt->check.errors, ast, "This package isn't allowed to do C FFI"); return AST_FATAL; } AST_GET_CHILDREN(ast, name, return_typeargs, args, namedargs, question); assert(name != NULL); ast_t* decl; if(!ffi_get_decl(&opt->check, ast, &decl, opt)) return AST_ERROR; if(decl != NULL) // We have a declaration return declared_ffi(opt, ast, decl); // We do not have a declaration for(ast_t* arg = ast_child(args); arg != NULL; arg = ast_sibling(arg)) { ast_t* a_type = ast_type(arg); if((a_type != NULL) && is_type_literal(a_type)) { ast_error(opt->check.errors, arg, "Cannot pass number literals as unchecked FFI arguments"); return AST_ERROR; } } ast_t* return_type = ast_child(return_typeargs); if(return_type == NULL) { ast_error(opt->check.errors, name, "FFIs without declarations must specify return type"); return AST_ERROR; } ast_settype(ast, return_type); ast_inheritflags(ast); if(ast_id(question) == TK_QUESTION) ast_seterror(ast); return AST_OK; }
static bool method_access(pass_opt_t* opt, ast_t* ast, ast_t* method) { AST_GET_CHILDREN(method, cap, id, typeparams, params, result, can_error, body); switch(ast_id(method)) { case TK_NEW: { AST_GET_CHILDREN(ast, left, right); ast_t* type = ast_type(left); if(is_typecheck_error(type)) return false; if(!constructor_type(opt, ast, ast_id(cap), type, &result)) return false; break; } case TK_BE: ast_setid(ast, TK_BEREF); break; case TK_FUN: ast_setid(ast, TK_FUNREF); break; default: assert(0); return false; } ast_settype(ast, type_for_fun(method)); if(ast_id(can_error) == TK_QUESTION) ast_seterror(ast); return is_method_called(opt, ast); }
bool expr_try(pass_opt_t* opt, ast_t* ast) { AST_GET_CHILDREN(ast, body, else_clause, then_clause); // It has to be possible for the left side to result in an error. if((ast_id(ast) == TK_TRY) && !ast_canerror(body)) { ast_error(body, "try expression never results in an error"); return false; } ast_t* body_type = ast_type(body); ast_t* else_type = ast_type(else_clause); ast_t* then_type = ast_type(then_clause); if(is_typecheck_error(body_type) || is_typecheck_error(else_type) || is_typecheck_error(then_type)) return false; ast_t* type = NULL; if(!is_control_type(body_type)) type = control_type_add_branch(type, body); if(!is_control_type(else_type)) type = control_type_add_branch(type, else_clause); if(type == NULL) { if(ast_sibling(ast) != NULL) { ast_error(ast_sibling(ast), "unreachable code"); return false; } type = ast_from(ast, TK_TRY); } // The then clause does not affect the type of the expression. if(is_control_type(then_type)) { ast_error(then_clause, "then clause always terminates the function"); return false; } if(is_type_literal(then_type)) { ast_error(then_clause, "Cannot infer type of unused literal"); return false; } ast_settype(ast, type); // Doesn't inherit error from the body. if(ast_canerror(else_clause) || ast_canerror(then_clause)) ast_seterror(ast); if(ast_cansend(body) || ast_cansend(else_clause) || ast_cansend(then_clause)) ast_setsend(ast); if(ast_mightsend(body) || ast_mightsend(else_clause) || ast_mightsend(then_clause)) ast_setmightsend(ast); literal_unify_control(ast, opt); // Push the symbol status from the then clause to our parent scope. ast_inheritstatus(ast_parent(ast), then_clause); return true; }
static ast_result_t declared_ffi(pass_opt_t* opt, ast_t* call, ast_t* decl) { assert(call != NULL); assert(decl != NULL); assert(ast_id(decl) == TK_FFIDECL); AST_GET_CHILDREN(call, call_name, call_ret_typeargs, args, named_args, call_error); AST_GET_CHILDREN(decl, decl_name, decl_ret_typeargs, params, named_params, decl_error); // Check args vs params ast_t* param = ast_child(params); ast_t* arg = ast_child(args); while((arg != NULL) && (param != NULL) && ast_id(param) != TK_ELLIPSIS) { ast_t* p_type = ast_childidx(param, 1); if(!coerce_literals(&arg, p_type, opt)) return AST_ERROR; ast_t* a_type = ast_type(arg); errorframe_t info = NULL; if((a_type != NULL) && !void_star_param(p_type, a_type) && !is_subtype(a_type, p_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, arg, "argument not a subtype of parameter"); ast_error_frame(&frame, param, "parameter type: %s", ast_print_type(p_type)); ast_error_frame(&frame, arg, "argument type: %s", ast_print_type(a_type)); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); return AST_ERROR; } arg = ast_sibling(arg); param = ast_sibling(param); } if(arg != NULL && param == NULL) { ast_error(opt->check.errors, arg, "too many arguments"); return AST_ERROR; } if(param != NULL && ast_id(param) != TK_ELLIPSIS) { ast_error(opt->check.errors, named_args, "too few arguments"); return AST_ERROR; } for(; arg != NULL; arg = ast_sibling(arg)) { ast_t* a_type = ast_type(arg); if((a_type != NULL) && is_type_literal(a_type)) { ast_error(opt->check.errors, arg, "Cannot pass number literals as unchecked FFI arguments"); return AST_ERROR; } } // Check return types ast_t* call_ret_type = ast_child(call_ret_typeargs); ast_t* decl_ret_type = ast_child(decl_ret_typeargs); errorframe_t info = NULL; if((call_ret_type != NULL) && !is_eqtype(call_ret_type, decl_ret_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, call_ret_type, "call return type does not match declaration"); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); return AST_ERROR; } // Check partiality if((ast_id(decl_error) == TK_NONE) && (ast_id(call_error) != TK_NONE)) { ast_error(opt->check.errors, call_error, "call is partial but the declaration is not"); return AST_ERROR; } if((ast_id(decl_error) == TK_QUESTION) || (ast_id(call_error) == TK_QUESTION)) { ast_seterror(call); } // Store the declaration so that codegen can generate a non-variadic // signature for the FFI call. ast_setdata(call, decl); ast_settype(call, decl_ret_type); ast_inheritflags(call); return AST_OK; }