This implements the proposal #58671.
Must be explicitly enabled and requires proposal approval.
For #58671.
Change-Id: I150e78f4f3282d6b7cf9d90feeb5f1c5a36d8c38
Reviewed-on: https://go-review.googlesource.com/c/go/+/492835
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Robert Griesemer <gri@google.com>
// If DisableUnusedImportCheck is set, packages are not checked
// for unused imports.
DisableUnusedImportCheck bool
+
+ // If InferMaxDefaultType is set, the minimum (smallest) default
+ // type that fits all untyped constant arguments for the same type
+ // parameter is selected in type inference. (go.dev/issue/58671)
+ InferMaxDefaultType bool
}
func srcimporter_setUsesCgo(conf *Config) {
flags := flag.NewFlagSet("", flag.PanicOnError)
flags.StringVar(&conf.GoVersion, "lang", "", "")
flags.BoolVar(&conf.FakeImportC, "fakeImportC", false, "")
+ flags.BoolVar(&conf.InferMaxDefaultType, "inferMaxDefaultType", false, "")
if err := parseFlags(filenames[0], nil, flags); err != nil {
t.Fatal(err)
}
u.tracef("== untyped arguments: %v", untyped)
}
- // Some generic parameters with untyped arguments may have been given a type by now.
- // Collect all remaining parameters that don't have a type yet and unify them with
- // the default types of the untyped arguments.
- // We need to collect them all before unifying them with their untyped arguments;
- // otherwise a parameter type that appears multiple times will have a type after
- // the first unification and will be skipped later on, leading to incorrect results.
- j := 0
- for _, i := range untyped {
- tpar := params.At(i).typ.(*TypeParam) // is type parameter by construction of untyped
- if u.at(tpar) == nil {
- untyped[j] = i
- j++
- }
- }
- // untyped[:j] are the indices of parameters without a type yet.
- // The respective default types are typed (not untyped) by construction.
- for _, i := range untyped[:j] {
- tpar := params.At(i).typ.(*TypeParam)
- arg := args[i]
- typ := Default(arg.typ)
- assert(isTyped(typ))
- if !u.unify(tpar, typ) {
- errorf("default type", tpar, typ, arg)
- return nil
+ if check.conf.InferMaxDefaultType {
+ // Some generic parameters with untyped arguments may have been given a type by now.
+ // Collect all remaining parameters that don't have a type yet and determine the
+ // maximum untyped type for each of those parameters, if possible.
+ var maxUntyped map[*TypeParam]Type // lazily allocated (we may not need it)
+ for _, index := range untyped {
+ tpar := params.At(index).typ.(*TypeParam) // is type parameter by construction of untyped
+ if u.at(tpar) == nil {
+ arg := args[index] // arg corresponding to tpar
+ if maxUntyped == nil {
+ maxUntyped = make(map[*TypeParam]Type)
+ }
+ max := maxUntyped[tpar]
+ if max == nil {
+ max = arg.typ
+ } else {
+ m := maxType(max, arg.typ)
+ if m == nil {
+ check.errorf(arg, CannotInferTypeArgs, "mismatched types %s and %s (cannot infer %s)", max, arg.typ, tpar)
+ return nil
+ }
+ max = m
+ }
+ maxUntyped[tpar] = max
+ }
+ }
+ // maxUntyped contains the maximum untyped type for each type parameter
+ // which doesn't have a type yet. Set the respective default types.
+ for tpar, typ := range maxUntyped {
+ d := Default(typ)
+ assert(isTyped(d))
+ u.set(tpar, d)
+ }
+ } else {
+ // Some generic parameters with untyped arguments may have been given a type by now.
+ // Collect all remaining parameters that don't have a type yet and unify them with
+ // the default types of the untyped arguments.
+ // We need to collect them all before unifying them with their untyped arguments;
+ // otherwise a parameter type that appears multiple times will have a type after
+ // the first unification and will be skipped later on, leading to incorrect results.
+ j := 0
+ for _, i := range untyped {
+ tpar := params.At(i).typ.(*TypeParam) // is type parameter by construction of untyped
+ if u.at(tpar) == nil {
+ untyped[j] = i
+ j++
+ }
+ }
+ // untyped[:j] are the indices of parameters without a type yet.
+ // The respective default types are typed (not untyped) by construction.
+ for _, i := range untyped[:j] {
+ tpar := params.At(i).typ.(*TypeParam)
+ arg := args[i]
+ typ := Default(arg.typ)
+ assert(isTyped(typ))
+ if !u.unify(tpar, typ) {
+ errorf("default type", tpar, typ, arg)
+ return nil
+ }
}
}
// If DisableUnusedImportCheck is set, packages are not checked
// for unused imports.
DisableUnusedImportCheck bool
+
+ // If _InferMaxDefaultType is set, the minimum (smallest) default
+ // type that fits all untyped constant arguments for the same type
+ // parameter is selected in type inference. (go.dev/issue/58671)
+ _InferMaxDefaultType bool
}
func srcimporter_setUsesCgo(conf *Config) {
flags := flag.NewFlagSet("", flag.PanicOnError)
flags.StringVar(&conf.GoVersion, "lang", "", "")
flags.BoolVar(&conf.FakeImportC, "fakeImportC", false, "")
+ flags.BoolVar(boolFieldAddr(&conf, "_InferMaxDefaultType"), "inferMaxDefaultType", false, "")
if err := parseFlags(filenames[0], srcs[0], flags); err != nil {
t.Fatal(err)
}
"context_test.go": nil,
"gccgosizes.go": nil,
"hilbert_test.go": nil,
- "infer.go": func(f *ast.File) { fixTokenPos(f); fixInferSig(f) },
+ "infer.go": func(f *ast.File) {
+ fixTokenPos(f)
+ fixInferSig(f)
+ renameIdent(f, "InferMaxDefaultType", "_InferMaxDefaultType")
+ },
// "initorder.go": fixErrErrorfCall, // disabled for now due to unresolved error_ use implications for gopls
"instantiate.go": func(f *ast.File) { fixTokenPos(f); fixCheckErrorfCall(f) },
"instantiate_test.go": func(f *ast.File) { renameImportPath(f, `"cmd/compile/internal/types2"`, `"go/types"`) },
u.tracef("== untyped arguments: %v", untyped)
}
- // Some generic parameters with untyped arguments may have been given a type by now.
- // Collect all remaining parameters that don't have a type yet and unify them with
- // the default types of the untyped arguments.
- // We need to collect them all before unifying them with their untyped arguments;
- // otherwise a parameter type that appears multiple times will have a type after
- // the first unification and will be skipped later on, leading to incorrect results.
- j := 0
- for _, i := range untyped {
- tpar := params.At(i).typ.(*TypeParam) // is type parameter by construction of untyped
- if u.at(tpar) == nil {
- untyped[j] = i
- j++
- }
- }
- // untyped[:j] are the indices of parameters without a type yet.
- // The respective default types are typed (not untyped) by construction.
- for _, i := range untyped[:j] {
- tpar := params.At(i).typ.(*TypeParam)
- arg := args[i]
- typ := Default(arg.typ)
- assert(isTyped(typ))
- if !u.unify(tpar, typ) {
- errorf("default type", tpar, typ, arg)
- return nil
+ if check.conf._InferMaxDefaultType {
+ // Some generic parameters with untyped arguments may have been given a type by now.
+ // Collect all remaining parameters that don't have a type yet and determine the
+ // maximum untyped type for each of those parameters, if possible.
+ var maxUntyped map[*TypeParam]Type // lazily allocated (we may not need it)
+ for _, index := range untyped {
+ tpar := params.At(index).typ.(*TypeParam) // is type parameter by construction of untyped
+ if u.at(tpar) == nil {
+ arg := args[index] // arg corresponding to tpar
+ if maxUntyped == nil {
+ maxUntyped = make(map[*TypeParam]Type)
+ }
+ max := maxUntyped[tpar]
+ if max == nil {
+ max = arg.typ
+ } else {
+ m := maxType(max, arg.typ)
+ if m == nil {
+ check.errorf(arg, CannotInferTypeArgs, "mismatched types %s and %s (cannot infer %s)", max, arg.typ, tpar)
+ return nil
+ }
+ max = m
+ }
+ maxUntyped[tpar] = max
+ }
+ }
+ // maxUntyped contains the maximum untyped type for each type parameter
+ // which doesn't have a type yet. Set the respective default types.
+ for tpar, typ := range maxUntyped {
+ d := Default(typ)
+ assert(isTyped(d))
+ u.set(tpar, d)
+ }
+ } else {
+ // Some generic parameters with untyped arguments may have been given a type by now.
+ // Collect all remaining parameters that don't have a type yet and unify them with
+ // the default types of the untyped arguments.
+ // We need to collect them all before unifying them with their untyped arguments;
+ // otherwise a parameter type that appears multiple times will have a type after
+ // the first unification and will be skipped later on, leading to incorrect results.
+ j := 0
+ for _, i := range untyped {
+ tpar := params.At(i).typ.(*TypeParam) // is type parameter by construction of untyped
+ if u.at(tpar) == nil {
+ untyped[j] = i
+ j++
+ }
+ }
+ // untyped[:j] are the indices of parameters without a type yet.
+ // The respective default types are typed (not untyped) by construction.
+ for _, i := range untyped[:j] {
+ tpar := params.At(i).typ.(*TypeParam)
+ arg := args[i]
+ typ := Default(arg.typ)
+ assert(isTyped(typ))
+ if !u.unify(tpar, typ) {
+ errorf("default type", tpar, typ, arg)
+ return nil
+ }
}
}
--- /dev/null
+// -inferMaxDefaultType
+
+// 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
+
+func g[P any](...P) P { var x P; return x }
+
+func _() {
+ var (
+ _ int = g(1, 2)
+ _ rune = g(1, 'a')
+ _ float64 = g(1, 'a', 2.3)
+ _ float64 = g('a', 2.3)
+ _ complex128 = g(2.3, 'a', 1i)
+ )
+ g(true, 'a' /* ERROR "mismatched types untyped bool and untyped rune (cannot infer P)" */)
+ g(1, "foo" /* ERROR "mismatched types untyped int and untyped string (cannot infer P)" */)
+ g(1, 2.3, "bar" /* ERROR "mismatched types untyped float and untyped string (cannot infer P)" */)
+}