def METHOD_NAME(x: Any, y: Any, trail: tuple[Any, ...]) -> None:
    """Compare two blobs of IR as best we can.
    FuncDecls, FuncIRs, and ClassIRs are compared by fullname to avoid
    infinite recursion.
    (More detailed comparisons should be done manually.)
    Types and signatures are compared using mypyc.sametype.
    Containers are compared recursively.
    Anything else is compared with ==.
    The `trail` argument is used in error messages.
    """
    assert type(x) is type(y), (f"Type mismatch at {trail}", type(x), type(y))
    if isinstance(x, (FuncDecl, FuncIR, ClassIR)):
        assert x.fullname == y.fullname, f"Name mismatch at {trail}"
    elif isinstance(x, dict):
        assert len(x.keys()) == len(y.keys()), f"Keys mismatch at {trail}"
        for (xk, xv), (yk, yv) in zip(x.items(), y.items()):
            METHOD_NAME(xk, yk, trail + ("keys",))
            METHOD_NAME(xv, yv, trail + (xk,))
    elif isinstance(x, dict):
        assert x.keys() == y.keys(), f"Keys mismatch at {trail}"
        for k in x.keys():
            METHOD_NAME(x[k], y[k], trail + (k,))
    elif isinstance(x, Iterable) and not isinstance(x, (str, set)):
        # Special case iterables to generate better assert error messages.
        # We can't use this for sets since the ordering is unpredictable,
        # and strings should be treated as atomic values.
        for i, (xv, yv) in enumerate(zip(x, y)):
            METHOD_NAME(xv, yv, trail + (i,))
    elif isinstance(x, RType):
        assert is_same_type(x, y), f"RType mismatch at {trail}"
    elif isinstance(x, FuncSignature):
        assert is_same_signature(x, y), f"Signature mismatch at {trail}"
    else:
        assert x == y, f"Value mismatch at {trail}"