]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types, types2: infer minimum default type for untyped arguments
authorRobert Griesemer <gri@golang.org>
Thu, 4 May 2023 17:38:58 +0000 (10:38 -0700)
committerGopher Robot <gobot@golang.org>
Mon, 8 May 2023 22:22:41 +0000 (22:22 +0000)
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>

src/cmd/compile/internal/types2/api.go
src/cmd/compile/internal/types2/check_test.go
src/cmd/compile/internal/types2/infer.go
src/go/types/api.go
src/go/types/check_test.go
src/go/types/generate_test.go
src/go/types/infer.go
src/internal/types/testdata/fixedbugs/issue58671.go [new file with mode: 0644]

index b798f2c888e8f31ad30dc28a5bb5e0349659b001..5860c3a92cd2fddeaab268a19e9d3e2aac838737 100644 (file)
@@ -169,6 +169,11 @@ type Config struct {
        // 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) {
index 26bb1aed9e6e8c5eaed271fbc6af2336a1dce87e..357ca7cf502fa09894e08f3a651d6e791b80ff9e 100644 (file)
@@ -133,6 +133,7 @@ func testFiles(t *testing.T, filenames []string, colDelta uint, manual bool) {
        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)
        }
index fed85c3d9efa4f64b1985285be80b7e1380a729a..0f4fc6d4b462adbd7af19adf26cf2c3b8e6c7d14 100644 (file)
@@ -277,30 +277,65 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
                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
+                       }
                }
        }
 
index 05773d134a703880f53a206d66736b4775389e97..14cd9cdcdb409bdbfe2ed14509d731acaa2b1602 100644 (file)
@@ -170,6 +170,11 @@ type Config struct {
        // 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) {
index d53aaeadc5e4dc547ec41048cd3bf88f5126239c..9dbcb326cffeedf2b38d380343f510fdd4e36b95 100644 (file)
@@ -146,6 +146,7 @@ func testFiles(t *testing.T, sizes Sizes, filenames []string, srcs [][]byte, man
        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)
        }
index c5e114aaec97610bbf8159b20e7f4c2386e23f18..73ad2c5b89157e14c0b1eb21148dd33c483425d4 100644 (file)
@@ -103,7 +103,11 @@ var filemap = map[string]action{
        "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"`) },
index 9ecef1e4481bb416dd295e274be5c9ee4643706b..e40b9921a49228176a39099126fe7664e3936d96 100644 (file)
@@ -279,30 +279,65 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
                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
+                       }
                }
        }
 
diff --git a/src/internal/types/testdata/fixedbugs/issue58671.go b/src/internal/types/testdata/fixedbugs/issue58671.go
new file mode 100644 (file)
index 0000000..166ffda
--- /dev/null
@@ -0,0 +1,22 @@
+// -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)" */)
+}