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