Home | History | Annotate | Download | only in types
      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 	"go/ast"
     12 	"go/constant"
     13 	"go/token"
     14 )
     15 
     16 // An operandMode specifies the (addressing) mode of an operand.
     17 type operandMode byte
     18 
     19 const (
     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 
     31 var 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 //
     49 type 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 //
     60 func (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 //
     96 func 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 
    165 func (x *operand) String() string {
    166 	return operandString(x, nil)
    167 }
    168 
    169 // setConst sets x to the untyped constant for literal lit.
    170 func (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.
    193 func (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 func (x *operand) assignableTo(conf *Config, T Type, reason *string) bool {
    205 	if x.mode == invalid || T == Typ[Invalid] {
    206 		return true // avoid spurious errors
    207 	}
    208 
    209 	V := x.typ
    210 
    211 	// x's type is identical to T
    212 	if Identical(V, T) {
    213 		return true
    214 	}
    215 
    216 	Vu := V.Underlying()
    217 	Tu := T.Underlying()
    218 
    219 	// x is an untyped value representable by a value of type T
    220 	// TODO(gri) This is borrowing from checker.convertUntyped and
    221 	//           checker.representable. Need to clean up.
    222 	if isUntyped(Vu) {
    223 		switch t := Tu.(type) {
    224 		case *Basic:
    225 			if x.isNil() && t.kind == UnsafePointer {
    226 				return true
    227 			}
    228 			if x.mode == constant_ {
    229 				return representableConst(x.val, conf, t, nil)
    230 			}
    231 			// The result of a comparison is an untyped boolean,
    232 			// but may not be a constant.
    233 			if Vb, _ := Vu.(*Basic); Vb != nil {
    234 				return Vb.kind == UntypedBool && isBoolean(Tu)
    235 			}
    236 		case *Interface:
    237 			return x.isNil() || t.Empty()
    238 		case *Pointer, *Signature, *Slice, *Map, *Chan:
    239 			return x.isNil()
    240 		}
    241 	}
    242 	// Vu is typed
    243 
    244 	// x's type V and T have identical underlying types
    245 	// and at least one of V or T is not a named type
    246 	if Identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
    247 		return true
    248 	}
    249 
    250 	// T is an interface type and x implements T
    251 	if Ti, ok := Tu.(*Interface); ok {
    252 		if m, wrongType := MissingMethod(x.typ, Ti, true); m != nil /* Implements(x.typ, Ti) */ {
    253 			if reason != nil {
    254 				if wrongType {
    255 					*reason = "wrong type for method " + m.Name()
    256 				} else {
    257 					*reason = "missing method " + m.Name()
    258 				}
    259 			}
    260 			return false
    261 		}
    262 		return true
    263 	}
    264 
    265 	// x is a bidirectional channel value, T is a channel
    266 	// type, x's type V and T have identical element types,
    267 	// and at least one of V or T is not a named type
    268 	if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
    269 		if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) {
    270 			return !isNamed(V) || !isNamed(T)
    271 		}
    272 	}
    273 
    274 	return false
    275 }
    276