mirror of
https://github.com/golang/go
synced 2024-11-17 02:54:45 -07:00
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 <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
3df4472201
commit
3d3f5d912b
@ -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)
|
||||
|
44
src/cmd/compile/internal/types2/testdata/fixedbugs/issue50426.go2
vendored
Normal file
44
src/cmd/compile/internal/types2/testdata/fixedbugs/issue50426.go2
vendored
Normal file
@ -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) }
|
@ -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 {
|
||||
|
44
src/go/types/testdata/fixedbugs/issue50426.go2
vendored
Normal file
44
src/go/types/testdata/fixedbugs/issue50426.go2
vendored
Normal file
@ -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) }
|
Loading…
Reference in New Issue
Block a user