Skip to content

Commit a762c82

Browse files
committed
go/ssa: add MultiConvert instruction
Adds a new MultiConvert instruction. MultiConvert instructions are a catch all for conversions involving a typeparameter that would result in multiple different types of conversion instruction [sequences]. Updates golang/go#56849 Change-Id: I6c2f53ccef1b933406096d6ca2867f1007a13bd3 Reviewed-on: https://go-review.googlesource.com/c/tools/+/457436 gopls-CI: kokoro <[email protected]> Run-TryBot: Tim King <[email protected]> Reviewed-by: Alan Donovan <[email protected]> Reviewed-by: Zvonimir Pavlinovic <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent f124b50 commit a762c82

File tree

9 files changed

+298
-112
lines changed

9 files changed

+298
-112
lines changed

go/callgraph/vta/graph.go

+68
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"golang.org/x/tools/go/callgraph"
1313
"golang.org/x/tools/go/ssa"
1414
"golang.org/x/tools/go/types/typeutil"
15+
"golang.org/x/tools/internal/typeparams"
1516
)
1617

1718
// node interface for VTA nodes.
@@ -375,6 +376,8 @@ func (b *builder) instr(instr ssa.Instruction) {
375376
// SliceToArrayPointer: t1 = slice to array pointer *[4]T <- []T (t0)
376377
// No interesting flow as sliceArrayElem(t1) == sliceArrayElem(t0).
377378
return
379+
case *ssa.MultiConvert:
380+
b.multiconvert(i)
378381
default:
379382
panic(fmt.Sprintf("unsupported instruction %v\n", instr))
380383
}
@@ -655,6 +658,71 @@ func addReturnFlows(b *builder, r *ssa.Return, site ssa.Value) {
655658
}
656659
}
657660

661+
func (b *builder) multiconvert(c *ssa.MultiConvert) {
662+
// TODO(zpavlinovic): decide what to do on MultiConvert long term.
663+
// TODO(zpavlinovic): add unit tests.
664+
typeSetOf := func(typ types.Type) []*typeparams.Term {
665+
// This is a adaptation of x/exp/typeparams.NormalTerms which x/tools cannot depend on.
666+
var terms []*typeparams.Term
667+
var err error
668+
switch typ := typ.(type) {
669+
case *typeparams.TypeParam:
670+
terms, err = typeparams.StructuralTerms(typ)
671+
case *typeparams.Union:
672+
terms, err = typeparams.UnionTermSet(typ)
673+
case *types.Interface:
674+
terms, err = typeparams.InterfaceTermSet(typ)
675+
default:
676+
// Common case.
677+
// Specializing the len=1 case to avoid a slice
678+
// had no measurable space/time benefit.
679+
terms = []*typeparams.Term{typeparams.NewTerm(false, typ)}
680+
}
681+
682+
if err != nil {
683+
return nil
684+
}
685+
return terms
686+
}
687+
// isValuePreserving returns true if a conversion from ut_src to
688+
// ut_dst is value-preserving, i.e. just a change of type.
689+
// Precondition: neither argument is a named type.
690+
isValuePreserving := func(ut_src, ut_dst types.Type) bool {
691+
// Identical underlying types?
692+
if types.IdenticalIgnoreTags(ut_dst, ut_src) {
693+
return true
694+
}
695+
696+
switch ut_dst.(type) {
697+
case *types.Chan:
698+
// Conversion between channel types?
699+
_, ok := ut_src.(*types.Chan)
700+
return ok
701+
702+
case *types.Pointer:
703+
// Conversion between pointers with identical base types?
704+
_, ok := ut_src.(*types.Pointer)
705+
return ok
706+
}
707+
return false
708+
}
709+
dst_terms := typeSetOf(c.Type())
710+
src_terms := typeSetOf(c.X.Type())
711+
for _, s := range src_terms {
712+
us := s.Type().Underlying()
713+
for _, d := range dst_terms {
714+
ud := d.Type().Underlying()
715+
if isValuePreserving(us, ud) {
716+
// This is equivalent to a ChangeType.
717+
b.addInFlowAliasEdges(b.nodeFromVal(c), b.nodeFromVal(c.X))
718+
return
719+
}
720+
// This is equivalent to either: SliceToArrayPointer,,
721+
// SliceToArrayPointer+Deref, Size 0 Array constant, or a Convert.
722+
}
723+
}
724+
}
725+
658726
// addInFlowEdge adds s -> d to g if d is node that can have an inflow, i.e., a node
659727
// that represents an interface or an unresolved function value. Otherwise, there
660728
// is no interesting type flow so the edge is omitted.

go/ssa/builder_go120_test.go

+51-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,55 @@ func TestBuildPackageGo120(t *testing.T) {
2929
{"slice to zero length array type parameter", "package p; var s []byte; func f[T ~[0]byte]() { tmp := (T)(s); var z T; _ = tmp == z}", nil},
3030
{"slice to non-zero length array type parameter", "package p; var s []byte; func h[T ~[1]byte | [4]byte]() { tmp := T(s); var z T; _ = tmp == z}", nil},
3131
{"slice to maybe-zero length array type parameter", "package p; var s []byte; func g[T ~[0]byte | [4]byte]() { tmp := T(s); var z T; _ = tmp == z}", nil},
32+
{
33+
"rune sequence to sequence cast patterns", `
34+
package p
35+
// Each of fXX functions describes a 1.20 legal cast between sequences of runes
36+
// as []rune, pointers to rune arrays, rune arrays, or strings.
37+
//
38+
// Comments listed given the current emitted instructions [approximately].
39+
// If multiple conversions are needed, these are seperated by |.
40+
// rune was selected as it leads to string casts (byte is similar).
41+
// The length 2 is not significant.
42+
// Multiple array lengths may occur in a cast in practice (including 0).
43+
func f00[S string, D string](s S) { _ = D(s) } // ChangeType
44+
func f01[S string, D []rune](s S) { _ = D(s) } // Convert
45+
func f02[S string, D []rune | string](s S) { _ = D(s) } // ChangeType | Convert
46+
func f03[S [2]rune, D [2]rune](s S) { _ = D(s) } // ChangeType
47+
func f04[S *[2]rune, D *[2]rune](s S) { _ = D(s) } // ChangeType
48+
func f05[S []rune, D string](s S) { _ = D(s) } // Convert
49+
func f06[S []rune, D [2]rune](s S) { _ = D(s) } // SliceToArrayPointer; Deref
50+
func f07[S []rune, D [2]rune | string](s S) { _ = D(s) } // SliceToArrayPointer; Deref | Convert
51+
func f08[S []rune, D *[2]rune](s S) { _ = D(s) } // SliceToArrayPointer
52+
func f09[S []rune, D *[2]rune | string](s S) { _ = D(s) } // SliceToArrayPointer; Deref | Convert
53+
func f10[S []rune, D *[2]rune | [2]rune](s S) { _ = D(s) } // SliceToArrayPointer | SliceToArrayPointer; Deref
54+
func f11[S []rune, D *[2]rune | [2]rune | string](s S) { _ = D(s) } // SliceToArrayPointer | SliceToArrayPointer; Deref | Convert
55+
func f12[S []rune, D []rune](s S) { _ = D(s) } // ChangeType
56+
func f13[S []rune, D []rune | string](s S) { _ = D(s) } // Convert | ChangeType
57+
func f14[S []rune, D []rune | [2]rune](s S) { _ = D(s) } // ChangeType | SliceToArrayPointer; Deref
58+
func f15[S []rune, D []rune | [2]rune | string](s S) { _ = D(s) } // ChangeType | SliceToArrayPointer; Deref | Convert
59+
func f16[S []rune, D []rune | *[2]rune](s S) { _ = D(s) } // ChangeType | SliceToArrayPointer
60+
func f17[S []rune, D []rune | *[2]rune | string](s S) { _ = D(s) } // ChangeType | SliceToArrayPointer | Convert
61+
func f18[S []rune, D []rune | *[2]rune | [2]rune](s S) { _ = D(s) } // ChangeType | SliceToArrayPointer | SliceToArrayPointer; Deref
62+
func f19[S []rune, D []rune | *[2]rune | [2]rune | string](s S) { _ = D(s) } // ChangeType | SliceToArrayPointer | SliceToArrayPointer; Deref | Convert
63+
func f20[S []rune | string, D string](s S) { _ = D(s) } // Convert | ChangeType
64+
func f21[S []rune | string, D []rune](s S) { _ = D(s) } // Convert | ChangeType
65+
func f22[S []rune | string, D []rune | string](s S) { _ = D(s) } // ChangeType | Convert | Convert | ChangeType
66+
func f23[S []rune | [2]rune, D [2]rune](s S) { _ = D(s) } // SliceToArrayPointer; Deref | ChangeType
67+
func f24[S []rune | *[2]rune, D *[2]rune](s S) { _ = D(s) } // SliceToArrayPointer | ChangeType
68+
`, nil,
69+
},
70+
{
71+
"matching named and underlying types", `
72+
package p
73+
type a string
74+
type b string
75+
func g0[S []rune | a | b, D []rune | a | b](s S) { _ = D(s) }
76+
func g1[S []rune | ~string, D []rune | a | b](s S) { _ = D(s) }
77+
func g2[S []rune | a | b, D []rune | ~string](s S) { _ = D(s) }
78+
func g3[S []rune | ~string, D []rune |~string](s S) { _ = D(s) }
79+
`, nil,
80+
},
3281
}
3382

3483
for _, tc := range tests {
@@ -44,7 +93,8 @@ func TestBuildPackageGo120(t *testing.T) {
4493

4594
pkg := types.NewPackage("p", "")
4695
conf := &types.Config{Importer: tc.importer}
47-
if _, _, err := ssautil.BuildPackage(conf, fset, pkg, files, ssa.SanityCheckFunctions); err != nil {
96+
_, _, err = ssautil.BuildPackage(conf, fset, pkg, files, ssa.SanityCheckFunctions)
97+
if err != nil {
4898
t.Errorf("unexpected error: %v", err)
4999
}
50100
})

go/ssa/doc.go

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
// *FieldAddr ✔ ✔
6767
// *FreeVar ✔
6868
// *Function ✔ ✔ (func)
69+
// *GenericConvert ✔ ✔
6970
// *Global ✔ ✔ (var)
7071
// *Go ✔
7172
// *If ✔

0 commit comments

Comments
 (0)