Summary
When SanityCheckFunctions mode is enabled in golang.org/x/tools/go/ssa, it panics on code containing a nested shift expression where the inner shift has an untyped constant as the left operand and a runtime value as the right operand.
Reproduction
package main
func nestedShift(bytes []byte) uint32 {
value := uint32(1)
return value << (1 << bytes[3]) // SSA sanity check fails here
}
Error Output
Error: function main.nestedShift, block 0: instruction has 'untyped' result: t2 = 1:untyped int << t1 : untyped int
Error: function main.nestedShift, block 0: operand #0 of convert uint <- untyped int (t2) is untyped: untyped int
panic: SanityCheck failed
Analysis
The issue arises because:
go/types returns untyped int as the type for the expression 1 << bytes[3] in its TypeAndValue
go/ssa builder uses this type directly when constructing the BinOp instruction (via emitArith)
- The sanity checker explicitly disallows untyped types in instruction results (
sanity.go:202)
According to the Go spec:
"If the left operand of a non-constant shift expression is an untyped constant, it is first implicitly converted to the type it would assume if the shift expression were replaced by its left operand alone."
This means 1 << bytes[3] should have type int (the default type for untyped int), not untyped int.
Verification
Using go/types to inspect the types:
BinaryExpr: value << (1 << bytes[3]) => Type: uint32 # Correct
BinaryExpr: 1 << bytes[3] => Type: untyped int # Should be int per spec
Expected Behavior
Either:
go/ssa should handle this case in emitArith by converting untyped left operands to their default type before creating the BinOp, OR
- The sanity check should not consider this an error
Environment
- Go version: 1.24.11
- golang.org/x/tools: v0.40.0
Workaround
We are currently working around this in our project (llgo) by fixing the TypesInfo before calling prog.CreatePackage.
Summary
When
SanityCheckFunctionsmode is enabled ingolang.org/x/tools/go/ssa, it panics on code containing a nested shift expression where the inner shift has an untyped constant as the left operand and a runtime value as the right operand.Reproduction
Error Output
Analysis
The issue arises because:
go/typesreturnsuntyped intas the type for the expression1 << bytes[3]in itsTypeAndValuego/ssabuilder uses this type directly when constructing theBinOpinstruction (viaemitArith)sanity.go:202)According to the Go spec:
This means
1 << bytes[3]should have typeint(the default type for untyped int), notuntyped int.Verification
Using
go/typesto inspect the types:Expected Behavior
Either:
go/ssashould handle this case inemitArithby converting untyped left operands to their default type before creating theBinOp, OREnvironment
Workaround
We are currently working around this in our project (llgo) by fixing the
TypesInfobefore callingprog.CreatePackage.