Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 6097ebe

Browse files
committed
go/types: record all instances, not just inferred instances
This change modifies the way we record instance information. It changes the Info.Inferred map to use the instantiated *ast.Ident as its key, and record information for all instances, not just those that were produced via function type inference. Accordingly, Info.Inferred is renamed to Info.Instances, and the Inferred type is renamed to Instance, with its Sig field changed to Type. This was largely motivated by suggestions from mdempsky on the go/types API proposal (#47916). In our analysis, always using the *ast.Ident as key and recording all instances makes the API easier to understand and use. Instance.TArgs is also renamed to TypeArgs, consistent with other name changes. Updates #47916 Change-Id: Ic25ad0cfd65fee6b05e513843c3866ee7a77cfe3 Reviewed-on: https://go-review.googlesource.com/c/go/+/349629 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> Reviewed-by: Robert Griesemer <gri@golang.org>
1 parent cc85bd0 commit 6097ebe

File tree

5 files changed

+163
-69
lines changed

5 files changed

+163
-69
lines changed

‎src/go/types/api.go

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,19 @@ type Info struct {
203203
// qualified identifiers are collected in the Uses map.
204204
Types map[ast.Expr]TypeAndValue
205205

206-
// Inferred maps calls of parameterized functions that use
207-
// type inference to the inferred type arguments and signature
208-
// of the function called. The recorded "call" expression may be
209-
// an *ast.CallExpr (as in f(x)), or an *ast.IndexExpr (s in f[T]).
210-
Inferred map[ast.Expr]Inferred
206+
// Instances maps identifiers denoting parameterized types or functions to
207+
// their type arguments and instantiated type.
208+
//
209+
// For example, Instances will map the identifier for 'T' in the type
210+
// instantiation T[int, string] to the type arguments [int, string] and
211+
// resulting instantiated *Named type. Given a parameterized function
212+
// func F[A any](A), Instances will map the identifier for 'F' in the call
213+
// expression F(int(1)) to the inferred type arguments [int], and resulting
214+
// instantiated *Signature.
215+
//
216+
// Invariant: Instantiating Uses[id].Type() with Instances[id].TypeArgs
217+
// results in an equivalent of Instances[id].Type.
218+
Instances map[*ast.Ident]Instance
211219

212220
// Defs maps identifiers to the objects they define (including
213221
// package names, dots "." of dot-imports, and blank "_" identifiers).
@@ -365,11 +373,13 @@ func (tv TypeAndValue) HasOk() bool {
365373
return tv.mode == commaok || tv.mode == mapindex
366374
}
367375

368-
// Inferred reports the Inferred type arguments and signature
369-
// for a parameterized function call that uses type inference.
370-
type Inferred struct {
371-
TArgs *TypeList
372-
Sig *Signature
376+
// Instance reports the type arguments and instantiated type for type and
377+
// function instantiations. For type instantiations, Type will be of dynamic
378+
// type *Named. For function instantiations, Type will be of dynamic type
379+
// *Signature.
380+
type Instance struct {
381+
TypeArgs *TypeList
382+
Type Type
373383
}
374384

375385
// An Initializer describes a package-level variable, or a list of variables in case

‎src/go/types/api_test.go

Lines changed: 109 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -402,126 +402,189 @@ func TestTypesInfo(t *testing.T) {
402402
}
403403
}
404404

405-
func TestInferredInfo(t *testing.T) {
405+
func TestInstanceInfo(t *testing.T) {
406406
var tests = []struct {
407407
src string
408-
fun string
408+
name string
409409
targs []string
410-
sig string
410+
typ string
411411
}{
412-
{genericPkg+`p0; func f[T any](T) {}; func _() { f(42) }`,
412+
{`package p0; func f[T any](T) {}; func _() { f(42) }`,
413413
`f`,
414414
[]string{`int`},
415415
`func(int)`,
416416
},
417-
{genericPkg+`p1; func f[T any](T) T { panic(0) }; func _() { f('@') }`,
417+
{`package p1; func f[T any](T) T { panic(0) }; func _() { f('@') }`,
418418
`f`,
419419
[]string{`rune`},
420420
`func(rune) rune`,
421421
},
422-
{genericPkg+`p2; func f[T any](...T) T { panic(0) }; func _() { f(0i) }`,
422+
{`package p2; func f[T any](...T) T { panic(0) }; func _() { f(0i) }`,
423423
`f`,
424424
[]string{`complex128`},
425425
`func(...complex128) complex128`,
426426
},
427-
{genericPkg+`p3; func f[A, B, C any](A, *B, []C) {}; func _() { f(1.2, new(string), []byte{}) }`,
427+
{`package p3; func f[A, B, C any](A, *B, []C) {}; func _() { f(1.2, new(string), []byte{}) }`,
428428
`f`,
429429
[]string{`float64`, `string`, `byte`},
430430
`func(float64, *string, []byte)`,
431431
},
432-
{genericPkg+`p4; func f[A, B any](A, *B, ...[]B) {}; func _() { f(1.2, new(byte)) }`,
432+
{`package p4; func f[A, B any](A, *B, ...[]B) {}; func _() { f(1.2, new(byte)) }`,
433433
`f`,
434434
[]string{`float64`, `byte`},
435435
`func(float64, *byte, ...[]byte)`,
436436
},
437437

438-
{genericPkg+`s1; func f[T any, P interface{~*T}](x T) {}; func _(x string) { f(x) }`,
438+
{`package s1; func f[T any, P interface{~*T}](x T) {}; func _(x string) { f(x) }`,
439439
`f`,
440440
[]string{`string`, `*string`},
441441
`func(x string)`,
442442
},
443-
{genericPkg+`s2; func f[T any, P interface{~*T}](x []T) {}; func _(x []int) { f(x) }`,
443+
{`package s2; func f[T any, P interface{~*T}](x []T) {}; func _(x []int) { f(x) }`,
444444
`f`,
445445
[]string{`int`, `*int`},
446446
`func(x []int)`,
447447
},
448-
{genericPkg+`s3; type C[T any] interface{~chan<- T}; func f[T any, P C[T]](x []T) {}; func _(x []int) { f(x) }`,
448+
{`package s3; type C[T any] interface{~chan<- T}; func f[T any, P C[T]](x []T) {}; func _(x []int) { f(x) }`,
449449
`f`,
450450
[]string{`int`, `chan<- int`},
451451
`func(x []int)`,
452452
},
453-
{genericPkg+`s4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T) {}; func _(x []int) { f(x) }`,
453+
{`package s4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T) {}; func _(x []int) { f(x) }`,
454454
`f`,
455455
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
456456
`func(x []int)`,
457457
},
458458

459-
{genericPkg+`t1; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = f[string] }`,
459+
{`package t1; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = f[string] }`,
460460
`f`,
461461
[]string{`string`, `*string`},
462462
`func() string`,
463463
},
464-
{genericPkg + `t2; type C[T any] interface{~chan<- T}; func f[T any, P C[T]]() []T { return nil }; func _() { _ = f[int] }`,
464+
{`package t2; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = (f[string]) }`,
465+
`f`,
466+
[]string{`string`, `*string`},
467+
`func() string`,
468+
},
469+
{`package t3; type C[T any] interface{~chan<- T}; func f[T any, P C[T]]() []T { return nil }; func _() { _ = f[int] }`,
465470
`f`,
466471
[]string{`int`, `chan<- int`},
467472
`func() []int`,
468473
},
469-
{genericPkg+`t3; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
474+
{`package t4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
470475
`f`,
471476
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
472477
`func() []int`,
473478
},
479+
{`package i0; import "lib"; func _() { lib.F(42) }`,
480+
`F`,
481+
[]string{`int`},
482+
`func(int)`,
483+
},
484+
{`package type0; type T[P interface{~int}] struct{ x P }; var _ T[int]`,
485+
`T`,
486+
[]string{`int`},
487+
`struct{x int}`,
488+
},
489+
{`package type1; type T[P interface{~int}] struct{ x P }; var _ (T[int])`,
490+
`T`,
491+
[]string{`int`},
492+
`struct{x int}`,
493+
},
494+
{`package type2; type T[P interface{~int}] struct{ x P }; var _ T[(int)]`,
495+
`T`,
496+
[]string{`int`},
497+
`struct{x int}`,
498+
},
499+
{`package type3; type T[P1 interface{~[]P2}, P2 any] struct{ x P1; y P2 }; var _ T[[]int, int]`,
500+
`T`,
501+
[]string{`[]int`, `int`},
502+
`struct{x []int; y int}`,
503+
},
504+
{`package type4; import "lib"; var _ lib.T[int]`,
505+
`T`,
506+
[]string{`int`},
507+
`[]int`,
508+
},
474509
}
475510

476511
for _, test := range tests {
477-
info := Info{}
478-
info.Inferred = make(map[ast.Expr]Inferred)
479-
name, err := mayTypecheck(t, "InferredInfo", test.src, &info)
480-
if err != nil {
481-
t.Errorf("package %s: %v", name, err)
482-
continue
483-
}
512+
const lib = `package lib
484513
485-
// look for inferred type arguments and signature
486-
var targs *TypeList
487-
var sig *Signature
488-
for call, inf := range info.Inferred {
489-
var fun ast.Expr
490-
switch x := call.(type) {
491-
case *ast.CallExpr:
492-
fun = x.Fun
493-
case *ast.IndexExpr:
494-
fun = x.X
495-
default:
496-
panic(fmt.Sprintf("unexpected call expression type %T", call))
514+
func F[P any](P) {}
515+
516+
type T[P any] []P
517+
`
518+
519+
imports := make(testImporter)
520+
conf := Config{Importer: imports}
521+
instances := make(map[*ast.Ident]Instance)
522+
uses := make(map[*ast.Ident]Object)
523+
makePkg := func(src string) *Package {
524+
f, err := parser.ParseFile(fset, "p.go", src, 0)
525+
if err != nil {
526+
t.Fatal(err)
527+
}
528+
pkg, err := conf.Check("", fset, []*ast.File{f}, &Info{Instances: instances, Uses: uses})
529+
if err != nil {
530+
t.Fatal(err)
497531
}
498-
if ExprString(fun) == test.fun {
499-
targs = inf.TArgs
500-
sig = inf.Sig
532+
imports[pkg.Name()] = pkg
533+
return pkg
534+
}
535+
makePkg(lib)
536+
pkg := makePkg(test.src)
537+
538+
// look for instance information
539+
var targs []Type
540+
var typ Type
541+
for ident, inst := range instances {
542+
if ExprString(ident) == test.name {
543+
for i := 0; i < inst.TypeArgs.Len(); i++ {
544+
targs = append(targs, inst.TypeArgs.At(i))
545+
}
546+
typ = inst.Type
547+
548+
// Check that we can find the corresponding parameterized type.
549+
ptype := uses[ident].Type()
550+
lister, _ := ptype.(interface{ TypeParams() *TypeParamList })
551+
if lister == nil || lister.TypeParams().Len() == 0 {
552+
t.Errorf("package %s: info.Types[%v] = %v, want parameterized type", pkg.Name(), ident, ptype)
553+
continue
554+
}
555+
556+
// Verify the invariant that re-instantiating the generic type with
557+
// TypeArgs results in an equivalent type.
558+
inst2, err := Instantiate(nil, ptype, targs, true)
559+
if err != nil {
560+
t.Errorf("Instantiate(%v, %v) failed: %v", ptype, targs, err)
561+
}
562+
if !Identical(inst.Type, inst2) {
563+
t.Errorf("%v and %v are not identical", inst.Type, inst2)
564+
}
501565
break
502566
}
503567
}
504568
if targs == nil {
505-
t.Errorf("package %s: no inferred information found for %s", name, test.fun)
569+
t.Errorf("package %s: no instance information found for %s", pkg.Name(), test.name)
506570
continue
507571
}
508572

509573
// check that type arguments are correct
510-
if targs.Len() != len(test.targs) {
511-
t.Errorf("package %s: got %d type arguments; want %d", name, targs.Len(), len(test.targs))
574+
if len(targs) != len(test.targs) {
575+
t.Errorf("package %s: got %d type arguments; want %d", pkg.Name(), len(targs), len(test.targs))
512576
continue
513577
}
514-
for i := 0; i < targs.Len(); i++ {
515-
targ := targs.At(i)
578+
for i, targ := range targs {
516579
if got := targ.String(); got != test.targs[i] {
517-
t.Errorf("package %s, %d. type argument: got %s; want %s", name, i, got, test.targs[i])
580+
t.Errorf("package %s, %d. type argument: got %s; want %s", pkg.Name(), i, got, test.targs[i])
518581
continue
519582
}
520583
}
521584

522-
// check that signature is correct
523-
if got := sig.String(); got != test.sig {
524-
t.Errorf("package %s: got %s; want %s", name, got, test.sig)
585+
// check that the types match
586+
if got := typ.Underlying().String(); got != test.typ {
587+
t.Errorf("package %s: got %s; want %s", pkg.Name(), got, test.typ)
525588
}
526589
}
527590
}

‎src/go/types/call.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ func (check *Checker) funcInst(x *operand, ix *typeparams.IndexExpr) {
3939
return
4040
}
4141

42-
// if we don't have enough type arguments, try type inference
43-
inferred := false
44-
4542
if got < want {
4643
targs = check.infer(ix.Orig, sig.TypeParams().list(), targs, nil, nil, true)
4744
if targs == nil {
@@ -51,7 +48,6 @@ func (check *Checker) funcInst(x *operand, ix *typeparams.IndexExpr) {
5148
return
5249
}
5350
got = len(targs)
54-
inferred = true
5551
}
5652
assert(got == want)
5753

@@ -66,9 +62,7 @@ func (check *Checker) funcInst(x *operand, ix *typeparams.IndexExpr) {
6662
// instantiate function signature
6763
res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature)
6864
assert(res.TypeParams().Len() == 0) // signature is not generic anymore
69-
if inferred {
70-
check.recordInferred(ix.Orig, targs, res)
71-
}
65+
check.recordInstance(ix.Orig, targs, res)
7266
x.typ = res
7367
x.mode = value
7468
x.expr = ix.Orig
@@ -354,7 +348,7 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
354348
// compute result signature
355349
rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature)
356350
assert(rsig.TypeParams().Len() == 0) // signature is not generic anymore
357-
check.recordInferred(call, targs, rsig)
351+
check.recordInstance(call.Fun, targs, rsig)
358352

359353
// Optimization: Only if the parameter list was adjusted do we
360354
// need to compute it from the adjusted list; otherwise we can

‎src/go/types/check.go

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -406,12 +406,38 @@ func (check *Checker) recordCommaOkTypes(x ast.Expr, a [2]Type) {
406406
}
407407
}
408408

409-
func (check *Checker) recordInferred(call ast.Expr, targs []Type, sig *Signature) {
410-
assert(call != nil)
411-
assert(sig != nil)
412-
if m := check.Inferred; m != nil {
413-
m[call] = Inferred{NewTypeList(targs), sig}
409+
// recordInstance records instantiation information into check.Info, if the
410+
// Instances map is non-nil. The given expr must be an ident, selector, or
411+
// index (list) expr with ident or selector operand.
412+
//
413+
// TODO(rfindley): the expr parameter is fragile. See if we can access the
414+
// instantiated identifier in some other way.
415+
func (check *Checker) recordInstance(expr ast.Expr, targs []Type, typ Type) {
416+
ident := instantiatedIdent(expr)
417+
assert(ident != nil)
418+
assert(typ != nil)
419+
if m := check.Instances; m != nil {
420+
m[ident] = Instance{NewTypeList(targs), typ}
421+
}
422+
}
423+
424+
func instantiatedIdent(expr ast.Expr) *ast.Ident {
425+
var selOrIdent ast.Expr
426+
switch e := expr.(type) {
427+
case *ast.IndexExpr:
428+
selOrIdent = e.X
429+
case *ast.IndexListExpr:
430+
selOrIdent = e.X
431+
case *ast.SelectorExpr, *ast.Ident:
432+
selOrIdent = e
433+
}
434+
switch x := selOrIdent.(type) {
435+
case *ast.Ident:
436+
return x
437+
case *ast.SelectorExpr:
438+
return x.Sel
414439
}
440+
panic("instantiated ident not found")
415441
}
416442

417443
func (check *Checker) recordDef(id *ast.Ident, obj Object) {

‎src/go/types/typexpr.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ func (check *Checker) instantiatedType(x ast.Expr, targsx []ast.Expr, def *Named
398398

399399
typ := check.instantiate(x.Pos(), base, targs, posList)
400400
def.setUnderlying(typ)
401+
check.recordInstance(x, targs, typ)
401402

402403
// make sure we check instantiation works at least once
403404
// and that the resulting type is valid

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /