1// Copyright 2012 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 defines operands and associated operations.
6
7package types
8
9import (
10	"bytes"
11	"go/ast"
12	"go/constant"
13	"go/token"
14)
15
16// An operandMode specifies the (addressing) mode of an operand.
17type operandMode byte
18
19const (
20	invalid   operandMode = iota // operand is invalid
21	novalue                      // operand represents no value (result of a function call w/o result)
22	builtin                      // operand is a built-in function
23	typexpr                      // operand is a type
24	constant_                    // operand is a constant; the operand's typ is a Basic type
25	variable                     // operand is an addressable variable
26	mapindex                     // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)
27	value                        // operand is a computed value
28	commaok                      // like value, but operand may be used in a comma,ok expression
29)
30
31var operandModeString = [...]string{
32	invalid:   "invalid operand",
33	novalue:   "no value",
34	builtin:   "built-in",
35	typexpr:   "type",
36	constant_: "constant",
37	variable:  "variable",
38	mapindex:  "map index expression",
39	value:     "value",
40	commaok:   "comma, ok expression",
41}
42
43// An operand represents an intermediate value during type checking.
44// Operands have an (addressing) mode, the expression evaluating to
45// the operand, the operand's type, a value for constants, and an id
46// for built-in functions.
47// The zero value of operand is a ready to use invalid operand.
48//
49type operand struct {
50	mode operandMode
51	expr ast.Expr
52	typ  Type
53	val  constant.Value
54	id   builtinId
55}
56
57// pos returns the position of the expression corresponding to x.
58// If x is invalid the position is token.NoPos.
59//
60func (x *operand) pos() token.Pos {
61	// x.expr may not be set if x is invalid
62	if x.expr == nil {
63		return token.NoPos
64	}
65	return x.expr.Pos()
66}
67
68// Operand string formats
69// (not all "untyped" cases can appear due to the type system,
70// but they fall out naturally here)
71//
72// mode       format
73//
74// invalid    <expr> (               <mode>                    )
75// novalue    <expr> (               <mode>                    )
76// builtin    <expr> (               <mode>                    )
77// typexpr    <expr> (               <mode>                    )
78//
79// constant   <expr> (<untyped kind> <mode>                    )
80// constant   <expr> (               <mode>       of type <typ>)
81// constant   <expr> (<untyped kind> <mode> <val>              )
82// constant   <expr> (               <mode> <val> of type <typ>)
83//
84// variable   <expr> (<untyped kind> <mode>                    )
85// variable   <expr> (               <mode>       of type <typ>)
86//
87// mapindex   <expr> (<untyped kind> <mode>                    )
88// mapindex   <expr> (               <mode>       of type <typ>)
89//
90// value      <expr> (<untyped kind> <mode>                    )
91// value      <expr> (               <mode>       of type <typ>)
92//
93// commaok    <expr> (<untyped kind> <mode>                    )
94// commaok    <expr> (               <mode>       of type <typ>)
95//
96func operandString(x *operand, qf Qualifier) string {
97	var buf bytes.Buffer
98
99	var expr string
100	if x.expr != nil {
101		expr = ExprString(x.expr)
102	} else {
103		switch x.mode {
104		case builtin:
105			expr = predeclaredFuncs[x.id].name
106		case typexpr:
107			expr = TypeString(x.typ, qf)
108		case constant_:
109			expr = x.val.String()
110		}
111	}
112
113	// <expr> (
114	if expr != "" {
115		buf.WriteString(expr)
116		buf.WriteString(" (")
117	}
118
119	// <untyped kind>
120	hasType := false
121	switch x.mode {
122	case invalid, novalue, builtin, typexpr:
123		// no type
124	default:
125		// should have a type, but be cautious (don't crash during printing)
126		if x.typ != nil {
127			if isUntyped(x.typ) {
128				buf.WriteString(x.typ.(*Basic).name)
129				buf.WriteByte(' ')
130				break
131			}
132			hasType = true
133		}
134	}
135
136	// <mode>
137	buf.WriteString(operandModeString[x.mode])
138
139	// <val>
140	if x.mode == constant_ {
141		if s := x.val.String(); s != expr {
142			buf.WriteByte(' ')
143			buf.WriteString(s)
144		}
145	}
146
147	// <typ>
148	if hasType {
149		if x.typ != Typ[Invalid] {
150			buf.WriteString(" of type ")
151			WriteType(&buf, x.typ, qf)
152		} else {
153			buf.WriteString(" with invalid type")
154		}
155	}
156
157	// )
158	if expr != "" {
159		buf.WriteByte(')')
160	}
161
162	return buf.String()
163}
164
165func (x *operand) String() string {
166	return operandString(x, nil)
167}
168
169// setConst sets x to the untyped constant for literal lit.
170func (x *operand) setConst(tok token.Token, lit string) {
171	var kind BasicKind
172	switch tok {
173	case token.INT:
174		kind = UntypedInt
175	case token.FLOAT:
176		kind = UntypedFloat
177	case token.IMAG:
178		kind = UntypedComplex
179	case token.CHAR:
180		kind = UntypedRune
181	case token.STRING:
182		kind = UntypedString
183	default:
184		unreachable()
185	}
186
187	x.mode = constant_
188	x.typ = Typ[kind]
189	x.val = constant.MakeFromLiteral(lit, tok, 0)
190}
191
192// isNil reports whether x is the nil value.
193func (x *operand) isNil() bool {
194	return x.mode == value && x.typ == Typ[UntypedNil]
195}
196
197// TODO(gri) The functions operand.assignableTo, checker.convertUntyped,
198//           checker.representable, and checker.assignment are
199//           overlapping in functionality. Need to simplify and clean up.
200
201// assignableTo reports whether x is assignable to a variable of type T.
202// If the result is false and a non-nil reason is provided, it may be set
203// to a more detailed explanation of the failure (result != "").
204// The check parameter may be nil if assignableTo is invoked through
205// an exported API call, i.e., when all methods have been type-checked.
206func (x *operand) assignableTo(check *Checker, T Type, reason *string) bool {
207	if x.mode == invalid || T == Typ[Invalid] {
208		return true // avoid spurious errors
209	}
210
211	V := x.typ
212
213	// x's type is identical to T
214	if Identical(V, T) {
215		return true
216	}
217
218	Vu := V.Underlying()
219	Tu := T.Underlying()
220
221	// x is an untyped value representable by a value of type T
222	// TODO(gri) This is borrowing from checker.convertUntyped and
223	//           checker.representable. Need to clean up.
224	if isUntyped(Vu) {
225		switch t := Tu.(type) {
226		case *Basic:
227			if x.isNil() && t.kind == UnsafePointer {
228				return true
229			}
230			if x.mode == constant_ {
231				return representableConst(x.val, check, t, nil)
232			}
233			// The result of a comparison is an untyped boolean,
234			// but may not be a constant.
235			if Vb, _ := Vu.(*Basic); Vb != nil {
236				return Vb.kind == UntypedBool && isBoolean(Tu)
237			}
238		case *Interface:
239			return x.isNil() || t.Empty()
240		case *Pointer, *Signature, *Slice, *Map, *Chan:
241			return x.isNil()
242		}
243	}
244	// Vu is typed
245
246	// x's type V and T have identical underlying types
247	// and at least one of V or T is not a named type
248	if Identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
249		return true
250	}
251
252	// T is an interface type and x implements T
253	if Ti, ok := Tu.(*Interface); ok {
254		if m, wrongType := check.missingMethod(x.typ, Ti, true); m != nil /* Implements(x.typ, Ti) */ {
255			if reason != nil {
256				if wrongType {
257					*reason = "wrong type for method " + m.Name()
258				} else {
259					*reason = "missing method " + m.Name()
260				}
261			}
262			return false
263		}
264		return true
265	}
266
267	// x is a bidirectional channel value, T is a channel
268	// type, x's type V and T have identical element types,
269	// and at least one of V or T is not a named type
270	if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
271		if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) {
272			return !isNamed(V) || !isNamed(T)
273		}
274	}
275
276	return false
277}
278