mirror of
https://github.com/golang/go
synced 2024-11-23 20:50:04 -07:00
go/types, types2: always rename type parameters during inference
Type inference uses a trick of "renaming" type parameters in the type parameter list to avoid cycles during unification. This separates the identity of type parameters from type arguments. When this trick was introduced in CL 385494, we restricted its application to scenarios where inference is truly self-recursive: the type parameter list being inferred was the same as the type parameter list of the outer function declaration. Unfortunately, the heuristic used to determine self-recursiveness was flawed: type-checking function literals clobbers the type-checker environment, losing information about the outer signature. We could fix this by introducing yet more state into the type-checker (e.g. a 'declSig' field that would hold the signature of the active function declaration), but it is simpler to just avoid this optimization and always perform type parameter renaming. We can always optimize later. This CL removes the check for true self-recursion, always performing the renaming. Fixes golang/go#57155 Change-Id: I34c7617005c1f0ccfe2192da0e5ed104be6b92c9 Reviewed-on: https://go-review.googlesource.com/c/go/+/456236 Run-TryBot: Robert Findley <rfindley@google.com> Reviewed-by: Robert Griesemer <gri@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
8247b9f17a
commit
e738a2f19b
@ -89,34 +89,22 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
|
|||||||
// f(p)
|
// f(p)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// We can turn the first example into the second example by renaming type
|
// We turn the first example into the second example by renaming type
|
||||||
// parameters in the original signature to give them a new identity. As an
|
// parameters in the original signature to give them a new identity.
|
||||||
// optimization, we do this only for self-recursive calls.
|
tparams2 := make([]*TypeParam, len(tparams))
|
||||||
|
for i, tparam := range tparams {
|
||||||
// We can detect if we are in a self-recursive call by comparing the
|
tname := NewTypeName(tparam.Obj().Pos(), tparam.Obj().Pkg(), tparam.Obj().Name(), nil)
|
||||||
// identity of the first type parameter in the current function with the
|
tparams2[i] = NewTypeParam(tname, nil)
|
||||||
// first type parameter in tparams. This works because type parameters are
|
tparams2[i].index = tparam.index // == i
|
||||||
// unique to their type parameter list.
|
|
||||||
selfRecursive := check.sig != nil && check.sig.tparams.Len() > 0 && tparams[0] == check.sig.tparams.At(0)
|
|
||||||
|
|
||||||
if selfRecursive {
|
|
||||||
// In self-recursive inference, rename the type parameters with new type
|
|
||||||
// parameters that are the same but for their pointer identity.
|
|
||||||
tparams2 := make([]*TypeParam, len(tparams))
|
|
||||||
for i, tparam := range tparams {
|
|
||||||
tname := NewTypeName(tparam.Obj().Pos(), tparam.Obj().Pkg(), tparam.Obj().Name(), nil)
|
|
||||||
tparams2[i] = NewTypeParam(tname, nil)
|
|
||||||
tparams2[i].index = tparam.index // == i
|
|
||||||
}
|
|
||||||
|
|
||||||
renameMap := makeRenameMap(tparams, tparams2)
|
|
||||||
for i, tparam := range tparams {
|
|
||||||
tparams2[i].bound = check.subst(pos, tparam.bound, renameMap, nil, check.context())
|
|
||||||
}
|
|
||||||
|
|
||||||
tparams = tparams2
|
|
||||||
params = check.subst(pos, params, renameMap, nil, check.context()).(*Tuple)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renameMap := makeRenameMap(tparams, tparams2)
|
||||||
|
for i, tparam := range tparams {
|
||||||
|
tparams2[i].bound = check.subst(pos, tparam.bound, renameMap, nil, check.context())
|
||||||
|
}
|
||||||
|
|
||||||
|
tparams = tparams2
|
||||||
|
params = check.subst(pos, params, renameMap, nil, check.context()).(*Tuple)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have more than 2 arguments, we may have arguments with named and unnamed types.
|
// If we have more than 2 arguments, we may have arguments with named and unnamed types.
|
||||||
|
@ -89,34 +89,22 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
|
|||||||
// f(p)
|
// f(p)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// We can turn the first example into the second example by renaming type
|
// We turn the first example into the second example by renaming type
|
||||||
// parameters in the original signature to give them a new identity. As an
|
// parameters in the original signature to give them a new identity.
|
||||||
// optimization, we do this only for self-recursive calls.
|
tparams2 := make([]*TypeParam, len(tparams))
|
||||||
|
for i, tparam := range tparams {
|
||||||
// We can detect if we are in a self-recursive call by comparing the
|
tname := NewTypeName(tparam.Obj().Pos(), tparam.Obj().Pkg(), tparam.Obj().Name(), nil)
|
||||||
// identity of the first type parameter in the current function with the
|
tparams2[i] = NewTypeParam(tname, nil)
|
||||||
// first type parameter in tparams. This works because type parameters are
|
tparams2[i].index = tparam.index // == i
|
||||||
// unique to their type parameter list.
|
|
||||||
selfRecursive := check.sig != nil && check.sig.tparams.Len() > 0 && tparams[0] == check.sig.tparams.At(0)
|
|
||||||
|
|
||||||
if selfRecursive {
|
|
||||||
// In self-recursive inference, rename the type parameters with new type
|
|
||||||
// parameters that are the same but for their pointer identity.
|
|
||||||
tparams2 := make([]*TypeParam, len(tparams))
|
|
||||||
for i, tparam := range tparams {
|
|
||||||
tname := NewTypeName(tparam.Obj().Pos(), tparam.Obj().Pkg(), tparam.Obj().Name(), nil)
|
|
||||||
tparams2[i] = NewTypeParam(tname, nil)
|
|
||||||
tparams2[i].index = tparam.index // == i
|
|
||||||
}
|
|
||||||
|
|
||||||
renameMap := makeRenameMap(tparams, tparams2)
|
|
||||||
for i, tparam := range tparams {
|
|
||||||
tparams2[i].bound = check.subst(posn.Pos(), tparam.bound, renameMap, nil, check.context())
|
|
||||||
}
|
|
||||||
|
|
||||||
tparams = tparams2
|
|
||||||
params = check.subst(posn.Pos(), params, renameMap, nil, check.context()).(*Tuple)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renameMap := makeRenameMap(tparams, tparams2)
|
||||||
|
for i, tparam := range tparams {
|
||||||
|
tparams2[i].bound = check.subst(posn.Pos(), tparam.bound, renameMap, nil, check.context())
|
||||||
|
}
|
||||||
|
|
||||||
|
tparams = tparams2
|
||||||
|
params = check.subst(posn.Pos(), params, renameMap, nil, check.context()).(*Tuple)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have more than 2 arguments, we may have arguments with named and unnamed types.
|
// If we have more than 2 arguments, we may have arguments with named and unnamed types.
|
||||||
|
14
src/internal/types/testdata/fixedbugs/issue57155.go
vendored
Normal file
14
src/internal/types/testdata/fixedbugs/issue57155.go
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Copyright 2022 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
func f[P *Q, Q any](p P, q Q) {
|
||||||
|
func() {
|
||||||
|
_ = f[P]
|
||||||
|
f(p, q)
|
||||||
|
f[P](p, q)
|
||||||
|
f[P, Q](p, q)
|
||||||
|
}()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user