Black Lives Matter. Support the Equal Justice Initiative.

Source file src/go/types/operand.go

Documentation: go/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  	"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  			var intro string
   162  			switch {
   163  			case isGeneric(x.typ):
   164  				intro = " of generic type "
   165  			case asTypeParam(x.typ) != nil:
   166  				intro = " of type parameter type "
   167  			default:
   168  				intro = " of type "
   169  			}
   170  			buf.WriteString(intro)
   171  			WriteType(&buf, x.typ, qf)
   172  		} else {
   173  			buf.WriteString(" with invalid type")
   174  		}
   175  	}
   176  
   177  	// )
   178  	if expr != "" {
   179  		buf.WriteByte(')')
   180  	}
   181  
   182  	return buf.String()
   183  }
   184  
   185  func (x *operand) String() string {
   186  	return operandString(x, nil)
   187  }
   188  
   189  // setConst sets x to the untyped constant for literal lit.
   190  func (x *operand) setConst(tok token.Token, lit string) {
   191  	var kind BasicKind
   192  	switch tok {
   193  	case token.INT:
   194  		kind = UntypedInt
   195  	case token.FLOAT:
   196  		kind = UntypedFloat
   197  	case token.IMAG:
   198  		kind = UntypedComplex
   199  	case token.CHAR:
   200  		kind = UntypedRune
   201  	case token.STRING:
   202  		kind = UntypedString
   203  	default:
   204  		unreachable()
   205  	}
   206  
   207  	val := constant.MakeFromLiteral(lit, tok, 0)
   208  	if val.Kind() == constant.Unknown {
   209  		x.mode = invalid
   210  		x.typ = Typ[Invalid]
   211  		return
   212  	}
   213  	x.mode = constant_
   214  	x.typ = Typ[kind]
   215  	x.val = val
   216  }
   217  
   218  // isNil reports whether x is the nil value.
   219  func (x *operand) isNil() bool {
   220  	return x.mode == value && x.typ == Typ[UntypedNil]
   221  }
   222  
   223  // assignableTo reports whether x is assignable to a variable of type T. If the
   224  // result is false and a non-nil reason is provided, it may be set to a more
   225  // detailed explanation of the failure (result != ""). The returned error code
   226  // is only valid if the (first) result is false. The check parameter may be nil
   227  // if assignableTo is invoked through an exported API call, i.e., when all
   228  // methods have been type-checked.
   229  func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, errorCode) {
   230  	if x.mode == invalid || T == Typ[Invalid] {
   231  		return true, 0 // avoid spurious errors
   232  	}
   233  
   234  	V := x.typ
   235  
   236  	// x's type is identical to T
   237  	if check.identical(V, T) {
   238  		return true, 0
   239  	}
   240  
   241  	Vu := optype(V)
   242  	Tu := optype(T)
   243  
   244  	// x is an untyped value representable by a value of type T.
   245  	if isUntyped(Vu) {
   246  		if t, ok := Tu.(*_Sum); ok {
   247  			return t.is(func(t Type) bool {
   248  				// TODO(gri) this could probably be more efficient
   249  				ok, _ := x.assignableTo(check, t, reason)
   250  				return ok
   251  			}), _IncompatibleAssign
   252  		}
   253  		newType, _, _ := check.implicitTypeAndValue(x, Tu)
   254  		return newType != nil, _IncompatibleAssign
   255  	}
   256  	// Vu is typed
   257  
   258  	// x's type V and T have identical underlying types
   259  	// and at least one of V or T is not a named type
   260  	if check.identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
   261  		return true, 0
   262  	}
   263  
   264  	// T is an interface type and x implements T
   265  	if Ti, ok := Tu.(*Interface); ok {
   266  		if m, wrongType := check.missingMethod(V, Ti, true); m != nil /* Implements(V, Ti) */ {
   267  			if reason != nil {
   268  				if wrongType != nil {
   269  					if check.identical(m.typ, wrongType.typ) {
   270  						*reason = fmt.Sprintf("missing method %s (%s has pointer receiver)", m.name, m.name)
   271  					} else {
   272  						*reason = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrongType.typ, m.typ)
   273  					}
   274  
   275  				} else {
   276  					*reason = "missing method " + m.Name()
   277  				}
   278  			}
   279  			return false, _InvalidIfaceAssign
   280  		}
   281  		return true, 0
   282  	}
   283  
   284  	// x is a bidirectional channel value, T is a channel
   285  	// type, x's type V and T have identical element types,
   286  	// and at least one of V or T is not a named type
   287  	if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
   288  		if Tc, ok := Tu.(*Chan); ok && check.identical(Vc.elem, Tc.elem) {
   289  			return !isNamed(V) || !isNamed(T), _InvalidChanAssign
   290  		}
   291  	}
   292  
   293  	return false, _IncompatibleAssign
   294  }
   295  

View as plain text