Skip to content

Commit 7397178

Browse files
committed
go/types: add support for inferring type instances
Add constraint type inference for type instances, to be consistent with inference of function values. Fixes #47990 Change-Id: Ib99b5215cb2da5c10badc4de7e9e60ca0e48489f Reviewed-on: https://go-review.googlesource.com/c/go/+/356489 Trust: Robert Findley <[email protected]> Run-TryBot: Robert Findley <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent 3befaf0 commit 7397178

File tree

8 files changed

+175
-81
lines changed

8 files changed

+175
-81
lines changed

src/go/types/call.go

+30-2
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,42 @@ func (check *Checker) funcInst(x *operand, ix *typeparams.IndexExpr) {
6060
}
6161

6262
// instantiate function signature
63-
res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature)
63+
res := check.instantiateSignature(x.Pos(), sig, targs, poslist)
6464
assert(res.TypeParams().Len() == 0) // signature is not generic anymore
6565
check.recordInstance(ix.Orig, targs, res)
6666
x.typ = res
6767
x.mode = value
6868
x.expr = ix.Orig
6969
}
7070

71+
func (check *Checker) instantiateSignature(pos token.Pos, typ *Signature, targs []Type, posList []token.Pos) (res *Signature) {
72+
assert(check != nil)
73+
assert(len(targs) == typ.TypeParams().Len())
74+
75+
if trace {
76+
check.trace(pos, "-- instantiating %s with %s", typ, targs)
77+
check.indent++
78+
defer func() {
79+
check.indent--
80+
check.trace(pos, "=> %s (under = %s)", res, res.Underlying())
81+
}()
82+
}
83+
84+
inst := check.instance(pos, typ, targs, check.conf.Context).(*Signature)
85+
assert(len(posList) <= len(targs))
86+
tparams := typ.TypeParams().list()
87+
if i, err := check.verify(pos, tparams, targs); err != nil {
88+
// best position for error reporting
89+
pos := pos
90+
if i < len(posList) {
91+
pos = posList[i]
92+
}
93+
check.softErrorf(atPos(pos), _Todo, err.Error())
94+
}
95+
96+
return inst
97+
}
98+
7199
func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
72100
ix := typeparams.UnpackIndexExpr(call.Fun)
73101
if ix != nil {
@@ -352,7 +380,7 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
352380
}
353381

354382
// compute result signature
355-
rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature)
383+
rsig = check.instantiateSignature(call.Pos(), sig, targs, nil)
356384
assert(rsig.TypeParams().Len() == 0) // signature is not generic anymore
357385
check.recordInstance(call.Fun, targs, rsig)
358386

src/go/types/instantiate.go

-50
Original file line numberDiff line numberDiff line change
@@ -49,56 +49,6 @@ func Instantiate(ctxt *Context, typ Type, targs []Type, validate bool) (Type, er
4949
return inst, err
5050
}
5151

52-
// instantiate creates an instance and defers verification of constraints to
53-
// later in the type checking pass. For Named types the resulting instance will
54-
// be unexpanded.
55-
func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, posList []token.Pos) (res Type) {
56-
assert(check != nil)
57-
if trace {
58-
check.trace(pos, "-- instantiating %s with %s", typ, NewTypeList(targs))
59-
check.indent++
60-
defer func() {
61-
check.indent--
62-
var under Type
63-
if res != nil {
64-
// Calling under() here may lead to endless instantiations.
65-
// Test case: type T[P any] T[P]
66-
// TODO(gri) investigate if that's a bug or to be expected.
67-
under = safeUnderlying(res)
68-
}
69-
check.trace(pos, "=> %s (under = %s)", res, under)
70-
}()
71-
}
72-
73-
inst := check.instance(pos, typ, targs, check.conf.Context)
74-
75-
assert(len(posList) <= len(targs))
76-
check.later(func() {
77-
// Collect tparams again because lazily loaded *Named types may not have
78-
// had tparams set up above.
79-
var tparams []*TypeParam
80-
switch t := typ.(type) {
81-
case *Named:
82-
tparams = t.TypeParams().list()
83-
case *Signature:
84-
tparams = t.TypeParams().list()
85-
}
86-
// Avoid duplicate errors; instantiate will have complained if tparams
87-
// and targs do not have the same length.
88-
if len(tparams) == len(targs) {
89-
if i, err := check.verify(pos, tparams, targs); err != nil {
90-
// best position for error reporting
91-
pos := pos
92-
if i < len(posList) {
93-
pos = posList[i]
94-
}
95-
check.softErrorf(atPos(pos), _Todo, err.Error())
96-
}
97-
}
98-
})
99-
return inst
100-
}
101-
10252
// instance creates a type or function instance using the given original type
10353
// typ and arguments targs. For Named types the resulting instance will be
10454
// unexpanded.

