Skip to content

Commit 726d898

Browse files
griesemergopherbot
authored andcommitted
go/types, types2: always try inference over methods when possible
During type inference, when comparing type parameters against their constraints, if a type argument is completely known it must implement its constraint. In this case, always unify the type argument's methods against the constraint methods, if any. Before this CL, this step was only attempted if the constraint had no core type. That left information unused which led to type inference failures where it should have succeeded. Fixes #66751. Change-Id: I71e96b71258624212186cf17ec47e67a589817b9 Reviewed-on: https://go-review.googlesource.com/c/go/+/617896 Reviewed-by: Tim King <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> Auto-Submit: Robert Griesemer <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 7703db6 commit 726d898

File tree

3 files changed

+134
-54
lines changed

3 files changed

+134
-54
lines changed

Diff for: src/cmd/compile/internal/types2/infer.go

+36-27
Original file line numberDiff line numberDiff line change
@@ -236,10 +236,10 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
236236
u.tracef("-- type parameter %s = %s: core(%s) = %s, single = %v", tpar, tx, tpar, core, single)
237237
}
238238

239-
// If there is a core term (i.e., a core type with tilde information)
240-
// unify the type parameter with the core type.
239+
// If the type parameter's constraint has a core term (i.e., a core type with tilde information)
240+
// try to unify the type parameter with that core type.
241241
if core != nil {
242-
// A type parameter can be unified with its core type in two cases.
242+
// A type parameter can be unified with its constraint's core type in two cases.
243243
switch {
244244
case tx != nil:
245245
if traceInference {
@@ -266,33 +266,42 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
266266
if traceInference {
267267
u.tracef("-> set type parameter %s to constraint core type %s", tpar, core.typ)
268268
}
269-
// The corresponding type argument tx is unknown and there's a single
270-
// specific type and no tilde.
269+
// The corresponding type argument tx is unknown and the core term
270+
// describes a single specific type and no tilde.
271271
// In this case the type argument must be that single type; set it.
272272
u.set(tpar, core.typ)
273273
}
274-
} else {
275-
if tx != nil {
276-
if traceInference {
277-
u.tracef("-> unify type parameter %s (type %s) methods with constraint methods", tpar, tx)
278-
}
279-
// We don't have a core type, but the type argument tx is known.
280-
// It must have (at least) all the methods of the type constraint,
281-
// and the method signatures must unify; otherwise tx cannot satisfy
282-
// the constraint.
283-
// TODO(gri) Now that unification handles interfaces, this code can
284-
// be reduced to calling u.unify(tx, tpar.iface(), assign)
285-
// (which will compare signatures exactly as we do below).
286-
// We leave it as is for now because missingMethod provides
287-
// a failure cause which allows for a better error message.
288-
// Eventually, unify should return an error with cause.
289-
var cause string
290-
constraint := tpar.iface()
291-
if m, _ := check.missingMethod(tx, constraint, true, func(x, y Type) bool { return u.unify(x, y, exact) }, &cause); m != nil {
292-
// TODO(gri) better error message (see TODO above)
293-
err.addf(pos, "%s (type %s) does not satisfy %s %s", tpar, tx, tpar.Constraint(), cause)
294-
return nil
295-
}
274+
}
275+
276+
// Independent of whether there is a core term, if the type argument tx is known
277+
// it must implement the methods of the type constraint, possibly after unification
278+
// of the relevant method signatures, otherwise tx cannot satisfy the constraint.
279+
// This unification step may provide additional type arguments.
280+
//
281+
// Note: The type argument tx may be known but contain references to other type
282+
// parameters (i.e., tx may still be parameterized).
283+
// In this case the methods of tx don't correctly reflect the final method set
284+
// and we may get a missing method error below. Skip this step in this case.
285+
//
286+
// TODO(gri) We should be able continue even with a parameterized tx if we add
287+
// a simplify step beforehand (see below). This will require factoring out the
288+
// simplify phase so we can call it from here.
289+
if tx != nil && !isParameterized(tparams, tx) {
290+
if traceInference {
291+
u.tracef("-> unify type parameter %s (type %s) methods with constraint methods", tpar, tx)
292+
}
293+
// TODO(gri) Now that unification handles interfaces, this code can
294+
// be reduced to calling u.unify(tx, tpar.iface(), assign)
295+
// (which will compare signatures exactly as we do below).
296+
// We leave it as is for now because missingMethod provides
297+
// a failure cause which allows for a better error message.
298+
// Eventually, unify should return an error with cause.
299+
var cause string
300+
constraint := tpar.iface()
301+
if m, _ := check.missingMethod(tx, constraint, true, func(x, y Type) bool { return u.unify(x, y, exact) }, &cause); m != nil {
302+
// TODO(gri) better error message (see TODO above)
303+
err.addf(pos, "%s (type %s) does not satisfy %s %s", tpar, tx, tpar.Constraint(), cause)
304+
return nil
296305
}
297306
}
298307
}

Diff for: src/go/types/infer.go

+36-27
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: src/internal/types/testdata/fixedbugs/issue66751.go

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
type S struct{}
8+
9+
func (*S) m(int) {}
10+
11+
func f[A interface {
12+
~*B
13+
m(C)
14+
}, B, C any]() {
15+
}
16+
17+
var _ = f[*S] // must be able to infer all remaining type arguments
18+
19+
// original test case from issue
20+
21+
type ptrTo[A any] interface{ ~*A }
22+
type hasFoo[A any] interface{ foo(A) }
23+
type both[A, B any] interface {
24+
ptrTo[A]
25+
hasFoo[B]
26+
}
27+
28+
type fooer[A any] struct{}
29+
30+
func (f *fooer[A]) foo(A) {}
31+
32+
func withPtr[A ptrTo[B], B any]() {}
33+
func withFoo[A hasFoo[B], B any]() {}
34+
func withBoth[A both[B, C], B, C any]() {}
35+
36+
func _() {
37+
withPtr[*fooer[int]]() // ok
38+
withFoo[*fooer[int]]() // ok
39+
withBoth[*fooer[int]]() // should be able to infer C
40+
}
41+
42+
// related test case reported in issue
43+
44+
type X struct{}
45+
46+
func (x X) M() int { return 42 }
47+
48+
func CallM1[T interface{ M() R }, R any](t T) R {
49+
return t.M()
50+
}
51+
52+
func CallM2[T interface {
53+
X
54+
M() R
55+
}, R any](t T) R {
56+
return t.M()
57+
}
58+
59+
func _() {
60+
CallM1(X{}) // ok
61+
CallM2(X{}) // should be able to infer R
62+
}

0 commit comments

Comments
 (0)