mirror of
https://github.com/golang/go
synced 2024-11-23 06:40:05 -07:00
go/types, types2: eliminate methodList in favor of just using Named.mu
In order to clean up context after fully expanding a type (in subsequent CLs), we must use a common mutex. Eliminate the lazy methodList type, which keeps a sync.Once per method, in favor of Named.mu. Updates #52728 Change-Id: I2d13319276df1330ee53046ef1823b0167a258d8 Reviewed-on: https://go-review.googlesource.com/c/go/+/404883 TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Robert Findley <rfindley@google.com> Reviewed-by: Robert Griesemer <gri@google.com>
This commit is contained in:
parent
846f971daa
commit
1323b0e8f0
@ -646,8 +646,8 @@ func (check *Checker) collectMethods(obj *TypeName) {
|
|||||||
// Checker.Files may be called multiple times; additional package files
|
// Checker.Files may be called multiple times; additional package files
|
||||||
// may add methods to already type-checked types. Add pre-existing methods
|
// may add methods to already type-checked types. Add pre-existing methods
|
||||||
// so that we can detect redeclarations.
|
// so that we can detect redeclarations.
|
||||||
for i := 0; i < base.methods.Len(); i++ {
|
for i := 0; i < base.NumMethods(); i++ {
|
||||||
m := base.methods.At(i, nil)
|
m := base.Method(i)
|
||||||
assert(m.name != "_")
|
assert(m.name != "_")
|
||||||
assert(mset.insert(m) == nil)
|
assert(mset.insert(m) == nil)
|
||||||
}
|
}
|
||||||
@ -679,8 +679,8 @@ func (check *Checker) collectMethods(obj *TypeName) {
|
|||||||
func (check *Checker) checkFieldUniqueness(base *Named) {
|
func (check *Checker) checkFieldUniqueness(base *Named) {
|
||||||
if t, _ := base.under().(*Struct); t != nil {
|
if t, _ := base.under().(*Struct); t != nil {
|
||||||
var mset objset
|
var mset objset
|
||||||
for i := 0; i < base.methods.Len(); i++ {
|
for i := 0; i < base.NumMethods(); i++ {
|
||||||
m := base.methods.At(i, nil)
|
m := base.Method(i)
|
||||||
assert(m.name != "_")
|
assert(m.name != "_")
|
||||||
assert(mset.insert(m) == nil)
|
assert(mset.insert(m) == nil)
|
||||||
}
|
}
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
// Copyright 2022 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 types2
|
|
||||||
|
|
||||||
import "sync"
|
|
||||||
|
|
||||||
// methodList holds a list of methods that may be lazily resolved by a provided
|
|
||||||
// resolution method.
|
|
||||||
type methodList struct {
|
|
||||||
methods []*Func
|
|
||||||
|
|
||||||
// guards synchronizes the instantiation of lazy methods. For lazy method
|
|
||||||
// lists, guards is non-nil and of the length passed to newLazyMethodList.
|
|
||||||
// For non-lazy method lists, guards is nil.
|
|
||||||
guards *[]sync.Once
|
|
||||||
}
|
|
||||||
|
|
||||||
// newMethodList creates a non-lazy method list holding the given methods.
|
|
||||||
func newMethodList(methods []*Func) *methodList {
|
|
||||||
return &methodList{methods: methods}
|
|
||||||
}
|
|
||||||
|
|
||||||
// newLazyMethodList creates a lazy method list of the given length. Methods
|
|
||||||
// may be resolved lazily for a given index by providing a resolver function.
|
|
||||||
func newLazyMethodList(length int) *methodList {
|
|
||||||
guards := make([]sync.Once, length)
|
|
||||||
return &methodList{
|
|
||||||
methods: make([]*Func, length),
|
|
||||||
guards: &guards,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// isLazy reports whether the receiver is a lazy method list.
|
|
||||||
func (l *methodList) isLazy() bool {
|
|
||||||
return l != nil && l.guards != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add appends a method to the method list if not not already present. Add
|
|
||||||
// panics if the receiver is lazy.
|
|
||||||
func (l *methodList) Add(m *Func) {
|
|
||||||
assert(!l.isLazy())
|
|
||||||
if i, _ := lookupMethod(l.methods, m.pkg, m.name, false); i < 0 {
|
|
||||||
l.methods = append(l.methods, m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup looks up the method identified by pkg and name in the receiver.
|
|
||||||
// Lookup panics if the receiver is lazy. If foldCase is true, method names
|
|
||||||
// are considered equal if they are equal with case folding.
|
|
||||||
func (l *methodList) Lookup(pkg *Package, name string, foldCase bool) (int, *Func) {
|
|
||||||
assert(!l.isLazy())
|
|
||||||
if l == nil {
|
|
||||||
return -1, nil
|
|
||||||
}
|
|
||||||
return lookupMethod(l.methods, pkg, name, foldCase)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the length of the method list.
|
|
||||||
func (l *methodList) Len() int {
|
|
||||||
if l == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return len(l.methods)
|
|
||||||
}
|
|
||||||
|
|
||||||
// At returns the i'th method of the method list. At panics if i is out of
|
|
||||||
// bounds, or if the receiver is lazy and resolve is nil.
|
|
||||||
func (l *methodList) At(i int, resolve func() *Func) *Func {
|
|
||||||
if !l.isLazy() {
|
|
||||||
return l.methods[i]
|
|
||||||
}
|
|
||||||
assert(resolve != nil)
|
|
||||||
(*l.guards)[i].Do(func() {
|
|
||||||
l.methods[i] = resolve()
|
|
||||||
})
|
|
||||||
return l.methods[i]
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
// Copyright 2022 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 types2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestLazyMethodList(t *testing.T) {
|
|
||||||
l := newLazyMethodList(2)
|
|
||||||
|
|
||||||
if got := l.Len(); got != 2 {
|
|
||||||
t.Fatalf("Len() = %d, want 2", got)
|
|
||||||
}
|
|
||||||
|
|
||||||
f0 := NewFunc(nopos, nil, "f0", nil)
|
|
||||||
f1 := NewFunc(nopos, nil, "f1", nil)
|
|
||||||
|
|
||||||
// Verify that methodList.At is idempotent, by calling it repeatedly with a
|
|
||||||
// resolve func that returns different pointer values (f0 or f1).
|
|
||||||
steps := []struct {
|
|
||||||
index int
|
|
||||||
resolve *Func // the *Func returned by the resolver
|
|
||||||
want *Func // the actual *Func returned by methodList.At
|
|
||||||
}{
|
|
||||||
{0, f0, f0},
|
|
||||||
{0, f1, f0},
|
|
||||||
{1, f1, f1},
|
|
||||||
{1, f0, f1},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, step := range steps {
|
|
||||||
got := l.At(step.index, func() *Func { return step.resolve })
|
|
||||||
if got != step.want {
|
|
||||||
t.Errorf("step %d: At(%d, ...) = %s, want %s", i, step.index, got.Name(), step.want.Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -96,14 +96,16 @@ type Named struct {
|
|||||||
underlying Type // possibly a *Named during setup; never a *Named once set up completely
|
underlying Type // possibly a *Named during setup; never a *Named once set up completely
|
||||||
tparams *TypeParamList // type parameters, or nil
|
tparams *TypeParamList // type parameters, or nil
|
||||||
|
|
||||||
// methods declared for this type (not the method set of this type).
|
// methods declared for this type (not the method set of this type)
|
||||||
// Signatures are type-checked lazily.
|
// Signatures are type-checked lazily.
|
||||||
// For non-instantiated types, this is a fully populated list of methods. For
|
// For non-instantiated types, this is a fully populated list of methods. For
|
||||||
// instantiated types, this is a 'lazy' list, and methods are individually
|
// instantiated types, methods are individually expanded when they are first
|
||||||
// expanded when they are first accessed.
|
// accessed.
|
||||||
methods *methodList
|
methods []*Func
|
||||||
|
// number of expanded methods (only valid for instantiated named types)
|
||||||
|
expandedMethods int // expandedMethods <= len(orig.methods)
|
||||||
|
|
||||||
// loader may be provided to lazily load type parameters, underlying, and methods.
|
// loader may be provided to lazily load type parameters, underlying type, and methods.
|
||||||
loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func)
|
loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +114,8 @@ type namedState uint32
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
unresolved namedState = iota // tparams, underlying type and methods might be unavailable
|
unresolved namedState = iota // tparams, underlying type and methods might be unavailable
|
||||||
resolved
|
resolved // resolve has run; methods might be incomplete (for instances)
|
||||||
|
complete // all data is known
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
|
// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
|
||||||
@ -122,7 +125,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
|||||||
if _, ok := underlying.(*Named); ok {
|
if _, ok := underlying.(*Named); ok {
|
||||||
panic("underlying type must not be *Named")
|
panic("underlying type must not be *Named")
|
||||||
}
|
}
|
||||||
return (*Checker)(nil).newNamed(obj, nil, underlying, newMethodList(methods))
|
return (*Checker)(nil).newNamed(obj, nil, underlying, methods)
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve resolves the type parameters, methods, and underlying type of n.
|
// resolve resolves the type parameters, methods, and underlying type of n.
|
||||||
@ -156,8 +159,12 @@ func (n *Named) resolve(ctxt *Context) *Named {
|
|||||||
n.tparams = n.orig.tparams
|
n.tparams = n.orig.tparams
|
||||||
n.underlying = underlying
|
n.underlying = underlying
|
||||||
n.fromRHS = n.orig.fromRHS // for cycle detection
|
n.fromRHS = n.orig.fromRHS // for cycle detection
|
||||||
n.methods = newLazyMethodList(n.orig.methods.Len())
|
|
||||||
|
if len(n.orig.methods) == 0 {
|
||||||
|
n.setState(complete)
|
||||||
|
} else {
|
||||||
n.setState(resolved)
|
n.setState(resolved)
|
||||||
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,17 +177,18 @@ func (n *Named) resolve(ctxt *Context) *Named {
|
|||||||
// also make the API more future-proof towards further extensions.
|
// also make the API more future-proof towards further extensions.
|
||||||
if n.loader != nil {
|
if n.loader != nil {
|
||||||
assert(n.underlying == nil)
|
assert(n.underlying == nil)
|
||||||
|
assert(n.TypeArgs().Len() == 0) // instances are created by instantiation, in which case n.loader is nil
|
||||||
|
|
||||||
tparams, underlying, methods := n.loader(n)
|
tparams, underlying, methods := n.loader(n)
|
||||||
|
|
||||||
n.tparams = bindTParams(tparams)
|
n.tparams = bindTParams(tparams)
|
||||||
n.underlying = underlying
|
n.underlying = underlying
|
||||||
n.fromRHS = underlying // for cycle detection
|
n.fromRHS = underlying // for cycle detection
|
||||||
n.methods = newMethodList(methods)
|
n.methods = methods
|
||||||
n.loader = nil
|
n.loader = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n.setState(resolved)
|
n.setState(complete)
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +204,7 @@ func (n *Named) setState(state namedState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
|
// newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
|
||||||
func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, methods *methodList) *Named {
|
func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, methods []*Func) *Named {
|
||||||
typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, methods: methods}
|
typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, methods: methods}
|
||||||
if typ.orig == nil {
|
if typ.orig == nil {
|
||||||
typ.orig = typ
|
typ.orig = typ
|
||||||
@ -262,14 +270,38 @@ func (t *Named) TypeArgs() *TypeList { return t.targs }
|
|||||||
// For an ordinary or instantiated type t, the receiver base type of these
|
// For an ordinary or instantiated type t, the receiver base type of these
|
||||||
// methods will be the named type t. For an uninstantiated generic type t, each
|
// methods will be the named type t. For an uninstantiated generic type t, each
|
||||||
// method receiver will be instantiated with its receiver type parameters.
|
// method receiver will be instantiated with its receiver type parameters.
|
||||||
func (t *Named) NumMethods() int { return t.resolve(nil).methods.Len() }
|
func (t *Named) NumMethods() int { return len(t.orig.resolve(nil).methods) }
|
||||||
|
|
||||||
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
|
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
|
||||||
func (t *Named) Method(i int) *Func {
|
func (t *Named) Method(i int) *Func {
|
||||||
t.resolve(nil)
|
t.resolve(nil)
|
||||||
return t.methods.At(i, func() *Func {
|
|
||||||
return t.expandMethod(i)
|
if t.state() >= complete {
|
||||||
})
|
return t.methods[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(t.TypeArgs().Len() > 0) // only instances should have incomplete methods
|
||||||
|
|
||||||
|
t.mu.Lock()
|
||||||
|
defer t.mu.Unlock()
|
||||||
|
|
||||||
|
if len(t.methods) != len(t.orig.methods) {
|
||||||
|
assert(len(t.methods) == 0)
|
||||||
|
t.methods = make([]*Func, len(t.orig.methods))
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.methods[i] == nil {
|
||||||
|
t.methods[i] = t.expandMethod(i)
|
||||||
|
t.expandedMethods++
|
||||||
|
|
||||||
|
// Check if we've created all methods at this point. If we have, mark the
|
||||||
|
// type as fully expanded.
|
||||||
|
if t.expandedMethods == len(t.orig.methods) {
|
||||||
|
t.setState(complete)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.methods[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// expandMethod substitutes type arguments in the i'th method for an
|
// expandMethod substitutes type arguments in the i'th method for an
|
||||||
@ -351,10 +383,9 @@ func (t *Named) SetUnderlying(underlying Type) {
|
|||||||
func (t *Named) AddMethod(m *Func) {
|
func (t *Named) AddMethod(m *Func) {
|
||||||
assert(t.targs.Len() == 0)
|
assert(t.targs.Len() == 0)
|
||||||
t.resolve(nil)
|
t.resolve(nil)
|
||||||
if t.methods == nil {
|
if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 {
|
||||||
t.methods = newMethodList(nil)
|
t.methods = append(t.methods, m)
|
||||||
}
|
}
|
||||||
t.methods.Add(m)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Named) Underlying() Type { return t.resolve(nil).underlying }
|
func (t *Named) Underlying() Type { return t.resolve(nil).underlying }
|
||||||
@ -462,7 +493,7 @@ func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Fu
|
|||||||
// If n is an instance, we may not have yet instantiated all of its methods.
|
// If n is an instance, we may not have yet instantiated all of its methods.
|
||||||
// Look up the method index in orig, and only instantiate method at the
|
// Look up the method index in orig, and only instantiate method at the
|
||||||
// matching index (if any).
|
// matching index (if any).
|
||||||
i, _ := n.orig.methods.Lookup(pkg, name, foldCase)
|
i, _ := lookupMethod(n.orig.methods, pkg, name, foldCase)
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ func TestSizeof(t *testing.T) {
|
|||||||
{Interface{}, 40, 80},
|
{Interface{}, 40, 80},
|
||||||
{Map{}, 16, 32},
|
{Map{}, 16, 32},
|
||||||
{Chan{}, 12, 24},
|
{Chan{}, 12, 24},
|
||||||
{Named{}, 56, 104},
|
{Named{}, 68, 128},
|
||||||
{TypeParam{}, 28, 48},
|
{TypeParam{}, 28, 48},
|
||||||
{term{}, 12, 24},
|
{term{}, 12, 24},
|
||||||
|
|
||||||
|
@ -722,8 +722,8 @@ func (check *Checker) collectMethods(obj *TypeName) {
|
|||||||
// Checker.Files may be called multiple times; additional package files
|
// Checker.Files may be called multiple times; additional package files
|
||||||
// may add methods to already type-checked types. Add pre-existing methods
|
// may add methods to already type-checked types. Add pre-existing methods
|
||||||
// so that we can detect redeclarations.
|
// so that we can detect redeclarations.
|
||||||
for i := 0; i < base.methods.Len(); i++ {
|
for i := 0; i < base.NumMethods(); i++ {
|
||||||
m := base.methods.At(i, nil)
|
m := base.Method(i)
|
||||||
assert(m.name != "_")
|
assert(m.name != "_")
|
||||||
assert(mset.insert(m) == nil)
|
assert(mset.insert(m) == nil)
|
||||||
}
|
}
|
||||||
@ -749,8 +749,8 @@ func (check *Checker) collectMethods(obj *TypeName) {
|
|||||||
func (check *Checker) checkFieldUniqueness(base *Named) {
|
func (check *Checker) checkFieldUniqueness(base *Named) {
|
||||||
if t, _ := base.under().(*Struct); t != nil {
|
if t, _ := base.under().(*Struct); t != nil {
|
||||||
var mset objset
|
var mset objset
|
||||||
for i := 0; i < base.methods.Len(); i++ {
|
for i := 0; i < base.NumMethods(); i++ {
|
||||||
m := base.methods.At(i, nil)
|
m := base.Method(i)
|
||||||
assert(m.name != "_")
|
assert(m.name != "_")
|
||||||
assert(mset.insert(m) == nil)
|
assert(mset.insert(m) == nil)
|
||||||
}
|
}
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
// Copyright 2022 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 types
|
|
||||||
|
|
||||||
import "sync"
|
|
||||||
|
|
||||||
// methodList holds a list of methods that may be lazily resolved by a provided
|
|
||||||
// resolution method.
|
|
||||||
type methodList struct {
|
|
||||||
methods []*Func
|
|
||||||
|
|
||||||
// guards synchronizes the instantiation of lazy methods. For lazy method
|
|
||||||
// lists, guards is non-nil and of the length passed to newLazyMethodList.
|
|
||||||
// For non-lazy method lists, guards is nil.
|
|
||||||
guards *[]sync.Once
|
|
||||||
}
|
|
||||||
|
|
||||||
// newMethodList creates a non-lazy method list holding the given methods.
|
|
||||||
func newMethodList(methods []*Func) *methodList {
|
|
||||||
return &methodList{methods: methods}
|
|
||||||
}
|
|
||||||
|
|
||||||
// newLazyMethodList creates a lazy method list of the given length. Methods
|
|
||||||
// may be resolved lazily for a given index by providing a resolver function.
|
|
||||||
func newLazyMethodList(length int) *methodList {
|
|
||||||
guards := make([]sync.Once, length)
|
|
||||||
return &methodList{
|
|
||||||
methods: make([]*Func, length),
|
|
||||||
guards: &guards,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// isLazy reports whether the receiver is a lazy method list.
|
|
||||||
func (l *methodList) isLazy() bool {
|
|
||||||
return l != nil && l.guards != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add appends a method to the method list if not not already present. Add
|
|
||||||
// panics if the receiver is lazy.
|
|
||||||
func (l *methodList) Add(m *Func) {
|
|
||||||
assert(!l.isLazy())
|
|
||||||
if i, _ := lookupMethod(l.methods, m.pkg, m.name, false); i < 0 {
|
|
||||||
l.methods = append(l.methods, m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup looks up the method identified by pkg and name in the receiver.
|
|
||||||
// Lookup panics if the receiver is lazy. If foldCase is true, method names
|
|
||||||
// are considered equal if they are equal with case folding.
|
|
||||||
func (l *methodList) Lookup(pkg *Package, name string, foldCase bool) (int, *Func) {
|
|
||||||
assert(!l.isLazy())
|
|
||||||
if l == nil {
|
|
||||||
return -1, nil
|
|
||||||
}
|
|
||||||
return lookupMethod(l.methods, pkg, name, foldCase)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the length of the method list.
|
|
||||||
func (l *methodList) Len() int {
|
|
||||||
if l == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return len(l.methods)
|
|
||||||
}
|
|
||||||
|
|
||||||
// At returns the i'th method of the method list. At panics if i is out of
|
|
||||||
// bounds, or if the receiver is lazy and resolve is nil.
|
|
||||||
func (l *methodList) At(i int, resolve func() *Func) *Func {
|
|
||||||
if !l.isLazy() {
|
|
||||||
return l.methods[i]
|
|
||||||
}
|
|
||||||
assert(resolve != nil)
|
|
||||||
(*l.guards)[i].Do(func() {
|
|
||||||
l.methods[i] = resolve()
|
|
||||||
})
|
|
||||||
return l.methods[i]
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
// Copyright 2022 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 types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/token"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestLazyMethodList(t *testing.T) {
|
|
||||||
l := newLazyMethodList(2)
|
|
||||||
|
|
||||||
if got := l.Len(); got != 2 {
|
|
||||||
t.Fatalf("Len() = %d, want 2", got)
|
|
||||||
}
|
|
||||||
|
|
||||||
f0 := NewFunc(token.NoPos, nil, "f0", nil)
|
|
||||||
f1 := NewFunc(token.NoPos, nil, "f1", nil)
|
|
||||||
|
|
||||||
// Verify that methodList.At is idempotent, by calling it repeatedly with a
|
|
||||||
// resolve func that returns different pointer values (f0 or f1).
|
|
||||||
steps := []struct {
|
|
||||||
index int
|
|
||||||
resolve *Func // the *Func returned by the resolver
|
|
||||||
want *Func // the actual *Func returned by methodList.At
|
|
||||||
}{
|
|
||||||
{0, f0, f0},
|
|
||||||
{0, f1, f0},
|
|
||||||
{1, f1, f1},
|
|
||||||
{1, f0, f1},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, step := range steps {
|
|
||||||
got := l.At(step.index, func() *Func { return step.resolve })
|
|
||||||
if got != step.want {
|
|
||||||
t.Errorf("step %d: At(%d, ...) = %s, want %s", i, step.index, got.Name(), step.want.Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -96,14 +96,16 @@ type Named struct {
|
|||||||
underlying Type // possibly a *Named during setup; never a *Named once set up completely
|
underlying Type // possibly a *Named during setup; never a *Named once set up completely
|
||||||
tparams *TypeParamList // type parameters, or nil
|
tparams *TypeParamList // type parameters, or nil
|
||||||
|
|
||||||
// methods declared for this type (not the method set of this type).
|
// methods declared for this type (not the method set of this type)
|
||||||
// Signatures are type-checked lazily.
|
// Signatures are type-checked lazily.
|
||||||
// For non-instantiated types, this is a fully populated list of methods. For
|
// For non-instantiated types, this is a fully populated list of methods. For
|
||||||
// instantiated types, this is a 'lazy' list, and methods are individually
|
// instantiated types, methods are individually expanded when they are first
|
||||||
// expanded when they are first accessed.
|
// accessed.
|
||||||
methods *methodList
|
methods []*Func
|
||||||
|
// number of expanded methods (only valid for instantiated named types)
|
||||||
|
expandedMethods int // expandedMethods <= len(orig.methods)
|
||||||
|
|
||||||
// loader may be provided to lazily load type parameters, underlying, and methods.
|
// loader may be provided to lazily load type parameters, underlying type, and methods.
|
||||||
loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func)
|
loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +114,8 @@ type namedState uint32
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
unresolved namedState = iota // tparams, underlying type and methods might be unavailable
|
unresolved namedState = iota // tparams, underlying type and methods might be unavailable
|
||||||
resolved
|
resolved // resolve has run; methods might be incomplete (for instances)
|
||||||
|
complete // all data is known
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
|
// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
|
||||||
@ -122,7 +125,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
|||||||
if _, ok := underlying.(*Named); ok {
|
if _, ok := underlying.(*Named); ok {
|
||||||
panic("underlying type must not be *Named")
|
panic("underlying type must not be *Named")
|
||||||
}
|
}
|
||||||
return (*Checker)(nil).newNamed(obj, nil, underlying, newMethodList(methods))
|
return (*Checker)(nil).newNamed(obj, nil, underlying, methods)
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve resolves the type parameters, methods, and underlying type of n.
|
// resolve resolves the type parameters, methods, and underlying type of n.
|
||||||
@ -156,8 +159,12 @@ func (n *Named) resolve(ctxt *Context) *Named {
|
|||||||
n.tparams = n.orig.tparams
|
n.tparams = n.orig.tparams
|
||||||
n.underlying = underlying
|
n.underlying = underlying
|
||||||
n.fromRHS = n.orig.fromRHS // for cycle detection
|
n.fromRHS = n.orig.fromRHS // for cycle detection
|
||||||
n.methods = newLazyMethodList(n.orig.methods.Len())
|
|
||||||
|
if len(n.orig.methods) == 0 {
|
||||||
|
n.setState(complete)
|
||||||
|
} else {
|
||||||
n.setState(resolved)
|
n.setState(resolved)
|
||||||
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,17 +177,18 @@ func (n *Named) resolve(ctxt *Context) *Named {
|
|||||||
// also make the API more future-proof towards further extensions.
|
// also make the API more future-proof towards further extensions.
|
||||||
if n.loader != nil {
|
if n.loader != nil {
|
||||||
assert(n.underlying == nil)
|
assert(n.underlying == nil)
|
||||||
|
assert(n.TypeArgs().Len() == 0) // instances are created by instantiation, in which case n.loader is nil
|
||||||
|
|
||||||
tparams, underlying, methods := n.loader(n)
|
tparams, underlying, methods := n.loader(n)
|
||||||
|
|
||||||
n.tparams = bindTParams(tparams)
|
n.tparams = bindTParams(tparams)
|
||||||
n.underlying = underlying
|
n.underlying = underlying
|
||||||
n.fromRHS = underlying // for cycle detection
|
n.fromRHS = underlying // for cycle detection
|
||||||
n.methods = newMethodList(methods)
|
n.methods = methods
|
||||||
n.loader = nil
|
n.loader = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n.setState(resolved)
|
n.setState(complete)
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +204,7 @@ func (n *Named) setState(state namedState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
|
// newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
|
||||||
func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, methods *methodList) *Named {
|
func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, methods []*Func) *Named {
|
||||||
typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, methods: methods}
|
typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, methods: methods}
|
||||||
if typ.orig == nil {
|
if typ.orig == nil {
|
||||||
typ.orig = typ
|
typ.orig = typ
|
||||||
@ -264,14 +272,38 @@ func (t *Named) TypeArgs() *TypeList { return t.targs }
|
|||||||
// For an ordinary or instantiated type t, the receiver base type of these
|
// For an ordinary or instantiated type t, the receiver base type of these
|
||||||
// methods will be the named type t. For an uninstantiated generic type t, each
|
// methods will be the named type t. For an uninstantiated generic type t, each
|
||||||
// method receiver will be instantiated with its receiver type parameters.
|
// method receiver will be instantiated with its receiver type parameters.
|
||||||
func (t *Named) NumMethods() int { return t.resolve(nil).methods.Len() }
|
func (t *Named) NumMethods() int { return len(t.orig.resolve(nil).methods) }
|
||||||
|
|
||||||
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
|
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
|
||||||
func (t *Named) Method(i int) *Func {
|
func (t *Named) Method(i int) *Func {
|
||||||
t.resolve(nil)
|
t.resolve(nil)
|
||||||
return t.methods.At(i, func() *Func {
|
|
||||||
return t.expandMethod(i)
|
if t.state() >= complete {
|
||||||
})
|
return t.methods[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(t.TypeArgs().Len() > 0) // only instances should have incomplete methods
|
||||||
|
|
||||||
|
t.mu.Lock()
|
||||||
|
defer t.mu.Unlock()
|
||||||
|
|
||||||
|
if len(t.methods) != len(t.orig.methods) {
|
||||||
|
assert(len(t.methods) == 0)
|
||||||
|
t.methods = make([]*Func, len(t.orig.methods))
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.methods[i] == nil {
|
||||||
|
t.methods[i] = t.expandMethod(i)
|
||||||
|
t.expandedMethods++
|
||||||
|
|
||||||
|
// Check if we've created all methods at this point. If we have, mark the
|
||||||
|
// type as fully expanded.
|
||||||
|
if t.expandedMethods == len(t.orig.methods) {
|
||||||
|
t.setState(complete)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.methods[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// expandMethod substitutes type arguments in the i'th method for an
|
// expandMethod substitutes type arguments in the i'th method for an
|
||||||
@ -353,10 +385,9 @@ func (t *Named) SetUnderlying(underlying Type) {
|
|||||||
func (t *Named) AddMethod(m *Func) {
|
func (t *Named) AddMethod(m *Func) {
|
||||||
assert(t.targs.Len() == 0)
|
assert(t.targs.Len() == 0)
|
||||||
t.resolve(nil)
|
t.resolve(nil)
|
||||||
if t.methods == nil {
|
if i, _ := lookupMethod(t.methods, m.pkg, m.name, false); i < 0 {
|
||||||
t.methods = newMethodList(nil)
|
t.methods = append(t.methods, m)
|
||||||
}
|
}
|
||||||
t.methods.Add(m)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Named) Underlying() Type { return t.resolve(nil).underlying }
|
func (t *Named) Underlying() Type { return t.resolve(nil).underlying }
|
||||||
@ -464,7 +495,7 @@ func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Fu
|
|||||||
// If n is an instance, we may not have yet instantiated all of its methods.
|
// If n is an instance, we may not have yet instantiated all of its methods.
|
||||||
// Look up the method index in orig, and only instantiate method at the
|
// Look up the method index in orig, and only instantiate method at the
|
||||||
// matching index (if any).
|
// matching index (if any).
|
||||||
i, _ := n.orig.methods.Lookup(pkg, name, foldCase)
|
i, _ := lookupMethod(n.orig.methods, pkg, name, foldCase)
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ func TestSizeof(t *testing.T) {
|
|||||||
{Interface{}, 40, 80},
|
{Interface{}, 40, 80},
|
||||||
{Map{}, 16, 32},
|
{Map{}, 16, 32},
|
||||||
{Chan{}, 12, 24},
|
{Chan{}, 12, 24},
|
||||||
{Named{}, 56, 104},
|
{Named{}, 68, 128},
|
||||||
{TypeParam{}, 28, 48},
|
{TypeParam{}, 28, 48},
|
||||||
{term{}, 12, 24},
|
{term{}, 12, 24},
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user