go/pointer: fix constraint gen for *ssa.Next
The existing implementation has an error coordinating the types of the
expressions 'k, v' in a range statement over a map 'k, v := range m'.
In particular, 'k', 'v' may have types other than the key and value
types of m and the difference between these types affects the constraint
generation of *ssa.Next (used to model iterating over maps). This fix
simply calculates the offset of the destination (the *types.Tuple of
*ssa.Next) independently from the offset of the source (the map 'm' in
the example above).
Fixes golang/go#45735
Change-Id: Ib11846a94c23f8815fa502bdb7f80ef0803bf182
Reviewed-on: https://go-review.googlesource.com/c/tools/+/335889
Reviewed-by: Tim King <taking@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Trust: Tim King <taking@google.com>
Run-TryBot: Tim King <taking@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
diff --git a/go/pointer/gen.go b/go/pointer/gen.go
index 5d2d621..36e1aef 100644
--- a/go/pointer/gen.go
+++ b/go/pointer/gen.go
@@ -1055,16 +1055,42 @@
// Do nothing. Next{Iter: *ssa.Range} handles this case.
case *ssa.Next:
- if !instr.IsString { // map
- // Assumes that Next is always directly applied to a Range result.
+ if !instr.IsString {
+ // Assumes that Next is always directly applied to a Range result
+ // for a map.
+
+ // Next results in a destination tuple (ok, dk, dv).
+ // Recall a map is modeled as type *M where M = struct{sk K; sv V}.
+ // Next copies from a src map struct{sk K; sv V} to a dst tuple (ok, dk, dv)
+ //
+ // When keys or value is a blank identifier in a range statement, e.g
+ // for _, v := range m { ... }
+ // or
+ // for _, _ = range m { ... }
+ // we skip copying from sk or dk as there is no use. dk and dv will have
+ // Invalid types if they are blank identifiers. This means that the
+ // size( (ok, dk, dv) ) may differ from 1 + size(struct{sk K; sv V}).
+ //
+ // We encode Next using one load of size sz from an offset in src osrc to an
+ // offset in dst odst. There are 4 cases to consider:
+ // odst | osrc | sz
+ // k, v | 1 | 0 | size(sk) + size(sv)
+ // k, _ | 1 | 0 | size(sk)
+ // _, v | 1+size(dk) | size(sk) | size(sv)
+ // _, _ | 1+size(dk) | size(sk) | 0
+
+ // get the source key and value size. Note the source types
+ // may be different than the 3-tuple types, but if this is the
+ // case then the source is assignable to the destination.
theMap := instr.Iter.(*ssa.Range).X
tMap := theMap.Type().Underlying().(*types.Map)
- ksize := a.sizeof(tMap.Key())
- vsize := a.sizeof(tMap.Elem())
+ sksize := a.sizeof(tMap.Key())
+ svsize := a.sizeof(tMap.Elem())
- // The k/v components of the Next tuple may each be invalid.
+ // get the key size of the destination tuple.
tTuple := instr.Type().(*types.Tuple)
+ dksize := a.sizeof(tTuple.At(1).Type())
// Load from the map's (k,v) into the tuple's (ok, k, v).
osrc := uint32(0) // offset within map object
@@ -1073,15 +1099,15 @@
// Is key valid?
if tTuple.At(1).Type() != tInvalid {
- sz += ksize
+ sz += sksize
} else {
- odst += ksize
- osrc += ksize
+ odst += dksize
+ osrc += sksize
}
// Is value valid?
if tTuple.At(2).Type() != tInvalid {
- sz += vsize
+ sz += svsize
}
a.genLoad(cgn, a.valueNode(instr)+nodeid(odst), theMap, osrc, sz)