go.tools/go/types: cleanup: more consistent exported predicate names
Renamed predicates: IsIdentical -> Identical IsAssignableTo -> AssignableTo Signature.IsVariadic -> Signature.Variadic Object.IsExported -> Object.Exported LGTM=adonovan R=adonovan CC=golang-codereviews https://golang.org/cl/53370043
This commit is contained in:
parent
aea9f68811
commit
ebfa4efbc4
|
@ -221,7 +221,7 @@ func (f *File) checkShadowing(ident *ast.Ident) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Don't complain if the types differ: that implies the programmer really wants two variables.
|
// Don't complain if the types differ: that implies the programmer really wants two variables.
|
||||||
if types.IsIdentical(obj.Type(), shadowed.Type()) {
|
if types.Identical(obj.Type(), shadowed.Type()) {
|
||||||
f.Badf(ident.Pos(), "declaration of %s shadows declaration at %s", obj.Name(), f.loc(shadowed.Pos()))
|
f.Badf(ident.Pos(), "declaration of %s shadows declaration at %s", obj.Name(), f.loc(shadowed.Pos()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,7 +132,7 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp
|
||||||
|
|
||||||
case *types.Array:
|
case *types.Array:
|
||||||
// Same as slice.
|
// Same as slice.
|
||||||
if types.IsIdentical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
|
if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
|
||||||
return true // %s matches []byte
|
return true // %s matches []byte
|
||||||
}
|
}
|
||||||
// Recur: []int matches %d.
|
// Recur: []int matches %d.
|
||||||
|
@ -140,7 +140,7 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp
|
||||||
|
|
||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
// Same as array.
|
// Same as array.
|
||||||
if types.IsIdentical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
|
if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
|
||||||
return true // %s matches []byte
|
return true // %s matches []byte
|
||||||
}
|
}
|
||||||
// Recur: []int matches %d. But watch out for
|
// Recur: []int matches %d. But watch out for
|
||||||
|
@ -269,7 +269,7 @@ func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
|
||||||
typ := f.pkg.types[call]
|
typ := f.pkg.types[call]
|
||||||
if typ != nil {
|
if typ != nil {
|
||||||
// We know it's called "Error", so just check the function signature.
|
// We know it's called "Error", so just check the function signature.
|
||||||
return types.IsIdentical(f.pkg.types[call.Fun], stringerMethodType)
|
return types.Identical(f.pkg.types[call.Fun], stringerMethodType)
|
||||||
}
|
}
|
||||||
// Without types, we can still check by hand.
|
// Without types, we can still check by hand.
|
||||||
// Is it a selector expression? Otherwise it's a function call, not a method call.
|
// Is it a selector expression? Otherwise it's a function call, not a method call.
|
||||||
|
|
|
@ -384,7 +384,7 @@ func (p *exporter) signature(sig *types.Signature) {
|
||||||
}
|
}
|
||||||
p.tuple(sig.Params())
|
p.tuple(sig.Params())
|
||||||
p.tuple(sig.Results())
|
p.tuple(sig.Results())
|
||||||
if sig.IsVariadic() {
|
if sig.Variadic() {
|
||||||
p.int(1)
|
p.int(1)
|
||||||
} else {
|
} else {
|
||||||
p.int(0)
|
p.int(0)
|
||||||
|
|
|
@ -63,7 +63,7 @@ func (c *rVBytesConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tSlice, ok := tDyn.Underlying().(*types.Slice)
|
tSlice, ok := tDyn.Underlying().(*types.Slice)
|
||||||
if ok && types.IsIdentical(tSlice.Elem(), types.Typ[types.Uint8]) {
|
if ok && types.Identical(tSlice.Elem(), types.Typ[types.Uint8]) {
|
||||||
if a.onlineCopy(c.result, slice) {
|
if a.onlineCopy(c.result, slice) {
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
|
@ -625,7 +625,7 @@ func (c *rVSetBytesConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tSlice, ok := tDyn.Underlying().(*types.Slice)
|
tSlice, ok := tDyn.Underlying().(*types.Slice)
|
||||||
if ok && types.IsIdentical(tSlice.Elem(), types.Typ[types.Uint8]) {
|
if ok && types.Identical(tSlice.Elem(), types.Typ[types.Uint8]) {
|
||||||
if a.onlineCopy(slice, c.x) {
|
if a.onlineCopy(slice, c.x) {
|
||||||
a.addWork(slice)
|
a.addWork(slice)
|
||||||
}
|
}
|
||||||
|
@ -1559,7 +1559,7 @@ func changeRecv(sig *types.Signature) *types.Signature {
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
p2[i+1] = params.At(i)
|
p2[i+1] = params.At(i)
|
||||||
}
|
}
|
||||||
return types.NewSignature(nil, nil, types.NewTuple(p2...), sig.Results(), sig.IsVariadic())
|
return types.NewSignature(nil, nil, types.NewTuple(p2...), sig.Results(), sig.Variadic())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *rtypeMethodByNameConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
func (c *rtypeMethodByNameConstraint) solve(a *analysis, _ *node, delta nodeset) {
|
||||||
|
|
|
@ -268,7 +268,7 @@ func (c *typeFilterConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||||
panic("indirect tagged object")
|
panic("indirect tagged object")
|
||||||
}
|
}
|
||||||
|
|
||||||
if types.IsAssignableTo(tDyn, c.typ) {
|
if types.AssignableTo(tDyn, c.typ) {
|
||||||
if a.addLabel(c.dst, ifaceObj) {
|
if a.addLabel(c.dst, ifaceObj) {
|
||||||
a.addWork(c.dst)
|
a.addWork(c.dst)
|
||||||
}
|
}
|
||||||
|
@ -277,9 +277,9 @@ func (c *typeFilterConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *untagConstraint) solve(a *analysis, n *node, delta nodeset) {
|
func (c *untagConstraint) solve(a *analysis, n *node, delta nodeset) {
|
||||||
predicate := types.IsAssignableTo
|
predicate := types.AssignableTo
|
||||||
if c.exact {
|
if c.exact {
|
||||||
predicate = types.IsIdentical
|
predicate = types.Identical
|
||||||
}
|
}
|
||||||
for ifaceObj := range delta {
|
for ifaceObj := range delta {
|
||||||
tDyn, v, indirect := a.taggedValue(ifaceObj)
|
tDyn, v, indirect := a.taggedValue(ifaceObj)
|
||||||
|
|
|
@ -843,7 +843,7 @@ func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallEx
|
||||||
|
|
||||||
// Actual->formal assignability conversions for normal parameters.
|
// Actual->formal assignability conversions for normal parameters.
|
||||||
np := sig.Params().Len() // number of normal parameters
|
np := sig.Params().Len() // number of normal parameters
|
||||||
if sig.IsVariadic() {
|
if sig.Variadic() {
|
||||||
np--
|
np--
|
||||||
}
|
}
|
||||||
for i := 0; i < np; i++ {
|
for i := 0; i < np; i++ {
|
||||||
|
@ -852,7 +852,7 @@ func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallEx
|
||||||
|
|
||||||
// Actual->formal assignability conversions for variadic parameter,
|
// Actual->formal assignability conversions for variadic parameter,
|
||||||
// and construction of slice.
|
// and construction of slice.
|
||||||
if sig.IsVariadic() {
|
if sig.Variadic() {
|
||||||
varargs := args[offset+np:]
|
varargs := args[offset+np:]
|
||||||
st := sig.Params().At(np).Type().(*types.Slice)
|
st := sig.Params().At(np).Type().(*types.Slice)
|
||||||
vt := st.Elem()
|
vt := st.Elem()
|
||||||
|
@ -2177,7 +2177,7 @@ func (p *Package) Build() {
|
||||||
// TODO(adonovan): ideally belongs in memberFromObject, but
|
// TODO(adonovan): ideally belongs in memberFromObject, but
|
||||||
// that would require package creation in topological order.
|
// that would require package creation in topological order.
|
||||||
for obj := range p.values {
|
for obj := range p.values {
|
||||||
if obj.IsExported() {
|
if obj.Exported() {
|
||||||
p.needMethodsOf(obj.Type())
|
p.needMethodsOf(obj.Type())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if types.IsIdentical(xt, yt) {
|
if types.Identical(xt, yt) {
|
||||||
// no conversion necessary
|
// no conversion necessary
|
||||||
} else if _, ok := xt.(*types.Interface); ok {
|
} else if _, ok := xt.(*types.Interface); ok {
|
||||||
y = emitConv(f, y, x.Type())
|
y = emitConv(f, y, x.Type())
|
||||||
|
@ -147,7 +147,7 @@ func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value {
|
||||||
//
|
//
|
||||||
func isValuePreserving(ut_src, ut_dst types.Type) bool {
|
func isValuePreserving(ut_src, ut_dst types.Type) bool {
|
||||||
// Identical underlying types?
|
// Identical underlying types?
|
||||||
if types.IsIdentical(ut_dst, ut_src) {
|
if types.Identical(ut_dst, ut_src) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
|
||||||
t_src := val.Type()
|
t_src := val.Type()
|
||||||
|
|
||||||
// Identical types? Conversion is a no-op.
|
// Identical types? Conversion is a no-op.
|
||||||
if types.IsIdentical(t_src, typ) {
|
if types.Identical(t_src, typ) {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -520,7 +520,7 @@ func writeSignature(w io.Writer, pkg *types.Package, name string, sig *types.Sig
|
||||||
}
|
}
|
||||||
io.WriteString(w, v.Name())
|
io.WriteString(w, v.Name())
|
||||||
io.WriteString(w, " ")
|
io.WriteString(w, " ")
|
||||||
if sig.IsVariadic() && i == len(params)-1 {
|
if sig.Variadic() && i == len(params)-1 {
|
||||||
io.WriteString(w, "...")
|
io.WriteString(w, "...")
|
||||||
io.WriteString(w, relType(v.Type().Underlying().(*types.Slice).Elem(), pkg))
|
io.WriteString(w, relType(v.Type().Underlying().(*types.Slice).Elem(), pkg))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -875,7 +875,7 @@ func typeAssert(i *interpreter, instr *ssa.TypeAssert, itf iface) value {
|
||||||
v = itf
|
v = itf
|
||||||
err = checkInterface(i, idst, itf)
|
err = checkInterface(i, idst, itf)
|
||||||
|
|
||||||
} else if types.IsIdentical(itf.t, instr.AssertedType) {
|
} else if types.Identical(itf.t, instr.AssertedType) {
|
||||||
v = copyVal(itf.v) // extract value
|
v = copyVal(itf.v) // extract value
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -96,7 +96,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// hashType returns a hash for t such that
|
// hashType returns a hash for t such that
|
||||||
// types.IsIdentical(x, y) => hashType(x) == hashType(y).
|
// types.Identical(x, y) => hashType(x) == hashType(y).
|
||||||
func hashType(t types.Type) int {
|
func hashType(t types.Type) int {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
h := int(hasher.Hash(t))
|
h := int(hasher.Hash(t))
|
||||||
|
@ -169,12 +169,12 @@ func (x structure) hash(t types.Type) int {
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// nil-tolerant variant of types.IsIdentical.
|
// nil-tolerant variant of types.Identical.
|
||||||
func sameType(x, y types.Type) bool {
|
func sameType(x, y types.Type) bool {
|
||||||
if x == nil {
|
if x == nil {
|
||||||
return y == nil
|
return y == nil
|
||||||
}
|
}
|
||||||
return y != nil && types.IsIdentical(x, y)
|
return y != nil && types.Identical(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x iface) eq(t types.Type, _y interface{}) bool {
|
func (x iface) eq(t types.Type, _y interface{}) bool {
|
||||||
|
@ -191,7 +191,7 @@ func (x rtype) hash(_ types.Type) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x rtype) eq(_ types.Type, y interface{}) bool {
|
func (x rtype) eq(_ types.Type, y interface{}) bool {
|
||||||
return types.IsIdentical(x.t, y.(rtype).t)
|
return types.Identical(x.t, y.(rtype).t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// equals returns true iff x and y are equal according to Go's
|
// equals returns true iff x and y are equal according to Go's
|
||||||
|
|
|
@ -120,7 +120,7 @@ func printCall(v *CallCommon, prefix string, instr Instruction) string {
|
||||||
}
|
}
|
||||||
b.WriteString(relName(arg, instr))
|
b.WriteString(relName(arg, instr))
|
||||||
}
|
}
|
||||||
if v.Signature().IsVariadic() {
|
if v.Signature().Variadic() {
|
||||||
b.WriteString("...")
|
b.WriteString("...")
|
||||||
}
|
}
|
||||||
b.WriteString(")")
|
b.WriteString(")")
|
||||||
|
|
|
@ -284,7 +284,7 @@ func createParams(fn *Function) {
|
||||||
for i, n := 0, tparams.Len(); i < n; i++ {
|
for i, n := 0, tparams.Len(); i < n; i++ {
|
||||||
last = fn.addParamObj(tparams.At(i))
|
last = fn.addParamObj(tparams.At(i))
|
||||||
}
|
}
|
||||||
if fn.Signature.IsVariadic() {
|
if fn.Signature.Variadic() {
|
||||||
last.typ = types.NewSlice(last.typ)
|
last.typ = types.NewSlice(last.typ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -415,5 +415,5 @@ func boundMethodWrapper(prog *Program, obj *types.Func) *Function {
|
||||||
}
|
}
|
||||||
|
|
||||||
func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
|
func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
|
||||||
return types.NewSignature(nil, recv, s.Params(), s.Results(), s.IsVariadic())
|
return types.NewSignature(nil, recv, s.Params(), s.Results(), s.Variadic())
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ func checkFuncValue(t *testing.T, prog *ssa.Program, obj *types.Func) {
|
||||||
obj, fnobj, fn.Name())
|
obj, fnobj, fn.Name())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !types.IsIdentical(fn.Type(), obj.Type()) {
|
if !types.Identical(fn.Type(), obj.Type()) {
|
||||||
t.Errorf("FuncValue(%s).Type() == %s", obj, fn.Type())
|
t.Errorf("FuncValue(%s).Type() == %s", obj, fn.Type())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ func checkConstValue(t *testing.T, prog *ssa.Program, obj *types.Const) {
|
||||||
t.Errorf("ConstValue(%s) == nil", obj)
|
t.Errorf("ConstValue(%s) == nil", obj)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !types.IsIdentical(c.Type(), obj.Type()) {
|
if !types.Identical(c.Type(), obj.Type()) {
|
||||||
t.Errorf("ConstValue(%s).Type() == %s", obj, c.Type())
|
t.Errorf("ConstValue(%s).Type() == %s", obj, c.Type())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,7 @@ func checkVarValue(t *testing.T, prog *ssa.Program, pkg *ssa.Package, ref []ast.
|
||||||
} else if gotAddr {
|
} else if gotAddr {
|
||||||
t.Errorf("%s: got address, want value", prefix)
|
t.Errorf("%s: got address, want value", prefix)
|
||||||
}
|
}
|
||||||
if !types.IsIdentical(v.Type(), expType) {
|
if !types.Identical(v.Type(), expType) {
|
||||||
t.Errorf("%s.Type() == %s, want %s", prefix, v.Type(), expType)
|
t.Errorf("%s.Type() == %s, want %s", prefix, v.Type(), expType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,7 +268,7 @@ func TestValueForExpr(t *testing.T) {
|
||||||
if gotAddr {
|
if gotAddr {
|
||||||
T = T.Underlying().(*types.Pointer).Elem() // deref
|
T = T.Underlying().(*types.Pointer).Elem() // deref
|
||||||
}
|
}
|
||||||
if !types.IsIdentical(T, mainInfo.TypeOf(e)) {
|
if !types.Identical(T, mainInfo.TypeOf(e)) {
|
||||||
t.Errorf("%s: got type %s, want %s", position, mainInfo.TypeOf(e), T)
|
t.Errorf("%s: got type %s, want %s", position, mainInfo.TypeOf(e), T)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1271,7 +1271,7 @@ type anInstruction struct {
|
||||||
// go invoke t3.Run(t2)
|
// go invoke t3.Run(t2)
|
||||||
// defer invoke t4.Handle(...t5)
|
// defer invoke t4.Handle(...t5)
|
||||||
//
|
//
|
||||||
// For all calls to variadic functions (Signature().IsVariadic()),
|
// For all calls to variadic functions (Signature().Variadic()),
|
||||||
// the last element of Args is a slice.
|
// the last element of Args is a slice.
|
||||||
//
|
//
|
||||||
type CallCommon struct {
|
type CallCommon struct {
|
||||||
|
|
|
@ -150,7 +150,7 @@ func testMainSlice(fn *Function, expfuncs []*Function, prefix string, slice type
|
||||||
|
|
||||||
var testfuncs []*Function
|
var testfuncs []*Function
|
||||||
for _, f := range expfuncs {
|
for _, f := range expfuncs {
|
||||||
if isTest(f.Name(), prefix) && types.IsIdentical(f.Signature, tFunc) {
|
if isTest(f.Name(), prefix) && types.Identical(f.Signature, tFunc) {
|
||||||
testfuncs = append(testfuncs, f)
|
testfuncs = append(testfuncs, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,10 +219,10 @@ func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, i
|
||||||
return pkg, err
|
return pkg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAssignableTo reports whether a value of type V is assignable to a variable of type T.
|
// AssignableTo reports whether a value of type V is assignable to a variable of type T.
|
||||||
func IsAssignableTo(V, T Type) bool {
|
func AssignableTo(V, T Type) bool {
|
||||||
x := operand{mode: value, typ: V}
|
x := operand{mode: value, typ: V}
|
||||||
return x.isAssignableTo(nil, T) // config not needed for non-constant x
|
return x.assignableTo(nil, T) // config not needed for non-constant x
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements reports whether a value of type V implements T, as follows:
|
// Implements reports whether a value of type V implements T, as follows:
|
||||||
|
|
|
@ -64,7 +64,7 @@ func (check *checker) assignment(x *operand, T Type) bool {
|
||||||
// spec: "If a left-hand side is the blank identifier, any typed or
|
// spec: "If a left-hand side is the blank identifier, any typed or
|
||||||
// non-constant value except for the predeclared identifier nil may
|
// non-constant value except for the predeclared identifier nil may
|
||||||
// be assigned to it."
|
// be assigned to it."
|
||||||
return T == nil || x.isAssignableTo(check.conf, T)
|
return T == nil || x.assignableTo(check.conf, T)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *checker) initConst(lhs *Const, x *operand) {
|
func (check *checker) initConst(lhs *Const, x *operand) {
|
||||||
|
|
|
@ -81,7 +81,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
// spec: "As a special case, append also accepts a first argument assignable
|
// spec: "As a special case, append also accepts a first argument assignable
|
||||||
// to type []byte with a second argument of string type followed by ... .
|
// to type []byte with a second argument of string type followed by ... .
|
||||||
// This form appends the bytes of the string.
|
// This form appends the bytes of the string.
|
||||||
if nargs == 2 && call.Ellipsis.IsValid() && x.isAssignableTo(check.conf, NewSlice(universeByte)) {
|
if nargs == 2 && call.Ellipsis.IsValid() && x.assignableTo(check.conf, NewSlice(universeByte)) {
|
||||||
arg(x, 1)
|
arg(x, 1)
|
||||||
if x.mode == invalid {
|
if x.mode == invalid {
|
||||||
return
|
return
|
||||||
|
@ -89,7 +89,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
if isString(x.typ) {
|
if isString(x.typ) {
|
||||||
if check.Types != nil {
|
if check.Types != nil {
|
||||||
sig := makeSig(S, S, NewSlice(universeByte))
|
sig := makeSig(S, S, NewSlice(universeByte))
|
||||||
sig.isVariadic = true
|
sig.variadic = true
|
||||||
check.recordBuiltinType(call.Fun, sig)
|
check.recordBuiltinType(call.Fun, sig)
|
||||||
}
|
}
|
||||||
x.mode = value
|
x.mode = value
|
||||||
|
@ -102,7 +102,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
|
|
||||||
// check general case by creating custom signature
|
// check general case by creating custom signature
|
||||||
sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature
|
sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature
|
||||||
sig.isVariadic = true
|
sig.variadic = true
|
||||||
check.arguments(x, call, sig, func(x *operand, i int) {
|
check.arguments(x, call, sig, func(x *operand, i int) {
|
||||||
// only evaluate arguments that have not been evaluated before
|
// only evaluate arguments that have not been evaluated before
|
||||||
if i < len(alist) {
|
if i < len(alist) {
|
||||||
|
@ -209,7 +209,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !IsIdentical(x.typ, y.typ) {
|
if !Identical(x.typ, y.typ) {
|
||||||
check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
|
check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -285,7 +285,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !IsIdentical(dst, src) {
|
if !Identical(dst, src) {
|
||||||
check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
|
check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -309,7 +309,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !x.isAssignableTo(check.conf, m.key) {
|
if !x.assignableTo(check.conf, m.key) {
|
||||||
check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key)
|
check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,7 @@ func (check *checker) arguments(x *operand, call *ast.CallExpr, sig *Signature,
|
||||||
passSlice := false
|
passSlice := false
|
||||||
if call.Ellipsis.IsValid() {
|
if call.Ellipsis.IsValid() {
|
||||||
// last argument is of the form x...
|
// last argument is of the form x...
|
||||||
if sig.isVariadic {
|
if sig.variadic {
|
||||||
passSlice = true
|
passSlice = true
|
||||||
} else {
|
} else {
|
||||||
check.errorf(call.Ellipsis, "cannot use ... in call to non-variadic %s", call.Fun)
|
check.errorf(call.Ellipsis, "cannot use ... in call to non-variadic %s", call.Fun)
|
||||||
|
@ -184,7 +184,7 @@ func (check *checker) arguments(x *operand, call *ast.CallExpr, sig *Signature,
|
||||||
}
|
}
|
||||||
|
|
||||||
// check argument count
|
// check argument count
|
||||||
if sig.isVariadic {
|
if sig.variadic {
|
||||||
// a variadic function accepts an "empty"
|
// a variadic function accepts an "empty"
|
||||||
// last argument: count one extra
|
// last argument: count one extra
|
||||||
n++
|
n++
|
||||||
|
@ -205,7 +205,7 @@ func (check *checker) argument(sig *Signature, i int, x *operand, passSlice bool
|
||||||
switch {
|
switch {
|
||||||
case i < n:
|
case i < n:
|
||||||
typ = sig.params.vars[i].typ
|
typ = sig.params.vars[i].typ
|
||||||
case sig.isVariadic:
|
case sig.variadic:
|
||||||
typ = sig.params.vars[n-1].typ
|
typ = sig.params.vars[n-1].typ
|
||||||
if debug {
|
if debug {
|
||||||
if _, ok := typ.(*Slice); !ok {
|
if _, ok := typ.(*Slice); !ok {
|
||||||
|
@ -227,7 +227,7 @@ func (check *checker) argument(sig *Signature, i int, x *operand, passSlice bool
|
||||||
check.errorf(x.pos(), "cannot use %s as parameter of type %s", x, typ)
|
check.errorf(x.pos(), "cannot use %s as parameter of type %s", x, typ)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else if sig.isVariadic && i >= n-1 {
|
} else if sig.variadic && i >= n-1 {
|
||||||
// use the variadic parameter slice's element type
|
// use the variadic parameter slice's element type
|
||||||
typ = typ.(*Slice).elem
|
typ = typ.(*Slice).elem
|
||||||
}
|
}
|
||||||
|
@ -261,7 +261,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
|
||||||
}
|
}
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
if !exp.IsExported() {
|
if !exp.Exported() {
|
||||||
check.errorf(e.Pos(), "%s not exported by package %s", sel, ident)
|
check.errorf(e.Pos(), "%s not exported by package %s", sel, ident)
|
||||||
// ok to continue
|
// ok to continue
|
||||||
}
|
}
|
||||||
|
@ -339,7 +339,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
|
||||||
x.typ = &Signature{
|
x.typ = &Signature{
|
||||||
params: NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "", x.typ)}, params...)...),
|
params: NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "", x.typ)}, params...)...),
|
||||||
results: sig.results,
|
results: sig.results,
|
||||||
isVariadic: sig.isVariadic,
|
variadic: sig.variadic,
|
||||||
}
|
}
|
||||||
|
|
||||||
check.addDeclDep(m)
|
check.addDeclDep(m)
|
||||||
|
|
|
@ -18,7 +18,7 @@ func (check *checker) conversion(x *operand, T Type) {
|
||||||
case constArg && isConstType(T):
|
case constArg && isConstType(T):
|
||||||
// constant conversion
|
// constant conversion
|
||||||
switch t := T.Underlying().(*Basic); {
|
switch t := T.Underlying().(*Basic); {
|
||||||
case isRepresentableConst(x.val, check.conf, t.kind, &x.val):
|
case representableConst(x.val, check.conf, t.kind, &x.val):
|
||||||
ok = true
|
ok = true
|
||||||
case x.isInteger() && isString(t):
|
case x.isInteger() && isString(t):
|
||||||
codepoint := int64(-1)
|
codepoint := int64(-1)
|
||||||
|
@ -65,7 +65,7 @@ func (check *checker) conversion(x *operand, T Type) {
|
||||||
|
|
||||||
func (x *operand) isConvertible(conf *Config, T Type) bool {
|
func (x *operand) isConvertible(conf *Config, T Type) bool {
|
||||||
// "x is assignable to T"
|
// "x is assignable to T"
|
||||||
if x.isAssignableTo(conf, T) {
|
if x.assignableTo(conf, T) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,14 +73,14 @@ func (x *operand) isConvertible(conf *Config, T Type) bool {
|
||||||
V := x.typ
|
V := x.typ
|
||||||
Vu := V.Underlying()
|
Vu := V.Underlying()
|
||||||
Tu := T.Underlying()
|
Tu := T.Underlying()
|
||||||
if IsIdentical(Vu, Tu) {
|
if Identical(Vu, Tu) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// "x's type and T are unnamed pointer types and their pointer base types have identical underlying types"
|
// "x's type and T are unnamed pointer types and their pointer base types have identical underlying types"
|
||||||
if V, ok := V.(*Pointer); ok {
|
if V, ok := V.(*Pointer); ok {
|
||||||
if T, ok := T.(*Pointer); ok {
|
if T, ok := T.(*Pointer); ok {
|
||||||
if IsIdentical(V.base.Underlying(), T.base.Underlying()) {
|
if Identical(V.base.Underlying(), T.base.Underlying()) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ func testEval(t *testing.T, pkg *Package, scope *Scope, str string, typ Type, ty
|
||||||
// compare types
|
// compare types
|
||||||
if typ != nil {
|
if typ != nil {
|
||||||
// we have a type, check identity
|
// we have a type, check identity
|
||||||
if !IsIdentical(gotTyp, typ) {
|
if !Identical(gotTyp, typ) {
|
||||||
t.Errorf("Eval(%q) got type %s, want %s", str, gotTyp, typ)
|
t.Errorf("Eval(%q) got type %s, want %s", str, gotTyp, typ)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ func (check *checker) unary(x *operand, op token.Token) {
|
||||||
// Typed constants must be representable in
|
// Typed constants must be representable in
|
||||||
// their type after each constant operation.
|
// their type after each constant operation.
|
||||||
if isTyped(typ) {
|
if isTyped(typ) {
|
||||||
check.isRepresentableAs(x, typ)
|
check.representable(x, typ)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -184,14 +184,14 @@ func roundFloat64(x exact.Value) exact.Value {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isRepresentableConst reports whether x can be represented as
|
// representableConst reports whether x can be represented as
|
||||||
// value of the given basic type kind and for the configuration
|
// value of the given basic type kind and for the configuration
|
||||||
// provided (only needed for int/uint sizes).
|
// provided (only needed for int/uint sizes).
|
||||||
//
|
//
|
||||||
// If rounded != nil, *rounded is set to the rounded value of x for
|
// If rounded != nil, *rounded is set to the rounded value of x for
|
||||||
// representable floating-point values; it is left alone otherwise.
|
// representable floating-point values; it is left alone otherwise.
|
||||||
// It is ok to provide the addressof the first argument for rounded.
|
// It is ok to provide the addressof the first argument for rounded.
|
||||||
func isRepresentableConst(x exact.Value, conf *Config, as BasicKind, rounded *exact.Value) bool {
|
func representableConst(x exact.Value, conf *Config, as BasicKind, rounded *exact.Value) bool {
|
||||||
switch x.Kind() {
|
switch x.Kind() {
|
||||||
case exact.Unknown:
|
case exact.Unknown:
|
||||||
return true
|
return true
|
||||||
|
@ -327,10 +327,10 @@ func isRepresentableConst(x exact.Value, conf *Config, as BasicKind, rounded *ex
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// isRepresentableAs checks that a constant operand is representable in the given basic type.
|
// representable checks that a constant operand is representable in the given basic type.
|
||||||
func (check *checker) isRepresentableAs(x *operand, typ *Basic) {
|
func (check *checker) representable(x *operand, typ *Basic) {
|
||||||
assert(x.mode == constant)
|
assert(x.mode == constant)
|
||||||
if !isRepresentableConst(x.val, check.conf, typ.kind, &x.val) {
|
if !representableConst(x.val, check.conf, typ.kind, &x.val) {
|
||||||
var msg string
|
var msg string
|
||||||
if isNumeric(x.typ) && isNumeric(typ) {
|
if isNumeric(x.typ) && isNumeric(typ) {
|
||||||
// numeric conversion : error msg
|
// numeric conversion : error msg
|
||||||
|
@ -490,7 +490,7 @@ func (check *checker) convertUntyped(x *operand, target Type) {
|
||||||
switch t := target.Underlying().(type) {
|
switch t := target.Underlying().(type) {
|
||||||
case *Basic:
|
case *Basic:
|
||||||
if x.mode == constant {
|
if x.mode == constant {
|
||||||
check.isRepresentableAs(x, t)
|
check.representable(x, t)
|
||||||
if x.mode == invalid {
|
if x.mode == invalid {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -563,7 +563,7 @@ func (check *checker) comparison(x, y *operand, op token.Token) {
|
||||||
// spec: "In any comparison, the first operand must be assignable
|
// spec: "In any comparison, the first operand must be assignable
|
||||||
// to the type of the second operand, or vice versa."
|
// to the type of the second operand, or vice versa."
|
||||||
err := ""
|
err := ""
|
||||||
if x.isAssignableTo(check.conf, y.typ) || y.isAssignableTo(check.conf, x.typ) {
|
if x.assignableTo(check.conf, y.typ) || y.assignableTo(check.conf, x.typ) {
|
||||||
defined := false
|
defined := false
|
||||||
switch op {
|
switch op {
|
||||||
case token.EQL, token.NEQ:
|
case token.EQL, token.NEQ:
|
||||||
|
@ -616,7 +616,7 @@ func (check *checker) shift(x, y *operand, op token.Token) {
|
||||||
|
|
||||||
// The lhs must be of integer type or be representable
|
// The lhs must be of integer type or be representable
|
||||||
// as an integer; otherwise the shift has no chance.
|
// as an integer; otherwise the shift has no chance.
|
||||||
if !isInteger(x.typ) && (!untypedx || !isRepresentableConst(x.val, nil, UntypedInt, nil)) {
|
if !isInteger(x.typ) && (!untypedx || !representableConst(x.val, nil, UntypedInt, nil)) {
|
||||||
check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
|
check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
return
|
return
|
||||||
|
@ -747,7 +747,7 @@ func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !IsIdentical(x.typ, y.typ) {
|
if !Identical(x.typ, y.typ) {
|
||||||
// only report an error if we have valid types
|
// only report an error if we have valid types
|
||||||
// (otherwise we had an error reported elsewhere already)
|
// (otherwise we had an error reported elsewhere already)
|
||||||
if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] {
|
if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] {
|
||||||
|
@ -778,7 +778,7 @@ func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token) {
|
||||||
// Typed constants must be representable in
|
// Typed constants must be representable in
|
||||||
// their type after each constant operation.
|
// their type after each constant operation.
|
||||||
if isTyped(typ) {
|
if isTyped(typ) {
|
||||||
check.isRepresentableAs(x, typ)
|
check.representable(x, typ)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ var (
|
||||||
want = Typ[UntypedNil]
|
want = Typ[UntypedNil]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if want != nil && !IsIdentical(typ, want) {
|
if want != nil && !Identical(typ, want) {
|
||||||
t.Errorf("got %s; want %s", typ, want)
|
t.Errorf("got %s; want %s", typ, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -261,7 +261,7 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b
|
||||||
if static {
|
if static {
|
||||||
return m, false
|
return m, false
|
||||||
}
|
}
|
||||||
case !IsIdentical(obj.Type(), m.typ):
|
case !Identical(obj.Type(), m.typ):
|
||||||
return m, true
|
return m, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -285,7 +285,7 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b
|
||||||
return m, false
|
return m, false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !IsIdentical(obj.Type(), m.typ) {
|
if !Identical(obj.Type(), m.typ) {
|
||||||
return m, true
|
return m, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ type Object interface {
|
||||||
Pkg() *Package // nil for objects in the Universe scope and labels
|
Pkg() *Package // nil for objects in the Universe scope and labels
|
||||||
Name() string // package local object name
|
Name() string // package local object name
|
||||||
Type() Type // object type
|
Type() Type // object type
|
||||||
IsExported() bool // reports whether the name starts with a capital letter
|
Exported() bool // reports whether the name starts with a capital letter
|
||||||
Id() string // object id (see Id below)
|
Id() string // object id (see Id below)
|
||||||
|
|
||||||
// String returns a human-readable string of the object.
|
// String returns a human-readable string of the object.
|
||||||
|
@ -81,7 +81,7 @@ func (obj *object) Pos() token.Pos { return obj.pos }
|
||||||
func (obj *object) Pkg() *Package { return obj.pkg }
|
func (obj *object) Pkg() *Package { return obj.pkg }
|
||||||
func (obj *object) Name() string { return obj.name }
|
func (obj *object) Name() string { return obj.name }
|
||||||
func (obj *object) Type() Type { return obj.typ }
|
func (obj *object) Type() Type { return obj.typ }
|
||||||
func (obj *object) IsExported() bool { return ast.IsExported(obj.name) }
|
func (obj *object) Exported() bool { return ast.IsExported(obj.name) }
|
||||||
func (obj *object) Id() string { return Id(obj.pkg, obj.name) }
|
func (obj *object) Id() string { return Id(obj.pkg, obj.name) }
|
||||||
func (obj *object) String() string { panic("abstract") }
|
func (obj *object) String() string { panic("abstract") }
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ func (obj *object) sameId(pkg *Package, name string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// obj.Name == name
|
// obj.Name == name
|
||||||
if obj.IsExported() {
|
if obj.Exported() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// not exported, so packages must be the same (pkg == nil for
|
// not exported, so packages must be the same (pkg == nil for
|
||||||
|
|
|
@ -194,12 +194,12 @@ func (x *operand) isNil() bool {
|
||||||
return x.mode == value && x.typ == Typ[UntypedNil]
|
return x.mode == value && x.typ == Typ[UntypedNil]
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(gri) The functions operand.isAssignableTo, checker.convertUntyped,
|
// TODO(gri) The functions operand.assignableTo, checker.convertUntyped,
|
||||||
// checker.isRepresentable, and checker.assignment are
|
// checker.representable, and checker.assignment are
|
||||||
// overlapping in functionality. Need to simplify and clean up.
|
// overlapping in functionality. Need to simplify and clean up.
|
||||||
|
|
||||||
// isAssignableTo reports whether x is assignable to a variable of type T.
|
// assignableTo reports whether x is assignable to a variable of type T.
|
||||||
func (x *operand) isAssignableTo(conf *Config, T Type) bool {
|
func (x *operand) assignableTo(conf *Config, T Type) bool {
|
||||||
if x.mode == invalid || T == Typ[Invalid] {
|
if x.mode == invalid || T == Typ[Invalid] {
|
||||||
return true // avoid spurious errors
|
return true // avoid spurious errors
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,7 @@ func (x *operand) isAssignableTo(conf *Config, T Type) bool {
|
||||||
V := x.typ
|
V := x.typ
|
||||||
|
|
||||||
// x's type is identical to T
|
// x's type is identical to T
|
||||||
if IsIdentical(V, T) {
|
if Identical(V, T) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +224,7 @@ func (x *operand) isAssignableTo(conf *Config, T Type) bool {
|
||||||
|
|
||||||
// x's type V and T have identical underlying types
|
// x's type V and T have identical underlying types
|
||||||
// and at least one of V or T is not a named type
|
// and at least one of V or T is not a named type
|
||||||
if IsIdentical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
|
if Identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +232,7 @@ func (x *operand) isAssignableTo(conf *Config, T Type) bool {
|
||||||
// type, x's type V and T have identical element types,
|
// type, x's type V and T have identical element types,
|
||||||
// and at least one of V or T is not a named type
|
// and at least one of V or T is not a named type
|
||||||
if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
|
if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
|
||||||
if Tc, ok := Tu.(*Chan); ok && IsIdentical(Vc.elem, Tc.elem) {
|
if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) {
|
||||||
return !isNamed(V) || !isNamed(T)
|
return !isNamed(V) || !isNamed(T)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,12 +253,12 @@ func (x *operand) isAssignableTo(conf *Config, T Type) bool {
|
||||||
|
|
||||||
// x is an untyped constant representable by a value of type T
|
// x is an untyped constant representable by a value of type T
|
||||||
// TODO(gri) This is borrowing from checker.convertUntyped and
|
// TODO(gri) This is borrowing from checker.convertUntyped and
|
||||||
// checker.isRepresentable. Need to clean up.
|
// checker.representable. Need to clean up.
|
||||||
if isUntyped(Vu) {
|
if isUntyped(Vu) {
|
||||||
switch t := Tu.(type) {
|
switch t := Tu.(type) {
|
||||||
case *Basic:
|
case *Basic:
|
||||||
if x.mode == constant {
|
if x.mode == constant {
|
||||||
return isRepresentableConst(x.val, conf, t.kind, nil)
|
return representableConst(x.val, conf, t.kind, nil)
|
||||||
}
|
}
|
||||||
// The result of a comparison is an untyped boolean,
|
// The result of a comparison is an untyped boolean,
|
||||||
// but may not be a constant.
|
// but may not be a constant.
|
||||||
|
@ -279,5 +279,5 @@ func (x *operand) isAssignableTo(conf *Config, T Type) bool {
|
||||||
func (x *operand) isInteger() bool {
|
func (x *operand) isInteger() bool {
|
||||||
return x.mode == invalid ||
|
return x.mode == invalid ||
|
||||||
isInteger(x.typ) ||
|
isInteger(x.typ) ||
|
||||||
x.mode == constant && isRepresentableConst(x.val, nil, UntypedInt, nil) // no *Config required for UntypedInt
|
x.mode == constant && representableConst(x.val, nil, UntypedInt, nil) // no *Config required for UntypedInt
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,9 +108,9 @@ func hasNil(typ Type) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsIdentical reports whether x and y are identical.
|
// Identical reports whether x and y are identical.
|
||||||
func IsIdentical(x, y Type) bool {
|
func Identical(x, y Type) bool {
|
||||||
return isIdenticalInternal(x, y, nil)
|
return identicalInternal(x, y, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An ifacePair is a node in a stack of interface type pairs compared for identity.
|
// An ifacePair is a node in a stack of interface type pairs compared for identity.
|
||||||
|
@ -123,7 +123,7 @@ func (p *ifacePair) identical(q *ifacePair) bool {
|
||||||
return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x
|
return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x
|
||||||
}
|
}
|
||||||
|
|
||||||
func isIdenticalInternal(x, y Type, p *ifacePair) bool {
|
func identicalInternal(x, y Type, p *ifacePair) bool {
|
||||||
if x == y {
|
if x == y {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -141,13 +141,13 @@ func isIdenticalInternal(x, y Type, p *ifacePair) bool {
|
||||||
// Two array types are identical if they have identical element types
|
// Two array types are identical if they have identical element types
|
||||||
// and the same array length.
|
// and the same array length.
|
||||||
if y, ok := y.(*Array); ok {
|
if y, ok := y.(*Array); ok {
|
||||||
return x.len == y.len && isIdenticalInternal(x.elem, y.elem, p)
|
return x.len == y.len && identicalInternal(x.elem, y.elem, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Slice:
|
case *Slice:
|
||||||
// Two slice types are identical if they have identical element types.
|
// Two slice types are identical if they have identical element types.
|
||||||
if y, ok := y.(*Slice); ok {
|
if y, ok := y.(*Slice); ok {
|
||||||
return isIdenticalInternal(x.elem, y.elem, p)
|
return identicalInternal(x.elem, y.elem, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Struct:
|
case *Struct:
|
||||||
|
@ -162,7 +162,7 @@ func isIdenticalInternal(x, y Type, p *ifacePair) bool {
|
||||||
if f.anonymous != g.anonymous ||
|
if f.anonymous != g.anonymous ||
|
||||||
x.Tag(i) != y.Tag(i) ||
|
x.Tag(i) != y.Tag(i) ||
|
||||||
!f.sameId(g.pkg, g.name) ||
|
!f.sameId(g.pkg, g.name) ||
|
||||||
!isIdenticalInternal(f.typ, g.typ, p) {
|
!identicalInternal(f.typ, g.typ, p) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,7 @@ func isIdenticalInternal(x, y Type, p *ifacePair) bool {
|
||||||
case *Pointer:
|
case *Pointer:
|
||||||
// Two pointer types are identical if they have identical base types.
|
// Two pointer types are identical if they have identical base types.
|
||||||
if y, ok := y.(*Pointer); ok {
|
if y, ok := y.(*Pointer); ok {
|
||||||
return isIdenticalInternal(x.base, y.base, p)
|
return identicalInternal(x.base, y.base, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Tuple:
|
case *Tuple:
|
||||||
|
@ -184,7 +184,7 @@ func isIdenticalInternal(x, y Type, p *ifacePair) bool {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
for i, v := range x.vars {
|
for i, v := range x.vars {
|
||||||
w := y.vars[i]
|
w := y.vars[i]
|
||||||
if !isIdenticalInternal(v.typ, w.typ, p) {
|
if !identicalInternal(v.typ, w.typ, p) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,9 +199,9 @@ func isIdenticalInternal(x, y Type, p *ifacePair) bool {
|
||||||
// and either both functions are variadic or neither is. Parameter and result
|
// and either both functions are variadic or neither is. Parameter and result
|
||||||
// names are not required to match.
|
// names are not required to match.
|
||||||
if y, ok := y.(*Signature); ok {
|
if y, ok := y.(*Signature); ok {
|
||||||
return x.isVariadic == y.isVariadic &&
|
return x.variadic == y.variadic &&
|
||||||
isIdenticalInternal(x.params, y.params, p) &&
|
identicalInternal(x.params, y.params, p) &&
|
||||||
isIdenticalInternal(x.results, y.results, p)
|
identicalInternal(x.results, y.results, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Interface:
|
case *Interface:
|
||||||
|
@ -247,7 +247,7 @@ func isIdenticalInternal(x, y Type, p *ifacePair) bool {
|
||||||
}
|
}
|
||||||
for i, f := range a {
|
for i, f := range a {
|
||||||
g := b[i]
|
g := b[i]
|
||||||
if f.Id() != g.Id() || !isIdenticalInternal(f.typ, g.typ, q) {
|
if f.Id() != g.Id() || !identicalInternal(f.typ, g.typ, q) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,14 +258,14 @@ func isIdenticalInternal(x, y Type, p *ifacePair) bool {
|
||||||
case *Map:
|
case *Map:
|
||||||
// Two map types are identical if they have identical key and value types.
|
// Two map types are identical if they have identical key and value types.
|
||||||
if y, ok := y.(*Map); ok {
|
if y, ok := y.(*Map); ok {
|
||||||
return isIdenticalInternal(x.key, y.key, p) && isIdenticalInternal(x.elem, y.elem, p)
|
return identicalInternal(x.key, y.key, p) && identicalInternal(x.elem, y.elem, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Chan:
|
case *Chan:
|
||||||
// Two channel types are identical if they have identical value types
|
// Two channel types are identical if they have identical value types
|
||||||
// and the same direction.
|
// and the same direction.
|
||||||
if y, ok := y.(*Chan); ok {
|
if y, ok := y.(*Chan); ok {
|
||||||
return x.dir == y.dir && isIdenticalInternal(x.elem, y.elem, p)
|
return x.dir == y.dir && identicalInternal(x.elem, y.elem, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Named:
|
case *Named:
|
||||||
|
|
|
@ -226,7 +226,7 @@ func (check *checker) resolveFiles(files []*ast.File) {
|
||||||
for _, obj := range imp.scope.elems {
|
for _, obj := range imp.scope.elems {
|
||||||
// A package scope may contain non-exported objects,
|
// A package scope may contain non-exported objects,
|
||||||
// do not import them!
|
// do not import them!
|
||||||
if obj.IsExported() {
|
if obj.Exported() {
|
||||||
// Note: This will change each imported object's scope!
|
// Note: This will change each imported object's scope!
|
||||||
// May be an issue for type aliases.
|
// May be an issue for type aliases.
|
||||||
check.declare(fileScope, nil, obj)
|
check.declare(fileScope, nil, obj)
|
||||||
|
|
|
@ -183,7 +183,7 @@ L:
|
||||||
// complain about duplicate types
|
// complain about duplicate types
|
||||||
// TODO(gri) use a type hash to avoid quadratic algorithm
|
// TODO(gri) use a type hash to avoid quadratic algorithm
|
||||||
for t, pos := range seen {
|
for t, pos := range seen {
|
||||||
if T == nil && t == nil || T != nil && t != nil && IsIdentical(T, t) {
|
if T == nil && t == nil || T != nil && t != nil && Identical(T, t) {
|
||||||
// talk about "case" rather than "type" because of nil case
|
// talk about "case" rather than "type" because of nil case
|
||||||
check.errorf(e.Pos(), "duplicate case in type switch")
|
check.errorf(e.Pos(), "duplicate case in type switch")
|
||||||
check.errorf(pos, "previous case %s", T)
|
check.errorf(pos, "previous case %s", T)
|
||||||
|
|
|
@ -68,7 +68,7 @@ func (m *M) Delete(key types.Type) bool {
|
||||||
hash := m.hasher.Hash(key)
|
hash := m.hasher.Hash(key)
|
||||||
bucket := m.table[hash]
|
bucket := m.table[hash]
|
||||||
for i, e := range bucket {
|
for i, e := range bucket {
|
||||||
if e.key != nil && types.IsIdentical(key, e.key) {
|
if e.key != nil && types.Identical(key, e.key) {
|
||||||
// We can't compact the bucket as it
|
// We can't compact the bucket as it
|
||||||
// would disturb iterators.
|
// would disturb iterators.
|
||||||
bucket[i] = entry{}
|
bucket[i] = entry{}
|
||||||
|
@ -86,7 +86,7 @@ func (m *M) Delete(key types.Type) bool {
|
||||||
func (m *M) At(key types.Type) interface{} {
|
func (m *M) At(key types.Type) interface{} {
|
||||||
if m != nil && m.table != nil {
|
if m != nil && m.table != nil {
|
||||||
for _, e := range m.table[m.hasher.Hash(key)] {
|
for _, e := range m.table[m.hasher.Hash(key)] {
|
||||||
if e.key != nil && types.IsIdentical(key, e.key) {
|
if e.key != nil && types.Identical(key, e.key) {
|
||||||
return e.value
|
return e.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ func (m *M) Set(key types.Type, value interface{}) (prev interface{}) {
|
||||||
for i, e := range bucket {
|
for i, e := range bucket {
|
||||||
if e.key == nil {
|
if e.key == nil {
|
||||||
hole = &bucket[i]
|
hole = &bucket[i]
|
||||||
} else if types.IsIdentical(key, e.key) {
|
} else if types.Identical(key, e.key) {
|
||||||
prev = e.value
|
prev = e.value
|
||||||
bucket[i].value = value
|
bucket[i].value = value
|
||||||
return
|
return
|
||||||
|
@ -219,7 +219,7 @@ func MakeHasher() Hasher {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash computes a hash value for the given type t such that
|
// Hash computes a hash value for the given type t such that
|
||||||
// IsIdentical(t, t') => Hash(t) == Hash(t').
|
// Identical(t, t') => Hash(t) == Hash(t').
|
||||||
func (h Hasher) Hash(t types.Type) uint32 {
|
func (h Hasher) Hash(t types.Type) uint32 {
|
||||||
hash, ok := h.memo[t]
|
hash, ok := h.memo[t]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -241,7 +241,7 @@ func hashString(s string) uint32 {
|
||||||
|
|
||||||
// hashFor computes the hash of t.
|
// hashFor computes the hash of t.
|
||||||
func (h Hasher) hashFor(t types.Type) uint32 {
|
func (h Hasher) hashFor(t types.Type) uint32 {
|
||||||
// See IsIdentical for rationale.
|
// See Identical for rationale.
|
||||||
switch t := t.(type) {
|
switch t := t.(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
return uint32(t.Kind())
|
return uint32(t.Kind())
|
||||||
|
@ -270,7 +270,7 @@ func (h Hasher) hashFor(t types.Type) uint32 {
|
||||||
|
|
||||||
case *types.Signature:
|
case *types.Signature:
|
||||||
var hash uint32 = 9091
|
var hash uint32 = 9091
|
||||||
if t.IsVariadic() {
|
if t.Variadic() {
|
||||||
hash *= 8863
|
hash *= 8863
|
||||||
}
|
}
|
||||||
return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
|
return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
|
||||||
|
|
|
@ -22,7 +22,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkEqualButNotIdentical(t *testing.T, x, y types.Type, comment string) {
|
func checkEqualButNotIdentical(t *testing.T, x, y types.Type, comment string) {
|
||||||
if !types.IsIdentical(x, y) {
|
if !types.Identical(x, y) {
|
||||||
t.Errorf("%s: not equal: %s, %s", comment, x, y)
|
t.Errorf("%s: not equal: %s, %s", comment, x, y)
|
||||||
}
|
}
|
||||||
if x == y {
|
if x == y {
|
||||||
|
@ -113,7 +113,7 @@ func TestTypeMap(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keys().
|
// Keys().
|
||||||
I := types.IsIdentical
|
I := types.Identical
|
||||||
switch k := tmap.Keys(); {
|
switch k := tmap.Keys(); {
|
||||||
case I(k[0], tChanInt1) && I(k[1], tPStr1): // ok
|
case I(k[0], tChanInt1) && I(k[1], tPStr1): // ok
|
||||||
case I(k[1], tChanInt1) && I(k[0], tPStr1): // ok
|
case I(k[1], tChanInt1) && I(k[0], tPStr1): // ok
|
||||||
|
|
|
@ -205,17 +205,17 @@ type Signature struct {
|
||||||
recv *Var // nil if not a method
|
recv *Var // nil if not a method
|
||||||
params *Tuple // (incoming) parameters from left to right; or nil
|
params *Tuple // (incoming) parameters from left to right; or nil
|
||||||
results *Tuple // (outgoing) results from left to right; or nil
|
results *Tuple // (outgoing) results from left to right; or nil
|
||||||
isVariadic bool // true if the last parameter's type is of the form ...T
|
variadic bool // true if the last parameter's type is of the form ...T
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSignature returns a new function type for the given receiver, parameters,
|
// NewSignature returns a new function type for the given receiver, parameters,
|
||||||
// and results, either of which may be nil. If isVariadic is set, the function
|
// and results, either of which may be nil. If variadic is set, the function
|
||||||
// is variadic, it must have at least one parameter, and the last parameter
|
// is variadic, it must have at least one parameter, and the last parameter
|
||||||
// must be of unnamed slice type.
|
// must be of unnamed slice type.
|
||||||
func NewSignature(scope *Scope, recv *Var, params, results *Tuple, isVariadic bool) *Signature {
|
func NewSignature(scope *Scope, recv *Var, params, results *Tuple, variadic bool) *Signature {
|
||||||
// TODO(gri) Should we rely on the correct (non-nil) incoming scope
|
// TODO(gri) Should we rely on the correct (non-nil) incoming scope
|
||||||
// or should this function allocate and populate a scope?
|
// or should this function allocate and populate a scope?
|
||||||
if isVariadic {
|
if variadic {
|
||||||
n := params.Len()
|
n := params.Len()
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
panic("types.NewSignature: variadic function must have at least one parameter")
|
panic("types.NewSignature: variadic function must have at least one parameter")
|
||||||
|
@ -224,7 +224,7 @@ func NewSignature(scope *Scope, recv *Var, params, results *Tuple, isVariadic bo
|
||||||
panic("types.NewSignature: variadic parameter must be of unnamed slice type")
|
panic("types.NewSignature: variadic parameter must be of unnamed slice type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &Signature{scope, recv, params, results, isVariadic}
|
return &Signature{scope, recv, params, results, variadic}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recv returns the receiver of signature s (if a method), or nil if a
|
// Recv returns the receiver of signature s (if a method), or nil if a
|
||||||
|
@ -241,8 +241,8 @@ func (s *Signature) Params() *Tuple { return s.params }
|
||||||
// Results returns the results of signature s, or nil.
|
// Results returns the results of signature s, or nil.
|
||||||
func (s *Signature) Results() *Tuple { return s.results }
|
func (s *Signature) Results() *Tuple { return s.results }
|
||||||
|
|
||||||
// IsVariadic reports whether the signature s is variadic.
|
// Variadic reports whether the signature s is variadic.
|
||||||
func (s *Signature) IsVariadic() bool { return s.isVariadic }
|
func (s *Signature) Variadic() bool { return s.variadic }
|
||||||
|
|
||||||
// An Interface represents an interface type.
|
// An Interface represents an interface type.
|
||||||
type Interface struct {
|
type Interface struct {
|
||||||
|
|
|
@ -190,7 +190,7 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, isVariadic bool) {
|
func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, variadic bool) {
|
||||||
buf.WriteByte('(')
|
buf.WriteByte('(')
|
||||||
if tup != nil {
|
if tup != nil {
|
||||||
for i, v := range tup.vars {
|
for i, v := range tup.vars {
|
||||||
|
@ -202,7 +202,7 @@ func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, isVariadic bool) {
|
||||||
buf.WriteByte(' ')
|
buf.WriteByte(' ')
|
||||||
}
|
}
|
||||||
typ := v.typ
|
typ := v.typ
|
||||||
if isVariadic && i == len(tup.vars)-1 {
|
if variadic && i == len(tup.vars)-1 {
|
||||||
buf.WriteString("...")
|
buf.WriteString("...")
|
||||||
typ = typ.(*Slice).elem
|
typ = typ.(*Slice).elem
|
||||||
}
|
}
|
||||||
|
@ -213,7 +213,7 @@ func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, isVariadic bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature) {
|
func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature) {
|
||||||
writeTuple(buf, this, sig.params, sig.isVariadic)
|
writeTuple(buf, this, sig.params, sig.variadic)
|
||||||
|
|
||||||
n := sig.results.Len()
|
n := sig.results.Len()
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
|
|
|
@ -151,7 +151,7 @@ func (check *checker) funcType(recv *ast.FieldList, ftyp *ast.FuncType, def *Nam
|
||||||
check.recordScope(ftyp, scope)
|
check.recordScope(ftyp, scope)
|
||||||
|
|
||||||
recv_, _ := check.collectParams(scope, recv, false)
|
recv_, _ := check.collectParams(scope, recv, false)
|
||||||
params, isVariadic := check.collectParams(scope, ftyp.Params, true)
|
params, variadic := check.collectParams(scope, ftyp.Params, true)
|
||||||
results, _ := check.collectParams(scope, ftyp.Results, false)
|
results, _ := check.collectParams(scope, ftyp.Results, false)
|
||||||
|
|
||||||
if len(recv_) > 0 {
|
if len(recv_) > 0 {
|
||||||
|
@ -197,7 +197,7 @@ func (check *checker) funcType(recv *ast.FieldList, ftyp *ast.FuncType, def *Nam
|
||||||
sig.scope = scope
|
sig.scope = scope
|
||||||
sig.params = NewTuple(params...)
|
sig.params = NewTuple(params...)
|
||||||
sig.results = NewTuple(results...)
|
sig.results = NewTuple(results...)
|
||||||
sig.isVariadic = isVariadic
|
sig.variadic = variadic
|
||||||
|
|
||||||
return sig
|
return sig
|
||||||
}
|
}
|
||||||
|
@ -386,7 +386,7 @@ func (check *checker) arrayLength(e ast.Expr) int64 {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, isVariadic bool) {
|
func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) {
|
||||||
if list == nil {
|
if list == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -396,7 +396,7 @@ func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicO
|
||||||
if t, _ := ftype.(*ast.Ellipsis); t != nil {
|
if t, _ := ftype.(*ast.Ellipsis); t != nil {
|
||||||
ftype = t.Elt
|
ftype = t.Elt
|
||||||
if variadicOk && i == len(list.List)-1 {
|
if variadicOk && i == len(list.List)-1 {
|
||||||
isVariadic = true
|
variadic = true
|
||||||
} else {
|
} else {
|
||||||
check.invalidAST(field.Pos(), "... not permitted")
|
check.invalidAST(field.Pos(), "... not permitted")
|
||||||
// ignore ... and continue
|
// ignore ... and continue
|
||||||
|
@ -421,7 +421,7 @@ func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicO
|
||||||
}
|
}
|
||||||
|
|
||||||
// For a variadic function, change the last parameter's type from T to []T.
|
// For a variadic function, change the last parameter's type from T to []T.
|
||||||
if isVariadic && len(params) > 0 {
|
if variadic && len(params) > 0 {
|
||||||
last := params[len(params)-1]
|
last := params[len(params)-1]
|
||||||
last.typ = &Slice{elem: last.typ}
|
last.typ = &Slice{elem: last.typ}
|
||||||
}
|
}
|
||||||
|
|
|
@ -206,7 +206,7 @@ func def(obj Object) {
|
||||||
}
|
}
|
||||||
// exported identifiers go into package unsafe
|
// exported identifiers go into package unsafe
|
||||||
scope := Universe
|
scope := Universe
|
||||||
if obj.IsExported() {
|
if obj.Exported() {
|
||||||
scope = Unsafe.scope
|
scope = Unsafe.scope
|
||||||
// set Pkg field
|
// set Pkg field
|
||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
|
|
|
@ -60,19 +60,19 @@ func implements(o *Oracle, qpos *QueryPos) (queryResult, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// T interface, U interface
|
// T interface, U interface
|
||||||
if !types.IsIdentical(T, U) {
|
if !types.Identical(T, U) {
|
||||||
if types.IsAssignableTo(U, T) {
|
if types.AssignableTo(U, T) {
|
||||||
to = append(to, U)
|
to = append(to, U)
|
||||||
}
|
}
|
||||||
if types.IsAssignableTo(T, U) {
|
if types.AssignableTo(T, U) {
|
||||||
from = append(from, U)
|
from = append(from, U)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// T interface, U concrete
|
// T interface, U concrete
|
||||||
if types.IsAssignableTo(U, T) {
|
if types.AssignableTo(U, T) {
|
||||||
to = append(to, U)
|
to = append(to, U)
|
||||||
} else if pU := types.NewPointer(U); types.IsAssignableTo(pU, T) {
|
} else if pU := types.NewPointer(U); types.AssignableTo(pU, T) {
|
||||||
to = append(to, pU)
|
to = append(to, pU)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,9 +82,9 @@ func implements(o *Oracle, qpos *QueryPos) (queryResult, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// T concrete, U interface
|
// T concrete, U interface
|
||||||
if types.IsAssignableTo(T, U) {
|
if types.AssignableTo(T, U) {
|
||||||
from = append(from, U)
|
from = append(from, U)
|
||||||
} else if pT := types.NewPointer(T); types.IsAssignableTo(pT, U) {
|
} else if pT := types.NewPointer(T); types.AssignableTo(pT, U) {
|
||||||
fromPtr = append(fromPtr, U)
|
fromPtr = append(fromPtr, U)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ func peers(o *Oracle, qpos *QueryPos) (queryResult, error) {
|
||||||
o.ptaConfig.AddQuery(queryOp.ch)
|
o.ptaConfig.AddQuery(queryOp.ch)
|
||||||
i := 0
|
i := 0
|
||||||
for _, op := range ops {
|
for _, op := range ops {
|
||||||
if types.IsIdentical(op.ch.Type().Underlying().(*types.Chan).Elem(), queryElemType) {
|
if types.Identical(op.ch.Type().Underlying().(*types.Chan).Elem(), queryElemType) {
|
||||||
o.ptaConfig.AddQuery(op.ch)
|
o.ptaConfig.AddQuery(op.ch)
|
||||||
ops[i] = op
|
ops[i] = op
|
||||||
i++
|
i++
|
||||||
|
|
Loading…
Reference in New Issue