diff --git a/include/daScript/ast/ast_infer_type.h b/include/daScript/ast/ast_infer_type.h index 0b8f4f656c..be795a8da8 100644 --- a/include/daScript/ast/ast_infer_type.h +++ b/include/daScript/ast/ast_infer_type.h @@ -534,6 +534,7 @@ namespace das { ExpressionPtr visit(ExprTryCatch *expr) override; // ExprReturn bool inferReturnType(TypeDeclPtr &resType, ExprReturn *expr); + bool isVoidReturnValueSettled(Expression *subexpr) const; void reportUnresolvedReturnValue(ExprReturn *expr); virtual void preVisit(ExprReturn *expr) override; void getDetailsAndSuggests(ExprReturn *expr, string &details, string &suggestions) const; diff --git a/src/ast/ast_infer_type.cpp b/src/ast/ast_infer_type.cpp index b66a602d75..e9eaefbd59 100644 --- a/src/ast/ast_infer_type.cpp +++ b/src/ast/ast_infer_type.cpp @@ -4717,7 +4717,8 @@ namespace das { TypeDecl::clone(expr->returnType, func->result); } } - if (expr->subexpr && expr->subexpr->type && expr->subexpr->type->isVoid() && !expr->moveSemantics) { + if (expr->subexpr && expr->subexpr->type && expr->subexpr->type->isVoid() && !expr->moveSemantics + && isVoidReturnValueSettled(expr->subexpr)) { const auto & resT = blocks.size() ? blocks.back()->type : func->result; if (resT && resT->isVoid()) { // lower 'return void_expr' to { void_expr; return; } so the backends diff --git a/src/ast/ast_infer_type_helper.cpp b/src/ast/ast_infer_type_helper.cpp index 09ec3ce206..a3a00df3c7 100644 --- a/src/ast/ast_infer_type_helper.cpp +++ b/src/ast/ast_infer_type_helper.cpp @@ -1050,6 +1050,22 @@ namespace das { expr->at, CompilationError::not_resolved_yet_expression_type); } } + bool InferTypes::isVoidReturnValueSettled(Expression *subexpr) const { + if (!subexpr->rtti_isCallFunc()) { + return true; + } + auto callee = ((ExprCallFunc *)subexpr)->func; + if (!callee) { + return false; + } + if (callee->builtIn || callee->isFullyInferred) { + return true; + } + if (callee->module && callee->module != program->thisModule.get()) { + return true; + } + return callee->hasReturn; + } bool InferTypes::inferReturnType(TypeDeclPtr &resType, ExprReturn *expr) { if (expr->subexpr && expr->subexpr->type && expr->subexpr->type->isVoid()) { // 'return void_expr' is legal when the result is void, or a bare auto which the @@ -1061,6 +1077,10 @@ namespace das { expr->at, CompilationError::invalid_result); return false; } + if (!isVoidReturnValueSettled(expr->subexpr)) { + error("subexpression type is not fully resolved yet", "", "", expr->at, CompilationError::not_resolved_yet_expression_type); + return false; + } if (resType->isVoid()) { return false; } diff --git a/tests/language/return_void_expression.das b/tests/language/return_void_expression.das index c0d078c455..02a013254c 100644 --- a/tests/language/return_void_expression.das +++ b/tests/language/return_void_expression.das @@ -1,5 +1,6 @@ options gen2 require dastest/testing_boost public +require daslib/constant_expression var g_hits = 0 @@ -49,6 +50,41 @@ def mixed_result(n : int) : int { return g_hits + n } +// until [constant_expression] folds the argument in, the raw instance is an empty void function +[generic, constant_expression(name), unused_argument(name)] +def get_by_name(name : string) { + static_if (!typeinfo is_argument(name)) { + static_if (name == "one") { + return 1 + } static_elif (name == "two") { + return 2 + } + } +} + +def pick_by_name(sel : int) { + if (sel == 1) { + return get_by_name("one") + } + return get_by_name("two") +} + +def ping(n : int) : void { + if (n <= 0) { + return + } + g_hits++ + return pong(n - 1) +} + +def pong(n : int) : void { + if (n <= 0) { + return + } + g_hits++ + return ping(n - 1) +} + [test] def test_return_void_expression(t : T?) { t |> run("block arrow with void body") @(t : T?) { @@ -133,4 +169,15 @@ def test_return_void_forwarding(t : T?) { g_hits = 0 t |> equal(mixed_result(10), 13) } + + t |> run("transiently-void generic call does not poison a bare-auto result") @(t : T?) { + t |> equal(pick_by_name(1), 1) + t |> equal(pick_by_name(2), 2) + } + + t |> run("mutually recursive void functions forward through return") @(t : T?) { + g_hits = 0 + ping(5) + t |> equal(g_hits, 5) + } }