// Unify parameter and argument types for generic parameters with typed arguments
// and collect the indices of generic parameters with untyped arguments.
// 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) {
// provide a better error message if we can
// the core types, if any, of non-local (unbound) type parameters.
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.
// Interpretation of trace:
// x ≡ y attempt to unify types x and y
// that inferring the type for a given type parameter P will
// automatically infer the same type for all other parameters
// unified (joined) with P.
- handles map[*TypeParam]*Type
- depth int // recursion depth during unification
+ handles map[*TypeParam]*Type
+ 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
// 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
// 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))
handles := make(map[*TypeParam]*Type, len(tparams))
// Allocate all handles up-front: in a correct program, all type parameters
}
handles[x] = &t
}
- return &unifier{handles, 0}
+ return &unifier{handles, 0, enableInterfaceInference}
}
// unifyMode controls the behavior of the unifier.
// we will fail at function instantiation or argument assignment time.
//
// 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 {
u.tracef("%s ≡ under %s", x, ny)
}
// x != y if we get here
assert(x != y)
- // 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
// 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
// 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.
// Look under the name, but not under type parameters (go.dev/issue/60564).
xi := asInterface(x)
}
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
// the same names, and corresponding function types unify.
n.Args[0] = arg
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
+ }
}
}
}
// Unify parameter and argument types for generic parameters with typed arguments
// and collect the indices of generic parameters with untyped arguments.
// 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) {
// provide a better error message if we can
// the core types, if any, of non-local (unbound) type parameters.
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.
// Interpretation of trace:
// x ≡ y attempt to unify types x and y
// that inferring the type for a given type parameter P will
// automatically infer the same type for all other parameters
// unified (joined) with P.
- handles map[*TypeParam]*Type
- depth int // recursion depth during unification
+ handles map[*TypeParam]*Type
+ 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
// 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
// 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))
handles := make(map[*TypeParam]*Type, len(tparams))
// Allocate all handles up-front: in a correct program, all type parameters
}
handles[x] = &t
}
- return &unifier{handles, 0}
+ return &unifier{handles, 0, enableInterfaceInference}
}
// unifyMode controls the behavior of the unifier.
// we will fail at function instantiation or argument assignment time.
//
// 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 {
u.tracef("%s ≡ under %s", x, ny)
}
// x != y if we get here
assert(x != y)
- // 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
// 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
// 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.
// Look under the name, but not under type parameters (go.dev/issue/60564).
xi := asInterface(x)
}
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
// the same names, and corresponding function types unify.
--- /dev/null
+// -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" */)
+}