]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: do not run CTI before FTI
authorRobert Griesemer <gri@golang.org>
Tue, 11 Jan 2022 01:29:21 +0000 (17:29 -0800)
committerRobert Griesemer <gri@golang.org>
Wed, 12 Jan 2022 01:49:33 +0000 (01:49 +0000)
Until now, CTI (constraint type inference) was run before
FTI (function type inference). This lead to situations
where CTI infered a type that is missing necessary methods
even though a function argument of correct type was given.
This can happen when constraint type inference produces a
inferred type that is the structural type of multiple types,
which then is an underlying type, possibly without methods.

This CL removes the initial CTI step; it is only applied
after FTI with type arguments is run, and again after FTI
with untyped arguments is run.

Various comments are adjusted to reflect the new reality.

Fixes #50426.

Change-Id: I700ae6e762d7aa00d742943a2880f1a1db33c2b8
Reviewed-on: https://go-review.googlesource.com/c/go/+/377594
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/types2/infer.go
src/cmd/compile/internal/types2/testdata/fixedbugs/issue50426.go2 [new file with mode: 0644]
src/go/types/infer.go
src/go/types/testdata/fixedbugs/issue50426.go2 [new file with mode: 0644]

index b203985b8d129027d75a8b16c10c54e7bc0307b5..d4fb97453d4f3aac3bbbd5762674ab0d44c1afba 100644 (file)
@@ -19,15 +19,17 @@ const useConstraintTypeInference = true
 // function arguments args, if any. There must be at least one type parameter, no more type arguments
 // than type parameters, and params and args must match in number (incl. zero).
 // If successful, infer returns the complete list of type arguments, one for each type parameter.
-// Otherwise the result is nil and appropriate errors will be reported unless report is set to false.
+// Otherwise the result is nil and appropriate errors will be reported.
 //
-// Inference proceeds in 3 steps:
+// Inference proceeds as follows:
 //
-//   1) Start with given type arguments.
-//   2) Infer type arguments from typed function arguments.
-//   3) Infer type arguments from untyped function arguments.
+//   Starting with given type arguments
+//   1) apply FTI (function type inference) with typed arguments,
+//   2) apply CTI (constraint type inference),
+//   3) apply FTI with untyped function arguments,
+//   4) apply CTI.
 //
