mirror of https://github.com/golang/go.git
[release-branch.go1.21] go/types, types2: disable interface inference for versions before Go 1.21
Change the internal constant enableInterfaceInference to a unifier field that can be controlled dynamically and set it for Go 1.21 or later. This restores Go 1.20 unification behavior for interfaces. Fixes #61930. Change-Id: Iefd6c0899811f8208a8be9cef2650a07787ae177 Reviewed-on: https://go-review.googlesource.com/c/go/+/519855 Reviewed-by: Robert Findley <rfindley@google.com> Auto-Submit: Robert Griesemer <gri@google.com> Reviewed-by: Robert Griesemer <gri@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Robert Griesemer <gri@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/520601
This commit is contained in:
parent
2b8026f025
commit
2d4746f37b
|
|
@ -96,7 +96,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
|
||||||
// Unify parameter and argument types for generic parameters with typed arguments
|
// Unify parameter and argument types for generic parameters with typed arguments
|
||||||
// and collect the indices of generic parameters with untyped arguments.
|
// and collect the indices of generic parameters with untyped arguments.
|
||||||
// Terminology: generic parameter = function parameter with a type-parameterized type
|
// Terminology: generic parameter = function parameter with a type-parameterized type
|
||||||
u := newUnifier(tparams, targs)
|
u := newUnifier(tparams, targs, check.allowVersion(check.pkg, pos, go1_21))
|
||||||
|
|
||||||
errorf := func(kind string, tpar, targ Type, arg *operand) {
|
errorf := func(kind string, tpar, targ Type, arg *operand) {
|
||||||
// provide a better error message if we can
|
// provide a better error message if we can
|
||||||
|
|
|
||||||
|
|
@ -53,11 +53,6 @@ const (
|
||||||
// the core types, if any, of non-local (unbound) type parameters.
|
// the core types, if any, of non-local (unbound) type parameters.
|
||||||
enableCoreTypeUnification = true
|
enableCoreTypeUnification = true
|
||||||
|
|
||||||
// If enableInterfaceInference is set, type inference uses
|
|
||||||
// shared methods for improved type inference involving
|
|
||||||
// interfaces.
|
|
||||||
enableInterfaceInference = true
|
|
||||||
|
|
||||||
// If traceInference is set, unification will print a trace of its operation.
|
// If traceInference is set, unification will print a trace of its operation.
|
||||||
// Interpretation of trace:
|
// Interpretation of trace:
|
||||||
// x ≡ y attempt to unify types x and y
|
// x ≡ y attempt to unify types x and y
|
||||||
|
|
@ -81,15 +76,16 @@ type unifier struct {
|
||||||
// that inferring the type for a given type parameter P will
|
// that inferring the type for a given type parameter P will
|
||||||
// automatically infer the same type for all other parameters
|
// automatically infer the same type for all other parameters
|
||||||
// unified (joined) with P.
|
// unified (joined) with P.
|
||||||
handles map[*TypeParam]*Type
|
handles map[*TypeParam]*Type
|
||||||
depth int // recursion depth during unification
|
depth int // recursion depth during unification
|
||||||
|
enableInterfaceInference bool // use shared methods for better inference
|
||||||
}
|
}
|
||||||
|
|
||||||
// newUnifier returns a new unifier initialized with the given type parameter
|
// newUnifier returns a new unifier initialized with the given type parameter
|
||||||
// and corresponding type argument lists. The type argument list may be shorter
|
// and corresponding type argument lists. The type argument list may be shorter
|
||||||
// than the type parameter list, and it may contain nil types. Matching type
|
// than the type parameter list, and it may contain nil types. Matching type
|
||||||
// parameters and arguments must have the same index.
|
// parameters and arguments must have the same index.
|
||||||
func newUnifier(tparams []*TypeParam, targs []Type) *unifier {
|
func newUnifier(tparams []*TypeParam, targs []Type, enableInterfaceInference bool) *unifier {
|
||||||
assert(len(tparams) >= len(targs))
|
assert(len(tparams) >= len(targs))
|
||||||
handles := make(map[*TypeParam]*Type, len(tparams))
|
handles := make(map[*TypeParam]*Type, len(tparams))
|
||||||
// Allocate all handles up-front: in a correct program, all type parameters
|
// Allocate all handles up-front: in a correct program, all type parameters
|
||||||
|
|
@ -103,7 +99,7 @@ func newUnifier(tparams []*TypeParam, targs []Type) *unifier {
|
||||||
}
|
}
|
||||||
handles[x] = &t
|
handles[x] = &t
|
||||||
}
|
}
|
||||||
return &unifier{handles, 0}
|
return &unifier{handles, 0, enableInterfaceInference}
|
||||||
}
|
}
|
||||||
|
|
||||||
// unifyMode controls the behavior of the unifier.
|
// unifyMode controls the behavior of the unifier.
|
||||||
|
|
@ -339,7 +335,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
|
||||||
// we will fail at function instantiation or argument assignment time.
|
// we will fail at function instantiation or argument assignment time.
|
||||||
//
|
//
|
||||||
// If we have at least one defined type, there is one in y.
|
// If we have at least one defined type, there is one in y.
|
||||||
if ny, _ := y.(*Named); mode&exact == 0 && ny != nil && isTypeLit(x) && !(enableInterfaceInference && IsInterface(x)) {
|
if ny, _ := y.(*Named); mode&exact == 0 && ny != nil && isTypeLit(x) && !(u.enableInterfaceInference && IsInterface(x)) {
|
||||||
if traceInference {
|
if traceInference {
|
||||||
u.tracef("%s ≡ under %s", x, ny)
|
u.tracef("%s ≡ under %s", x, ny)
|
||||||
}
|
}
|
||||||
|
|
@ -437,12 +433,12 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
|
||||||
emode |= exact
|
emode |= exact
|
||||||
}
|
}
|
||||||
|
|
||||||
// If EnableInterfaceInference is set and we don't require exact unification,
|
// If u.EnableInterfaceInference is set and we don't require exact unification,
|
||||||
// if both types are interfaces, one interface must have a subset of the
|
// if both types are interfaces, one interface must have a subset of the
|
||||||
// methods of the other and corresponding method signatures must unify.
|
// methods of the other and corresponding method signatures must unify.
|
||||||
// If only one type is an interface, all its methods must be present in the
|
// If only one type is an interface, all its methods must be present in the
|
||||||
// other type and corresponding method signatures must unify.
|
// other type and corresponding method signatures must unify.
|
||||||
if enableInterfaceInference && mode&exact == 0 {
|
if u.enableInterfaceInference && mode&exact == 0 {
|
||||||
// One or both interfaces may be defined types.
|
// One or both interfaces may be defined types.
|
||||||
// Look under the name, but not under type parameters (go.dev/issue/60564).
|
// Look under the name, but not under type parameters (go.dev/issue/60564).
|
||||||
xi := asInterface(x)
|
xi := asInterface(x)
|
||||||
|
|
@ -632,7 +628,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Interface:
|
case *Interface:
|
||||||
assert(!enableInterfaceInference || mode&exact != 0) // handled before this switch
|
assert(!u.enableInterfaceInference || mode&exact != 0) // handled before this switch
|
||||||
|
|
||||||
// Two interface types unify if they have the same set of methods with
|
// Two interface types unify if they have the same set of methods with
|
||||||
// the same names, and corresponding function types unify.
|
// the same names, and corresponding function types unify.
|
||||||
|
|
|
||||||
|
|
@ -242,6 +242,14 @@ func fixInferSig(f *ast.File) {
|
||||||
n.Args[0] = arg
|
n.Args[0] = arg
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
case "allowVersion":
|
||||||
|
// rewrite check.allowVersion(..., pos, ...) to check.allowVersion(..., posn, ...)
|
||||||
|
if ident, _ := n.Args[1].(*ast.Ident); ident != nil && ident.Name == "pos" {
|
||||||
|
pos := n.Args[1].Pos()
|
||||||
|
arg := newIdent(pos, "posn")
|
||||||
|
n.Args[1] = arg
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
|
||||||
// Unify parameter and argument types for generic parameters with typed arguments
|
// Unify parameter and argument types for generic parameters with typed arguments
|
||||||
// and collect the indices of generic parameters with untyped arguments.
|
// and collect the indices of generic parameters with untyped arguments.
|
||||||
// Terminology: generic parameter = function parameter with a type-parameterized type
|
// Terminology: generic parameter = function parameter with a type-parameterized type
|
||||||
u := newUnifier(tparams, targs)
|
u := newUnifier(tparams, targs, check.allowVersion(check.pkg, posn, go1_21))
|
||||||
|
|
||||||
errorf := func(kind string, tpar, targ Type, arg *operand) {
|
errorf := func(kind string, tpar, targ Type, arg *operand) {
|
||||||
// provide a better error message if we can
|
// provide a better error message if we can
|
||||||
|
|
|
||||||
|
|
@ -55,11 +55,6 @@ const (
|
||||||
// the core types, if any, of non-local (unbound) type parameters.
|
// the core types, if any, of non-local (unbound) type parameters.
|
||||||
enableCoreTypeUnification = true
|
enableCoreTypeUnification = true
|
||||||
|
|
||||||
// If enableInterfaceInference is set, type inference uses
|
|
||||||
// shared methods for improved type inference involving
|
|
||||||
// interfaces.
|
|
||||||
enableInterfaceInference = true
|
|
||||||
|
|
||||||
// If traceInference is set, unification will print a trace of its operation.
|
// If traceInference is set, unification will print a trace of its operation.
|
||||||
// Interpretation of trace:
|
// Interpretation of trace:
|
||||||
// x ≡ y attempt to unify types x and y
|
// x ≡ y attempt to unify types x and y
|
||||||
|
|
@ -83,15 +78,16 @@ type unifier struct {
|
||||||
// that inferring the type for a given type parameter P will
|
// that inferring the type for a given type parameter P will
|
||||||
// automatically infer the same type for all other parameters
|
// automatically infer the same type for all other parameters
|
||||||
// unified (joined) with P.
|
// unified (joined) with P.
|
||||||
handles map[*TypeParam]*Type
|
handles map[*TypeParam]*Type
|
||||||
depth int // recursion depth during unification
|
depth int // recursion depth during unification
|
||||||
|
enableInterfaceInference bool // use shared methods for better inference
|
||||||
}
|
}
|
||||||
|
|
||||||
// newUnifier returns a new unifier initialized with the given type parameter
|
// newUnifier returns a new unifier initialized with the given type parameter
|
||||||
// and corresponding type argument lists. The type argument list may be shorter
|
// and corresponding type argument lists. The type argument list may be shorter
|
||||||
// than the type parameter list, and it may contain nil types. Matching type
|
// than the type parameter list, and it may contain nil types. Matching type
|
||||||
// parameters and arguments must have the same index.
|
// parameters and arguments must have the same index.
|
||||||
func newUnifier(tparams []*TypeParam, targs []Type) *unifier {
|
func newUnifier(tparams []*TypeParam, targs []Type, enableInterfaceInference bool) *unifier {
|
||||||
assert(len(tparams) >= len(targs))
|
assert(len(tparams) >= len(targs))
|
||||||
handles := make(map[*TypeParam]*Type, len(tparams))
|
handles := make(map[*TypeParam]*Type, len(tparams))
|
||||||
// Allocate all handles up-front: in a correct program, all type parameters
|
// Allocate all handles up-front: in a correct program, all type parameters
|
||||||
|
|
@ -105,7 +101,7 @@ func newUnifier(tparams []*TypeParam, targs []Type) *unifier {
|
||||||
}
|
}
|
||||||
handles[x] = &t
|
handles[x] = &t
|
||||||
}
|
}
|
||||||
return &unifier{handles, 0}
|
return &unifier{handles, 0, enableInterfaceInference}
|
||||||
}
|
}
|
||||||
|
|
||||||
// unifyMode controls the behavior of the unifier.
|
// unifyMode controls the behavior of the unifier.
|
||||||
|
|
@ -341,7 +337,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
|
||||||
// we will fail at function instantiation or argument assignment time.
|
// we will fail at function instantiation or argument assignment time.
|
||||||
//
|
//
|
||||||
// If we have at least one defined type, there is one in y.
|
// If we have at least one defined type, there is one in y.
|
||||||
if ny, _ := y.(*Named); mode&exact == 0 && ny != nil && isTypeLit(x) && !(enableInterfaceInference && IsInterface(x)) {
|
if ny, _ := y.(*Named); mode&exact == 0 && ny != nil && isTypeLit(x) && !(u.enableInterfaceInference && IsInterface(x)) {
|
||||||
if traceInference {
|
if traceInference {
|
||||||
u.tracef("%s ≡ under %s", x, ny)
|
u.tracef("%s ≡ under %s", x, ny)
|
||||||
}
|
}
|
||||||
|
|
@ -439,12 +435,12 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
|
||||||
emode |= exact
|
emode |= exact
|
||||||
}
|
}
|
||||||
|
|
||||||
// If EnableInterfaceInference is set and we don't require exact unification,
|
// If u.EnableInterfaceInference is set and we don't require exact unification,
|
||||||
// if both types are interfaces, one interface must have a subset of the
|
// if both types are interfaces, one interface must have a subset of the
|
||||||
// methods of the other and corresponding method signatures must unify.
|
// methods of the other and corresponding method signatures must unify.
|
||||||
// If only one type is an interface, all its methods must be present in the
|
// If only one type is an interface, all its methods must be present in the
|
||||||
// other type and corresponding method signatures must unify.
|
// other type and corresponding method signatures must unify.
|
||||||
if enableInterfaceInference && mode&exact == 0 {
|
if u.enableInterfaceInference && mode&exact == 0 {
|
||||||
// One or both interfaces may be defined types.
|
// One or both interfaces may be defined types.
|
||||||
// Look under the name, but not under type parameters (go.dev/issue/60564).
|
// Look under the name, but not under type parameters (go.dev/issue/60564).
|
||||||
xi := asInterface(x)
|
xi := asInterface(x)
|
||||||
|
|
@ -634,7 +630,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Interface:
|
case *Interface:
|
||||||
assert(!enableInterfaceInference || mode&exact != 0) // handled before this switch
|
assert(!u.enableInterfaceInference || mode&exact != 0) // handled before this switch
|
||||||
|
|
||||||
// Two interface types unify if they have the same set of methods with
|
// Two interface types unify if they have the same set of methods with
|
||||||
// the same names, and corresponding function types unify.
|
// the same names, and corresponding function types unify.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// -lang=go1.20
|
||||||
|
|
||||||
|
// Copyright 2023 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
|
||||||
|
|
||||||
|
type T[P any] interface{}
|
||||||
|
|
||||||
|
func f1[P any](T[P]) {}
|
||||||
|
func f2[P any](T[P], P) {}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var t T[int]
|
||||||
|
f1(t)
|
||||||
|
|
||||||
|
var s string
|
||||||
|
f2(t, s /* ERROR "type string of s does not match inferred type int for P" */)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue