From 190cb937f7acdc1568a09c0cbbe1c14031c94ca9 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 19 Apr 2021 18:48:16 -0700 Subject: [PATCH] cmd/compile/internal/types2: fix type inference Don't let type parameters that are not filled in with concrete type arguments escape from constraint type inference - such inferred types are not "real". While at it, implement a tparamsList.String method for debugging. Fixes #45548. Change-Id: I40f13ff7af08d0357a5c66234bfcdd0b7ed5fdd6 Reviewed-on: https://go-review.googlesource.com/c/go/+/311651 Trust: Robert Griesemer Run-TryBot: Robert Griesemer Reviewed-by: Robert Findley --- .../internal/types2/fixedbugs/issue45548.go2 | 13 +++++++++++++ src/cmd/compile/internal/types2/infer.go | 19 +++++++++++++++++++ src/cmd/compile/internal/types2/unify.go | 18 ++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 src/cmd/compile/internal/types2/fixedbugs/issue45548.go2 diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue45548.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue45548.go2 new file mode 100644 index 00000000000..b1e42497e85 --- /dev/null +++ b/src/cmd/compile/internal/types2/fixedbugs/issue45548.go2 @@ -0,0 +1,13 @@ +// Copyright 2021 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 f[F interface{type *Q}, G interface{type *R}, Q, R any](q Q, r R) {} + +func _() { + f[*float64, *int](1, 2) + f[*float64](1, 2) + f(1, 2) +} diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go index 13a9ccda0c0..c136823fd8e 100644 --- a/src/cmd/compile/internal/types2/infer.go +++ b/src/cmd/compile/internal/types2/infer.go @@ -451,6 +451,25 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty dirty = dirty[:n] } + // Once nothing changes anymore, we may still have type parameters left; + // e.g., a structural constraint *P may match a type parameter Q but we + // don't have any type arguments to fill in for *P or Q (issue #45548). + // Don't let such inferences escape, instead nil them out. + for i, typ := range types { + if typ != nil && isParameterized(tparams, typ) { + types[i] = nil + } + } + + // update index + index = -1 + for i, typ := range types { + if typ == nil { + index = i + break + } + } + return } diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go index d2ea2b952b0..e1832bbb2a3 100644 --- a/src/cmd/compile/internal/types2/unify.go +++ b/src/cmd/compile/internal/types2/unify.go @@ -6,6 +6,8 @@ package types2 +import "bytes" + // The unifier maintains two separate sets of type parameters x and y // which are used to resolve type parameters in the x and y arguments // provided to the unify call. For unidirectional unification, only @@ -69,6 +71,22 @@ type tparamsList struct { indices []int // len(d.indices) == len(d.tparams) } +// String returns a string representation for a tparamsList. For debugging. +func (d *tparamsList) String() string { + var buf bytes.Buffer + buf.WriteByte('[') + for i, tname := range d.tparams { + if i > 0 { + buf.WriteString(", ") + } + writeType(&buf, tname.typ, nil, nil) + buf.WriteString(": ") + writeType(&buf, d.at(i), nil, nil) + } + buf.WriteByte(']') + return buf.String() +} + // init initializes d with the given type parameters. // The type parameters must be in the order in which they appear in their declaration // (this ensures that the tparams indices match the respective type parameter index).