// 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 implements typechecking of builtin function calls.

package types

import (
	
	
	
)

// builtin type-checks a call to the built-in specified by id and
// reports whether the call is valid, with *x holding the result;
// but x.expr is not set. If the call is invalid, the result is
// false, and *x is undefined.
//
func ( *Checker) ( *operand,  *ast.CallExpr,  builtinId) ( bool) {
	// append is the only built-in that permits the use of ... for the last argument
	 := predeclaredFuncs[]
	if .Ellipsis.IsValid() &&  != _Append {
		.invalidOp(atPos(.Ellipsis),
			_InvalidDotDotDot,
			"invalid use of ... with built-in %s", .name)
		.use(.Args...)
		return
	}

	// For len(x) and cap(x) we need to know if x contains any function calls or
	// receive operations. Save/restore current setting and set hasCallOrRecv to
	// false for the evaluation of x so that we can check it afterwards.
	// Note: We must do this _before_ calling unpack because unpack evaluates the
	//       first argument before we even call arg(x, 0)!
	if  == _Len ||  == _Cap {
		defer func( bool) {
			.hasCallOrRecv = 
		}(.hasCallOrRecv)
		.hasCallOrRecv = false
	}

	// determine actual arguments
	var  getter
	 := len(.Args)
	switch  {
	default:
		// make argument getter
		, , _ = unpack(func( *operand,  int) { .multiExpr(, .Args[]) }, , false)
		if  == nil {
			return
		}
		// evaluate first argument, if present
		if  > 0 {
			(, 0)
			if .mode == invalid {
				return
			}
		}
	case _Make, _New, _Offsetof, _Trace:
		// arguments require special handling
	}

	// check argument count
	{
		 := ""
		if  < .nargs {
			 = "not enough"
		} else if !.variadic &&  > .nargs {
			 = "too many"
		}
		if  != "" {
			.invalidOp(inNode(, .Rparen), _WrongArgCount, "%s arguments for %s (expected %d, found %d)", , , .nargs, )
			return
		}
	}

	switch  {
	case _Append:
		// append(s S, x ...T) S, where T is the element type of S
		// spec: "The variadic function append appends zero or more values x to s of type
		// S, which must be a slice type, and returns the resulting slice, also of type S.
		// The values x are passed to a parameter of type ...T where T is the element type
		// of S and the respective parameter passing rules apply."
		 := .typ
		var  Type
		if ,  := .Underlying().(*Slice);  != nil {
			 = .elem
		} else {
			.invalidArg(, _InvalidAppend, "%s is not a slice", )
			return
		}

		// remember arguments that have been evaluated already
		 := []operand{*}

		// spec: "As a special case, append also accepts a first argument assignable
		// to type []byte with a second argument of string type followed by ... .
		// This form appends the bytes of the string.
		if  == 2 && .Ellipsis.IsValid() {
			if ,  := .assignableTo(, NewSlice(universeByte), nil);  {
				(, 1)
				if .mode == invalid {
					return
				}
				if isString(.typ) {
					if .Types != nil {
						 := makeSig(, , .typ)
						.variadic = true
						.recordBuiltinType(.Fun, )
					}
					.mode = value
					.typ = 
					break
				}
				 = append(, *)
				// fallthrough
			}
		}

		// check general case by creating custom signature
		 := makeSig(, , NewSlice()) // []T required for variadic signature
		.variadic = true
		.arguments(, , , func( *operand,  int) {
			// only evaluate arguments that have not been evaluated before
			if  < len() {
				* = []
				return
			}
			(, )
		}, )
		// ok to continue even if check.arguments reported errors

		.mode = value
		.typ = 
		if .Types != nil {
			.recordBuiltinType(.Fun, )
		}

	case _Cap, _Len:
		// cap(x)
		// len(x)
		 := invalid
		var  Type
		var  constant.Value
		switch  = implicitArrayDeref(.typ.Underlying()); t := .(type) {
		case *Basic:
			if isString() &&  == _Len {
				if .mode == constant_ {
					 = constant_
					 = constant.MakeInt64(int64(len(constant.StringVal(.val))))
				} else {
					 = value
				}
			}

		case *Array:
			 = value
			// spec: "The expressions len(s) and cap(s) are constants
			// if the type of s is an array or pointer to an array and
			// the expression s does not contain channel receives or
			// function calls; in this case s is not evaluated."
			if !.hasCallOrRecv {
				 = constant_
				if .len >= 0 {
					 = constant.MakeInt64(.len)
				} else {
					 = constant.MakeUnknown()
				}
			}

		case *Slice, *Chan:
			 = value

		case *Map:
			if  == _Len {
				 = value
			}
		}

		if  == invalid &&  != Typ[Invalid] {
			 := _InvalidCap
			if  == _Len {
				 = _InvalidLen
			}
			.invalidArg(, , "%s for %s", , .name)
			return
		}

		.mode = 
		.typ = Typ[Int]
		.val = 
		if .Types != nil &&  != constant_ {
			.recordBuiltinType(.Fun, makeSig(.typ, ))
		}

	case _Close:
		// close(c)
		,  := .typ.Underlying().(*Chan)
		if  == nil {
			.invalidArg(, _InvalidClose, "%s is not a channel", )
			return
		}
		if .dir == RecvOnly {
			.invalidArg(, _InvalidClose, "%s must not be a receive-only channel", )
			return
		}

		.mode = novalue
		if .Types != nil {
			.recordBuiltinType(.Fun, makeSig(nil, ))
		}

	case _Complex:
		// complex(x, y floatT) complexT
		var  operand
		(&, 1)
		if .mode == invalid {
			return
		}

		// convert or check untyped arguments
		 := 0
		if isUntyped(.typ) {
			 |= 1
		}
		if isUntyped(.typ) {
			 |= 2
		}
		switch  {
		case 0:
			// x and y are typed => nothing to do
		case 1:
			// only x is untyped => convert to type of y
			.convertUntyped(, .typ)
		case 2:
			// only y is untyped => convert to type of x
			.convertUntyped(&, .typ)
		case 3:
			// x and y are untyped =>
			// 1) if both are constants, convert them to untyped
			//    floating-point numbers if possible,
			// 2) if one of them is not constant (possible because
			//    it contains a shift that is yet untyped), convert
			//    both of them to float64 since they must have the
			//    same type to succeed (this will result in an error
			//    because shifts of floats are not permitted)
			if .mode == constant_ && .mode == constant_ {
				 := func( *operand) {
					if isNumeric(.typ) && constant.Sign(constant.Imag(.val)) == 0 {
						.typ = Typ[UntypedFloat]
					}
				}
				()
				(&)
			} else {
				.convertUntyped(, Typ[Float64])
				.convertUntyped(&, Typ[Float64])
				// x and y should be invalid now, but be conservative
				// and check below
			}
		}
		if .mode == invalid || .mode == invalid {
			return
		}

		// both argument types must be identical
		if !.identical(.typ, .typ) {
			.invalidArg(, _InvalidComplex, "mismatched types %s and %s", .typ, .typ)
			return
		}

		// the argument types must be of floating-point type
		if !isFloat(.typ) {
			.invalidArg(, _InvalidComplex, "arguments have type %s, expected floating-point", .typ)
			return
		}

		// if both arguments are constants, the result is a constant
		if .mode == constant_ && .mode == constant_ {
			.val = constant.BinaryOp(constant.ToFloat(.val), token.ADD, constant.MakeImag(constant.ToFloat(.val)))
		} else {
			.mode = value
		}

		// determine result type
		var  BasicKind
		switch .typ.Underlying().(*Basic).kind {
		case Float32:
			 = Complex64
		case Float64:
			 = Complex128
		case UntypedFloat:
			 = UntypedComplex
		default:
			unreachable()
		}
		 := Typ[]

		if .Types != nil && .mode != constant_ {
			.recordBuiltinType(.Fun, makeSig(, .typ, .typ))
		}

		.typ = 

	case _Copy:
		// copy(x, y []T) int
		var  Type
		if ,  := .typ.Underlying().(*Slice);  != nil {
			 = .elem
		}

		var  operand
		(&, 1)
		if .mode == invalid {
			return
		}
		var  Type
		switch t := .typ.Underlying().(type) {
		case *Basic:
			if isString(.typ) {
				 = universeByte
			}
		case *Slice:
			 = .elem
		}

		if  == nil ||  == nil {
			.invalidArg(, _InvalidCopy, "copy expects slice arguments; found %s and %s", , &)
			return
		}

		if !.identical(, ) {
			.invalidArg(, _InvalidCopy, "arguments to copy %s and %s have different element types %s and %s", , &, , )
			return
		}

		if .Types != nil {
			.recordBuiltinType(.Fun, makeSig(Typ[Int], .typ, .typ))
		}
		.mode = value
		.typ = Typ[Int]

	case _Delete:
		// delete(m, k)
		,  := .typ.Underlying().(*Map)
		if  == nil {
			.invalidArg(, _InvalidDelete, "%s is not a map", )
			return
		}
		(, 1) // k
		if .mode == invalid {
			return
		}

		if ,  := .assignableTo(, .key, nil); ! {
			.invalidArg(, , "%s is not assignable to %s", , .key)
			return
		}

		.mode = novalue
		if .Types != nil {
			.recordBuiltinType(.Fun, makeSig(nil, , .key))
		}

	case _Imag, _Real:
		// imag(complexT) floatT
		// real(complexT) floatT

		// convert or check untyped argument
		if isUntyped(.typ) {
			if .mode == constant_ {
				// an untyped constant number can always be considered
				// as a complex constant
				if isNumeric(.typ) {
					.typ = Typ[UntypedComplex]
				}
			} else {
				// an untyped non-constant argument may appear if
				// it contains a (yet untyped non-constant) shift
				// expression: convert it to complex128 which will
				// result in an error (shift of complex value)
				.convertUntyped(, Typ[Complex128])
				// x should be invalid now, but be conservative and check
				if .mode == invalid {
					return
				}
			}
		}

		// the argument must be of complex type
		if !isComplex(.typ) {
			 := _InvalidImag
			if  == _Real {
				 = _InvalidReal
			}
			.invalidArg(, , "argument has type %s, expected complex type", .typ)
			return
		}

		// if the argument is a constant, the result is a constant
		if .mode == constant_ {
			if  == _Real {
				.val = constant.Real(.val)
			} else {
				.val = constant.Imag(.val)
			}
		} else {
			.mode = value
		}

		// determine result type
		var  BasicKind
		switch .typ.Underlying().(*Basic).kind {
		case Complex64:
			 = Float32
		case Complex128:
			 = Float64
		case UntypedComplex:
			 = UntypedFloat
		default:
			unreachable()
		}
		 := Typ[]

		if .Types != nil && .mode != constant_ {
			.recordBuiltinType(.Fun, makeSig(, .typ))
		}

		.typ = 

	case _Make:
		// make(T, n)
		// make(T, n, m)
		// (no argument evaluated yet)
		 := .Args[0]
		 := .typ()
		if  == Typ[Invalid] {
			return
		}

		var  int // minimum number of arguments
		switch .Underlying().(type) {
		case *Slice:
			 = 2
		case *Map, *Chan:
			 = 1
		default:
			.invalidArg(, _InvalidMake, "cannot make %s; type must be slice, map, or channel", )
			return
		}
		if  <  || +1 <  {
			.errorf(, _WrongArgCount, "%v expects %d or %d arguments; found %d", , , +1, )
			return
		}
		 := []Type{}
		var  []int64 // constant integer arguments, if any
		for ,  := range .Args[1:] {
			,  := .index(, -1) // ok to continue with typ == Typ[Invalid]
			 = append(, )
			if  >= 0 {
				 = append(, )
			}
		}
		if len() == 2 && [0] > [1] {
			.invalidArg(.Args[1], _SwappedMakeArgs, "length and capacity swapped")
			// safe to continue
		}
		.mode = value
		.typ = 
		if .Types != nil {
			.recordBuiltinType(.Fun, makeSig(.typ, ...))
		}

	case _New:
		// new(T)
		// (no argument evaluated yet)
		 := .typ(.Args[0])
		if  == Typ[Invalid] {
			return
		}

		.mode = value
		.typ = &Pointer{base: }
		if .Types != nil {
			.recordBuiltinType(.Fun, makeSig(.typ, ))
		}

	case _Panic:
		// panic(x)
		// record panic call if inside a function with result parameters
		// (for use in Checker.isTerminating)
		if .sig != nil && .sig.results.Len() > 0 {
			// function has result parameters
			 := .isPanic
			if  == nil {
				// allocate lazily
				 = make(map[*ast.CallExpr]bool)
				.isPanic = 
			}
			[] = true
		}

		.assignment(, &emptyInterface, "argument to panic")
		if .mode == invalid {
			return
		}

		.mode = novalue
		if .Types != nil {
			.recordBuiltinType(.Fun, makeSig(nil, &emptyInterface))
		}

	case _Print, _Println:
		// print(x, y, ...)
		// println(x, y, ...)
		var  []Type
		if  > 0 {
			 = make([]Type, )
			for  := 0;  < ; ++ {
				if  > 0 {
					(, ) // first argument already evaluated
				}
				.assignment(, nil, "argument to "+predeclaredFuncs[].name)
				if .mode == invalid {
					// TODO(gri) "use" all arguments?
					return
				}
				[] = .typ
			}
		}

		.mode = novalue
		if .Types != nil {
			.recordBuiltinType(.Fun, makeSig(nil, ...))
		}

	case _Recover:
		// recover() interface{}
		.mode = value
		.typ = &emptyInterface
		if .Types != nil {
			.recordBuiltinType(.Fun, makeSig(.typ))
		}

	case _Alignof:
		// unsafe.Alignof(x T) uintptr
		.assignment(, nil, "argument to unsafe.Alignof")
		if .mode == invalid {
			return
		}

		.mode = constant_
		.val = constant.MakeInt64(.conf.alignof(.typ))
		.typ = Typ[Uintptr]
		// result is constant - no need to record signature

	case _Offsetof:
		// unsafe.Offsetof(x T) uintptr, where x must be a selector
		// (no argument evaluated yet)
		 := .Args[0]
		,  := unparen().(*ast.SelectorExpr)
		if  == nil {
			.invalidArg(, _BadOffsetofSyntax, "%s is not a selector expression", )
			.use()
			return
		}

		.expr(, .X)
		if .mode == invalid {
			return
		}

		 := derefStructPtr(.typ)
		 := .Sel.Name
		, ,  := .lookupFieldOrMethod(, false, .pkg, )
		switch .(type) {
		case nil:
			.invalidArg(, _MissingFieldOrMethod, "%s has no single field %s", , )
			return
		case *Func:
			// TODO(gri) Using derefStructPtr may result in methods being found
			// that don't actually exist. An error either way, but the error
			// message is confusing. See: https://play.golang.org/p/al75v23kUy ,
			// but go/types reports: "invalid argument: x.m is a method value".
			.invalidArg(, _InvalidOffsetof, "%s is a method value", )
			return
		}
		if  {
			.invalidArg(, _InvalidOffsetof, "field %s is embedded via a pointer in %s", , )
			return
		}

		// TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)?
		.recordSelection(, FieldVal, , , , false)

		 := .conf.offsetof(, )
		.mode = constant_
		.val = constant.MakeInt64()
		.typ = Typ[Uintptr]
		// result is constant - no need to record signature

	case _Sizeof:
		// unsafe.Sizeof(x T) uintptr
		.assignment(, nil, "argument to unsafe.Sizeof")
		if .mode == invalid {
			return
		}

		.mode = constant_
		.val = constant.MakeInt64(.conf.sizeof(.typ))
		.typ = Typ[Uintptr]
		// result is constant - no need to record signature

	case _Assert:
		// assert(pred) causes a typechecker error if pred is false.
		// The result of assert is the value of pred if there is no error.
		// Note: assert is only available in self-test mode.
		if .mode != constant_ || !isBoolean(.typ) {
			.invalidArg(, _Test, "%s is not a boolean constant", )
			return
		}
		if .val.Kind() != constant.Bool {
			.errorf(, _Test, "internal error: value of %s should be a boolean constant", )
			return
		}
		if !constant.BoolVal(.val) {
			.errorf(, _Test, "%v failed", )
			// compile-time assertion failure - safe to continue
		}
		// result is constant - no need to record signature

	case _Trace:
		// trace(x, y, z, ...) dumps the positions, expressions, and
		// values of its arguments. The result of trace is the value
		// of the first argument.
		// Note: trace is only available in self-test mode.
		// (no argument evaluated yet)
		if  == 0 {
			.dump("%v: trace() without arguments", .Pos())
			.mode = novalue
			break
		}
		var  operand
		 := 
		for ,  := range .Args {
			.rawExpr(, , nil) // permit trace for types, e.g.: new(trace(T))
			.dump("%v: %s", .Pos(), )
			 = & // use incoming x only for first argument
		}
		// trace is only available in test mode - no need to record signature

	default:
		unreachable()
	}

	return true
}

// makeSig makes a signature for the given argument and result types.
// Default types are used for untyped arguments, and res may be nil.
func makeSig( Type,  ...Type) *Signature {
	 := make([]*Var, len())
	for ,  := range  {
		[] = NewVar(token.NoPos, nil, "", Default())
	}
	 := NewTuple(...)
	var  *Tuple
	if  != nil {
		assert(!isUntyped())
		 = NewTuple(NewVar(token.NoPos, nil, "", ))
	}
	return &Signature{params: , results: }
}

// implicitArrayDeref returns A if typ is of the form *A and A is an array;
// otherwise it returns typ.
//
func implicitArrayDeref( Type) Type {
	if ,  := .(*Pointer);  {
		if ,  := .base.Underlying().(*Array);  {
			return 
		}
	}
	return 
}

// unparen returns e with any enclosing parentheses stripped.
func unparen( ast.Expr) ast.Expr {
	for {
		,  := .(*ast.ParenExpr)
		if ! {
			return 
		}
		 = .X
	}
}