Go Generic Inference as LeetCode Problems¶
Go's generic type inference is hard to read if I start from unify.go, infer.go, and call.go directly. A simpler way is to treat it like a small set of LeetCode problems: each problem has a clear input, a target behavior, and a check for whether the model is good enough.
Idea from Yuchen. Text written by AI.
This is not a full explanation of the compiler. It is only a compact scaffold for reading the source later.
Problem 1: Match Two Type Trees¶
Given two type trees:
- template: may contain type parameters such as
T - concrete: contains real types such as
int,[]string, or*User
Implement unify(template, concrete).
Rules:
- If both nodes have the same outer shape, compare their children.
- If the template node is a type parameter, record the answer.
- If the shapes differ, fail.
Example:
The core idea is a lock-step DFS: compare the outer shell first, then recurse into the inside.
Source anchor: (*unifier).nify in unify.go.
Problem 2: Keep Repeated Variables Consistent¶
Now T can appear more than once.
Example:
The first argument says T = int, while the second says T = string. The solver must reject that.
A more useful case:
In the compiler this is not just map[TypeParam]Type. It uses handles, so two type parameters can point to the same inferred state after join.
Source anchor: newUnifier, join, setHandle, at, and set in unify.go.
Problem 3: Support Loose Top-Level Matching¶
Named types make exact matching too strict.
Example:
In assignment mode, MyInt may match int at the top level, because the underlying type is int. But this looseness does not simply apply everywhere: inside container elements, the compiler switches back toward exact matching.
LeetCode version:
- Try exact shape matching.
- If the current mode allows assignment, compare the underlying type.
- When recursing into element types, tighten the mode again.
Source anchor: unifyMode and the assign handling in unify.go.
Problem 4: Solve Inner Generic Calls Without Polluting Outer State¶
Consider:
While solving G, the solver should infer U = T. It should not mutate the outer T state by accident.
LeetCode version:
- The inner solver owns only
U. - Outer
Tis treated as a symbolic type. - The result is a mapping like
U = T. - Later substitution can turn it into
U = intif outerT = int.
For recursive generic calls, the compiler also renames type parameters so the inner call has fresh identities.
Source anchor: asBoundTypeParam in unify.go and renameTParams in infer.go.
Problem 5: Repeat Until No More Types Can Be Found¶
Some type parameters are not solved by arguments directly.
Example:
If x is int, then:
LeetCode version:
- Start with direct answers from call arguments.
- Scan unsolved variables.
- Substitute known answers into constraints.
- Repeat while progress is made.
- Stop when all variables are solved or no new answer appears.
This is the fixed-point part of inference.
Source anchor: (*Checker).infer in infer.go.
Summary¶
The useful mental model is small:
- DFS matches type structure.
- Handles keep repeated type parameters consistent.
- Assignment mode allows limited top-level flexibility.
- Inner generic calls solve against their own variables.
- Constraint inference repeats until it reaches a fixed point.
That is enough scaffolding before reading the real compiler code.