// Identical reports whether x and y are identical types.
// Receivers of Signature types are ignored.
func Identical(x, y Type) bool {
- return (*Checker)(nil).identical(x, y)
+ return identical(x, y, true, nil)
}
// IdenticalIgnoreTags reports whether x and y are identical types if tags are ignored.
// Receivers of Signature types are ignored.
func IdenticalIgnoreTags(x, y Type) bool {
- return (*Checker)(nil).identicalIgnoreTags(x, y)
+ return identical(x, y, false, nil)
}
}
// both argument types must be identical
- if !check.identical(x.typ, y.typ) {
+ if !Identical(x.typ, y.typ) {
check.invalidArg(x, _InvalidComplex, "mismatched types %s and %s", x.typ, y.typ)
return
}
return
}
- if !check.identical(dst, src) {
+ if !Identical(dst, src) {
check.invalidArg(x, _InvalidCopy, "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
return
}
base := derefStructPtr(x.typ)
sel := selx.Sel.Name
- obj, index, indirect := check.lookupFieldOrMethod(base, false, check.pkg, sel)
+ obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel)
switch obj.(type) {
case nil:
check.invalidArg(x, _MissingFieldOrMethod, "%s has no single field %s", base, sel)
check.instantiatedOperand(x)
- obj, index, indirect = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
+ obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
if obj == nil {
switch {
case index != nil:
} else {
changeCase = string(unicode.ToUpper(r)) + sel[1:]
}
- if obj, _, _ = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil {
+ if obj, _, _ = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil {
why += ", but does have " + changeCase
}
}
V := x.typ
Vu := under(V)
Tu := under(T)
- if check.identicalIgnoreTags(Vu, Tu) {
+ if IdenticalIgnoreTags(Vu, Tu) {
return true
}
// have identical underlying types if tags are ignored"
if V, ok := V.(*Pointer); ok {
if T, ok := T.(*Pointer); ok {
- if check.identicalIgnoreTags(under(V.base), under(T.base)) {
+ if IdenticalIgnoreTags(under(V.base), under(T.base)) {
return true
}
}
if s := asSlice(V); s != nil {
if p := asPointer(T); p != nil {
if a := asArray(p.Elem()); a != nil {
- if check.identical(s.Elem(), a.Elem()) {
+ if Identical(s.Elem(), a.Elem()) {
if check == nil || check.allowVersion(check.pkg, 1, 17) {
return true
}
return
}
- if !check.identical(x.typ, y.typ) {
+ if !Identical(x.typ, y.typ) {
// only report an error if we have valid types
// (otherwise we had an error reported elsewhere already)
if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] {
xkey := keyVal(x.val)
if asInterface(utyp.key) != nil {
for _, vtyp := range visited[xkey] {
- if check.identical(vtyp, x.typ) {
+ if Identical(vtyp, x.typ) {
duplicate = true
break
}
}
var msg string
if wrongType != nil {
- if check.identical(method.typ, wrongType.typ) {
+ if Identical(method.typ, wrongType.typ) {
msg = fmt.Sprintf("missing method %s (%s has pointer receiver)", method.name, method.name)
} else {
msg = fmt.Sprintf("wrong type for method %s (have %s, want %s)", method.name, wrongType.typ, method.typ)
// Unify parameter and argument types for generic parameters with typed arguments
// and collect the indices of generic parameters with untyped arguments.
// Terminology: generic parameter = function parameter with a type-parameterized type
- u := newUnifier(check, false)
+ u := newUnifier(false)
u.x.init(tparams)
// Set the type arguments which we know already.
// Setup bidirectional unification between those structural bounds
// and the corresponding type arguments (which may be nil!).
- u := newUnifier(check, false)
+ u := newUnifier(false)
u.x.init(tparams)
u.y = u.x // type parameters between LHS and RHS of unification are identical
}
// check != nil
check.later(func() {
- if !check.allowVersion(m.pkg, 1, 14) || !check.identical(m.typ, other.Type()) {
+ if !check.allowVersion(m.pkg, 1, 14) || !Identical(m.typ, other.Type()) {
check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
}
import "go/token"
+// Internal use of LookupFieldOrMethod: If the obj result is a method
+// associated with a concrete (non-interface) type, the method's signature
+// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing
+// the method's type.
+
// LookupFieldOrMethod looks up a field or method with given package and name
// in T and returns the corresponding *Var or *Func, an index sequence, and a
// bool indicating if there were any pointer indirections on the path to the
// the method's formal receiver base type, nor was the receiver addressable.
//
func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
- return (*Checker)(nil).lookupFieldOrMethod(T, addressable, pkg, name)
-}
-
-// Internal use of Checker.lookupFieldOrMethod: If the obj result is a method
-// associated with a concrete (non-interface) type, the method's signature
-// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing
-// the method's type.
-// TODO(gri) Now that we provide the *Checker, we can probably remove this
-// caveat by calling Checker.objDecl from lookupFieldOrMethod. Investigate.
-
-// lookupFieldOrMethod is like the external version but completes interfaces
-// as necessary.
-func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
// Methods cannot be associated to a named pointer type
// (spec: "The type denoted by T is called the receiver base type;
// it must not be a pointer or interface type and it must be declared
// not have found it for T (see also issue 8590).
if t := asNamed(T); t != nil {
if p, _ := t.Underlying().(*Pointer); p != nil {
- obj, index, indirect = check.rawLookupFieldOrMethod(p, false, pkg, name)
+ obj, index, indirect = lookupFieldOrMethod(p, false, pkg, name)
if _, ok := obj.(*Func); ok {
return nil, nil, false
}
}
}
- return check.rawLookupFieldOrMethod(T, addressable, pkg, name)
+ return lookupFieldOrMethod(T, addressable, pkg, name)
}
// TODO(gri) The named type consolidation and seen maps below must be
// types always have only one representation (even when imported
// indirectly via different packages.)
-// rawLookupFieldOrMethod should only be called by lookupFieldOrMethod and missingMethod.
-func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
+// lookupFieldOrMethod should only be called by LookupFieldOrMethod and missingMethod.
+func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
// WARNING: The code in this function is extremely subtle - do not modify casually!
- // This function and NewMethodSet should be kept in sync.
if name == "_" {
return // blank fields/methods are never found
return
}
- current = check.consolidateMultiples(next)
+ current = consolidateMultiples(next)
}
return nil, nil, false // not found
// consolidateMultiples collects multiple list entries with the same type
// into a single entry marked as containing multiples. The result is the
// consolidated list.
-func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType {
+func consolidateMultiples(list []embeddedType) []embeddedType {
if len(list) <= 1 {
return list // at most one entry - nothing to do
}
n := 0 // number of entries w/ unique type
prev := make(map[Type]int) // index at which type was previously seen
for _, e := range list {
- if i, found := check.lookupType(prev, e.typ); found {
+ if i, found := lookupType(prev, e.typ); found {
list[i].multiples = true
// ignore this entry
} else {
return list[:n]
}
-func (check *Checker) lookupType(m map[Type]int, typ Type) (int, bool) {
+func lookupType(m map[Type]int, typ Type) (int, bool) {
// fast path: maybe the types are equal
if i, found := m[typ]; found {
return i, true
}
for t, i := range m {
- if check.identical(t, typ) {
+ if Identical(t, typ) {
return i, true
}
}
// to see if they can be made to match.
// TODO(gri) is this always correct? what about type bounds?
// (Alternative is to rename/subst type parameters and compare.)
- u := newUnifier(check, true)
+ u := newUnifier(true)
u.x.init(ftyp.tparams)
if !u.unify(ftyp, mtyp) {
return m, f
Vn := asNamed(Vd)
for _, m := range T.typeSet().methods {
// TODO(gri) should this be calling lookupFieldOrMethod instead (and why not)?
- obj, _, _ := check.rawLookupFieldOrMethod(V, false, m.pkg, m.name)
+ obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name)
// Check if *V implements this method of T.
if obj == nil {
ptr := NewPointer(V)
- obj, _, _ = check.rawLookupFieldOrMethod(ptr, false, m.pkg, m.name)
+ obj, _, _ = lookupFieldOrMethod(ptr, false, m.pkg, m.name)
if obj != nil {
return m, obj.(*Func)
}
// to see if they can be made to match.
// TODO(gri) is this always correct? what about type bounds?
// (Alternative is to rename/subst type parameters and compare.)
- u := newUnifier(check, true)
+ u := newUnifier(true)
u.x.init(ftyp.rparams)
if !u.unify(ftyp, mtyp) {
return m, f
}
}
- // It's ok to call consolidateMultiples with a nil *Checker because
- // MethodSets are not used internally (outside debug mode). When used
- // externally, interfaces are expected to be completed and then we do
- // not need a *Checker to complete them when (indirectly) calling
- // Checker.identical via consolidateMultiples.
- current = (*Checker)(nil).consolidateMultiples(next)
+ current = consolidateMultiples(next)
}
if len(base) == 0 {
}
// x's type is identical to T
- if check.identical(V, T) {
+ if Identical(V, T) {
return true, 0
}
// x's type V and T have identical underlying types
// and at least one of V or T is not a named type
- if check.identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
+ if Identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
return true, 0
}
if m, wrongType := check.missingMethod(V, Ti, true); m != nil /* Implements(V, Ti) */ {
if reason != nil {
if wrongType != nil {
- if check.identical(m.typ, wrongType.typ) {
+ if Identical(m.typ, wrongType.typ) {
*reason = fmt.Sprintf("missing method %s (%s has pointer receiver)", m.name, m.name)
} else {
*reason = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrongType.typ, m.typ)
// type, x's type V and T have identical element types,
// and at least one of V or T is not a named type
if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
- if Tc, ok := Tu.(*Chan); ok && check.identical(Vc.elem, Tc.elem) {
+ if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) {
return !isNamed(V) || !isNamed(T), _InvalidChanAssign
}
}
return false
}
-// identical reports whether x and y are identical types.
-// Receivers of Signature types are ignored.
-func (check *Checker) identical(x, y Type) bool {
- return check.identical0(x, y, true, nil)
-}
-
-// identicalIgnoreTags reports whether x and y are identical types if tags are ignored.
-// Receivers of Signature types are ignored.
-func (check *Checker) identicalIgnoreTags(x, y Type) bool {
- return check.identical0(x, y, false, nil)
-}
-
// An ifacePair is a node in a stack of interface type pairs compared for identity.
type ifacePair struct {
x, y *Interface
}
// For changes to this code the corresponding changes should be made to unifier.nify.
-func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
+func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
// types must be expanded for comparison
x = expandf(x)
y = expandf(y)
if y, ok := y.(*Array); ok {
// If one or both array lengths are unknown (< 0) due to some error,
// assume they are the same to avoid spurious follow-on errors.
- return (x.len < 0 || y.len < 0 || x.len == y.len) && check.identical0(x.elem, y.elem, cmpTags, p)
+ return (x.len < 0 || y.len < 0 || x.len == y.len) && identical(x.elem, y.elem, cmpTags, p)
}
case *Slice:
// Two slice types are identical if they have identical element types.
if y, ok := y.(*Slice); ok {
- return check.identical0(x.elem, y.elem, cmpTags, p)
+ return identical(x.elem, y.elem, cmpTags, p)
}
case *Struct:
if f.embedded != g.embedded ||
cmpTags && x.Tag(i) != y.Tag(i) ||
!f.sameId(g.pkg, g.name) ||
- !check.identical0(f.typ, g.typ, cmpTags, p) {
+ !identical(f.typ, g.typ, cmpTags, p) {
return false
}
}
case *Pointer:
// Two pointer types are identical if they have identical base types.
if y, ok := y.(*Pointer); ok {
- return check.identical0(x.base, y.base, cmpTags, p)
+ return identical(x.base, y.base, cmpTags, p)
}
case *Tuple:
if x != nil {
for i, v := range x.vars {
w := y.vars[i]
- if !check.identical0(v.typ, w.typ, cmpTags, p) {
+ if !identical(v.typ, w.typ, cmpTags, p) {
return false
}
}
// parameter names.
if y, ok := y.(*Signature); ok {
return x.variadic == y.variadic &&
- check.identicalTParams(x.tparams, y.tparams, cmpTags, p) &&
- check.identical0(x.params, y.params, cmpTags, p) &&
- check.identical0(x.results, y.results, cmpTags, p)
+ identicalTParams(x.tparams, y.tparams, cmpTags, p) &&
+ identical(x.params, y.params, cmpTags, p) &&
+ identical(x.results, y.results, cmpTags, p)
}
case *Union:
}
for i, f := range a {
g := b[i]
- if f.Id() != g.Id() || !check.identical0(f.typ, g.typ, cmpTags, q) {
+ if f.Id() != g.Id() || !identical(f.typ, g.typ, cmpTags, q) {
return false
}
}
case *Map:
// Two map types are identical if they have identical key and value types.
if y, ok := y.(*Map); ok {
- return check.identical0(x.key, y.key, cmpTags, p) && check.identical0(x.elem, y.elem, cmpTags, p)
+ return identical(x.key, y.key, cmpTags, p) && identical(x.elem, y.elem, cmpTags, p)
}
case *Chan:
// Two channel types are identical if they have identical value types
// and the same direction.
if y, ok := y.(*Chan); ok {
- return x.dir == y.dir && check.identical0(x.elem, y.elem, cmpTags, p)
+ return x.dir == y.dir && identical(x.elem, y.elem, cmpTags, p)
}
case *Named:
return false
}
-func (check *Checker) identicalTParams(x, y []*TypeName, cmpTags bool, p *ifacePair) bool {
+func identicalTParams(x, y []*TypeName, cmpTags bool, p *ifacePair) bool {
if len(x) != len(y) {
return false
}
for i, x := range x {
y := y[i]
- if !check.identical0(x.typ.(*TypeParam).bound, y.typ.(*TypeParam).bound, cmpTags, p) {
+ if !identical(x.typ.(*TypeParam).bound, y.typ.(*TypeParam).bound, cmpTags, p) {
return false
}
}
// look for duplicate types for a given value
// (quadratic algorithm, but these lists tend to be very short)
for _, vt := range seen[val] {
- if check.identical(v.typ, vt.typ) {
+ if Identical(v.typ, vt.typ) {
check.errorf(&v, _DuplicateCase, "duplicate case %s in expression switch", &v)
check.error(atPos(vt.pos), _DuplicateCase, "\tprevious case") // secondary error, \t indented
continue L
// look for duplicate types
// (quadratic algorithm, but type switches tend to be reasonably small)
for t, other := range seen {
- if T == nil && t == nil || T != nil && t != nil && check.identical(T, t) {
+ if T == nil && t == nil || T != nil && t != nil && Identical(T, t) {
// talk about "case" rather than "type" because of nil case
Ts := "nil"
if T != nil {
import (
"bytes"
+ "fmt"
"sort"
)
// and the respective types inferred for each type parameter.
// A unifier is created by calling newUnifier.
type unifier struct {
- check *Checker
exact bool
x, y tparamsList // x and y must initialized via tparamsList.init
types []Type // inferred types, shared by x and y
// exactly. If exact is not set, a named type's underlying type
// is considered if unification would fail otherwise, and the
// direction of channels is ignored.
-func newUnifier(check *Checker, exact bool) *unifier {
- u := &unifier{check: check, exact: exact}
+func newUnifier(exact bool) *unifier {
+ u := &unifier{exact: exact}
u.x.unifier = u
u.y.unifier = u
return u
// avoid a crash in case of nil type
default:
- u.check.dump("### u.nify(%s, %s), u.x.tparams = %s", x, y, u.x.tparams)
- unreachable()
+ panic(fmt.Sprintf("### u.nify(%s, %s), u.x.tparams = %s", x, y, u.x.tparams))
}
return false