src/go/types/named.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,8 @@ func expandNamed(ctxt *Context, n *Named, instPos token.Pos) (tparams *TypeParam
241241

242242
check := n.check
243243

244-
if check.validateTArgLen(instPos, n.orig.tparams.Len(), n.targs.Len()) {
244+
// Mismatching arg and tparam length may be checked elsewhere.
245+
if n.orig.tparams.Len() == n.targs.Len() {
245246
// We must always have a context, to avoid infinite recursion.
246247
ctxt = check.bestContext(ctxt)
247248
h := ctxt.typeHash(n.orig, n.targs.list())

src/go/types/testdata/check/tinference.go2 renamed to src/go/types/testdata/check/funcinference.go2

+13-17
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,25 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
package tinferenceB
5+
package funcInference
66

77
import "strconv"
88

99
type any interface{}
1010

11-
// TODO(rFindley) the below partially applied function types should probably
12-
// not be permitted (spec question).
11+
func f0[A any, B interface{~*C}, C interface{~*D}, D interface{~*A}](A, B, C, D) {}
12+
func _() {
13+
f := f0[string]
14+
f("a", nil, nil, nil)
15+
f0("a", nil, nil, nil)
16+
}
1317

14-
// Embedding stand-alone type parameters is not permitted for now. Disabled.
15-
// func f0[A any, B interface{~C}, C interface{~D}, D interface{~A}](A, B, C, D)
16-
// func _() {
17-
// f := f0[string]
18-
// f("a", "b", "c", "d")
19-
// f0("a", "b", "c", "d")
20-
// }
21-
//
22-
// func f1[A any, B interface{~A}](A, B)
23-
// func _() {
24-
// f := f1[int]
25-
// f(int(0), int(0))
26-
// f1(int(0), int(0))
27-
// }
18+
func f1[A any, B interface{~*A}](A, B) {}
19+
func _() {
20+
f := f1[int]
21+
f(int(0), new(int))
22+
f1(int(0), new(int))
23+
}
2824

2925
func f2[A any, B interface{~[]A}](A, B) {}
3026
func _() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2021 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 typeInference
6+
7+
// basic inference
8+
type Tb[P ~*Q, Q any] int
9+
func _() {
10+
var x Tb[*int]
11+
var y Tb[*int, int]
12+
x = y
13+
_ = x
14+
}
15+
16+
// recursive inference
17+
type Tr[A any, B ~*C, C ~*D, D ~*A] int
18+
func _() {
19+
var x Tr[string]
20+
var y Tr[string, ***string, **string, *string]
21+
var z Tr[int, ***int, **int, *int]
22+
x = y
23+
x = z // ERROR cannot use z .* as Tr
24+
_ = x
25+
}
26+
27+
// other patterns of inference
28+
type To0[A any, B ~[]A] int
29+
type To1[A any, B ~struct{a A}] int
30+
type To2[A any, B ~[][]A] int
31+
type To3[A any, B ~[3]*A] int
32+
type To4[A any, B any, C ~struct{a A; b B}] int
33+
func _() {
34+
var _ To0[int]
35+
var _ To1[int]
36+
var _ To2[int]
37+
var _ To3[int]
38+
var _ To4[int, string]
39+
}
40+
41+
// failed inference
42+
type Tf0[A, B any] int
43+
type Tf1[A any, B ~struct{a A; c C}, C any] int
44+
func _() {
45+
var _ Tf0 /* ERROR cannot infer B */ /* ERROR got 1 arguments but 2 type parameters */ [int]
46+
var _ Tf1 /* ERROR cannot infer B */ /* ERROR got 1 arguments but 3 type parameters */ [int]
47+
}

src/go/types/testdata/check/typeinst2.go2

+4
Original file line numberDiff line numberDiff line change
@@ -255,3 +255,7 @@ var _ = f0_[int]
255255
var _ = f0_[bool /* ERROR does not satisfy I0_ */ ]
256256
var _ = f0_[string /* ERROR does not satisfy I0_ */ ]
257257
var _ = f0_[float64 /* ERROR does not satisfy I0_ */ ]
258+
259+
// Using a function instance as a type is an error.
260+
var _ f0 // ERROR not a type
261+
var _ f0 /* ERROR not a type */ [int]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2021 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+
import "unsafe"
8+
9+
func F1[T any](_ [unsafe.Sizeof(F1[int])]T) (res T) { return }
10+
func F2[T any](_ T) (res [unsafe.Sizeof(F2[string])]int) { return }
11+
func F3[T any](_ [unsafe.Sizeof(F1[string])]int) {}

src/go/types/typexpr.go

+68-11
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,6 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
265265
if !check.allowVersion(check.pkg, 1, 18) {
266266
check.softErrorf(inNode(e, ix.Lbrack), _Todo, "type instantiation requires go1.18 or later")
267267
}
268-
// TODO(rfindley): type instantiation should require go1.18
269268
return check.instantiatedType(ix.X, ix.Indices, def)
270269

271270
case *ast.ParenExpr:
@@ -375,13 +374,24 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
375374
return typ
376375
}
377376

378-
func (check *Checker) instantiatedType(x ast.Expr, targsx []ast.Expr, def *Named) Type {
377+
func (check *Checker) instantiatedType(x ast.Expr, targsx []ast.Expr, def *Named) (res Type) {
378+
if trace {
379+
check.trace(x.Pos(), "-- instantiating %s with %s", x, targsx)
380+
check.indent++
381+
defer func() {
382+
check.indent--
383+
// Don't format the underlying here. It will always be nil.
384+
check.trace(x.Pos(), "=> %s", res)
385+
}()
386+
}
387+
379388
gtyp := check.genericType(x, true)
380389
if gtyp == Typ[Invalid] {
381390
return gtyp // error already reported
382391
}
383-
base, _ := gtyp.(*Named)
384-
if base == nil {
392+
393+
origin, _ := gtyp.(*Named)
394+
if origin == nil {
385395
panic(fmt.Sprintf("%v: cannot instantiate %v", x.Pos(), gtyp))
386396
}
387397

@@ -398,17 +408,64 @@ func (check *Checker) instantiatedType(x ast.Expr, targsx []ast.Expr, def *Named
398408
posList[i] = arg.Pos()
399409
}
400410

401-
typ := check.instantiate(x.Pos(), base, targs, posList)
402-
def.setUnderlying(typ)
403-
check.recordInstance(x, targs, typ)
411+
// create the instance
412+
h := check.conf.Context.typeHash(origin, targs)
413+
// targs may be incomplete, and require inference. In any case we should de-duplicate.
414+
inst := check.conf.Context.typeForHash(h, nil)
415+
// If inst is non-nil, we can't just return here. Inst may have been
416+
// constructed via recursive substitution, in which case we wouldn't do the
417+
// validation below. Ensure that the validation (and resulting errors) runs
418+
// for each instantiated type in the source.
419+
if inst == nil {
420+
tname := NewTypeName(x.Pos(), origin.obj.pkg, origin.obj.name, nil)
421+
inst = check.newNamed(tname, origin, nil, nil, nil) // underlying, methods and tparams are set when named is resolved
422+
inst.targs = NewTypeList(targs)
423+
inst = check.conf.Context.typeForHash(h, inst)
424+
}
425+
def.setUnderlying(inst)
426+
427+
inst.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, []*Func) {
428+
tparams := origin.TypeParams().list()
429+
430+
inferred := targs
431+
if len(targs) < len(tparams) {
432+
// If inference fails, len(inferred) will be 0, and inst.underlying will
433+
// be set to Typ[Invalid] in expandNamed.
434+
inferred = check.infer(x, tparams, targs, nil, nil)
435+
if len(inferred) > len(targs) {
436+
inst.targs = NewTypeList(inferred)
437+
}
438+
}
404439

405-
// make sure we check instantiation works at least once
406-
// and that the resulting type is valid
440+
check.recordInstance(x, inferred, inst)
441+
return expandNamed(ctxt, n, x.Pos())
442+
}
443+
444+
// origin.tparams may not be set up, so we need to do expansion later.
407445
check.later(func() {
408-
check.validType(typ, nil)
446+
// This is an instance from the source, not from recursive substitution,
447+
// and so it must be resolved during type-checking so that we can report
448+
// errors.
449+
inst.resolve(check.conf.Context)
450+
// Since check is non-nil, we can still mutate inst. Unpinning the resolver
451+
// frees some memory.
452+
inst.resolver = nil
453+
454+
if check.validateTArgLen(x.Pos(), inst.tparams.Len(), inst.targs.Len()) {
455+
if i, err := check.verify(x.Pos(), inst.tparams.list(), inst.targs.list()); err != nil {
456+
// best position for error reporting
457+
pos := x.Pos()
458+
if i < len(posList) {
459+
pos = posList[i]
460+
}
461+
check.softErrorf(atPos(pos), _Todo, err.Error())
462+
}
463+
}
464+
465+
check.validType(inst, nil)
409466
})
410467

411-
return typ
468+
return inst
412469
}
413470

414471
// arrayLength type-checks the array length expression e

0 commit comments

Comments
 (0)