// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file defines operands and associated operations.

package types

import (
	
	
	
	
	
)

// An operandMode specifies the (addressing) mode of an operand.
type operandMode byte

const (
	invalid   operandMode = iota // operand is invalid
	novalue                      // operand represents no value (result of a function call w/o result)
	builtin                      // operand is a built-in function
	typexpr                      // operand is a type
	constant_                    // operand is a constant; the operand's typ is a Basic type
	variable                     // operand is an addressable variable
	mapindex                     // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)
	value                        // operand is a computed value
	commaok                      // like value, but operand may be used in a comma,ok expression
	commaerr                     // like commaok, but second value is error, not boolean
	cgofunc                      // operand is a cgo function
)

var operandModeString = [...]string{
	invalid:   "invalid operand",
	novalue:   "no value",
	builtin:   "built-in",
	typexpr:   "type",
	constant_: "constant",
	variable:  "variable",
	mapindex:  "map index expression",
	value:     "value",
	commaok:   "comma, ok expression",
	commaerr:  "comma, error expression",
	cgofunc:   "cgo function",
}

// An operand represents an intermediate value during type checking.
// Operands have an (addressing) mode, the expression evaluating to
// the operand, the operand's type, a value for constants, and an id
// for built-in functions.
// The zero value of operand is a ready to use invalid operand.
//
type operand struct {
	mode operandMode
	expr ast.Expr
	typ  Type
	val  constant.Value
	id   builtinId
}

// Pos returns the position of the expression corresponding to x.
// If x is invalid the position is token.NoPos.
//
func ( *operand) () token.Pos {
	// x.expr may not be set if x is invalid
	if .expr == nil {
		return token.NoPos
	}
	return .expr.Pos()
}

// Operand string formats
// (not all "untyped" cases can appear due to the type system,
// but they fall out naturally here)
//
// mode       format
//
// invalid    <expr> (               <mode>                    )
// novalue    <expr> (               <mode>                    )
// builtin    <expr> (               <mode>                    )
// typexpr    <expr> (               <mode>                    )
//
// constant   <expr> (<untyped kind> <mode>                    )
// constant   <expr> (               <mode>       of type <typ>)
// constant   <expr> (<untyped kind> <mode> <val>              )
// constant   <expr> (               <mode> <val> of type <typ>)
//
// variable   <expr> (<untyped kind> <mode>                    )
// variable   <expr> (               <mode>       of type <typ>)
//
// mapindex   <expr> (<untyped kind> <mode>                    )
// mapindex   <expr> (               <mode>       of type <typ>)
//
// value      <expr> (<untyped kind> <mode>                    )
// value      <expr> (               <mode>       of type <typ>)
//
// commaok    <expr> (<untyped kind> <mode>                    )
// commaok    <expr> (               <mode>       of type <typ>)
//
// commaerr   <expr> (<untyped kind> <mode>                    )
// commaerr   <expr> (               <mode>       of type <typ>)
//
// cgofunc    <expr> (<untyped kind> <mode>                    )
// cgofunc    <expr> (               <mode>       of type <typ>)
//
func operandString( *operand,  Qualifier) string {
	var  bytes.Buffer

	var  string
	if .expr != nil {
		 = ExprString(.expr)
	} else {
		switch .mode {
		case builtin:
			 = predeclaredFuncs[.id].name
		case typexpr:
			 = TypeString(.typ, )
		case constant_:
			 = .val.String()
		}
	}

	// <expr> (
	if  != "" {
		.WriteString()
		.WriteString(" (")
	}

	// <untyped kind>
	 := false
	switch .mode {
	case invalid, novalue, builtin, typexpr:
		// no type
	default:
		// should have a type, but be cautious (don't crash during printing)
		if .typ != nil {
			if isUntyped(.typ) {
				.WriteString(.typ.(*Basic).name)
				.WriteByte(' ')
				break
			}
			 = true
		}
	}

	// <mode>
	.WriteString(operandModeString[.mode])

	// <val>
	if .mode == constant_ {
		if  := .val.String();  !=  {
			.WriteByte(' ')
			.WriteString()
		}
	}

	// <typ>
	if  {
		if .typ != Typ[Invalid] {
			.WriteString(" of type ")
			WriteType(&, .typ, )
		} else {
			.WriteString(" with invalid type")
		}
	}

	// )
	if  != "" {
		.WriteByte(')')
	}

	return .String()
}

func ( *operand) () string {
	return operandString(, nil)
}

// setConst sets x to the untyped constant for literal lit.
func ( *operand) ( token.Token,  string) {
	var  BasicKind
	switch  {
	case token.INT:
		 = UntypedInt
	case token.FLOAT:
		 = UntypedFloat
	case token.IMAG:
		 = UntypedComplex
	case token.CHAR:
		 = UntypedRune
	case token.STRING:
		 = UntypedString
	default:
		unreachable()
	}

	 := constant.MakeFromLiteral(, , 0)
	if .Kind() == constant.Unknown {
		.mode = invalid
		.typ = Typ[Invalid]
		return
	}
	.mode = constant_
	.typ = Typ[]
	.val = 
}

// isNil reports whether x is the nil value.
func ( *operand) () bool {
	return .mode == value && .typ == Typ[UntypedNil]
}

// assignableTo reports whether x is assignable to a variable of type T. If the
// result is false and a non-nil reason is provided, it may be set to a more
// detailed explanation of the failure (result != ""). The check parameter may
// be nil if assignableTo is invoked through an exported API call, i.e., when
// all methods have been type-checked.
func ( *operand) ( *Checker,  Type,  *string) (bool, errorCode) {
	if .mode == invalid ||  == Typ[Invalid] {
		return true, 0 // avoid spurious errors
	}

	 := .typ

	// x's type is identical to T
	if .identical(, ) {
		return true, 0
	}

	 := .Underlying()
	 := .Underlying()

	// x is an untyped value representable by a value of type T.
	if isUntyped() {
		if ,  := .(*Basic);  && .mode == constant_ {
			return representableConst(.val, , , nil), _IncompatibleAssign
		}
		return .implicitType(, ) != nil, _IncompatibleAssign
	}
	// Vu is typed

	// x's type V and T have identical underlying types and at least one of V or
	// T is not a named type.
	if .identical(, ) && (!isNamed() || !isNamed()) {
		return true, 0
	}

	// T is an interface type and x implements T
	if ,  := .(*Interface);  {
		if ,  := .missingMethod(, , true);  != nil /* Implements(V, Ti) */ {
			if  != nil {
				if  != nil {
					if .identical(.typ, .typ) {
						* = fmt.Sprintf("missing method %s (%s has pointer receiver)", .name, .name)
					} else {
						* = fmt.Sprintf("wrong type for method %s (have %s, want %s)", .Name(), .typ, .typ)
					}

				} else {
					* = "missing method " + .Name()
				}
			}
			return false, _InvalidIfaceAssign
		}
		return true, 0
	}

	// x is a bidirectional channel value, T is a channel
	// type, x's type V and T have identical element types,
	// and at least one of V or T is not a named type
	if ,  := .(*Chan);  && .dir == SendRecv {
		if ,  := .(*Chan);  && .identical(.elem, .elem) {
			if !isNamed() || !isNamed() {
				return true, 0
			} else {
				return false, _InvalidChanAssign
			}
		}
	}

	return false, _IncompatibleAssign
}