Various improvements to enable importing stdlib module - #41

Merged
gf712 merged 12 commits into
mainfrom
improve-import
Aug 24, 2026
Merged

Various improvements to enable importing stdlib module#41
gf712 merged 12 commits into
mainfrom
improve-import

Conversation

@gf712

Copy link
Copy Markdown
Owner

No description provided.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comments.

@claude

claudeBot commented Aug 23, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Comment on lines +78 to +94
if (loop && !loop.isLoopOrelse(region)) { return false; }
}
return false;
}

// True when a break/continue that binds to the loop whose body is `body` is
// somewhere replace_loop_branch_yields cannot reach yet — inside a nested
// region that has not been flattened into ours. Branching it to our target
// block now would be a cross-region block reference, which is invalid IR.
//
// The caller defers (fails the match) until the nested op lowers and inlines
// the yield into our region, the same innermost-first trick TryOpLowering uses
// for nested trys. Terminates because the innermost such op has nothing nested
// to wait on.
bool has_pending_nested_orelse_control(mlir::Region &body)
{
if (body.empty()) { return false; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_pending_nested_orelse_control only checks the immediately-nested loop's own orelse for a kinded break/continue yield, then WalkResult::skip()s that loop's entire subtree (line 91). So a break/continue written in a second-level orelse — "else nested inside another else", e.g. for a: for b: pass else: for c: pass else: break — is never discovered: the inner loop L2 sits inside L1's orelse and is skipped before it's ever visited as its own loop. The enclosing loop therefore lowers without deferring, and after L1/L2 later flatten, the kinded py.br_yield ends up with no py.for_loop/py.while parent left, violating BranchYieldOp's HasParent trait.

This PR's own integration/tests/loop_else_break_binding.py exercises exactly this shape ("An else nested inside another else", "Three elses deep") and its docstring explicitly claims the deferral "follows the whole chain" — contradicting the comment on line 89-90 here, which is unsound for this shape. (bug)

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@gf712
gf712 merged commit aa17453 into mainAug 24, 2026
4 checks passed
@gf712
gf712 deleted the improve-import branch August 24, 2026 08:57
Comment on lines +140 to +159
// Rewrites a loop orelse region's normal-completion (kindless) py.br_yield ops
// into branches to the loop's exit block.
//
// Only the kindless ones. A `break`/`continue` written in an orelse binds to
// the loop *enclosing* this one, so those are left in place: once this region
// is inlined they sit directly in the enclosing loop's body, where its own
// replace_loop_branch_yields claims them.
void replace_orelse_completion_yields(mlir::PatternRewriter &rewriter,
mlir::Region &region,
mlir::Block *exit_target)
{
if (region.empty()) { return; }
region.walk<WalkOrder::PreOrder>([&rewriter, exit_target](mlir::Operation *operation) {
if (is_flattened_region_op(operation)) { return WalkResult::skip(); }
auto yield_op = mlir::dyn_cast<mlir::py::BranchYieldOp>(operation);
if (!yield_op || yield_op.getKind().has_value()) { return WalkResult::advance(); }
rewriter.setInsertionPoint(yield_op);
rewriter.replaceOpWithNewOp<mlir::cf::BranchOp>(yield_op, exit_target);
return WalkResult::advance();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break/continue in a nested loop's else that binds to an outer loop now silently skips finally/with.__exit__ cleanup on that control-flow path (bug)

TryOpLowering/WithOpLowering run before ConvertLoopsPass and (per the comment at ~line 409 in this file) assume "a loop consumes its own break/continue," so replace_controlflow_yield and collect_loop_control_kinds skip over any nested ForLoopOp/WhileOp entirely. This PR's new replace_orelse_completion_yields deliberately leaves kinded (break/continue) yields in a nested orelse in place so an outer loop can claim them later — but that means they now escape past the try/with lowering pass without ever being routed through build_finally_loop_exits / LeaveExceptionHandle. For example:

forain [1, 2, 3]:
try:
forbin []:
passelse:
breakfinally:
print("cleanup")

binds break to the outer for per Python semantics, but the generated code branches straight out of the loop without ever entering finally, so "cleanup" is never printed. The same gap applies to with (WithOpLowering has the identical skip), where __exit__ is silently never invoked. This is a new failure mode introduced by the orelse-binding mechanism (pre-PR, the break was fully swallowed by the inner loop, so the try's already-wired finally path still ran) — worth teaching TryOpLowering/WithOpLowering about pending outward-binding orelse yields, or deferring them the same way nested loops now defer to each other.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@gf712
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Various improvements to enable importing stdlib module - #41

Merged
gf712 merged 12 commits into
mainfrom
improve-import
Aug 24, 2026
Merged

Various improvements to enable importing stdlib module#41
gf712 merged 12 commits into
mainfrom
improve-import

Conversation

@gf712

Copy link
Copy Markdown
Owner

No description provided.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comments.

@claude

claudeBot commented Aug 23, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Comment on lines +78 to +94
if (loop && !loop.isLoopOrelse(region)) { return false; }
}
return false;
}

// True when a break/continue that binds to the loop whose body is `body` is
// somewhere replace_loop_branch_yields cannot reach yet — inside a nested
// region that has not been flattened into ours. Branching it to our target
// block now would be a cross-region block reference, which is invalid IR.
//
// The caller defers (fails the match) until the nested op lowers and inlines
// the yield into our region, the same innermost-first trick TryOpLowering uses
// for nested trys. Terminates because the innermost such op has nothing nested
// to wait on.
bool has_pending_nested_orelse_control(mlir::Region &body)
{
if (body.empty()) { return false; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_pending_nested_orelse_control only checks the immediately-nested loop's own orelse for a kinded break/continue yield, then WalkResult::skip()s that loop's entire subtree (line 91). So a break/continue written in a second-level orelse — "else nested inside another else", e.g. for a: for b: pass else: for c: pass else: break — is never discovered: the inner loop L2 sits inside L1's orelse and is skipped before it's ever visited as its own loop. The enclosing loop therefore lowers without deferring, and after L1/L2 later flatten, the kinded py.br_yield ends up with no py.for_loop/py.while parent left, violating BranchYieldOp's HasParent trait.

This PR's own integration/tests/loop_else_break_binding.py exercises exactly this shape ("An else nested inside another else", "Three elses deep") and its docstring explicitly claims the deferral "follows the whole chain" — contradicting the comment on line 89-90 here, which is unsound for this shape. (bug)

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@gf712
gf712 merged commit aa17453 into mainAug 24, 2026
4 checks passed
@gf712
gf712 deleted the improve-import branch August 24, 2026 08:57
Comment on lines +140 to +159
// Rewrites a loop orelse region's normal-completion (kindless) py.br_yield ops
// into branches to the loop's exit block.
//
// Only the kindless ones. A `break`/`continue` written in an orelse binds to
// the loop *enclosing* this one, so those are left in place: once this region
// is inlined they sit directly in the enclosing loop's body, where its own
// replace_loop_branch_yields claims them.
void replace_orelse_completion_yields(mlir::PatternRewriter &rewriter,
mlir::Region &region,
mlir::Block *exit_target)
{
if (region.empty()) { return; }
region.walk<WalkOrder::PreOrder>([&rewriter, exit_target](mlir::Operation *operation) {
if (is_flattened_region_op(operation)) { return WalkResult::skip(); }
auto yield_op = mlir::dyn_cast<mlir::py::BranchYieldOp>(operation);
if (!yield_op || yield_op.getKind().has_value()) { return WalkResult::advance(); }
rewriter.setInsertionPoint(yield_op);
rewriter.replaceOpWithNewOp<mlir::cf::BranchOp>(yield_op, exit_target);
return WalkResult::advance();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break/continue in a nested loop's else that binds to an outer loop now silently skips finally/with.__exit__ cleanup on that control-flow path (bug)

TryOpLowering/WithOpLowering run before ConvertLoopsPass and (per the comment at ~line 409 in this file) assume "a loop consumes its own break/continue," so replace_controlflow_yield and collect_loop_control_kinds skip over any nested ForLoopOp/WhileOp entirely. This PR's new replace_orelse_completion_yields deliberately leaves kinded (break/continue) yields in a nested orelse in place so an outer loop can claim them later — but that means they now escape past the try/with lowering pass without ever being routed through build_finally_loop_exits / LeaveExceptionHandle. For example:

forain [1, 2, 3]:
try:
forbin []:
passelse:
breakfinally:
print("cleanup")

binds break to the outer for per Python semantics, but the generated code branches straight out of the loop without ever entering finally, so "cleanup" is never printed. The same gap applies to with (WithOpLowering has the identical skip), where __exit__ is silently never invoked. This is a new failure mode introduced by the orelse-binding mechanism (pre-PR, the break was fully swallowed by the inner loop, so the try's already-wired finally path still ran) — worth teaching TryOpLowering/WithOpLowering about pending outward-binding orelse yields, or deferring them the same way nested loops now defer to each other.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@gf712
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Various improvements to enable importing stdlib module - #41

Merged
gf712 merged 12 commits into
mainfrom
improve-import
Aug 24, 2026
Merged

Various improvements to enable importing stdlib module#41
gf712 merged 12 commits into
mainfrom
improve-import

Conversation

@gf712

Copy link
Copy Markdown
Owner

No description provided.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comments.

@claude

claudeBot commented Aug 23, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Comment on lines +78 to +94
if (loop && !loop.isLoopOrelse(region)) { return false; }
}
return false;
}

// True when a break/continue that binds to the loop whose body is `body` is
// somewhere replace_loop_branch_yields cannot reach yet — inside a nested
// region that has not been flattened into ours. Branching it to our target
// block now would be a cross-region block reference, which is invalid IR.
//
// The caller defers (fails the match) until the nested op lowers and inlines
// the yield into our region, the same innermost-first trick TryOpLowering uses
// for nested trys. Terminates because the innermost such op has nothing nested
// to wait on.
bool has_pending_nested_orelse_control(mlir::Region &body)
{
if (body.empty()) { return false; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_pending_nested_orelse_control only checks the immediately-nested loop's own orelse for a kinded break/continue yield, then WalkResult::skip()s that loop's entire subtree (line 91). So a break/continue written in a second-level orelse — "else nested inside another else", e.g. for a: for b: pass else: for c: pass else: break — is never discovered: the inner loop L2 sits inside L1's orelse and is skipped before it's ever visited as its own loop. The enclosing loop therefore lowers without deferring, and after L1/L2 later flatten, the kinded py.br_yield ends up with no py.for_loop/py.while parent left, violating BranchYieldOp's HasParent trait.

This PR's own integration/tests/loop_else_break_binding.py exercises exactly this shape ("An else nested inside another else", "Three elses deep") and its docstring explicitly claims the deferral "follows the whole chain" — contradicting the comment on line 89-90 here, which is unsound for this shape. (bug)

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@gf712
gf712 merged commit aa17453 into mainAug 24, 2026
4 checks passed
@gf712
gf712 deleted the improve-import branch August 24, 2026 08:57
Comment on lines +140 to +159
// Rewrites a loop orelse region's normal-completion (kindless) py.br_yield ops
// into branches to the loop's exit block.
//
// Only the kindless ones. A `break`/`continue` written in an orelse binds to
// the loop *enclosing* this one, so those are left in place: once this region
// is inlined they sit directly in the enclosing loop's body, where its own
// replace_loop_branch_yields claims them.
void replace_orelse_completion_yields(mlir::PatternRewriter &rewriter,
mlir::Region &region,
mlir::Block *exit_target)
{
if (region.empty()) { return; }
region.walk<WalkOrder::PreOrder>([&rewriter, exit_target](mlir::Operation *operation) {
if (is_flattened_region_op(operation)) { return WalkResult::skip(); }
auto yield_op = mlir::dyn_cast<mlir::py::BranchYieldOp>(operation);
if (!yield_op || yield_op.getKind().has_value()) { return WalkResult::advance(); }
rewriter.setInsertionPoint(yield_op);
rewriter.replaceOpWithNewOp<mlir::cf::BranchOp>(yield_op, exit_target);
return WalkResult::advance();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break/continue in a nested loop's else that binds to an outer loop now silently skips finally/with.__exit__ cleanup on that control-flow path (bug)

TryOpLowering/WithOpLowering run before ConvertLoopsPass and (per the comment at ~line 409 in this file) assume "a loop consumes its own break/continue," so replace_controlflow_yield and collect_loop_control_kinds skip over any nested ForLoopOp/WhileOp entirely. This PR's new replace_orelse_completion_yields deliberately leaves kinded (break/continue) yields in a nested orelse in place so an outer loop can claim them later — but that means they now escape past the try/with lowering pass without ever being routed through build_finally_loop_exits / LeaveExceptionHandle. For example:

forain [1, 2, 3]:
try:
forbin []:
passelse:
breakfinally:
print("cleanup")

binds break to the outer for per Python semantics, but the generated code branches straight out of the loop without ever entering finally, so "cleanup" is never printed. The same gap applies to with (WithOpLowering has the identical skip), where __exit__ is silently never invoked. This is a new failure mode introduced by the orelse-binding mechanism (pre-PR, the break was fully swallowed by the inner loop, so the try's already-wired finally path still ran) — worth teaching TryOpLowering/WithOpLowering about pending outward-binding orelse yields, or deferring them the same way nested loops now defer to each other.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@gf712
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Various improvements to enable importing stdlib module - #41

Merged
gf712 merged 12 commits into
mainfrom
improve-import
Aug 24, 2026
Merged

Various improvements to enable importing stdlib module#41
gf712 merged 12 commits into
mainfrom
improve-import

Conversation

@gf712

Copy link
Copy Markdown
Owner

No description provided.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comments.

@claude

claudeBot commented Aug 23, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Comment on lines +78 to +94
if (loop && !loop.isLoopOrelse(region)) { return false; }
}
return false;
}

// True when a break/continue that binds to the loop whose body is `body` is
// somewhere replace_loop_branch_yields cannot reach yet — inside a nested
// region that has not been flattened into ours. Branching it to our target
// block now would be a cross-region block reference, which is invalid IR.
//
// The caller defers (fails the match) until the nested op lowers and inlines
// the yield into our region, the same innermost-first trick TryOpLowering uses
// for nested trys. Terminates because the innermost such op has nothing nested
// to wait on.
bool has_pending_nested_orelse_control(mlir::Region &body)
{
if (body.empty()) { return false; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_pending_nested_orelse_control only checks the immediately-nested loop's own orelse for a kinded break/continue yield, then WalkResult::skip()s that loop's entire subtree (line 91). So a break/continue written in a second-level orelse — "else nested inside another else", e.g. for a: for b: pass else: for c: pass else: break — is never discovered: the inner loop L2 sits inside L1's orelse and is skipped before it's ever visited as its own loop. The enclosing loop therefore lowers without deferring, and after L1/L2 later flatten, the kinded py.br_yield ends up with no py.for_loop/py.while parent left, violating BranchYieldOp's HasParent trait.

This PR's own integration/tests/loop_else_break_binding.py exercises exactly this shape ("An else nested inside another else", "Three elses deep") and its docstring explicitly claims the deferral "follows the whole chain" — contradicting the comment on line 89-90 here, which is unsound for this shape. (bug)

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@gf712
gf712 merged commit aa17453 into mainAug 24, 2026
4 checks passed
@gf712
gf712 deleted the improve-import branch August 24, 2026 08:57
Comment on lines +140 to +159
// Rewrites a loop orelse region's normal-completion (kindless) py.br_yield ops
// into branches to the loop's exit block.
//
// Only the kindless ones. A `break`/`continue` written in an orelse binds to
// the loop *enclosing* this one, so those are left in place: once this region
// is inlined they sit directly in the enclosing loop's body, where its own
// replace_loop_branch_yields claims them.
void replace_orelse_completion_yields(mlir::PatternRewriter &rewriter,
mlir::Region &region,
mlir::Block *exit_target)
{
if (region.empty()) { return; }
region.walk<WalkOrder::PreOrder>([&rewriter, exit_target](mlir::Operation *operation) {
if (is_flattened_region_op(operation)) { return WalkResult::skip(); }
auto yield_op = mlir::dyn_cast<mlir::py::BranchYieldOp>(operation);
if (!yield_op || yield_op.getKind().has_value()) { return WalkResult::advance(); }
rewriter.setInsertionPoint(yield_op);
rewriter.replaceOpWithNewOp<mlir::cf::BranchOp>(yield_op, exit_target);
return WalkResult::advance();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break/continue in a nested loop's else that binds to an outer loop now silently skips finally/with.__exit__ cleanup on that control-flow path (bug)

TryOpLowering/WithOpLowering run before ConvertLoopsPass and (per the comment at ~line 409 in this file) assume "a loop consumes its own break/continue," so replace_controlflow_yield and collect_loop_control_kinds skip over any nested ForLoopOp/WhileOp entirely. This PR's new replace_orelse_completion_yields deliberately leaves kinded (break/continue) yields in a nested orelse in place so an outer loop can claim them later — but that means they now escape past the try/with lowering pass without ever being routed through build_finally_loop_exits / LeaveExceptionHandle. For example:

forain [1, 2, 3]:
try:
forbin []:
passelse:
breakfinally:
print("cleanup")

binds break to the outer for per Python semantics, but the generated code branches straight out of the loop without ever entering finally, so "cleanup" is never printed. The same gap applies to with (WithOpLowering has the identical skip), where __exit__ is silently never invoked. This is a new failure mode introduced by the orelse-binding mechanism (pre-PR, the break was fully swallowed by the inner loop, so the try's already-wired finally path still ran) — worth teaching TryOpLowering/WithOpLowering about pending outward-binding orelse yields, or deferring them the same way nested loops now defer to each other.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@gf712
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Various improvements to enable importing stdlib module - #41

Merged
gf712 merged 12 commits into
mainfrom
improve-import
Aug 24, 2026
Merged

Various improvements to enable importing stdlib module#41
gf712 merged 12 commits into
mainfrom
improve-import

Conversation

@gf712

Copy link
Copy Markdown
Owner

No description provided.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comments.

@claude

claudeBot commented Aug 23, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Comment on lines +78 to +94
if (loop && !loop.isLoopOrelse(region)) { return false; }
}
return false;
}

// True when a break/continue that binds to the loop whose body is `body` is
// somewhere replace_loop_branch_yields cannot reach yet — inside a nested
// region that has not been flattened into ours. Branching it to our target
// block now would be a cross-region block reference, which is invalid IR.
//
// The caller defers (fails the match) until the nested op lowers and inlines
// the yield into our region, the same innermost-first trick TryOpLowering uses
// for nested trys. Terminates because the innermost such op has nothing nested
// to wait on.
bool has_pending_nested_orelse_control(mlir::Region &body)
{
if (body.empty()) { return false; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_pending_nested_orelse_control only checks the immediately-nested loop's own orelse for a kinded break/continue yield, then WalkResult::skip()s that loop's entire subtree (line 91). So a break/continue written in a second-level orelse — "else nested inside another else", e.g. for a: for b: pass else: for c: pass else: break — is never discovered: the inner loop L2 sits inside L1's orelse and is skipped before it's ever visited as its own loop. The enclosing loop therefore lowers without deferring, and after L1/L2 later flatten, the kinded py.br_yield ends up with no py.for_loop/py.while parent left, violating BranchYieldOp's HasParent trait.

This PR's own integration/tests/loop_else_break_binding.py exercises exactly this shape ("An else nested inside another else", "Three elses deep") and its docstring explicitly claims the deferral "follows the whole chain" — contradicting the comment on line 89-90 here, which is unsound for this shape. (bug)

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@gf712
gf712 merged commit aa17453 into mainAug 24, 2026
4 checks passed
@gf712
gf712 deleted the improve-import branch August 24, 2026 08:57
Comment on lines +140 to +159
// Rewrites a loop orelse region's normal-completion (kindless) py.br_yield ops
// into branches to the loop's exit block.
//
// Only the kindless ones. A `break`/`continue` written in an orelse binds to
// the loop *enclosing* this one, so those are left in place: once this region
// is inlined they sit directly in the enclosing loop's body, where its own
// replace_loop_branch_yields claims them.
void replace_orelse_completion_yields(mlir::PatternRewriter &rewriter,
mlir::Region &region,
mlir::Block *exit_target)
{
if (region.empty()) { return; }
region.walk<WalkOrder::PreOrder>([&rewriter, exit_target](mlir::Operation *operation) {
if (is_flattened_region_op(operation)) { return WalkResult::skip(); }
auto yield_op = mlir::dyn_cast<mlir::py::BranchYieldOp>(operation);
if (!yield_op || yield_op.getKind().has_value()) { return WalkResult::advance(); }
rewriter.setInsertionPoint(yield_op);
rewriter.replaceOpWithNewOp<mlir::cf::BranchOp>(yield_op, exit_target);
return WalkResult::advance();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break/continue in a nested loop's else that binds to an outer loop now silently skips finally/with.__exit__ cleanup on that control-flow path (bug)

TryOpLowering/WithOpLowering run before ConvertLoopsPass and (per the comment at ~line 409 in this file) assume "a loop consumes its own break/continue," so replace_controlflow_yield and collect_loop_control_kinds skip over any nested ForLoopOp/WhileOp entirely. This PR's new replace_orelse_completion_yields deliberately leaves kinded (break/continue) yields in a nested orelse in place so an outer loop can claim them later — but that means they now escape past the try/with lowering pass without ever being routed through build_finally_loop_exits / LeaveExceptionHandle. For example:

forain [1, 2, 3]:
try:
forbin []:
passelse:
breakfinally:
print("cleanup")

binds break to the outer for per Python semantics, but the generated code branches straight out of the loop without ever entering finally, so "cleanup" is never printed. The same gap applies to with (WithOpLowering has the identical skip), where __exit__ is silently never invoked. This is a new failure mode introduced by the orelse-binding mechanism (pre-PR, the break was fully swallowed by the inner loop, so the try's already-wired finally path still ran) — worth teaching TryOpLowering/WithOpLowering about pending outward-binding orelse yields, or deferring them the same way nested loops now defer to each other.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@gf712
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Various improvements to enable importing stdlib module - #41

Merged
gf712 merged 12 commits into
mainfrom
improve-import
Aug 24, 2026
Merged

Various improvements to enable importing stdlib module#41
gf712 merged 12 commits into
mainfrom
improve-import

Conversation

@gf712

Copy link
Copy Markdown
Owner

No description provided.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comments.

@claude

claudeBot commented Aug 23, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Comment on lines +78 to +94
if (loop && !loop.isLoopOrelse(region)) { return false; }
}
return false;
}

// True when a break/continue that binds to the loop whose body is `body` is
// somewhere replace_loop_branch_yields cannot reach yet — inside a nested
// region that has not been flattened into ours. Branching it to our target
// block now would be a cross-region block reference, which is invalid IR.
//
// The caller defers (fails the match) until the nested op lowers and inlines
// the yield into our region, the same innermost-first trick TryOpLowering uses
// for nested trys. Terminates because the innermost such op has nothing nested
// to wait on.
bool has_pending_nested_orelse_control(mlir::Region &body)
{
if (body.empty()) { return false; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_pending_nested_orelse_control only checks the immediately-nested loop's own orelse for a kinded break/continue yield, then WalkResult::skip()s that loop's entire subtree (line 91). So a break/continue written in a second-level orelse — "else nested inside another else", e.g. for a: for b: pass else: for c: pass else: break — is never discovered: the inner loop L2 sits inside L1's orelse and is skipped before it's ever visited as its own loop. The enclosing loop therefore lowers without deferring, and after L1/L2 later flatten, the kinded py.br_yield ends up with no py.for_loop/py.while parent left, violating BranchYieldOp's HasParent trait.

This PR's own integration/tests/loop_else_break_binding.py exercises exactly this shape ("An else nested inside another else", "Three elses deep") and its docstring explicitly claims the deferral "follows the whole chain" — contradicting the comment on line 89-90 here, which is unsound for this shape. (bug)

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@gf712
gf712 merged commit aa17453 into mainAug 24, 2026
4 checks passed
@gf712
gf712 deleted the improve-import branch August 24, 2026 08:57
Comment on lines +140 to +159
// Rewrites a loop orelse region's normal-completion (kindless) py.br_yield ops
// into branches to the loop's exit block.
//
// Only the kindless ones. A `break`/`continue` written in an orelse binds to
// the loop *enclosing* this one, so those are left in place: once this region
// is inlined they sit directly in the enclosing loop's body, where its own
// replace_loop_branch_yields claims them.
void replace_orelse_completion_yields(mlir::PatternRewriter &rewriter,
mlir::Region &region,
mlir::Block *exit_target)
{
if (region.empty()) { return; }
region.walk<WalkOrder::PreOrder>([&rewriter, exit_target](mlir::Operation *operation) {
if (is_flattened_region_op(operation)) { return WalkResult::skip(); }
auto yield_op = mlir::dyn_cast<mlir::py::BranchYieldOp>(operation);
if (!yield_op || yield_op.getKind().has_value()) { return WalkResult::advance(); }
rewriter.setInsertionPoint(yield_op);
rewriter.replaceOpWithNewOp<mlir::cf::BranchOp>(yield_op, exit_target);
return WalkResult::advance();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break/continue in a nested loop's else that binds to an outer loop now silently skips finally/with.__exit__ cleanup on that control-flow path (bug)

TryOpLowering/WithOpLowering run before ConvertLoopsPass and (per the comment at ~line 409 in this file) assume "a loop consumes its own break/continue," so replace_controlflow_yield and collect_loop_control_kinds skip over any nested ForLoopOp/WhileOp entirely. This PR's new replace_orelse_completion_yields deliberately leaves kinded (break/continue) yields in a nested orelse in place so an outer loop can claim them later — but that means they now escape past the try/with lowering pass without ever being routed through build_finally_loop_exits / LeaveExceptionHandle. For example:

forain [1, 2, 3]:
try:
forbin []:
passelse:
breakfinally:
print("cleanup")

binds break to the outer for per Python semantics, but the generated code branches straight out of the loop without ever entering finally, so "cleanup" is never printed. The same gap applies to with (WithOpLowering has the identical skip), where __exit__ is silently never invoked. This is a new failure mode introduced by the orelse-binding mechanism (pre-PR, the break was fully swallowed by the inner loop, so the try's already-wired finally path still ran) — worth teaching TryOpLowering/WithOpLowering about pending outward-binding orelse yields, or deferring them the same way nested loops now defer to each other.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@gf712
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Various improvements to enable importing stdlib module - #41

Merged
gf712 merged 12 commits into
mainfrom
improve-import
Aug 24, 2026
Merged

Various improvements to enable importing stdlib module#41
gf712 merged 12 commits into
mainfrom
improve-import

Conversation

@gf712

Copy link
Copy Markdown
Owner

No description provided.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comments.

@claude

claudeBot commented Aug 23, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Comment on lines +78 to +94
if (loop && !loop.isLoopOrelse(region)) { return false; }
}
return false;
}

// True when a break/continue that binds to the loop whose body is `body` is
// somewhere replace_loop_branch_yields cannot reach yet — inside a nested
// region that has not been flattened into ours. Branching it to our target
// block now would be a cross-region block reference, which is invalid IR.
//
// The caller defers (fails the match) until the nested op lowers and inlines
// the yield into our region, the same innermost-first trick TryOpLowering uses
// for nested trys. Terminates because the innermost such op has nothing nested
// to wait on.
bool has_pending_nested_orelse_control(mlir::Region &body)
{
if (body.empty()) { return false; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_pending_nested_orelse_control only checks the immediately-nested loop's own orelse for a kinded break/continue yield, then WalkResult::skip()s that loop's entire subtree (line 91). So a break/continue written in a second-level orelse — "else nested inside another else", e.g. for a: for b: pass else: for c: pass else: break — is never discovered: the inner loop L2 sits inside L1's orelse and is skipped before it's ever visited as its own loop. The enclosing loop therefore lowers without deferring, and after L1/L2 later flatten, the kinded py.br_yield ends up with no py.for_loop/py.while parent left, violating BranchYieldOp's HasParent trait.

This PR's own integration/tests/loop_else_break_binding.py exercises exactly this shape ("An else nested inside another else", "Three elses deep") and its docstring explicitly claims the deferral "follows the whole chain" — contradicting the comment on line 89-90 here, which is unsound for this shape. (bug)

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@gf712
gf712 merged commit aa17453 into mainAug 24, 2026
4 checks passed
@gf712
gf712 deleted the improve-import branch August 24, 2026 08:57
Comment on lines +140 to +159
// Rewrites a loop orelse region's normal-completion (kindless) py.br_yield ops
// into branches to the loop's exit block.
//
// Only the kindless ones. A `break`/`continue` written in an orelse binds to
// the loop *enclosing* this one, so those are left in place: once this region
// is inlined they sit directly in the enclosing loop's body, where its own
// replace_loop_branch_yields claims them.
void replace_orelse_completion_yields(mlir::PatternRewriter &rewriter,
mlir::Region &region,
mlir::Block *exit_target)
{
if (region.empty()) { return; }
region.walk<WalkOrder::PreOrder>([&rewriter, exit_target](mlir::Operation *operation) {
if (is_flattened_region_op(operation)) { return WalkResult::skip(); }
auto yield_op = mlir::dyn_cast<mlir::py::BranchYieldOp>(operation);
if (!yield_op || yield_op.getKind().has_value()) { return WalkResult::advance(); }
rewriter.setInsertionPoint(yield_op);
rewriter.replaceOpWithNewOp<mlir::cf::BranchOp>(yield_op, exit_target);
return WalkResult::advance();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break/continue in a nested loop's else that binds to an outer loop now silently skips finally/with.__exit__ cleanup on that control-flow path (bug)

TryOpLowering/WithOpLowering run before ConvertLoopsPass and (per the comment at ~line 409 in this file) assume "a loop consumes its own break/continue," so replace_controlflow_yield and collect_loop_control_kinds skip over any nested ForLoopOp/WhileOp entirely. This PR's new replace_orelse_completion_yields deliberately leaves kinded (break/continue) yields in a nested orelse in place so an outer loop can claim them later — but that means they now escape past the try/with lowering pass without ever being routed through build_finally_loop_exits / LeaveExceptionHandle. For example:

forain [1, 2, 3]:
try:
forbin []:
passelse:
breakfinally:
print("cleanup")

binds break to the outer for per Python semantics, but the generated code branches straight out of the loop without ever entering finally, so "cleanup" is never printed. The same gap applies to with (WithOpLowering has the identical skip), where __exit__ is silently never invoked. This is a new failure mode introduced by the orelse-binding mechanism (pre-PR, the break was fully swallowed by the inner loop, so the try's already-wired finally path still ran) — worth teaching TryOpLowering/WithOpLowering about pending outward-binding orelse yields, or deferring them the same way nested loops now defer to each other.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@gf712
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Various improvements to enable importing stdlib module - #41

Merged
gf712 merged 12 commits into
mainfrom
improve-import
Aug 24, 2026
Merged

Various improvements to enable importing stdlib module#41
gf712 merged 12 commits into
mainfrom
improve-import

Conversation

@gf712

Copy link
Copy Markdown
Owner

No description provided.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 21, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comments.

@claude

claudeBot commented Aug 23, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Comment on lines +78 to +94
if (loop && !loop.isLoopOrelse(region)) { return false; }
}
return false;
}

// True when a break/continue that binds to the loop whose body is `body` is
// somewhere replace_loop_branch_yields cannot reach yet — inside a nested
// region that has not been flattened into ours. Branching it to our target
// block now would be a cross-region block reference, which is invalid IR.
//
// The caller defers (fails the match) until the nested op lowers and inlines
// the yield into our region, the same innermost-first trick TryOpLowering uses
// for nested trys. Terminates because the innermost such op has nothing nested
// to wait on.
bool has_pending_nested_orelse_control(mlir::Region &body)
{
if (body.empty()) { return false; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_pending_nested_orelse_control only checks the immediately-nested loop's own orelse for a kinded break/continue yield, then WalkResult::skip()s that loop's entire subtree (line 91). So a break/continue written in a second-level orelse — "else nested inside another else", e.g. for a: for b: pass else: for c: pass else: break — is never discovered: the inner loop L2 sits inside L1's orelse and is skipped before it's ever visited as its own loop. The enclosing loop therefore lowers without deferring, and after L1/L2 later flatten, the kinded py.br_yield ends up with no py.for_loop/py.while parent left, violating BranchYieldOp's HasParent trait.

This PR's own integration/tests/loop_else_break_binding.py exercises exactly this shape ("An else nested inside another else", "Three elses deep") and its docstring explicitly claims the deferral "follows the whole chain" — contradicting the comment on line 89-90 here, which is unsound for this shape. (bug)

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

@gf712
gf712 merged commit aa17453 into mainAug 24, 2026
4 checks passed
@gf712
gf712 deleted the improve-import branch August 24, 2026 08:57
Comment on lines +140 to +159
// Rewrites a loop orelse region's normal-completion (kindless) py.br_yield ops
// into branches to the loop's exit block.
//
// Only the kindless ones. A `break`/`continue` written in an orelse binds to
// the loop *enclosing* this one, so those are left in place: once this region
// is inlined they sit directly in the enclosing loop's body, where its own
// replace_loop_branch_yields claims them.
void replace_orelse_completion_yields(mlir::PatternRewriter &rewriter,
mlir::Region &region,
mlir::Block *exit_target)
{
if (region.empty()) { return; }
region.walk<WalkOrder::PreOrder>([&rewriter, exit_target](mlir::Operation *operation) {
if (is_flattened_region_op(operation)) { return WalkResult::skip(); }
auto yield_op = mlir::dyn_cast<mlir::py::BranchYieldOp>(operation);
if (!yield_op || yield_op.getKind().has_value()) { return WalkResult::advance(); }
rewriter.setInsertionPoint(yield_op);
rewriter.replaceOpWithNewOp<mlir::cf::BranchOp>(yield_op, exit_target);
return WalkResult::advance();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break/continue in a nested loop's else that binds to an outer loop now silently skips finally/with.__exit__ cleanup on that control-flow path (bug)

TryOpLowering/WithOpLowering run before ConvertLoopsPass and (per the comment at ~line 409 in this file) assume "a loop consumes its own break/continue," so replace_controlflow_yield and collect_loop_control_kinds skip over any nested ForLoopOp/WhileOp entirely. This PR's new replace_orelse_completion_yields deliberately leaves kinded (break/continue) yields in a nested orelse in place so an outer loop can claim them later — but that means they now escape past the try/with lowering pass without ever being routed through build_finally_loop_exits / LeaveExceptionHandle. For example:

forain [1, 2, 3]:
try:
forbin []:
passelse:
breakfinally:
print("cleanup")

binds break to the outer for per Python semantics, but the generated code branches straight out of the loop without ever entering finally, so "cleanup" is never printed. The same gap applies to with (WithOpLowering has the identical skip), where __exit__ is silently never invoked. This is a new failure mode introduced by the orelse-binding mechanism (pre-PR, the break was fully swallowed by the inner loop, so the try's already-wired finally path still ran) — worth teaching TryOpLowering/WithOpLowering about pending outward-binding orelse yields, or deferring them the same way nested loops now defer to each other.

@claude

claudeBot commented Aug 24, 2026

Copy link
Copy Markdown

Code review

Found 1 new issue — see the inline comment.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@gf712