From: Robert Griesemer Date: Tue, 11 Jan 2022 01:29:21 +0000 (-0800) Subject: go/types, types2: do not run CTI before FTI X-Git-Tag: go1.18beta2~112 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=3d3f5d912b776424e50be276bc3a4ae02bf8d143;p=gostls13.git go/types, types2: do not run CTI before FTI 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 Reviewed-by: Robert Findley --- diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go index b203985b8d..d4fb97453d 100644 --- a/src/cmd/compile/internal/types2/infer.go +++ b/src/cmd/compile/internal/types2/infer.go @@ -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 index 0000000000..17ec0ce529 --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue50426.go2 @@ -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) } diff --git a/src/go/types/infer.go b/src/go/types/infer.go index a5088f2705..e139e45fff 100644 --- a/src/go/types/infer.go +++ b/src/go/types/infer.go @@ -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 index 0000000000..17ec0ce529 --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue50426.go2 @@ -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) }