From 57c3f938221c427b78da7087f8a832ba4a271a7c Mon Sep 17 00:00:00 2001 From: Julius Ikkala Date: Fri, 23 May 2025 22:27:37 +0300 Subject: Implement throw & catch statements (#6916) * Implement throw statement It already existed in the IR, so only parsing, checking and lowering was missing. * Initial catch implementation Likely very broken. * Error out when catch() isn't last in scope * Prevent accessing variables from scope preceding catch As those may actually not be available at that point. * Add IError and use it in Result type lowering * Add diagnostic tests * Allow caught throws in non-throw functions * Fix catch propagating between functions & SPIR-V merge issue * Add test for non-trivial error types * Fix MSVC build * Fix invalid value type from Result lowering * Also lower error handling in templates * Lower result types only after specialization * Attempt to disambiguate error enums by witness table * Revert matching by witness, types should be distinct too * Don't assert valueField when getting Result's error value It may not exist if the function returns void, but getting the error value is still legitimate. * Update tests for new error numbers & get rid of expected.txt * Change catch lowering to resemble breaking a loop ... To make SPIR-V happy. * Fix dead catch blocks and invalid cached dominator tree * More SPIR-V adjustment * Lower catch as two nested loops * Add defer interaction test and revert broken defer changes * Fix enum type when throwing literals * Cleanup and bikeshedding * Document error handling mechanism * Fix table of contents * Use boolean tag in Result * Use anyValue storage for Result * Remove IError * Fix formatting * Eradicate success values from docs and tests * Use parseModernParamDecl for catch parameter * Implement do-catch syntax * Implement catch-all * Fix formatting * Fix marshalling native calls that throw --------- Co-authored-by: Yong He --- source/slang/slang-check-expr.cpp | 69 +++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 25 deletions(-) (limited to 'source/slang/slang-check-expr.cpp') diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp index db507c060..75b1b7024 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -3951,45 +3951,64 @@ Expr* SemanticsExprVisitor::visitTryExpr(TryExpr* expr) return expr; auto parentFunc = this->m_parentFunc; - // TODO: check if the try clause is caught. - // For now we assume all `try`s are not caught (because we don't have catch yet). - if (!parentFunc) + auto base = as(expr->base); + if (!base) { - getSink()->diagnose(expr, Diagnostics::uncaughtTryCallInNonThrowFunc); + getSink()->diagnose(expr, Diagnostics::tryClauseMustApplyToInvokeExpr); return expr; } - if (parentFunc->errorType->equals(m_astBuilder->getBottomType())) + + auto callee = as(base->functionExpr); + if (!callee) { - getSink()->diagnose(expr, Diagnostics::uncaughtTryCallInNonThrowFunc); + getSink()->diagnose(expr, Diagnostics::calleeOfTryCallMustBeFunc); return expr; } - if (!as(expr->base)) + + auto funcCallee = as(callee->declRef.getDecl()); + Stmt* catchStmt = nullptr; + if (funcCallee) { - getSink()->diagnose(expr, Diagnostics::tryClauseMustApplyToInvokeExpr); + if (funcCallee->errorType->equals(m_astBuilder->getBottomType())) + { + getSink()->diagnose(expr, Diagnostics::tryInvokeCalleeShouldThrow, callee->declRef); + return expr; + } + catchStmt = findMatchingCatchStmt(funcCallee->errorType); + } + + if (FindOuterStmt(catchStmt)) + { + // 'try' may jump outside a defer statement, which isn't allowed for + // now. + getSink()->diagnose(expr, Diagnostics::uncaughtTryInsideDefer); return expr; } - auto base = as(expr->base); - if (auto callee = as(base->functionExpr)) + + if (!catchStmt) { - if (auto funcCallee = as(callee->declRef.getDecl())) + // Uncaught try. + if (!parentFunc) { - if (funcCallee->errorType->equals(m_astBuilder->getBottomType())) - { - getSink()->diagnose(expr, Diagnostics::tryInvokeCalleeShouldThrow, callee->declRef); - } - if (!parentFunc->errorType->equals(funcCallee->errorType)) - { - getSink()->diagnose( - expr, - Diagnostics::errorTypeOfCalleeIncompatibleWithCaller, - callee->declRef, - funcCallee->errorType, - parentFunc->errorType); - } + getSink()->diagnose(expr, Diagnostics::uncaughtTryCallInNonThrowFunc); + return expr; + } + if (parentFunc->errorType->equals(m_astBuilder->getBottomType())) + { + getSink()->diagnose(expr, Diagnostics::uncaughtTryCallInNonThrowFunc); + return expr; + } + if (funcCallee && !parentFunc->errorType->equals(funcCallee->errorType)) + { + getSink()->diagnose( + expr, + Diagnostics::errorTypeOfCalleeIncompatibleWithCaller, + callee->declRef, + funcCallee->errorType, + parentFunc->errorType); return expr; } } - getSink()->diagnose(expr, Diagnostics::calleeOfTryCallMustBeFunc); return expr; } -- cgit v1.2.3