1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// This file implements Selections.
6
7package types2
8
9import (
10	"bytes"
11	"fmt"
12)
13
14// SelectionKind describes the kind of a selector expression x.f
15// (excluding qualified identifiers).
16//
17// If x is a struct or *struct, a selector expression x.f may denote a
18// sequence of selection operations x.a.b.c.f. The SelectionKind
19// describes the kind of the final (explicit) operation; all the
20// previous (implicit) operations are always field selections.
21// Each element of Indices specifies an implicit field (a, b, c)
22// by its index in the struct type of the field selection operand.
23//
24// For a FieldVal operation, the final selection refers to the field
25// specified by Selection.Obj.
26//
27// For a MethodVal operation, the final selection refers to a method.
28// If the "pointerness" of the method's declared receiver does not
29// match that of the effective receiver after implicit field
30// selection, then an & or * operation is implicitly applied to the
31// receiver variable or value.
32// So, x.f denotes (&x.a.b.c).f when f requires a pointer receiver but
33// x.a.b.c is a non-pointer variable; and it denotes (*x.a.b.c).f when
34// f requires a non-pointer receiver but x.a.b.c is a pointer value.
35//
36// All pointer indirections, whether due to implicit or explicit field
37// selections or * operations inserted for "pointerness", panic if
38// applied to a nil pointer, so a method call x.f() may panic even
39// before the function call.
40//
41// By contrast, a MethodExpr operation T.f is essentially equivalent
42// to a function literal of the form:
43//
44//	func(x T, args) (results) { return x.f(args) }
45//
46// Consequently, any implicit field selections and * operations
47// inserted for "pointerness" are not evaluated until the function is
48// called, so a T.f or (*T).f expression never panics.
49type SelectionKind int
50
51const (
52	FieldVal   SelectionKind = iota // x.f is a struct field selector
53	MethodVal                       // x.f is a method selector
54	MethodExpr                      // x.f is a method expression
55)
56
57// A Selection describes a selector expression x.f.
58// For the declarations:
59//
60//	type T struct{ x int; E }
61//	type E struct{}
62//	func (e E) m() {}
63//	var p *T
64//
65// the following relations exist:
66//
67//	Selector    Kind          Recv    Obj    Type       Index     Indirect
68//
69//	p.x         FieldVal      T       x      int        {0}       true
70//	p.m         MethodVal     *T      m      func()     {1, 0}    true
71//	T.m         MethodExpr    T       m      func(T)    {1, 0}    false
72type Selection struct {
73	kind     SelectionKind
74	recv     Type   // type of x
75	obj      Object // object denoted by x.f
76	index    []int  // path from x to x.f
77	indirect bool   // set if there was any pointer indirection on the path
78}
79
80// Kind returns the selection kind.
81func (s *Selection) Kind() SelectionKind { return s.kind }
82
83// Recv returns the type of x in x.f.
84func (s *Selection) Recv() Type { return s.recv }
85
86// Obj returns the object denoted by x.f; a *Var for
87// a field selection, and a *Func in all other cases.
88func (s *Selection) Obj() Object { return s.obj }
89
90// Type returns the type of x.f, which may be different from the type of f.
91// See Selection for more information.
92func (s *Selection) Type() Type {
93	switch s.kind {
94	case MethodVal:
95		// The type of x.f is a method with its receiver type set
96		// to the type of x.
97		sig := *s.obj.(*Func).typ.(*Signature)
98		recv := *sig.recv
99		recv.typ = s.recv
100		sig.recv = &recv
101		return &sig
102
103	case MethodExpr:
104		// The type of x.f is a function (without receiver)
105		// and an additional first argument with the same type as x.
106		// TODO(gri) Similar code is already in call.go - factor!
107		// TODO(gri) Compute this eagerly to avoid allocations.
108		sig := *s.obj.(*Func).typ.(*Signature)
109		arg0 := *sig.recv
110		sig.recv = nil
111		arg0.typ = s.recv
112		var params []*Var
113		if sig.params != nil {
114			params = sig.params.vars
115		}
116		sig.params = NewTuple(append([]*Var{&arg0}, params...)...)
117		return &sig
118	}
119
120	// In all other cases, the type of x.f is the type of x.
121	return s.obj.Type()
122}
123
124// Index describes the path from x to f in x.f.
125// The last index entry is the field or method index of the type declaring f;
126// either:
127//
128//  1. the list of declared methods of a named type; or
129//  2. the list of methods of an interface type; or
130//  3. the list of fields of a struct type.
131//
132// The earlier index entries are the indices of the embedded fields implicitly
133// traversed to get from (the type of) x to f, starting at embedding depth 0.
134func (s *Selection) Index() []int { return s.index }
135
136// Indirect reports whether any pointer indirection was required to get from
137// x to f in x.f.
138//
139// Beware: Indirect spuriously returns true (Go issue #8353) for a
140// MethodVal selection in which the receiver argument and parameter
141// both have type *T so there is no indirection.
142// Unfortunately, a fix is too risky.
143func (s *Selection) Indirect() bool { return s.indirect }
144
145func (s *Selection) String() string { return SelectionString(s, nil) }
146
147// SelectionString returns the string form of s.
148// The Qualifier controls the printing of
149// package-level objects, and may be nil.
150//
151// Examples:
152//
153//	"field (T) f int"
154//	"method (T) f(X) Y"
155//	"method expr (T) f(X) Y"
156func SelectionString(s *Selection, qf Qualifier) string {
157	var k string
158	switch s.kind {
159	case FieldVal:
160		k = "field "
161	case MethodVal:
162		k = "method "
163	case MethodExpr:
164		k = "method expr "
165	default:
166		panic("unreachable")
167	}
168	var buf bytes.Buffer
169	buf.WriteString(k)
170	buf.WriteByte('(')
171	WriteType(&buf, s.Recv(), qf)
172	fmt.Fprintf(&buf, ") %s", s.obj.Name())
173	if T := s.Type(); s.kind == FieldVal {
174		buf.WriteByte(' ')
175		WriteType(&buf, T, qf)
176	} else {
177		WriteSignature(&buf, T.(*Signature), qf)
178	}
179	return buf.String()
180}
181