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-ir-lower-error-handling.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-ir-lower-error-handling.cpp')
| -rw-r--r-- | source/slang/slang-ir-lower-error-handling.cpp | 48 |
1 files changed, 28 insertions, 20 deletions
diff --git a/source/slang/slang-ir-lower-error-handling.cpp b/source/slang/slang-ir-lower-error-handling.cpp index 917658ac4..a885fc2d2 100644 --- a/source/slang/slang-ir-lower-error-handling.cpp +++ b/source/slang/slang-ir-lower-error-handling.cpp @@ -15,6 +15,7 @@ struct ErrorHandlingLoweringContext InstWorkList workList; InstHashSet workListSet; + List<IRFuncType*> oldFuncTypes; ErrorHandlingLoweringContext(IRModule* inModule) : module(inModule), workList(inModule), workListSet(inModule) @@ -37,8 +38,10 @@ struct ErrorHandlingLoweringContext return; IRBuilder builder(module); builder.setInsertBefore(funcType); + auto resultType = builder.getResultType(funcType->getResultType(), throwAttr->getErrorType()); + List<IRType*> paramTypes; for (UInt i = 0; i < funcType->getParamCount(); i++) { @@ -48,6 +51,7 @@ struct ErrorHandlingLoweringContext } auto newFuncType = builder.getFuncType(paramTypes, resultType); funcType->replaceUsesWith(newFuncType); + funcType->removeAndDeallocate(); } void processTryCall(IRTryCall* tryCall) @@ -99,14 +103,27 @@ struct ErrorHandlingLoweringContext auto failBlock = tryCall->getFailureBlock(); auto successBlock = tryCall->getSuccessBlock(); - builder.emitIf(isFail, failBlock, successBlock); - - // Replace the params in failBlock to `getResultError(call)`. - builder.setInsertBefore(failBlock->getFirstOrdinaryInst()); - auto errorParam = failBlock->getFirstParam(); - auto errVal = builder.emitGetResultError(call); - errorParam->replaceUsesWith(errVal); - errorParam->removeAndDeallocate(); + if (failBlock->getFirstParam()) + { + // The isFail branch could otherwise just jump to the handler, but + // there's unfortunately the error parameter that needs to be passed as + // well, and it can't be done in IfElse. So there's an extra block in + // between to do that. + auto handlerJumpBlock = builder.createBlock(); + auto branch = builder.emitIf(isFail, handlerJumpBlock, successBlock); + + builder.setInsertAfter(branch->getParent()); + builder.addInst(handlerJumpBlock); + builder.setInsertInto(handlerJumpBlock); + + auto errVal = builder.emitGetResultError(call); + builder.emitBranch(failBlock, 1, &errVal); + } + else + { + // Catch-all with no parameter, so we can just jump to it directly. + builder.emitIf(isFail, failBlock, successBlock); + } // Replace the params in successBlock to `getResultValue(call)`. builder.setInsertBefore(successBlock->getFirstOrdinaryInst()); @@ -173,6 +190,9 @@ struct ErrorHandlingLoweringContext case kIROp_Throw: processThrow(cast<IRThrow>(inst)); break; + case kIROp_FuncType: + oldFuncTypes.add(cast<IRFuncType>(inst)); + break; default: break; } @@ -206,18 +226,6 @@ struct ErrorHandlingLoweringContext // Lower all functypes. // Function types with an IRThrowTypeAttribute will be translated into a normal function // type that returns `Result<T,E>`. - List<IRFuncType*> oldFuncTypes; - for (auto child : module->getGlobalInsts()) - { - switch (child->getOp()) - { - case kIROp_FuncType: - oldFuncTypes.add(cast<IRFuncType>(child)); - break; - default: - break; - } - } for (auto funcType : oldFuncTypes) { processFuncType(funcType); |
