// 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 nopos.
func ( *operand) () token.Pos {
	// x.expr may not be set if x is invalid
	if .expr == nil {
		return 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 {
	// special-case nil
	if .mode == value && .typ == Typ[UntypedNil] {
		return "nil"
	}

	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 isValid(.typ) {
			var  string
			if isGeneric(.typ) {
				 = " of generic type "
			} else {
				 = " of type "
			}
			.WriteString()
			WriteType(&, .typ, )
			if ,  := .typ.(*TypeParam);  != nil {
				.WriteString(" constrained by ")
				WriteType(&, .bound, ) // do not compute interface type sets here
				// If we have the type set and it's empty, say so for better error messages.
				if hasEmptyTypeset() {
					.WriteString(" with empty type set")
				}
			}
		} 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 (untyped) 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 cause is provided, it may be set to a more
// detailed explanation of the failure (result != ""). The returned error code
// is only valid if the (first) result is false. 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, Code) {
	if .mode == invalid || !isValid() {
		return true, 0 // avoid spurious errors
	}

	 := .typ

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

	 := under()
	 := under()
	,  := .(*TypeParam)
	,  := .(*TypeParam)

	// x is an untyped value representable by a value of type T.
	if isUntyped() {
		assert( == nil)
		if  != nil {
			// T is a type parameter: x is assignable to T if it is
			// representable by each specific type in the type set of T.
			return .is(func( *term) bool {
				if  == nil {
					return false
				}
				// A term may be a tilde term but the underlying
				// type of an untyped value doesn't change so we
				// don't need to do anything special.
				, ,  := .implicitTypeAndValue(, .typ)
				return  != nil
			}), IncompatibleAssign
		}
		, ,  := .implicitTypeAndValue(, )
		return  != 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
	// and neither V nor T is a type parameter.
	if Identical(, ) && (!hasName() || !hasName()) &&  == nil &&  == nil {
		return true, 0
	}

	// T is an interface type, but not a type parameter, and V implements T.
	// Also handle the case where T is a pointer to an interface so that we get
	// the Checker.implements error cause.
	if ,  := .(*Interface);  &&  == nil || isInterfacePtr() {
		if .implements(.Pos(), , , false, ) {
			return true, 0
		}
		// V doesn't implement T but V may still be assignable to T if V
		// is a type parameter; do not report an error in that case yet.
		if  == nil {
			return false, InvalidIfaceAssign
		}
		if  != nil {
			* = ""
		}
	}

	// If V is an interface, check if a missing type assertion is the problem.
	if ,  := .(*Interface);  != nil &&  == nil {
		if .implements(.Pos(), , , false, nil) {
			// T implements V, so give hint about type assertion.
			if  != nil {
				* = "need type assertion"
			}
			return false, IncompatibleAssign
		}
	}

	// 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) {
			return !hasName() || !hasName(), InvalidChanAssign
		}
	}

	// optimization: if we don't have type parameters, we're done
	if  == nil &&  == nil {
		return false, IncompatibleAssign
	}

	 := func( string,  ...any) {
		if  != nil &&  != nil {
			 := .sprintf(, ...)
			if * != "" {
				 += "\n\t" + *
			}
			* = 
		}
	}

	// x's type V is not a named type and T is a type parameter, and
	// x is assignable to each specific type in T's type set.
	if !hasName() &&  != nil {
		 := false
		 := IncompatibleAssign
		.is(func( *term) bool {
			if  == nil {
				return false // no specific types
			}
			,  = .(, .typ, )
			if ! {
				("cannot assign %s to %s (in %s)", .typ, .typ, )
				return false
			}
			return true
		})
		return , 
	}

	// x's type V is a type parameter and T is not a named type,
	// and values x' of each specific type in V's type set are
	// assignable to T.
	if  != nil && !hasName() {
		 := * // don't clobber outer x
		 := false
		 := IncompatibleAssign
		.is(func( *term) bool {
			if  == nil {
				return false // no specific types
			}
			.typ = .typ
			,  = .(, , )
			if ! {
				("cannot assign %s (in %s) to %s", .typ, , )
				return false
			}
			return true
		})
		return , 
	}

	return false, IncompatibleAssign
}