1
0
mirror of https://github.com/golang/go synced 2024-11-24 04:20:03 -07:00

go/types, types2: simplify unify.inferred signature

Rather than referring back to the type parameter list stored with
the unifier, return inferred types for a given list of type parameters.
This decouples the unifier more and opens the door for inference to
consider type parameters from multiple types for inference.

While at it, introduce an internal flag to control whether
inference results of the two inference implementations should
be compared or not.

Change-Id: I23b254c6c1c750f5bd1360aa2bb088cc466434f3
Reviewed-on: https://go-review.googlesource.com/c/go/+/466795
Reviewed-by: Robert Griesemer <gri@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Robert Griesemer 2023-02-08 16:51:58 -08:00 committed by Gopher Robot
parent e0504bcd72
commit da47cd6192
6 changed files with 86 additions and 82 deletions

View File

@ -91,8 +91,8 @@ func (check *Checker) infer1(pos syntax.Pos, tparams []*TypeParam, targs []Type,
return
}
// provide a better error message if we can
targs, index := u.inferred()
if index == 0 {
targs := u.inferred(tparams)
if targs[0] == nil {
// The first type parameter couldn't be inferred.
// If none of them could be inferred, don't try
// to provide the inferred type in the error msg.
@ -156,9 +156,8 @@ func (check *Checker) infer1(pos syntax.Pos, tparams []*TypeParam, targs []Type,
}
// If we've got all type arguments, we're done.
var index int
targs, index = u.inferred()
if index < 0 {
targs = u.inferred(tparams)
if u.unknowns() == 0 {
return targs
}
@ -166,7 +165,7 @@ func (check *Checker) infer1(pos syntax.Pos, tparams []*TypeParam, targs []Type,
// See how far we get with constraint type inference.
// Note that even if we don't have any type arguments, constraint type inference
// may produce results for constraints that explicitly specify a type.
targs, index = check.inferB(tparams, targs)
targs, index := check.inferB(tparams, targs)
if targs == nil || index < 0 {
return targs
}
@ -193,8 +192,8 @@ func (check *Checker) infer1(pos syntax.Pos, tparams []*TypeParam, targs []Type,
}
// If we've got all type arguments, we're done.
targs, index = u.inferred()
if index < 0 {
targs = u.inferred(tparams)
if u.unknowns() == 0 {
return targs
}
@ -496,14 +495,14 @@ func (check *Checker) inferB(tparams []*TypeParam, targs []Type) (types []Type,
n = nn
}
// u.inferred() now contains the incoming type arguments plus any additional type
// u.inferred(tparams) now contains the incoming type arguments plus any additional type
// arguments which were inferred from core terms. The newly inferred non-nil
// entries may still contain references to other type parameters.
// For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int
// was given, unification produced the type list [int, []C, *A]. We eliminate the
// remaining type parameters by substituting the type parameters in this type list
// until nothing changes anymore.
types, _ = u.inferred()
types = u.inferred(tparams)
if debug {
for i, targ := range targs {
assert(targ == nil || types[i] == targ)

View File

@ -11,6 +11,10 @@ import (
. "internal/types/errors"
)
// If compareWithInfer1, infer2 results must match infer1 results.
// Disable before releasing Go 1.21.
const compareWithInfer1 = true
// infer attempts to infer the complete set of type arguments for generic function instantiation/call
// based on the given type parameters tparams, type arguments targs, function parameters params, and
// function arguments args, if any. There must be at least one type parameter, no more type arguments
@ -19,18 +23,22 @@ import (
// type parameter. Otherwise the result is nil and appropriate errors will be reported.
func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand) []Type {
r2 := check.infer2(pos, tparams, targs, params, args)
r1 := check.infer1(pos, tparams, targs, params, args, r2 == nil) // be silent on errors if infer2 failed
assert(len(r2) == len(r1))
for i, targ2 := range r2 {
targ1 := r1[i]
var c comparer
c.ignoreInvalids = true
if !c.identical(targ2, targ1, nil) {
tpar := tparams[i]
check.dump("%v: type argument for %s: infer1: %s, infer2: %s", tpar.Obj().Pos(), tpar, targ1, targ2)
panic("inconsistent type inference")
if compareWithInfer1 {
r1 := check.infer1(pos, tparams, targs, params, args, r2 == nil) // be silent on errors if infer2 failed
assert(len(r2) == len(r1))
for i, targ2 := range r2 {
targ1 := r1[i]
var c comparer
c.ignoreInvalids = true
if !c.identical(targ2, targ1, nil) {
tpar := tparams[i]
check.dump("%v: type argument for %s: infer1: %s, infer2: %s", tpar.Obj().Pos(), tpar, targ1, targ2)
panic("inconsistent type inference")
}
}
}
return r2
}
@ -99,8 +107,8 @@ func (check *Checker) infer2(pos syntax.Pos, tparams []*TypeParam, targs []Type,
errorf := func(kind string, tpar, targ Type, arg *operand) {
// provide a better error message if we can
targs, index := u.inferred()
if index == 0 {
targs := u.inferred(tparams)
if targs[0] == nil {
// The first type parameter couldn't be inferred.
// If none of them could be inferred, don't try
// to provide the inferred type in the error msg.
@ -170,7 +178,7 @@ func (check *Checker) infer2(pos syntax.Pos, tparams []*TypeParam, targs []Type,
}
if traceInference {
inferred, _ := u.inferred()
inferred := u.inferred(tparams)
u.tracef("=> %s ➞ %s\n", tparams, inferred)
}
@ -261,7 +269,7 @@ func (check *Checker) infer2(pos syntax.Pos, tparams []*TypeParam, targs []Type,
}
if traceInference {
inferred, _ := u.inferred()
inferred := u.inferred(tparams)
u.tracef("=> %s ➞ %s\n", tparams, inferred)
}
@ -302,14 +310,14 @@ func (check *Checker) infer2(pos syntax.Pos, tparams []*TypeParam, targs []Type,
// --- simplify ---
// u.inferred() now contains the incoming type arguments plus any additional type
// u.inferred(tparams) now contains the incoming type arguments plus any additional type
// arguments which were inferred. The inferred non-nil entries may still contain
// references to other type parameters found in constraints.
// For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int
// was given, unification produced the type list [int, []C, *A]. We eliminate the
// remaining type parameters by substituting the type parameters in this type list
// until nothing changes anymore.
inferred, _ = u.inferred()
inferred = u.inferred(tparams)
if debug {
for i, targ := range targs {
assert(targ == nil || inferred[i] == targ)

View File

@ -41,7 +41,7 @@ const (
// A unifier is created by calling newUnifier.
type unifier struct {
// tparams is the initial list of type parameters provided.
// Only used to print/return types in reproducible order.
// Only used to print types in reproducible order.
tparams []*TypeParam
// handles maps each type parameter to its inferred type through
// an indirection *Type called (inferred type) "handle".
@ -181,21 +181,16 @@ func (u *unifier) unknowns() int {
return n
}
// inferred returns the list of inferred types (via unification) for the type parameters
// recorded with u, and an index. If all types were inferred, the returned index is < 0.
// Otherwise, it is the index of the first type parameter which couldn't be inferred;
// i.e., for which list[index] is nil.
func (u *unifier) inferred() (list []Type, index int) {
list = make([]Type, len(u.tparams))
index = -1
for i, x := range u.tparams {
t := u.at(x)
list[i] = t
if index < 0 && t == nil {
index = i
}
// inferred returns the list of inferred types for the given type parameter list.
// The result is never nil and has the same length as tparams; result types that
// could not be inferred are nil. Corresponding type parameters and result types
// have identical indices.
func (u *unifier) inferred(tparams []*TypeParam) []Type {
list := make([]Type, len(tparams))
for i, x := range tparams {
list[i] = u.at(x)
}
return
return list
}
func (u *unifier) nifyEq(x, y Type, p *ifacePair) bool {

View File

@ -93,8 +93,8 @@ func (check *Checker) infer1(posn positioner, tparams []*TypeParam, targs []Type
return
}
// provide a better error message if we can
targs, index := u.inferred()
if index == 0 {
targs := u.inferred(tparams)
if targs[0] == nil {
// The first type parameter couldn't be inferred.
// If none of them could be inferred, don't try
// to provide the inferred type in the error msg.
@ -158,9 +158,8 @@ func (check *Checker) infer1(posn positioner, tparams []*TypeParam, targs []Type
}
// If we've got all type arguments, we're done.
var index int
targs, index = u.inferred()
if index < 0 {
targs = u.inferred(tparams)
if u.unknowns() == 0 {
return targs
}
@ -168,7 +167,7 @@ func (check *Checker) infer1(posn positioner, tparams []*TypeParam, targs []Type
// See how far we get with constraint type inference.
// Note that even if we don't have any type arguments, constraint type inference
// may produce results for constraints that explicitly specify a type.
targs, index = check.inferB(tparams, targs)
targs, index := check.inferB(tparams, targs)
if targs == nil || index < 0 {
return targs
}
@ -195,8 +194,8 @@ func (check *Checker) infer1(posn positioner, tparams []*TypeParam, targs []Type
}
// If we've got all type arguments, we're done.
targs, index = u.inferred()
if index < 0 {
targs = u.inferred(tparams)
if u.unknowns() == 0 {
return targs
}
@ -498,14 +497,14 @@ func (check *Checker) inferB(tparams []*TypeParam, targs []Type) (types []Type,
n = nn
}
// u.inferred() now contains the incoming type arguments plus any additional type
// u.inferred(tparams) now contains the incoming type arguments plus any additional type
// arguments which were inferred from core terms. The newly inferred non-nil
// entries may still contain references to other type parameters.
// For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int
// was given, unification produced the type list [int, []C, *A]. We eliminate the
// remaining type parameters by substituting the type parameters in this type list
// until nothing changes anymore.
types, _ = u.inferred()
types = u.inferred(tparams)
if debug {
for i, targ := range targs {
assert(targ == nil || types[i] == targ)

View File

@ -13,6 +13,10 @@ import (
. "internal/types/errors"
)
// If compareWithInfer1, infer2 results must match infer1 results.
// Disable before releasing Go 1.21.
const compareWithInfer1 = true
// infer attempts to infer the complete set of type arguments for generic function instantiation/call
// based on the given type parameters tparams, type arguments targs, function parameters params, and
// function arguments args, if any. There must be at least one type parameter, no more type arguments
@ -21,18 +25,22 @@ import (
// type parameter. Otherwise the result is nil and appropriate errors will be reported.
func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand) []Type {
r2 := check.infer2(posn, tparams, targs, params, args)
r1 := check.infer1(posn, tparams, targs, params, args, r2 == nil) // be silent on errors if infer2 failed
assert(len(r2) == len(r1))
for i, targ2 := range r2 {
targ1 := r1[i]
var c comparer
c.ignoreInvalids = true
if !c.identical(targ2, targ1, nil) {
tpar := tparams[i]
check.dump("%v: type argument for %s: infer1: %s, infer2: %s", tpar.Obj().Pos(), tpar, targ1, targ2)
panic("inconsistent type inference")
if compareWithInfer1 {
r1 := check.infer1(posn, tparams, targs, params, args, r2 == nil) // be silent on errors if infer2 failed
assert(len(r2) == len(r1))
for i, targ2 := range r2 {
targ1 := r1[i]
var c comparer
c.ignoreInvalids = true
if !c.identical(targ2, targ1, nil) {
tpar := tparams[i]
check.dump("%v: type argument for %s: infer1: %s, infer2: %s", tpar.Obj().Pos(), tpar, targ1, targ2)
panic("inconsistent type inference")
}
}
}
return r2
}
@ -101,8 +109,8 @@ func (check *Checker) infer2(posn positioner, tparams []*TypeParam, targs []Type
errorf := func(kind string, tpar, targ Type, arg *operand) {
// provide a better error message if we can
targs, index := u.inferred()
if index == 0 {
targs := u.inferred(tparams)
if targs[0] == nil {
// The first type parameter couldn't be inferred.
// If none of them could be inferred, don't try
// to provide the inferred type in the error msg.
@ -172,7 +180,7 @@ func (check *Checker) infer2(posn positioner, tparams []*TypeParam, targs []Type
}
if traceInference {
inferred, _ := u.inferred()
inferred := u.inferred(tparams)
u.tracef("=> %s ➞ %s\n", tparams, inferred)
}
@ -263,7 +271,7 @@ func (check *Checker) infer2(posn positioner, tparams []*TypeParam, targs []Type
}
if traceInference {
inferred, _ := u.inferred()
inferred := u.inferred(tparams)
u.tracef("=> %s ➞ %s\n", tparams, inferred)
}
@ -304,14 +312,14 @@ func (check *Checker) infer2(posn positioner, tparams []*TypeParam, targs []Type
// --- simplify ---
// u.inferred() now contains the incoming type arguments plus any additional type
// u.inferred(tparams) now contains the incoming type arguments plus any additional type
// arguments which were inferred. The inferred non-nil entries may still contain
// references to other type parameters found in constraints.
// For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int
// was given, unification produced the type list [int, []C, *A]. We eliminate the
// remaining type parameters by substituting the type parameters in this type list
// until nothing changes anymore.
inferred, _ = u.inferred()
inferred = u.inferred(tparams)
if debug {
for i, targ := range targs {
assert(targ == nil || inferred[i] == targ)

View File

@ -43,7 +43,7 @@ const (
// A unifier is created by calling newUnifier.
type unifier struct {
// tparams is the initial list of type parameters provided.
// Only used to print/return types in reproducible order.
// Only used to print types in reproducible order.
tparams []*TypeParam
// handles maps each type parameter to its inferred type through
// an indirection *Type called (inferred type) "handle".
@ -183,21 +183,16 @@ func (u *unifier) unknowns() int {
return n
}
// inferred returns the list of inferred types (via unification) for the type parameters
// recorded with u, and an index. If all types were inferred, the returned index is < 0.
// Otherwise, it is the index of the first type parameter which couldn't be inferred;
// i.e., for which list[index] is nil.
func (u *unifier) inferred() (list []Type, index int) {
list = make([]Type, len(u.tparams))
index = -1
for i, x := range u.tparams {
t := u.at(x)
list[i] = t
if index < 0 && t == nil {
index = i
}
// inferred returns the list of inferred types for the given type parameter list.
// The result is never nil and has the same length as tparams; result types that
// could not be inferred are nil. Corresponding type parameters and result types
// have identical indices.
func (u *unifier) inferred(tparams []*TypeParam) []Type {
list := make([]Type, len(tparams))
for i, x := range tparams {
list[i] = u.at(x)
}
return
return list
}
func (u *unifier) nifyEq(x, y Type, p *ifacePair) bool {