mirror of https://github.com/golang/go.git
go/go2go: support instantiating with an instantiated type
Add metrics test case to test GO2PATH. Change-Id: I48c8a418d743ccd80cdcf36fc043ca0e2dd932f4
This commit is contained in:
parent
42884d7524
commit
01ab391f82
|
|
@ -0,0 +1,122 @@
|
|||
// Copyright 2020 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 metrics provides tracking arbitrary metrics composed of
|
||||
// values of comparable types.
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"maps"
|
||||
)
|
||||
|
||||
// Metric1 tracks metrics of values of some type.
|
||||
type Metric1(type T comparable) struct {
|
||||
mu sync.Mutex
|
||||
m map[T]int
|
||||
}
|
||||
|
||||
// Add adds another instance of some value.
|
||||
func (m *Metric1(T)) Add(v T) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.m == nil {
|
||||
m.m = make(map[T]int)
|
||||
}
|
||||
m.m[v]++
|
||||
}
|
||||
|
||||
// Count returns the number of instances we've seen of v.
|
||||
func (m *Metric1(T)) Count(v T) int {
|
||||
return m.m[v]
|
||||
}
|
||||
|
||||
// Metrics returns all the values we've seen, in an indeterminate order.
|
||||
func (m *Metric1(T)) Metrics() []T {
|
||||
return maps.Keys(m.m)
|
||||
}
|
||||
|
||||
contract cmp2(T1, T2) {
|
||||
comparable(T1)
|
||||
comparable(T2)
|
||||
}
|
||||
|
||||
type key2(type T1, T2 cmp2) struct {
|
||||
f1 T1
|
||||
f2 T2
|
||||
}
|
||||
|
||||
// Metric2 tracks metrics of pairs of values.
|
||||
type Metric2(type T1, T2 cmp2) struct {
|
||||
mu sync.Mutex
|
||||
m map[key2(T1, T2)]int
|
||||
}
|
||||
|
||||
// Add adds another instance of some pair of values.
|
||||
func (m *Metric2(T1, T2)) Add(v1 T1, v2 T2) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.m == nil {
|
||||
m.m = make(map[key2(T1, T2)]int)
|
||||
}
|
||||
m.m[key2(T1, T2){v1, v2}]++
|
||||
}
|
||||
|
||||
// Count returns the number of instances we've seen of v1/v2.
|
||||
func (m *Metric2(T1, T2)) Count(v1 T1, v2 T2) int {
|
||||
return m.m[key2(T1, T2){v1, v2}]
|
||||
}
|
||||
|
||||
// Metrics returns all the values we've seen, in an indeterminate order.
|
||||
func (m *Metric2(T1, T2)) Metrics() (r1 []T1, r2 []T2) {
|
||||
for _, k := range maps.Keys(m.m) {
|
||||
r1 = append(r1, k.f1)
|
||||
r2 = append(r2, k.f2)
|
||||
}
|
||||
return r1, r2
|
||||
}
|
||||
|
||||
contract cmp3(T1, T2, T3) {
|
||||
comparable(T1)
|
||||
comparable(T2)
|
||||
comparable(T3)
|
||||
}
|
||||
|
||||
type key3(type T1, T2, T3 cmp3) struct {
|
||||
f1 T1
|
||||
f2 T2
|
||||
f3 T3
|
||||
}
|
||||
|
||||
// Metric3 tracks metrics of triplets of values.
|
||||
type Metric3(type T1, T2, T3 cmp3) struct {
|
||||
mu sync.Mutex
|
||||
m map[key3(T1, T2, T3)]int
|
||||
}
|
||||
|
||||
// Add adds another instance of some triplet of values.
|
||||
func (m *Metric3(T1, T2, T3)) Add(v1 T1, v2 T2, v3 T3) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.m == nil {
|
||||
m.m = make(map[key3(T1, T2, T3)]int)
|
||||
}
|
||||
m.m[key3(T1, T2, T3){v1, v2, v3}]++
|
||||
}
|
||||
|
||||
// Count returns the number of instances we've seen of v1/v2/v3.
|
||||
func (m *Metric3(T1, T2, T3)) Count(v1 T1, v2 T2, v3 T3) int {
|
||||
return m.m[key3(T1, T2, T3){v1, v2, v3}]
|
||||
}
|
||||
|
||||
// Metrics returns all the values we've seen, in an indeterminate order.
|
||||
func (m *Metric3(T1, T2, T3)) Metrics() (r1 []T1, r2 []T2, r3 []T3) {
|
||||
for k := range m.m {
|
||||
r1 = append(r1, k.f1)
|
||||
r2 = append(r2, k.f2)
|
||||
r3 = append(r3, k.f3)
|
||||
}
|
||||
return r1, r2, r3
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2020 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 metrics
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"slices"
|
||||
)
|
||||
|
||||
type S struct{ a, b, c string }
|
||||
|
||||
func TestMetrics(t *testing.T) {
|
||||
m1 := Metric1(string){}
|
||||
if got := m1.Count("a"); got != 0 {
|
||||
t.Errorf("Count(%q) = %d, want 0", "a", got)
|
||||
}
|
||||
m1.Add("a")
|
||||
m1.Add("a")
|
||||
if got := m1.Count("a"); got != 2 {
|
||||
t.Errorf("Count(%q) = %d, want 2", "a", got)
|
||||
}
|
||||
if got, want := m1.Metrics(), []string{"a"}; !slices.Equal(got, want) {
|
||||
t.Errorf("Metrics = %v, want %v", got, want)
|
||||
}
|
||||
|
||||
m2 := Metric2(int, float64){}
|
||||
m2.Add(1, 1)
|
||||
m2.Add(2, 2)
|
||||
m2.Add(3, 3)
|
||||
m2.Add(3, 3)
|
||||
k1, k2 := m2.Metrics()
|
||||
|
||||
sort.Ints(k1)
|
||||
w1 := []int{1, 2, 3}
|
||||
if !slices.Equal(k1, w1) {
|
||||
t.Errorf("Metric2.Metrics first slice = %v, want %v", k1, w1)
|
||||
}
|
||||
|
||||
sort.Float64s(k2)
|
||||
w2 := []float64{1, 2, 3}
|
||||
if !slices.Equal(k2, w2) {
|
||||
t.Errorf("Metric2.Metrics first slice = %v, want %v", k2, w2)
|
||||
}
|
||||
|
||||
m3 := Metric3(string, S, S){}
|
||||
m3.Add("a", S{"d", "e", "f"}, S{"g", "h", "i"})
|
||||
m3.Add("a", S{"d", "e", "f"}, S{"g", "h", "i"})
|
||||
m3.Add("a", S{"d", "e", "f"}, S{"g", "h", "i"})
|
||||
m3.Add("b", S{"d", "e", "f"}, S{"g", "h", "i"})
|
||||
if got := m3.Count("a", S{"d", "e", "f"}, S{"g", "h", "i"}); got != 3 {
|
||||
t.Errorf("Count(%v, %v, %v) = %d, want 3", "a", S{"d", "e", "f"}, S{"g", "h", "i"}, got)
|
||||
}
|
||||
}
|
||||
|
|
@ -20,17 +20,19 @@ const nameSep = '୦'
|
|||
const nameIntro = '୮'
|
||||
|
||||
var nameCodes = map[rune]int{
|
||||
' ': 0,
|
||||
'*': 1,
|
||||
';': 2,
|
||||
',': 3,
|
||||
'{': 4,
|
||||
'}': 5,
|
||||
'[': 6,
|
||||
']': 7,
|
||||
'(': 8,
|
||||
')': 9,
|
||||
'.': 10,
|
||||
' ': 0,
|
||||
'*': 1,
|
||||
';': 2,
|
||||
',': 3,
|
||||
'{': 4,
|
||||
'}': 5,
|
||||
'[': 6,
|
||||
']': 7,
|
||||
'(': 8,
|
||||
')': 9,
|
||||
'.': 10,
|
||||
nameSep: 11,
|
||||
nameIntro: 12,
|
||||
}
|
||||
|
||||
// instantiatedName returns the name of a newly instantiated function.
|
||||
|
|
@ -49,10 +51,7 @@ func (t *translator) instantiatedName(qid qualifiedIdent, types []types.Type) (s
|
|||
// This is not possible in general but we assume that
|
||||
// identifiers will not contain nameSep or nameIntro.
|
||||
for _, r := range s {
|
||||
if r == nameSep || r == nameIntro {
|
||||
panic(fmt.Sprintf("identifier %q contains mangling rune %c", s, r))
|
||||
}
|
||||
if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
|
||||
if (unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_') && r != nameSep && r != nameIntro {
|
||||
sb.WriteRune(r)
|
||||
} else {
|
||||
code, ok := nameCodes[r]
|
||||
|
|
@ -66,6 +65,8 @@ func (t *translator) instantiatedName(qid qualifiedIdent, types []types.Type) (s
|
|||
return sb.String(), nil
|
||||
}
|
||||
|
||||
// importableName returns a name that we define in each package, so that
|
||||
// we have something to import to avoid an unused package error.
|
||||
func (t *translator) importableName() string {
|
||||
return "Importable" + string(nameSep)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -689,8 +689,15 @@ func (t *translator) instantiationTypes(call *ast.CallExpr) (argList []ast.Expr,
|
|||
typeArgs = true
|
||||
} else {
|
||||
for _, typ := range inferred.Targs {
|
||||
typeList = append(typeList, typ)
|
||||
arg := ast.NewIdent(typ.String())
|
||||
if named, ok := typ.(*types.Named); ok && len(named.TArgs()) > 0 {
|
||||
var narg *ast.Ident
|
||||
typ, narg = t.lookupInstantiatedType(named)
|
||||
if narg != nil {
|
||||
arg = ast.NewIdent(narg.Name)
|
||||
}
|
||||
}
|
||||
typeList = append(typeList, typ)
|
||||
argList = append(argList, arg)
|
||||
t.setType(arg, typ)
|
||||
}
|
||||
|
|
@ -699,6 +706,51 @@ func (t *translator) instantiationTypes(call *ast.CallExpr) (argList []ast.Expr,
|
|||
return
|
||||
}
|
||||
|
||||
// lookupInstantiatedType looks for an existing instantiation of an
|
||||
// instantiated type.
|
||||
func (t *translator) lookupInstantiatedType(typ *types.Named) (types.Type, *ast.Ident) {
|
||||
// The name should be something like pkg.name<type1, type2>.
|
||||
name := typ.Obj().Name()
|
||||
idx := strings.Index(name, "<")
|
||||
if idx < 0 {
|
||||
return typ, nil
|
||||
}
|
||||
name = name[:idx]
|
||||
fields := strings.Split(name, ".")
|
||||
if len(fields) > 2 {
|
||||
panic(fmt.Sprintf("unparseable instantiated name %q", name))
|
||||
}
|
||||
if len(fields) > 1 {
|
||||
name = fields[1]
|
||||
}
|
||||
|
||||
tpkg := typ.Obj().Pkg()
|
||||
nobj := tpkg.Scope().Lookup(name)
|
||||
if nobj == nil {
|
||||
panic(fmt.Sprintf("can't find %q in scope of package %q", name, tpkg.Name()))
|
||||
}
|
||||
|
||||
targs := typ.TArgs()
|
||||
instantiations := t.typeInstantiations[nobj.Type()]
|
||||
for _, inst := range instantiations {
|
||||
if t.sameTypes(targs, inst.types) {
|
||||
newName := inst.decl.Name
|
||||
nm := typ.NumMethods()
|
||||
methods := make([]*types.Func, 0, nm)
|
||||
for i := 0; i < nm; i++ {
|
||||
methods = append(methods, typ.Method(i))
|
||||
}
|
||||
obj := typ.Obj()
|
||||
obj = types.NewTypeName(obj.Pos(), obj.Pkg(), newName, nil)
|
||||
nt := types.NewNamed(obj, typ.Underlying(), methods)
|
||||
nt.SetTArgs(targs)
|
||||
return nt, inst.decl
|
||||
}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("did not find instantiation for %v %v\n", typ, typ.Underlying()))
|
||||
}
|
||||
|
||||
// sameTypes reports whether two type slices are the same.
|
||||
func (t *translator) sameTypes(a, b []types.Type) bool {
|
||||
if len(a) != len(b) {
|
||||
|
|
|
|||
|
|
@ -168,6 +168,31 @@ func (t *translator) doInstantiateType(ta *typeArgs, typ types.Type) types.Type
|
|||
}
|
||||
return types.NewChan(typ.Dir(), elem)
|
||||
case *types.Named:
|
||||
targs := typ.TArgs()
|
||||
targsChanged := false
|
||||
if len(targs) > 0 {
|
||||
newTargs := make([]types.Type, 0, len(targs))
|
||||
for _, targ := range targs {
|
||||
newTarg := t.instantiateType(ta, targ)
|
||||
if newTarg != targ {
|
||||
targsChanged = true
|
||||
}
|
||||
newTargs = append(newTargs, newTarg)
|
||||
}
|
||||
targs = newTargs
|
||||
}
|
||||
if targsChanged {
|
||||
nm := typ.NumMethods()
|
||||
methods := make([]*types.Func, 0, nm)
|
||||
for i := 0; i < nm; i++ {
|
||||
methods = append(methods, typ.Method(i))
|
||||
}
|
||||
obj := typ.Obj()
|
||||
obj = types.NewTypeName(obj.Pos(), obj.Pkg(), obj.Name(), nil)
|
||||
nt := types.NewNamed(obj, typ.Underlying(), methods)
|
||||
nt.SetTArgs(targs)
|
||||
return nt
|
||||
}
|
||||
return typ
|
||||
case *types.TypeParam:
|
||||
if instType, ok := ta.typ(typ); ok {
|
||||
|
|
|
|||
Loading…
Reference in New Issue