diff options
| author | Julius Ikkala <julius.ikkala@gmail.com> | 2025-05-23 22:27:37 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-05-23 12:27:37 -0700 |
| commit | 57c3f938221c427b78da7087f8a832ba4a271a7c (patch) | |
| tree | e9a6d26278dc1ad75b222ac4fc9b7a1d8449e576 /source/slang/slang-check-expr.cpp | |
| parent | d108bfa677c70808b32bd77e93637ed34c19c75d (diff) | |
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<T, E>
* Use anyValue storage for Result<T,E>
* 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 <yonghe@outlook.com>
Diffstat (limited to 'source/slang/slang-check-expr.cpp')
| -rw-r--r-- | source/slang/slang-check-expr.cpp | 69 |
1 files changed, 44 insertions, 25 deletions
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<InvokeExpr>(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<DeclRefExpr>(base->functionExpr); + if (!callee) { - getSink()->diagnose(expr, Diagnostics::uncaughtTryCallInNonThrowFunc); + getSink()->diagnose(expr, Diagnostics::calleeOfTryCallMustBeFunc); return expr; } - if (!as<InvokeExpr>(expr->base)) + + auto funcCallee = as<FuncDecl>(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<DeferStmt>(catchStmt)) + { + // 'try' may jump outside a defer statement, which isn't allowed for + // now. + getSink()->diagnose(expr, Diagnostics::uncaughtTryInsideDefer); return expr; } - auto base = as<InvokeExpr>(expr->base); - if (auto callee = as<DeclRefExpr>(base->functionExpr)) + + if (!catchStmt) { - if (auto funcCallee = as<FuncDecl>(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; } |
