github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/types2/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 types2 8 9 import ( 10 "bytes" 11 "fmt" 12 "go/constant" 13 "go/token" 14 15 "github.com/go-asm/go/cmd/compile/syntax" 16 . "github.com/go-asm/go/types/errors" 17 ) 18 19 // An operandMode specifies the (addressing) mode of an operand. 20 type operandMode byte 21 22 const ( 23 invalid operandMode = iota // operand is invalid 24 novalue // operand represents no value (result of a function call w/o result) 25 builtin // operand is a built-in function 26 typexpr // operand is a type 27 constant_ // operand is a constant; the operand's typ is a Basic type 28 variable // operand is an addressable variable 29 mapindex // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment) 30 value // operand is a computed value 31 nilvalue // operand is the nil value 32 commaok // like value, but operand may be used in a comma,ok expression 33 commaerr // like commaok, but second value is error, not boolean 34 cgofunc // operand is a cgo function 35 ) 36 37 var operandModeString = [...]string{ 38 invalid: "invalid operand", 39 novalue: "no value", 40 builtin: "built-in", 41 typexpr: "type", 42 constant_: "constant", 43 variable: "variable", 44 mapindex: "map index expression", 45 value: "value", 46 nilvalue: "nil", 47 commaok: "comma, ok expression", 48 commaerr: "comma, error expression", 49 cgofunc: "cgo function", 50 } 51 52 // An operand represents an intermediate value during type checking. 53 // Operands have an (addressing) mode, the expression evaluating to 54 // the operand, the operand's type, a value for constants, and an id 55 // for built-in functions. 56 // The zero value of operand is a ready to use invalid operand. 57 type operand struct { 58 mode operandMode 59 expr syntax.Expr 60 typ Type 61 val constant.Value 62 id builtinId 63 } 64 65 // Pos returns the position of the expression corresponding to x. 66 // If x is invalid the position is nopos. 67 func (x *operand) Pos() syntax.Pos { 68 // x.expr may not be set if x is invalid 69 if x.expr == nil { 70 return nopos 71 } 72 return x.expr.Pos() 73 } 74 75 // Operand string formats 76 // (not all "untyped" cases can appear due to the type system, 77 // but they fall out naturally here) 78 // 79 // mode format 80 // 81 // invalid <expr> ( <mode> ) 82 // novalue <expr> ( <mode> ) 83 // builtin <expr> ( <mode> ) 84 // typexpr <expr> ( <mode> ) 85 // 86 // constant <expr> (<untyped kind> <mode> ) 87 // constant <expr> ( <mode> of type <typ>) 88 // constant <expr> (<untyped kind> <mode> <val> ) 89 // constant <expr> ( <mode> <val> of type <typ>) 90 // 91 // variable <expr> (<untyped kind> <mode> ) 92 // variable <expr> ( <mode> of type <typ>) 93 // 94 // mapindex <expr> (<untyped kind> <mode> ) 95 // mapindex <expr> ( <mode> of type <typ>) 96 // 97 // value <expr> (<untyped kind> <mode> ) 98 // value <expr> ( <mode> of type <typ>) 99 // 100 // nilvalue untyped nil 101 // nilvalue nil ( of type <typ>) 102 // 103 // commaok <expr> (<untyped kind> <mode> ) 104 // commaok <expr> ( <mode> of type <typ>) 105 // 106 // commaerr <expr> (<untyped kind> <mode> ) 107 // commaerr <expr> ( <mode> of type <typ>) 108 // 109 // cgofunc <expr> (<untyped kind> <mode> ) 110 // cgofunc <expr> ( <mode> of type <typ>) 111 func operandString(x *operand, qf Qualifier) string { 112 // special-case nil 113 if x.mode == nilvalue { 114 switch x.typ { 115 case nil, Typ[Invalid]: 116 return "nil (with invalid type)" 117 case Typ[UntypedNil]: 118 return "nil" 119 default: 120 return fmt.Sprintf("nil (of type %s)", TypeString(x.typ, qf)) 121 } 122 } 123 124 var buf bytes.Buffer 125 126 var expr string 127 if x.expr != nil { 128 expr = syntax.String(x.expr) 129 } else { 130 switch x.mode { 131 case builtin: 132 expr = predeclaredFuncs[x.id].name 133 case typexpr: 134 expr = TypeString(x.typ, qf) 135 case constant_: 136 expr = x.val.String() 137 } 138 } 139 140 // <expr> ( 141 if expr != "" { 142 buf.WriteString(expr) 143 buf.WriteString(" (") 144 } 145 146 // <untyped kind> 147 hasType := false 148 switch x.mode { 149 case invalid, novalue, builtin, typexpr: 150 // no type 151 default: 152 // should have a type, but be cautious (don't crash during printing) 153 if x.typ != nil { 154 if isUntyped(x.typ) { 155 buf.WriteString(x.typ.(*Basic).name) 156 buf.WriteByte(' ') 157 break 158 } 159 hasType = true 160 } 161 } 162 163 // <mode> 164 buf.WriteString(operandModeString[x.mode]) 165 166 // <val> 167 if x.mode == constant_ { 168 if s := x.val.String(); s != expr { 169 buf.WriteByte(' ') 170 buf.WriteString(s) 171 } 172 } 173 174 // <typ> 175 if hasType { 176 if isValid(x.typ) { 177 var intro string 178 if isGeneric(x.typ) { 179 intro = " of generic type " 180 } else { 181 intro = " of type " 182 } 183 buf.WriteString(intro) 184 WriteType(&buf, x.typ, qf) 185 if tpar, _ := x.typ.(*TypeParam); tpar != nil { 186 buf.WriteString(" constrained by ") 187 WriteType(&buf, tpar.bound, qf) // do not compute interface type sets here 188 // If we have the type set and it's empty, say so for better error messages. 189 if hasEmptyTypeset(tpar) { 190 buf.WriteString(" with empty type set") 191 } 192 } 193 } else { 194 buf.WriteString(" with invalid type") 195 } 196 } 197 198 // ) 199 if expr != "" { 200 buf.WriteByte(')') 201 } 202 203 return buf.String() 204 } 205 206 func (x *operand) String() string { 207 return operandString(x, nil) 208 } 209 210 // setConst sets x to the untyped constant for literal lit. 211 func (x *operand) setConst(k syntax.LitKind, lit string) { 212 var kind BasicKind 213 switch k { 214 case syntax.IntLit: 215 kind = UntypedInt 216 case syntax.FloatLit: 217 kind = UntypedFloat 218 case syntax.ImagLit: 219 kind = UntypedComplex 220 case syntax.RuneLit: 221 kind = UntypedRune 222 case syntax.StringLit: 223 kind = UntypedString 224 default: 225 unreachable() 226 } 227 228 val := constant.MakeFromLiteral(lit, kind2tok[k], 0) 229 if val.Kind() == constant.Unknown { 230 x.mode = invalid 231 x.typ = Typ[Invalid] 232 return 233 } 234 x.mode = constant_ 235 x.typ = Typ[kind] 236 x.val = val 237 } 238 239 // isNil reports whether x is the (untyped) nil value. 240 func (x *operand) isNil() bool { return x.mode == nilvalue } 241 242 // assignableTo reports whether x is assignable to a variable of type T. If the 243 // result is false and a non-nil cause is provided, it may be set to a more 244 // detailed explanation of the failure (result != ""). The returned error code 245 // is only valid if the (first) result is false. The check parameter may be nil 246 // if assignableTo is invoked through an exported API call, i.e., when all 247 // methods have been type-checked. 248 func (x *operand) assignableTo(check *Checker, T Type, cause *string) (bool, Code) { 249 if x.mode == invalid || !isValid(T) { 250 return true, 0 // avoid spurious errors 251 } 252 253 V := x.typ 254 255 // x's type is identical to T 256 if Identical(V, T) { 257 return true, 0 258 } 259 260 Vu := under(V) 261 Tu := under(T) 262 Vp, _ := V.(*TypeParam) 263 Tp, _ := T.(*TypeParam) 264 265 // x is an untyped value representable by a value of type T. 266 if isUntyped(Vu) { 267 assert(Vp == nil) 268 if Tp != nil { 269 // T is a type parameter: x is assignable to T if it is 270 // representable by each specific type in the type set of T. 271 return Tp.is(func(t *term) bool { 272 if t == nil { 273 return false 274 } 275 // A term may be a tilde term but the underlying 276 // type of an untyped value doesn't change so we 277 // don't need to do anything special. 278 newType, _, _ := check.implicitTypeAndValue(x, t.typ) 279 return newType != nil 280 }), IncompatibleAssign 281 } 282 newType, _, _ := check.implicitTypeAndValue(x, T) 283 return newType != nil, IncompatibleAssign 284 } 285 // Vu is typed 286 287 // x's type V and T have identical underlying types 288 // and at least one of V or T is not a named type 289 // and neither V nor T is a type parameter. 290 if Identical(Vu, Tu) && (!hasName(V) || !hasName(T)) && Vp == nil && Tp == nil { 291 return true, 0 292 } 293 294 // T is an interface type, but not a type parameter, and V implements T. 295 // Also handle the case where T is a pointer to an interface so that we get 296 // the Checker.implements error cause. 297 if _, ok := Tu.(*Interface); ok && Tp == nil || isInterfacePtr(Tu) { 298 if check.implements(x.Pos(), V, T, false, cause) { 299 return true, 0 300 } 301 // V doesn't implement T but V may still be assignable to T if V 302 // is a type parameter; do not report an error in that case yet. 303 if Vp == nil { 304 return false, InvalidIfaceAssign 305 } 306 if cause != nil { 307 *cause = "" 308 } 309 } 310 311 // If V is an interface, check if a missing type assertion is the problem. 312 if Vi, _ := Vu.(*Interface); Vi != nil && Vp == nil { 313 if check.implements(x.Pos(), T, V, false, nil) { 314 // T implements V, so give hint about type assertion. 315 if cause != nil { 316 *cause = "need type assertion" 317 } 318 return false, IncompatibleAssign 319 } 320 } 321 322 // x is a bidirectional channel value, T is a channel 323 // type, x's type V and T have identical element types, 324 // and at least one of V or T is not a named type. 325 if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv { 326 if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) { 327 return !hasName(V) || !hasName(T), InvalidChanAssign 328 } 329 } 330 331 // optimization: if we don't have type parameters, we're done 332 if Vp == nil && Tp == nil { 333 return false, IncompatibleAssign 334 } 335 336 errorf := func(format string, args ...interface{}) { 337 if check != nil && cause != nil { 338 msg := check.sprintf(format, args...) 339 if *cause != "" { 340 msg += "\n\t" + *cause 341 } 342 *cause = msg 343 } 344 } 345 346 // x's type V is not a named type and T is a type parameter, and 347 // x is assignable to each specific type in T's type set. 348 if !hasName(V) && Tp != nil { 349 ok := false 350 code := IncompatibleAssign 351 Tp.is(func(T *term) bool { 352 if T == nil { 353 return false // no specific types 354 } 355 ok, code = x.assignableTo(check, T.typ, cause) 356 if !ok { 357 errorf("cannot assign %s to %s (in %s)", x.typ, T.typ, Tp) 358 return false 359 } 360 return true 361 }) 362 return ok, code 363 } 364 365 // x's type V is a type parameter and T is not a named type, 366 // and values x' of each specific type in V's type set are 367 // assignable to T. 368 if Vp != nil && !hasName(T) { 369 x := *x // don't clobber outer x 370 ok := false 371 code := IncompatibleAssign 372 Vp.is(func(V *term) bool { 373 if V == nil { 374 return false // no specific types 375 } 376 x.typ = V.typ 377 ok, code = x.assignableTo(check, T, cause) 378 if !ok { 379 errorf("cannot assign %s (in %s) to %s", V.typ, Vp, T) 380 return false 381 } 382 return true 383 }) 384 return ok, code 385 } 386 387 return false, IncompatibleAssign 388 } 389 390 // kind2tok translates syntax.LitKinds into token.Tokens. 391 var kind2tok = [...]token.Token{ 392 syntax.IntLit: token.INT, 393 syntax.FloatLit: token.FLOAT, 394 syntax.ImagLit: token.IMAG, 395 syntax.RuneLit: token.CHAR, 396 syntax.StringLit: token.STRING, 397 }