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 		// has type
    126 		if isUntyped(x.typ) {
    127 			buf.WriteString(x.typ.(*Basic).name)
    128 			buf.WriteByte(' ')
    129 			break
    130 		}
    131 		hasType = true
    132 	}
    133 
    134 	// <mode>
    135 	buf.WriteString(operandModeString[x.mode])
    136 
    137 	// <val>
    138 	if x.mode == constant_ {
    139 		if s := x.val.String(); s != expr {
    140 			buf.WriteByte(' ')
    141 			buf.WriteString(s)
    142 		}
    143 	}
    144 
    145 	// <typ>
    146 	if hasType {
    147 		if x.typ != Typ[Invalid] {
    148 			buf.WriteString(" of type ")
    149 			WriteType(&buf, x.typ, qf)
    150 		} else {
    151 			buf.WriteString(" with invalid type")
    152 		}
    153 	}
    154 
    155 	// )
    156 	if expr != "" {
    157 		buf.WriteByte(')')
    158 	}
    159 
    160 	return buf.String()
    161 }
    162 
    163 func (x *operand) String() string {
    164 	return operandString(x, nil)
    165 }
    166 
    167 // setConst sets x to the untyped constant for literal lit.
    168 func (x *operand) setConst(tok token.Token, lit string) {
    169 	var kind BasicKind
    170 	switch tok {
    171 	case token.INT:
    172 		kind = UntypedInt
    173 	case token.FLOAT:
    174 		kind = UntypedFloat
    175 	case token.IMAG:
    176 		kind = UntypedComplex
    177 	case token.CHAR:
    178 		kind = UntypedRune
    179 	case token.STRING:
    180 		kind = UntypedString
    181 	default:
    182 		unreachable()
    183 	}
    184 
    185 	x.mode = constant_
    186 	x.typ = Typ[kind]
    187 	x.val = constant.MakeFromLiteral(lit, tok, 0)
    188 }
    189 
    190 // isNil reports whether x is the nil value.
    191 func (x *operand) isNil() bool {
    192 	return x.mode == value && x.typ == Typ[UntypedNil]
    193 }
    194 
    195 // TODO(gri) The functions operand.assignableTo, checker.convertUntyped,
    196 //           checker.representable, and checker.assignment are
    197 //           overlapping in functionality. Need to simplify and clean up.
    198 
    199 // assignableTo reports whether x is assignable to a variable of type T.
    200 // If the result is false and a non-nil reason is provided, it may be set
    201 // to a more detailed explanation of the failure (result != "").
    202 func (x *operand) assignableTo(conf *Config, T Type, reason *string) bool {
    203 	if x.mode == invalid || T == Typ[Invalid] {
    204 		return true // avoid spurious errors
    205 	}
    206 
    207 	V := x.typ
    208 
    209 	// x's type is identical to T
    210 	if Identical(V, T) {
    211 		return true
    212 	}
    213 
    214 	Vu := V.Underlying()
    215 	Tu := T.Underlying()
    216 
    217 	// x is an untyped value representable by a value of type T
    218 	// TODO(gri) This is borrowing from checker.convertUntyped and
    219 	//           checker.representable. Need to clean up.
    220 	if isUntyped(Vu) {
    221 		switch t := Tu.(type) {
    222 		case *Basic:
    223 			if x.isNil() && t.kind == UnsafePointer {
    224 				return true
    225 			}
    226 			if x.mode == constant_ {
    227 				return representableConst(x.val, conf, t, nil)
    228 			}
    229 			// The result of a comparison is an untyped boolean,
    230 			// but may not be a constant.
    231 			if Vb, _ := Vu.(*Basic); Vb != nil {
    232 				return Vb.kind == UntypedBool && isBoolean(Tu)
    233 			}
    234 		case *Interface:
    235 			return x.isNil() || t.Empty()
    236 		case *Pointer, *Signature, *Slice, *Map, *Chan:
    237 			return x.isNil()
    238 		}
    239 	}
    240 	// Vu is typed
    241 
    242 	// x's type V and T have identical underlying types
    243 	// and at least one of V or T is not a named type
    244 	if Identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
    245 		return true
    246 	}
    247 
    248 	// T is an interface type and x implements T
    249 	if Ti, ok := Tu.(*Interface); ok {
    250 		if m, wrongType := MissingMethod(x.typ, Ti, true); m != nil /* Implements(x.typ, Ti) */ {
    251 			if reason != nil {
    252 				if wrongType {
    253 					*reason = "wrong type for method " + m.Name()
    254 				} else {
    255 					*reason = "missing method " + m.Name()
    256 				}
    257 			}
    258 			return false
    259 		}
    260 		return true
    261 	}
    262 
    263 	// x is a bidirectional channel value, T is a channel
    264 	// type, x's type V and T have identical element types,
    265 	// and at least one of V or T is not a named type
    266 	if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
    267 		if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) {
    268 			return !isNamed(V) || !isNamed(T)
    269 		}
    270 	}
    271 
    272 	return false
    273 }
    274