Skip to content

checker, transform: fix generic regressions exposed by the strconv Numeric constraint - #29444

Merged
medvednikov merged 2 commits into
vlang:masterfrom
medvednikov:fix/constraint-walk-regressions
Oct 4, 2026
Merged

medvednikov merged 2 commits into
vlang:masterfrom
medvednikov:fix/constraint-walk-regressions

Conversation

@medvednikov

Copy link
Copy Markdown
Member

Since #29396 (strconv: use generic constraints for format_thousands), strconv declares the Numeric constraint, and every program imports strconv through builtin. That exposed two existing compiler bugs in every program:

  1. x is immutable in generic functions. program_declares_constraints() is now always true, so the generic constraint walk runs over every generic body. The walk binds locals without their mut, so resolving a chained call such as sb.str().trim_space() on a mut sb reported sb as immutable. This broke 19 test files in vlib/encoding/cbor, vlib/encoding/cose and vlib/encoding/cwt (generic.v:122:21: error: ksub is immutable), and any user generic function with the same pattern.
    Fix: the walk now drops what resolving a call reports. It only reports what the constraints forbid; the body is still fully checked where it is instantiated.

  2. C error for smartcasts on a generic sum type over a pointer (vlib/v/tests/generics/generic_sumtype_str_test.v: no member named 'strconv__andint' in 'struct Maybe_ptrint'). m is T on a Maybe[&int] did not find the &int variant from the trimmed int, and fell back to the first module sum type with an int variant, which is now strconv.Numeric.
    Fix: when the subject is a sum type, never take another sum type's variant.

Both regression tests declare their own constraint and sum type, so they keep guarding these bugs whatever strconv declares.

…int walk

Since strconv declares `Numeric` (vlang#29396), every program declares a
constraint, so the constraint walk runs over the body of every generic
function. Resolving a chained call there, `sb.str().trim_space()`, checks
the receiver call `sb.str()`, where the walk binds `sb` without its `mut`,
and that check reported `sb` as immutable.

The walk now drops what resolving a call reports: it reports what the
constraints forbid, and the body is checked where it is instantiated. This
covers every way a body binds a local (declarations, multi-returns,
`if mut x :=` guards, `for mut x in`, or-blocks), and `shared` locals in
`lock` blocks, which the walk does not track either.
… type

`m is T` on a `Maybe[&int]` did not find the `&int` variant from the
trimmed pattern `int`, and fell back to the first module sum type with an
`int` variant, `strconv.Numeric` since vlang#29396, which generated
`m.strconv__andint` for the smartcast.

When the subject is a sum type, the pattern is a variant of that sum type:
fall back to it rather than to any other sum type.
@medvednikov
medvednikov merged commit 8ab3242 into vlang:master Oct 4, 2026
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.

1 participant