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-stmt.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-stmt.cpp')
| -rw-r--r-- | source/slang/slang-check-stmt.cpp | 76 |
1 files changed, 62 insertions, 14 deletions
diff --git a/source/slang/slang-check-stmt.cpp b/source/slang/slang-check-stmt.cpp index 2dc8c2685..9d1899462 100644 --- a/source/slang/slang-check-stmt.cpp +++ b/source/slang/slang-check-stmt.cpp @@ -41,6 +41,19 @@ void SemanticsVisitor::checkStmt(Stmt* stmt, SemanticsContext const& context) checkModifiers(stmt); } +CatchStmt* SemanticsVisitor::findMatchingCatchStmt(Type* errorType) +{ + for (auto outerStmtInfo = m_outerStmts; outerStmtInfo; outerStmtInfo = outerStmtInfo->next) + { + if (auto catchStmt = as<CatchStmt>(outerStmtInfo->stmt)) + { + if (!catchStmt->errorVar || catchStmt->errorVar->getType()->equals(errorType)) + return catchStmt; + } + } + return nullptr; +} + void SemanticsStmtVisitor::visitDeclStmt(DeclStmt* stmt) { // When we encounter a declaration during statement checking, @@ -118,20 +131,6 @@ void SemanticsStmtVisitor::checkStmt(Stmt* stmt) SemanticsVisitor::checkStmt(stmt, *this); } -template<typename T> -T* SemanticsStmtVisitor::FindOuterStmt(Stmt* searchUntil) -{ - for (auto outerStmtInfo = m_outerStmts; outerStmtInfo && outerStmtInfo->stmt != searchUntil; - outerStmtInfo = outerStmtInfo->next) - { - auto outerStmt = outerStmtInfo->stmt; - auto found = as<T>(outerStmt); - if (found) - return found; - } - return nullptr; -} - Stmt* SemanticsStmtVisitor::findOuterStmtWithLabel(Name* label) { for (auto outerStmtInfo = m_outerStmts; outerStmtInfo; outerStmtInfo = outerStmtInfo->next) @@ -616,6 +615,55 @@ void SemanticsStmtVisitor::visitDeferStmt(DeferStmt* stmt) subContext.checkStmt(stmt->statement); } +void SemanticsStmtVisitor::visitThrowStmt(ThrowStmt* stmt) +{ + stmt->expression = CheckTerm(stmt->expression); + Stmt* catchStmt = findMatchingCatchStmt(stmt->expression->type); + + auto parentFunc = getParentFunc(); + if (!catchStmt && (!parentFunc || parentFunc->errorType->equals(m_astBuilder->getBottomType()))) + { + getSink()->diagnose(stmt, Diagnostics::uncaughtThrowInNonThrowFunc); + return; + } + + if (!catchStmt && !stmt->expression->type->equals(m_astBuilder->getErrorType())) + { + if (!parentFunc->errorType->equals(stmt->expression->type)) + { + getSink()->diagnose( + stmt->expression, + Diagnostics::throwTypeIncompatibleWithErrorType, + stmt->expression->type, + parentFunc->errorType); + } + } + + if (FindOuterStmt<DeferStmt>(catchStmt)) + { + // Allowing 'throw' to escape a defer statement gets quite complex, for + // similar reasons as 'return' - if you have two (or more) defers, + // both of which exit the outer scope, it's unclear which one gets + // called and when. Both can't fully run. That kind of goes against the + // point of 'defer', which is to _always_ run some code when exiting + // scopes. + getSink()->diagnose(stmt, Diagnostics::uncaughtThrowInsideDefer); + } +} + +void SemanticsStmtVisitor::visitCatchStmt(CatchStmt* stmt) +{ + if (stmt->errorVar) + { + ensureDeclBase(stmt->errorVar, DeclCheckState::DefinitionChecked, this); + stmt->errorVar->hiddenFromLookup = false; + } + + WithOuterStmt subContext(this, stmt); + subContext.checkStmt(stmt->tryBody); + subContext.checkStmt(stmt->handleBody); +} + void SemanticsStmtVisitor::visitExpressionStmt(ExpressionStmt* stmt) { stmt->expression = CheckExpr(stmt->expression); |
