ssa: canonicalize basic aliases in generic symbols - #2367
Conversation
There was a problem hiding this comment.
Review: canonicalize generic basic aliases
The change is correct and well-targeted. typeArgString now maps byte->uint8 and rune->int32 so that generic ABI symbol names are stable regardless of which spelling go/types cached first for an instantiation. I verified against Go 1.25 go/types that this is real work, not dead code: types.Universe.Lookup("byte").Type() returns a *types.Basic whose .String() is "byte" (with Kind()==Byte), so without the new switch a byte type-argument would render as "byte" and diverge from a uint8 instantiation. The rationale comment is accurate.
Reviewed across correctness, performance, security, and documentation:
- Performance — no concern; this is compile-time symbol generation, one
Kind()read plus a small switch. - Security — no concern; operates on trusted
go/typesdata with fixed canonical strings, and reduces (never introduces) symbol collisions sincebyte/uint8are the identical type. - Tests —
TestTypeArgs_CanonicalizesBasicAliasesand the updatedin.goCHECK lines ([0]byte->[0]uint8) match the new behavior.
Only minor, non-blocking notes below.
| return types.Typ[types.Uint8].String() | ||
| case types.Rune: | ||
| return types.Typ[types.Int32].String() | ||
| } |
There was a problem hiding this comment.
Minor: this canonicalization now lives in four places in the package (typeArgString here, BasicName below at ~L311, and Str/reflectTypeArgBaseString in type.go), split between Kind()-based and Name()-based mappings. Consider a small shared helper (e.g. canonicalBasicName(*types.Basic) string) to keep the byte->uint8 / rune->int32 rule from drifting. Also, for consistency with BasicName you could return the literals "uint8"/"int32" instead of types.Typ[...].String(). Non-blocking.
| case *types.Basic: | ||
| // byte and rune are aliases for uint8 and int32. go/types may cache a | ||
| // generic instance using either spelling, so ABI symbols must not | ||
| // depend on which spelling was instantiated first. |
There was a problem hiding this comment.
Nit: the switch is keyed on Kind(), and types.Byte/types.Rune are the same constants as types.Uint8/types.Int32. So these cases also match plain uint8/int32 arguments (harmless no-op here), and a future case types.Uint8:/case types.Int32: would be a duplicate-case compile error. A one-line note that the branch is keyed on kind (which can't distinguish the alias from the target) would prevent that foot-gun. Non-blocking.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
LLGo baseline benchmarks
Program measurements
Core language and compiler benchmarks
Compared with |
Summary
Extracted from #2234.
Canonicalizes basic aliases
byte(uint8) andrune(int32) in generic ABI symbols (ssa/abi.typeArgString).go/typesmay cache a generic instance using either spelling depending on which spelling was instantiated first. Canonicalizingbyteandrunetouint8andint32ensures that generic ABI symbols do not depend on type instantiation order.Validation
go test -v ./ssa/abi -count=1go test -v ./cl -run '^TestRunAndTestFromTestgo/tpnamed$' -count=1go test -v ./cl -run '^TestRunAndTestFromTestgo$' -count=1