Skip to content

Add Tuple overloads for the non-arithmetic IROperator helpers - #9425

Open
abadams wants to merge 2 commits into
mainfrom
abadams/more_tuple_helpers
Open

Add Tuple overloads for the non-arithmetic IROperator helpers#9425
abadams wants to merge 2 commits into
mainfrom
abadams/more_tuple_helpers

Conversation

@abadams

@abadams abadams commented Sep 3, 2026

Copy link
Copy Markdown
Member

Motivation

select already works on Tuples, but there was no way to mark one side of a tuple-select as likely, because likely only took an Expr. This came up in a pipeline where a tuple-valued select needed loop partitioning driven by one of its arms.

What this does

Adds Tuple overloads that distribute across the elements for the IROperator helpers that don't imply any math:

  • likely, likely_if_innermost, strict_float
  • memoize_tag
  • require
  • print, print_when (all elements printed on one line, with the print attached to the first element)

The arithmetic helpers deliberately don't get overloads, and nothing in Internal does.

FuncRefs and one-element Tuples

A FuncRef converts implicitly to both Expr and Tuple, so a bare Tuple overload would have made existing calls like likely(f(x)) ambiguous. Each helper (and the existing select(cond, FuncRef, FuncRef)) therefore gets a FuncRef overload that dispatches on the tupleness of the Func by going through Tuple(f) and returning a Tuple.

To keep single-valued Funcs usable in Expr contexts, one-element Tuples now convert implicitly to Expr (in the same spirit as one-dimensional RDoms converting to RVars), and Tuple(FuncRef) accepts single-valued Funcs. select on FuncRefs no longer errors on Tuple-valued Funcs as a result.

Tuple also gains begin()/end() so it can be iterated with range-for, and the Python hl.Tuple gets __len__, __getitem__ and __iter__.

Debug.h

Debug.h forward-declares the Expr stream printer for every source file but not the Tuple one. With the new conversion, streaming a Tuple from a file that doesn't include IRPrinter.h silently resolved to the Expr printer (and recursed inside the conversion's own error message). The Tuple printer is now declared alongside it.

Tests

  • New test/correctness/tuple_helpers.cpp checks each helper wraps every element in the right intrinsic, that likely on a Tuple actually triggers loop partitioning, that require/print/print_when behave at runtime, that FuncRefs of both single- and Tuple-valued Funcs resolve, and that one-element Tuples convert to Expr.
  • New test/error/tuple_to_expr.cpp checks that a multi-element Tuple refuses to convert.
  • Python: test_tuple_helpers in iroperator.py. The Python require binding also now accepts string message args like the C++ one.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NZWjqjzMdpvXGhoxGKWLdJ

likely, likely_if_innermost, strict_float, memoize_tag, require, print
and print_when now accept a Tuple and distribute across its elements.
print and print_when print every element on one line, attached to the
first element. The Python bindings get the same overloads.

A FuncRef converts to both Expr and Tuple, so each helper (and select)
also gets a FuncRef overload that goes through Tuple(f) and returns a
Tuple. To keep single-valued Funcs usable in Expr contexts, one-element
Tuples now convert implicitly to Expr, and Tuple(FuncRef) accepts
single-valued Funcs. Tuple also gains begin()/end() so it can be
iterated with range-for, and the Python Tuple gets __len__,
__getitem__ and __iter__.

Debug.h now forward-declares the Tuple stream printer next to the Expr
one. Without it, streaming a Tuple from a file that doesn't include
IRPrinter.h resolves to the Expr printer via the new conversion.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NZWjqjzMdpvXGhoxGKWLdJ

@alexreinking alexreinking left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks like a nice usability improvement! The Python binding strategy LGTM, too.

Adding begin()/end() to Tuple made several pre-existing index loops
over Tuples convertible to range-based for; convert them. Also use
empty() instead of size() == 0 in the Python print helper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 74.00000% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.03%. Comparing base (6e5de35) to head (5d18081).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
src/Tuple.cpp 63.63% 3 Missing and 1 partial ⚠️
src/Tuple.h 50.00% 4 Missing ⚠️
src/Derivative.cpp 62.50% 2 Missing and 1 partial ⚠️
src/IRPrinter.cpp 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9425      +/-   ##
==========================================
+ Coverage   69.93%   70.03%   +0.09%     
==========================================
  Files         261      261              
  Lines       79410    79431      +21     
  Branches    19363    19364       +1     
==========================================
+ Hits        55532    55626      +94     
- Misses      17937    17950      +13     
+ Partials     5941     5855      -86     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to 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.

2 participants