-// Constraint type inference is used after each step to expand the set of type arguments.
+// The process stops as soon as all type arguments are known or an error occurs.
 func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand) (result []Type) {
        if debug {
                defer func() {
@@ -46,7 +48,6 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
        // Function parameters and arguments must match in number.
        assert(params.Len() == len(args))
 
-       // --- 0 ---
        // If we already have all type arguments, we're done.
        if len(targs) == n {
                return targs
@@ -54,25 +55,13 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
        // len(targs) < n
 
        // --- 1 ---
-       // Explicitly provided type arguments take precedence over any inferred types;
-       // and types inferred via constraint type inference take precedence over types
-       // inferred from function arguments.
-       // If we have type arguments, see how far we get with constraint type inference.
-       if len(targs) > 0 && useConstraintTypeInference {
-               var index int
-               targs, index = check.inferB(pos, tparams, targs)
-               if targs == nil || index < 0 {
-                       return targs
-               }
-       }
-
-       // Continue with the type arguments we have now. Avoid matching generic
+       // Continue with the type arguments we have. Avoid matching generic
        // parameters that already have type arguments against function arguments:
        // It may fail because matching uses type identity while parameter passing
        // uses assignment rules. Instantiate the parameter list with the type
        // arguments we have, and continue with that parameter list.
 
-       // First, make sure we have a "full" list of type arguments, so of which
+       // First, make sure we have a "full" list of type arguments, some of which
        // may be nil (unknown).
        if len(targs) < n {
                targs2 := make([]Type, n)
@@ -90,7 +79,6 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
                params = check.subst(nopos, params, smap, nil).(*Tuple)
        }
 
-       // --- 2 ---
        // 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
@@ -167,6 +155,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
                return targs
        }
 
+       // --- 2 ---
        // 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.
@@ -207,6 +196,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
                return targs
        }
 
+       // --- 4 ---
        // Again, follow up with constraint type inference.
        if useConstraintTypeInference {
                targs, index = check.inferB(pos, tparams, targs)
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue50426.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue50426.go2
new file mode 100644 (file)
index 0000000..17ec0ce
--- /dev/null
@@ -0,0 +1,44 @@
+// Copyright 2022 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 A1 [2]uint64
+type A2 [2]uint64
+
+func (a A1) m() A1 { return a }
+func (a A2) m() A2 { return a }
+
+func f[B any, T interface {
+       A1 | A2
+       m() T
+}](v T) {
+}
+
+func _() {
+       var v A2
+       // Use function type inference to infer type A2 for T.
+       // Don't use constraint type inference before function
+       // type inference for typed arguments, otherwise it would
+       // infer type [2]uint64 for T which doesn't have method m
+       // (was the bug).
+       f[int](v)
+}
+
+// Keep using constraint type inference before function type
+// inference for untyped arguments so we infer type float64
+// for E below, and not int (which would not work).
+func g[S ~[]E, E any](S, E) {}
+
+func _() {
+       var s []float64
+       g[[]float64](s, 0)
+}
+
+// Keep using constraint type inference after function
+// type inference for untyped arguments so we infer
+// missing type arguments for which we only have the
+// untyped arguments as starting point.
+func h[E any, R []E](v E) R { return R{v} }
+func _() []int              { return h(0) }
index a5088f27050bf35fffbd2afbc534697d9ffc7885..e139e45fffe901cb84d43580303fbdd8121a216d 100644 (file)
@@ -18,15 +18,17 @@ import (
 // function arguments args, if any. There must be at least one type parameter, no more type arguments
 // than type parameters, and params and args must match in number (incl. zero).
 // If successful, infer returns the complete list of type arguments, one for each type parameter.
-// Otherwise the result is nil and appropriate errors will be reported unless report is set to false.
+// Otherwise the result is nil and appropriate errors will be reported.
 //
-// Inference proceeds in 3 steps:
+// Inference proceeds as follows:
 //
-//   1) Start with given type arguments.
-//   2) Infer type arguments from typed function arguments.
-//   3) Infer type arguments from untyped function arguments.
+//   Starting with given type arguments
+//   1) apply FTI (function type inference) with typed arguments,
+//   2) apply CTI (constraint type inference),
+//   3) apply FTI with untyped function arguments,
+//   4) apply CTI.
 //
-// Constraint type inference is used after each step to expand the set of type arguments.
+// The process stops as soon as all type arguments are known or an error occurs.
 func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand) (result []Type) {
        if debug {
                defer func() {
@@ -45,7 +47,6 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
        // Function parameters and arguments must match in number.
        assert(params.Len() == len(args))
 
-       // --- 0 ---
        // If we already have all type arguments, we're done.
        if len(targs) == n {
                return targs
@@ -53,25 +54,13 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
        // len(targs) < n
 
        // --- 1 ---
-       // Explicitly provided type arguments take precedence over any inferred types;
-       // and types inferred via constraint type inference take precedence over types
-       // inferred from function arguments.
-       // If we have type arguments, see how far we get with constraint type inference.
-       if len(targs) > 0 {
-               var index int
-               targs, index = check.inferB(posn, tparams, targs)
-               if targs == nil || index < 0 {
-                       return targs
-               }
-       }
-
-       // Continue with the type arguments we have now. Avoid matching generic
+       // Continue with the type arguments we have. Avoid matching generic
        // parameters that already have type arguments against function arguments:
        // It may fail because matching uses type identity while parameter passing
        // uses assignment rules. Instantiate the parameter list with the type
        // arguments we have, and continue with that parameter list.
 
-       // First, make sure we have a "full" list of type arguments, so of which
+       // First, make sure we have a "full" list of type arguments, some of which
        // may be nil (unknown).
        if len(targs) < n {
                targs2 := make([]Type, n)
@@ -89,7 +78,6 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
                params = check.subst(token.NoPos, params, smap, nil).(*Tuple)
        }
 
-       // --- 2 ---
        // 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
@@ -171,6 +159,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
                return targs
        }
 
+       // --- 2 ---
        // 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.
@@ -209,6 +198,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
                return targs
        }
 
+       // --- 4 ---
        // Again, follow up with constraint type inference.
        targs, index = check.inferB(posn, tparams, targs)
        if targs == nil || index < 0 {
diff --git a/src/go/types/testdata/fixedbugs/issue50426.go2 b/src/go/types/testdata/fixedbugs/issue50426.go2
new file mode 100644 (file)
index 0000000..17ec0ce
--- /dev/null
@@ -0,0 +1,44 @@
+// Copyright 2022 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 A1 [2]uint64
+type A2 [2]uint64
+
+func (a A1) m() A1 { return a }
+func (a A2) m() A2 { return a }
+
+func f[B any, T interface {
+       A1 | A2
+       m() T
+}](v T) {
+}
+
+func _() {
+       var v A2
+       // Use function type inference to infer type A2 for T.
+       // Don't use constraint type inference before function
+       // type inference for typed arguments, otherwise it would
+       // infer type [2]uint64 for T which doesn't have method m
+       // (was the bug).
+       f[int](v)
+}
+
+// Keep using constraint type inference before function type
+// inference for untyped arguments so we infer type float64
+// for E below, and not int (which would not work).
+func g[S ~[]E, E any](S, E) {}
+
+func _() {
+       var s []float64
+       g[[]float64](s, 0)
+}
+
+// Keep using constraint type inference after function
+// type inference for untyped arguments so we infer
+// missing type arguments for which we only have the
+// untyped arguments as starting point.
+func h[E any, R []E](v E) R { return R{v} }
+func _() []int              { return h(0) }