]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] go/types: recursive substitution must terminate (bug fix)
authorRob Findley <rfindley@google.com>
Fri, 16 Jul 2021 18:30:15 +0000 (14:30 -0400)
committerRobert Findley <rfindley@google.com>
Mon, 19 Jul 2021 15:49:58 +0000 (15:49 +0000)
This is a port of CL 333383 to go/types.

Change-Id: I7ff68116cbe63337dbcc834c473a2a5588274e36
Reviewed-on: https://go-review.googlesource.com/c/go/+/335115
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/go/types/api_test.go
src/go/types/subst.go

index e6c209dda0d8e2bf074e051555901ec9e2964c41..9ca24db1de6eb5c299908d7daf3392709f193d6b 100644 (file)
@@ -1817,3 +1817,26 @@ func f(x T) T { return foo.F(x) }
                }
        }
 }
+
+func TestInstantiate(t *testing.T) {
+       // eventually we like more tests but this is a start
+       const src = genericPkg + "p; type T[P any] *T[P]"
+       pkg, err := pkgFor(".", src, nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       // type T should have one type parameter
+       T := pkg.Scope().Lookup("T").Type().(*Named)
+       if n := len(T.TParams()); n != 1 {
+               t.Fatalf("expected 1 type parameter; found %d", n)
+       }
+
+       // instantiation should succeed (no endless recursion)
+       res := Instantiate(token.NoPos, T, []Type{Typ[Int]})
+
+       // instantiated type should point to itself
+       if res.Underlying().(*Pointer).Elem() != res {
+               t.Fatalf("unexpected result type: %s", res)
+       }
+}
index 4809b8c47a3f0c0e41de28acf3ee339f775755b6..64146be27eb1c161a23ffb3a95f4b3e02cc903e7 100644 (file)
@@ -237,15 +237,27 @@ func (check *Checker) subst(pos token.Pos, typ Type, smap *substMap) Type {
        }
 
        // general case
-       subst := subster{check, pos, make(map[Type]Type), smap}
+       var subst subster
+       subst.pos = pos
+       subst.smap = smap
+       if check != nil {
+               subst.check = check
+               subst.typMap = check.typMap
+       } else {
+               // If we don't have a *Checker and its global type map,
+               // use a local version. Besides avoiding duplicate work,
+               // the type map prevents infinite recursive substitution
+               // for recursive types (example: type T[P any] *T[P]).
+               subst.typMap = make(map[string]*Named)
+       }
        return subst.typ(typ)
 }
 
 type subster struct {
-       check *Checker
-       pos   token.Pos
-       cache map[Type]Type
-       smap  *substMap
+       pos    token.Pos
+       smap   *substMap
+       check  *Checker // nil if called via Instantiate
+       typMap map[string]*Named
 }
 
 func (subst *subster) typ(typ Type) Type {
@@ -390,22 +402,16 @@ func (subst *subster) typ(typ Type) Type {
                // before creating a new named type, check if we have this one already
                h := instantiatedHash(t, newTargs)
                dump(">>> new type hash: %s", h)
-               if subst.check != nil {
-                       if named, found := subst.check.typMap[h]; found {
-                               dump(">>> found %s", named)
-                               subst.cache[t] = named
-                               return named
-                       }
+               if named, found := subst.typMap[h]; found {
+                       dump(">>> found %s", named)
+                       return named
                }
 
-               // create a new named type and populate caches to avoid endless recursion
+               // create a new named type and populate typMap to avoid endless recursion
                tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil)
                named := subst.check.newNamed(tname, t, t.Underlying(), t.TParams(), t.methods) // method signatures are updated lazily
                named.targs = newTargs
-               if subst.check != nil {
-                       subst.check.typMap[h] = named
-               }
-               subst.cache[t] = named
+               subst.typMap[h] = named
 
                // do the substitution
                dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, newTargs)