implements.go raw
1 package unused
2
3 import (
4 "go/types"
5 )
6
7 // lookupMethod returns the index of and method with matching package and name, or (-1, nil).
8 func lookupMethod(T *types.Interface, pkg *types.Package, name string) (int, *types.Func) {
9 if name != "_" {
10 for i := 0; i < T.NumMethods(); i++ {
11 m := T.Method(i)
12 if sameId(m, pkg, name) {
13 return i, m
14 }
15 }
16 }
17 return -1, nil
18 }
19
20 func sameId(obj types.Object, pkg *types.Package, name string) bool {
21 // spec:
22 // "Two identifiers are different if they are spelled differently,
23 // or if they appear in different packages and are not exported.
24 // Otherwise, they are the same."
25 if name != obj.Name() {
26 return false
27 }
28 // obj.Name == name
29 if obj.Exported() {
30 return true
31 }
32 // not exported, so packages must be the same (pkg == nil for
33 // fields in Universe scope; this can only happen for types
34 // introduced via Eval)
35 if pkg == nil || obj.Pkg() == nil {
36 return pkg == obj.Pkg()
37 }
38 // pkg != nil && obj.pkg != nil
39 return pkg.Path() == obj.Pkg().Path()
40 }
41
42 func implements(V types.Type, T *types.Interface, msV *types.MethodSet) ([]*types.Selection, bool) {
43 // fast path for common case
44 if T.Empty() {
45 return nil, true
46 }
47
48 if ityp, _ := V.Underlying().(*types.Interface); ityp != nil {
49 // TODO(dh): is this code reachable?
50 for i := 0; i < T.NumMethods(); i++ {
51 m := T.Method(i)
52 _, obj := lookupMethod(ityp, m.Pkg(), m.Name())
53 switch {
54 case obj == nil:
55 return nil, false
56 case !types.Identical(obj.Type(), m.Type()):
57 return nil, false
58 }
59 }
60 return nil, true
61 }
62
63 // A concrete type implements T if it implements all methods of T.
64 var sels []*types.Selection
65 var c methodsChecker
66 for i := 0; i < T.NumMethods(); i++ {
67 m := T.Method(i)
68 sel := msV.Lookup(m.Pkg(), m.Name())
69 if sel == nil {
70 return nil, false
71 }
72
73 f, _ := sel.Obj().(*types.Func)
74 if f == nil {
75 return nil, false
76 }
77
78 if !c.methodIsCompatible(f, m) {
79 return nil, false
80 }
81
82 sels = append(sels, sel)
83 }
84 return sels, true
85 }
86
87 type methodsChecker struct {
88 typeParams map[*types.TypeParam]types.Type
89 }
90
91 // Currently, this doesn't support methods like `foo(x []T)`.
92 func (c *methodsChecker) methodIsCompatible(implFunc *types.Func, interfaceFunc *types.Func) bool {
93 if types.Identical(implFunc.Type(), interfaceFunc.Type()) {
94 return true
95 }
96 implSig, implOk := implFunc.Type().(*types.Signature)
97 interfaceSig, interfaceOk := interfaceFunc.Type().(*types.Signature)
98 if !implOk || !interfaceOk {
99 // probably not reachable. handle conservatively.
100 return false
101 }
102
103 if !c.typesAreCompatible(implSig.Params(), interfaceSig.Params()) {
104 return false
105 }
106
107 if !c.typesAreCompatible(implSig.Results(), interfaceSig.Results()) {
108 return false
109 }
110
111 return true
112 }
113
114 func (c *methodsChecker) typesAreCompatible(implTypes, interfaceTypes *types.Tuple) bool {
115 if implTypes.Len() != interfaceTypes.Len() {
116 return false
117 }
118 for i := 0; i < implTypes.Len(); i++ {
119 if !c.typeIsCompatible(implTypes.At(i).Type(), interfaceTypes.At(i).Type()) {
120 return false
121 }
122 }
123 return true
124 }
125
126 func (c *methodsChecker) typeIsCompatible(implType, interfaceType types.Type) bool {
127 if types.Identical(implType, interfaceType) {
128 return true
129 }
130 // We only support trivial use of type parameters. This isn't fully compatible with compiler type checking yet.
131 tp, ok := interfaceType.(*types.TypeParam)
132 if !ok {
133 return false
134 }
135 if c.typeParams == nil {
136 c.typeParams = make(map[*types.TypeParam]types.Type)
137 }
138 if c.typeParams[tp] == nil {
139 if !satisfiesConstraint(implType, tp) {
140 return false
141 }
142 c.typeParams[tp] = implType
143 return true
144 }
145 return types.Identical(c.typeParams[tp], implType)
146 }
147
148 func satisfiesConstraint(t types.Type, tp *types.TypeParam) bool {
149 bound := tp.Constraint().Underlying().(*types.Interface)
150 return types.Satisfies(t, bound)
151 }
152