From 0d2f7d411be7ce4e6f7ab8075647631dcbb3ac53 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 11 Jun 2013 10:00:00 -0700 Subject: [PATCH] go.tools/go/types: fix isAssignable and IsIdentical - Imported objects that are explicitly exported may have a nil package; don't use it for qualified name computation (it's not needed). - isAssignable must check all possibilities before declaring failure. Fixes golang/go#5675. R=adonovan CC=golang-dev https://golang.org/cl/10141044 --- go/types/operand.go | 13 +++++++------ go/types/predicates.go | 26 +++++++++++++++++++++----- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/go/types/operand.go b/go/types/operand.go index d001daba..e30043ae 100644 --- a/go/types/operand.go +++ b/go/types/operand.go @@ -139,19 +139,20 @@ func (x *operand) isAssignable(ctxt *Context, T Type) bool { Vu := V.Underlying() Tu := T.Underlying() - // x's type V and T have identical underlying types - // and at least one of V or T is not a named type - if IsIdentical(Vu, Tu) { - return !isNamed(V) || !isNamed(T) - } - // T is an interface type and x implements T + // (Do this check first as it might succeed early.) if Ti, ok := Tu.(*Interface); ok { if m, _ := missingMethod(x.typ, Ti); m == nil { return true } } + // x's type V and T have identical underlying types + // and at least one of V or T is not a named type + if IsIdentical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) { + return true + } + // x is a bidirectional channel value, T is a channel // type, x's type V and T have identical element types, // and at least one of V or T is not a named type diff --git a/go/types/predicates.go b/go/types/predicates.go index c4bdf2f0..c8c5224f 100644 --- a/go/types/predicates.go +++ b/go/types/predicates.go @@ -6,6 +6,8 @@ package types +import "go/ast" + func isNamed(typ Type) bool { if _, ok := typ.(*Basic); ok { return ok @@ -211,6 +213,19 @@ func identicalTypes(a, b *Tuple) bool { return true } +// qname computes the "qualified name" of a function. +// TODO(gri) This is similar in functionality to Field.isMatch. +// Try to consolidate. +func qname(f *Func) string { + if ast.IsExported(f.name) { + return f.name + } + if f.pkg == nil { + panic("unexported function without package information") + } + return f.pkg.path + "." + f.name +} + // identicalMethods returns true if both object sets a and b have the // same length and corresponding methods have identical types. // TODO(gri) make this more efficient (e.g., sort them on completion) @@ -226,14 +241,15 @@ func identicalMethods(a, b *Scope) bool { m := make(map[string]*Func) for _, obj := range a.entries { x := obj.(*Func) - qname := x.pkg.path + "." + x.name - assert(m[qname] == nil) // method list must not have duplicate entries - m[qname] = x + k := qname(x) + assert(m[k] == nil) // method list must not have duplicate entries + m[k] = x } + for _, obj := range b.entries { y := obj.(*Func) - qname := y.pkg.path + "." + y.name - if x := m[qname]; x == nil || !IsIdentical(x.typ, y.typ) { + k := qname(y) + if x := m[k]; x == nil || !IsIdentical(x.typ, y.typ) { return false } }