fix(PythonToLaurel): Translate raise as exception instead of hole#876
Draft
olivier-aws wants to merge 6 commits intomainfrom
Draft
fix(PythonToLaurel): Translate raise as exception instead of hole#876olivier-aws wants to merge 6 commits intomainfrom
raise as exception instead of hole#876olivier-aws wants to merge 6 commits intomainfrom
Conversation
…f hole The raise statement was translated as a deterministic Hole (<?>), which carries Unknown type. The Laurel-to-Core translator rejects Unknown types with "could not infer type", causing an internal error. Replace the Hole with the correct exception semantics: 1. Havoc maybe_except (non-deterministic Error value) 2. Assume isError(maybe_except) (constrain to actual error) 3. Exit $body (terminate the function) This mirrors how the pipeline models exceptions: maybe_except tracks exception state, and isError checks are inserted after each statement in try blocks to dispatch to exception handlers. Existing tests test_try_except and test_multiple_except improved from inconclusive to passing since raise now properly triggers handlers. Bug: raise statement inside except block translated as hole, causing could not infer type
…de except block Make raise translation context-aware by adding raiseExitLabel to TranslationContext. Inside a try body, raise exits to the catchers label (handlers). Inside a handler, raise uses the enclosing scope exit label, correctly propagating to outer try/except blocks instead of unconditionally exiting the function. Changes: - Add raiseExitLabel field to TranslationContext - Set raiseExitLabel to catchersLabel when entering try body - Restore enclosing raiseExitLabel when entering except handler - Use raiseExitLabel in raise translation (default "$body") - Document intentional omission of exception expression/cause in Raise pattern (abstract error model uses error/no-error only) - Add comment in test_raise_variants.py explaining why always-raising functions produce unknown results (no feasible return path) - Update expected files to reflect correct raise propagation semantics
…ice for sound dispatch Each handler is guarded by isError(maybe_except) AND a non-deterministic boolean (handler_matches_*). This models that we don't know which handler matches the exception type. The handler clears the error after its body and exits the try block, so subsequent handlers are skipped on that path. On the fall-through path the next handler gets a chance. Previously, all handlers shared the same isError guard, which caused either vacuous truth (path infeasibility) or all handlers running unconditionally. The non-deterministic choice variable ensures each handler is independently reachable without creating contradictions.
cc32403 to
6629a36
Compare
Verifies that the analysis does not assume a specific handler catches when multiple except handlers are present. The assert is correctly reported as unknown on paths where the non-matching handler fires.
…terleaved exception checks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #874
Problem
raisestatements in Python were translated as a deterministicHole(<?>), which hasUnknowntype. The Laurel-to-Core translator rejectsUnknowntypes with "could not infer type", causing an internal error when analyzing any function containingraiseinside anexceptblock.Fix
Replace the
Holetranslation with three statements that model exception semantics:maybe_except := <??>— havoc (non-deterministic Error value)assume isError(maybe_except)— constrain to be a real errorexit <label>— exit to the catchers label (inside a try body) or exit the function (at top level)A
raiseExitLabelfield is added toTranslationContextto track whereraiseshould transfer control:$body)Each except handler is guarded by
isError(maybe_except) & handler_matches_Nwherehandler_matches_Nis a non-deterministic boolean. This models that we don't know which handler matches the exception type (sound over-approximation). The handler clears the error after its body and exits the try block, so subsequent handlers are skipped on that path.Translation example
Given this Python code:
The try/raise/except block is translated to the following Laurel IR (formatted for readability):
The analysis explores all paths non-deterministically:
result = "caught_a"→ assert passes ✅result = "caught_b"→ assert unknown ❓Changes
Strata/Languages/Python/PythonToLaurel.lean— core fix (raise translation + handler dispatch)StrataTest/Languages/Python/tests/test_raise_in_except.py— exact reproducer from Internal error when analyzing a Python function with raise #874StrataTest/Languages/Python/tests/test_raise_variants.py— edge cases (raise in try, bare re-raise, raise new in except, top-level raise)StrataTest/Languages/Python/tests/test_except_dispatch.py— soundness test for multiple handler dispatchtest_try_except,test_multiple_except, and othersTesting
All 156
pyAnalyzeLaureltests pass. The exact reproducer from #874 now produces "Analysis success".