Problem
With variable-length tuple parameters (#2833), no toolchain stage validates that the concrete lengths of the arguments and the out argument are mutually consistent, so mismatches surface as internal errors instead of diagnostics.
The PAST-level out check uses type_info.is_compatible_type / is_concretizable, and is_concretizable(VarArg[T], TupleType) deliberately ignores length; the operator's VarArg[...] return type is also never specialized against the concrete call-site argument lengths.
Examples
Calling a tuple[IField, ...] -> tuple[IField, ...] operator with a 3-element input and a 2-element out passes type checking; embedded execution then dies with a bare AssertionError inside utils.tree_map, and the roundtrip backend with the opaque ValueError: 'target_domain' cannot be 'NEVER' unless allow_uninferred=True from domain inference.
A fundamentally ambiguous variant — the return length depends on a runtime value, so no single length can be consistent with out:
@gtx.field_operator
def testee(a: tuple[IField, ...], b: tuple[IField, ...], c: bool) -> tuple[IField, ...]:
return a if c else b
testee(two_fields, three_fields, c, out=two_fields_out) # accepted, crashes later
Direction
Proper error handling means specializing the VarArg return type against the concrete argument lengths when they become known (e.g. in with_args), yielding a located diagnostic for the plain mismatch. The ambiguous conditional-return case above should then be disallowed outright — the two branch lengths cannot be unified. The workaround for users is a dedicated field operator per branch, each with a single consistent length.
Note foast_to_past.py uses a different predicate (is_concretizable) than the PAST type deduction (is_compatible_type) for the same conceptual check; the fix should unify them.
Current state
test_var_len_tuple_length_mismatch_rejected in test_tuples.py (on the #2833 branch) pins the ambiguous example as xfail(strict=True), asserting the desired DSLError; it starts passing once the check exists.
Disclaimer: This issue was written largely with the help of AI.
Problem
With variable-length tuple parameters (#2833), no toolchain stage validates that the concrete lengths of the arguments and the
outargument are mutually consistent, so mismatches surface as internal errors instead of diagnostics.The PAST-level
outcheck usestype_info.is_compatible_type/is_concretizable, andis_concretizable(VarArg[T], TupleType)deliberately ignores length; the operator'sVarArg[...]return type is also never specialized against the concrete call-site argument lengths.Examples
Calling a
tuple[IField, ...] -> tuple[IField, ...]operator with a 3-element input and a 2-elementoutpasses type checking; embedded execution then dies with a bareAssertionErrorinsideutils.tree_map, and the roundtrip backend with the opaqueValueError: 'target_domain' cannot be 'NEVER' unless allow_uninferred=Truefrom domain inference.A fundamentally ambiguous variant — the return length depends on a runtime value, so no single length can be consistent with
out:Direction
Proper error handling means specializing the
VarArgreturn type against the concrete argument lengths when they become known (e.g. inwith_args), yielding a located diagnostic for the plain mismatch. The ambiguous conditional-return case above should then be disallowed outright — the two branch lengths cannot be unified. The workaround for users is a dedicated field operator per branch, each with a single consistent length.Note
foast_to_past.pyuses a different predicate (is_concretizable) than the PAST type deduction (is_compatible_type) for the same conceptual check; the fix should unify them.Current state
test_var_len_tuple_length_mismatch_rejectedintest_tuples.py(on the #2833 branch) pins the ambiguous example asxfail(strict=True), asserting the desiredDSLError; it starts passing once the check exists.Disclaimer: This issue was written largely with the help of AI.