Skip to content

fix(parser): name the cause when concat, a docstring, or a bare loop variable is refused #121

Description

@zhen8838

Three authoring forms are refused with a message that names neither the
construct nor what to write instead. Each cost a debugging round to identify,
and in each case the fix in the source is one character to one line — so the
whole cost was the diagnostic.

tilefoundry 0.0.2.dev43+g45032b967 (45032b9), target nvidia.h200_sxm.

1. tf.concat with a list

return tf.concat([xs[:, 32:64], xs[:, 0:32]], axis=1)
expression.attribute: pattern did not match (role 'inputs') at case.py:22:26

The parser wants varargs — tf.concat(a, b, axis=1). The list form is how
torch, numpy, jax and tvm all spell it, so it is the first thing an author
tries. Nothing in the message says concat, says list, or says varargs.
Either accept the sequence form or say tf.concat takes its inputs as varargs, not a sequence.

2. A docstring in a @func body

@func
def f(x: Tensor[(N, W), "f32"]) -> Tensor[(N, W), "f32"]:
    """What this stage does."""
    with Mesh(("cta",), layout=(N,), names=("u",)) as m:
        return tf.reshard(x, (N @ m.u, W), "smem")
expression.attribute: pattern did not match (role 'statement_value') at case.py:20:5

Every other Python function may carry one, and the shipped models put their
prose in # comments — which reads like a style choice until you find out it
is enforced. Dropping a leading string constant from the body is a two-line
change in the block pattern; refusing it with a @func body may not hold a docstring; use # comments is a one-line change. Either is better than
pointing at column 5 of a string literal.

3. A bare loop variable as a slice bound

for t in tile(S, N):
    xs = tf.reshard(x[:, t:t + N, :], (1, N @ m.u, W), "smem")
tilefoundry: error: dim_expr: expected int, DimVar, or Expr, got slice

base = t + 0 then x[:, base:base + N, :] is accepted and identical.
docs/tutorial/authoring.md writes base = start + 0 in Stage5_CachePrepared
without saying why, which is the only hint available. The message describes a
slice arriving where a dim was expected, which is the opposite of the problem
(a loop variable arrived where a dim was expected, inside a slice). Accepting
the bare variable is the right fix; failing that, say a loop induction variable must be bound to a name before it is used as a slice bound: write base = t + 0.

Reproducer

One file per case, each a whole program; tools/issues/authoring_diagnostics.py
in our tree runs all five we hit and prints the last stderr line of each. The
three above are reproduced by:

tilefoundry analyze case.py:M /tmp/r.txt --compute-cost --dim seq_len=8192

with case.py as the snippet plus this prelude:

from __future__ import annotations
from tilefoundry import func, module
from tilefoundry.dsl import ConstTensor, DimVar, Mesh, Tensor, tf
from tilefoundry.dsl.tf import *  # noqa: F401, F403
from tilefoundry.ir.types.shard import Topology
from tilefoundry.target import CudaTarget

N, W = 128, 64
S = DimVar("seq_len", 0, 8193)
_H200, _CTA = CudaTarget("nvidia.h200_sxm"), Topology("cta", N)


@module(entry="f", target=_H200, topologies=(_CTA,))
class M:
    ...

Why these three together

They share one shape: the parser fails a pattern and reports the pattern's
name. expression.attribute: pattern did not match (role 'inputs') is a fact
about the matcher, not about the program. A pattern that is the last
alternative to fail on a call to a known op could name that op and what it
expected; that one change covers cases 1 and 2 and most of what an author hits
while learning the grammar.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions