Skip to content

Commit eb8198d

Browse files
committed
cmd/compile: deal with constructed types that have shapes in them
We convert type args to shape types inside instantiations. If an instantiation constructs a compound type based on that shape type and uses that as a type arg to another generic function being called, then we have a type arg with a shape type embedded inside of it. In that case, we need to substitute out those embedded shape types with their underlying type. If we don't do this, we may create extra unneeded shape types that have these other shape types embedded in them. This may lead to generating extra shape instantiations, and a mismatch between the instantiations that we used in generating dictionaries and the instantations that are actually called. Updates #51303 Change-Id: Ieef894a5fac176cfd1415f95926086277ad09759 Reviewed-on: https://go-review.googlesource.com/c/go/+/387674 Reviewed-by: Keith Randall <[email protected]> Trust: Dan Scales <[email protected]> Run-TryBot: Dan Scales <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent b33592d commit eb8198d

File tree

3 files changed

+155
-0
lines changed

3 files changed

+155
-0
lines changed

src/cmd/compile/internal/typecheck/subr.go

+86
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,68 @@ func genericTypeName(sym *types.Sym) string {
14241424
return sym.Name[0:strings.Index(sym.Name, "[")]
14251425
}
14261426

1427+
// getShapes appends the list of the shape types that are used within type t to
1428+
// listp. The type traversal is simplified for two reasons: (1) we can always stop a
1429+
// type traversal when t.HasShape() is false; and (2) shape types can't appear inside
1430+
// a named type, except for the type args of a generic type. So, the traversal will
1431+
// always stop before we have to deal with recursive types.
1432+
func getShapes(t *types.Type, listp *[]*types.Type) {
1433+
if !t.HasShape() {
1434+
return
1435+
}
1436+
if t.IsShape() {
1437+
*listp = append(*listp, t)
1438+
return
1439+
}
1440+
1441+
if t.Sym() != nil {
1442+
// A named type can't have shapes in it, except for type args of a
1443+
// generic type. We will have to deal with this differently once we
1444+
// alloc local types in generic functions (#47631).
1445+
for _, rparam := range t.RParams() {
1446+
getShapes(rparam, listp)
1447+
}
1448+
return
1449+
}
1450+
1451+
switch t.Kind() {
1452+
case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN:
1453+
getShapes(t.Elem(), listp)
1454+
1455+
case types.TSTRUCT:
1456+
for _, f := range t.FieldSlice() {
1457+
getShapes(f.Type, listp)
1458+
}
1459+
1460+
case types.TFUNC:
1461+
for _, f := range t.Recvs().FieldSlice() {
1462+
getShapes(f.Type, listp)
1463+
}
1464+
for _, f := range t.Params().FieldSlice() {
1465+
getShapes(f.Type, listp)
1466+
}
1467+
for _, f := range t.Results().FieldSlice() {
1468+
getShapes(f.Type, listp)
1469+
}
1470+
for _, f := range t.TParams().FieldSlice() {
1471+
getShapes(f.Type, listp)
1472+
}
1473+
1474+
case types.TINTER:
1475+
for _, f := range t.Methods().Slice() {
1476+
getShapes(f.Type, listp)
1477+
}
1478+
1479+
case types.TMAP:
1480+
getShapes(t.Key(), listp)
1481+
getShapes(t.Elem(), listp)
1482+
1483+
default:
1484+
panic(fmt.Sprintf("Bad type in getShapes: %v", t.Kind()))
1485+
}
1486+
1487+
}
1488+
14271489
// Shapify takes a concrete type and a type param index, and returns a GCshape type that can
14281490
// be used in place of the input type and still generate identical code.
14291491
// No methods are added - all methods calls directly on a shape should
@@ -1442,6 +1504,30 @@ func genericTypeName(sym *types.Sym) string {
14421504
// instantiation.
14431505
func Shapify(t *types.Type, index int, tparam *types.Type) *types.Type {
14441506
assert(!t.IsShape())
1507+
if t.HasShape() {
1508+
// We are sometimes dealing with types from a shape instantiation
1509+
// that were constructed from existing shape types, so t may
1510+
// sometimes have shape types inside it. In that case, we find all
1511+
// those shape types with getShapes() and replace them with their
1512+
// underlying type.
1513+
//
1514+
// If we don't do this, we may create extra unneeded shape types that
1515+
// have these other shape types embedded in them. This may lead to
1516+
// generating extra shape instantiations, and a mismatch between the
1517+
// instantiations that we used in generating dictionaries and the
1518+
// instantations that are actually called. (#51303).
1519+
list := []*types.Type{}
1520+
getShapes(t, &list)
1521+
list2 := make([]*types.Type, len(list))
1522+
for i, shape := range list {
1523+
list2[i] = shape.Underlying()
1524+
}
1525+
ts := Tsubster{
1526+
Tparams: list,
1527+
Targs: list2,
1528+
}
1529+
t = ts.Typ(t)
1530+
}
14451531
// Map all types with the same underlying type to the same shape.
14461532
u := t.Underlying()
14471533

test/typeparam/issue51303.go

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// run -gcflags=-G=3
2+
3+
// Copyright 2022 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package main
8+
9+
import (
10+
"fmt"
11+
)
12+
13+
func main() {
14+
x := [][]int{{1}}
15+
y := [][]int{{2, 3}}
16+
IntersectSS(x, y)
17+
}
18+
19+
type list[E any] interface {
20+
~[]E
21+
Equal(x, y E) bool
22+
}
23+
24+
// ss is a set of sets
25+
type ss[E comparable, T []E] []T
26+
27+
func (ss[E, T]) Equal(a, b T) bool {
28+
return SetEq(a, b)
29+
}
30+
31+
func IntersectSS[E comparable](x, y [][]E) [][]E {
32+
return IntersectT[[]E, ss[E, []E]](ss[E, []E](x), ss[E, []E](y))
33+
}
34+
35+
func IntersectT[E any, L list[E]](x, y L) L {
36+
var z L
37+
outer:
38+
for _, xe := range x {
39+
fmt.Println("xe", xe)
40+
for _, ye := range y {
41+
fmt.Println("ye", ye)
42+
fmt.Println("x", x)
43+
if x.Equal(xe, ye) {
44+
fmt.Println("appending")
45+
z = append(z, xe)
46+
continue outer
47+
}
48+
}
49+
}
50+
return z
51+
}
52+
53+
func SetEq[S []E, E comparable](x, y S) bool {
54+
fmt.Println("SetEq", x, y)
55+
outer:
56+
for _, xe := range x {
57+
for _, ye := range y {
58+
if xe == ye {
59+
continue outer
60+
}
61+
}
62+
return false // xs wasn't found in y
63+
}
64+
return true
65+
}

test/typeparam/issue51303.out

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
xe [1]
2+
ye [2 3]
3+
x [[1]]
4+
SetEq [1] [2 3]

0 commit comments

Comments
 (0)