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