// 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) {
	 := .Args

	// append is the only built-in that permits the use of ... for the last argument
	 := predeclaredFuncs[]
	if .Ellipsis.IsValid() &&  != _Append {
		.errorf(atPos(.Ellipsis),
			InvalidDotDotDot,
			invalidOp+"invalid use of ... with built-in %s", .name)
		.use(...)
		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 exprList because exprList evaluates
	//       all arguments.
	if  == _Len ||  == _Cap {
		defer func( bool) {
			.hasCallOrRecv = 
		}(.hasCallOrRecv)
		.hasCallOrRecv = false
	}

	// Evaluate arguments for built-ins that use ordinary (value) arguments.
	// For built-ins with special argument handling (make, new, etc.),
	// evaluation is done by the respective built-in code.
	var  []*operand // not valid for _Make, _New, _Offsetof, _Trace
	var  int
	switch  {
	default:
		// check all arguments
		 = .exprList()
		 = len()
		for ,  := range  {
			if .mode == invalid {
				return
			}
		}
		// first argument is always in x
		if  > 0 {
			* = *[0]
		}
	case _Make, _New, _Offsetof, _Trace:
		// arguments require special handling
		 = len()
	}

	// check argument count
	{
		 := ""
		if  < .nargs {
			 = "not enough"
		} else if !.variadic &&  > .nargs {
			 = "too many"
		}
		if  != "" {
			.errorf(inNode(, .Rparen), WrongArgCount, invalidOp+"%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 ,  := coreType().(*Slice);  != nil {
			 = .elem
		} else {
			var  string
			switch {
			case .isNil():
				 = "have untyped nil"
			case isTypeParam():
				if  := coreType();  != nil {
					 = .sprintf("%s has core type %s", , )
				} else {
					 = .sprintf("%s has no core type", )
				}
			default:
				 = .sprintf("have %s", )
			}
			// don't use invalidArg prefix here as it would repeat "argument" in the error message
			.errorf(, InvalidAppend, "first argument to append must be a slice; %s", )
			return
		}

		// 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  := coreString(.typ);  != nil && isString() {
					if .recordTypes() {
						 := makeSig(, , .typ)
						.variadic = true
						.recordBuiltinType(.Fun, )
					}
					.mode = value
					.typ = 
					break
				}
			}
		}

		// check general case by creating custom signature
		 := makeSig(, , NewSlice()) // []T required for variadic signature
		.variadic = true
		.arguments(, , nil, nil, , nil, nil) // discard result (we know the result type)
		// ok to continue even if check.arguments reported errors

		.mode = value
		.typ = 
		if .recordTypes() {
			.recordBuiltinType(.Fun, )
		}

	case _Cap, _Len:
		// cap(x)
		// len(x)
		 := invalid
		var  constant.Value
		switch t := arrayPtrDeref(under(.typ)).(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
			}

		case *Interface:
			if !isTypeParam(.typ) {
				break
			}
			if .typeSet().underIs(func( Type) bool {
				switch t := arrayPtrDeref().(type) {
				case *Basic:
					if isString() &&  == _Len {
						return true
					}
				case *Array, *Slice, *Chan:
					return true
				case *Map:
					if  == _Len {
						return true
					}
				}
				return false
			}) {
				 = value
			}
		}

		if  == invalid {
			// avoid error if underlying type is invalid
			if isValid(under(.typ)) {
				 := InvalidCap
				if  == _Len {
					 = InvalidLen
				}
				.errorf(, , invalidArg+"%s for %s", , .name)
			}
			return
		}

		// record the signature before changing x.typ
		if .recordTypes() &&  != constant_ {
			.recordBuiltinType(.Fun, makeSig(Typ[Int], .typ))
		}

		.mode = 
		.typ = Typ[Int]
		.val = 

	case _Clear:
		// clear(m)
		.verifyVersionf(.Fun, go1_21, "clear")

		if !underIs(.typ, func( Type) bool {
			switch .(type) {
			case *Map, *Slice:
				return true
			}
			.errorf(, InvalidClear, invalidArg+"cannot clear %s: argument must be (or constrained by) map or slice", )
			return false
		}) {
			return
		}

		.mode = novalue
		if .recordTypes() {
			.recordBuiltinType(.Fun, makeSig(nil, .typ))
		}

	case _Close:
		// close(c)
		if !underIs(.typ, func( Type) bool {
			,  := .(*Chan)
			if  == nil {
				.errorf(, InvalidClose, invalidOp+"cannot close non-channel %s", )
				return false
			}
			if .dir == RecvOnly {
				.errorf(, InvalidClose, invalidOp+"cannot close receive-only channel %s", )
				return false
			}
			return true
		}) {
			return
		}
		.mode = novalue
		if .recordTypes() {
			.recordBuiltinType(.Fun, makeSig(nil, .typ))
		}

	case _Complex:
		// complex(x, y floatT) complexT
		 := [1]

		// 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) {
			.errorf(, InvalidComplex, invalidOp+"%v (mismatched types %s and %s)", , .typ, .typ)
			return
		}

		// the argument types must be of floating-point type
		// (applyTypeFunc never calls f with a type parameter)
		 := func( Type) Type {
			assert(!isTypeParam())
			if ,  := under().(*Basic);  != nil {
				switch .kind {
				case Float32:
					return Typ[Complex64]
				case Float64:
					return Typ[Complex128]
				case UntypedFloat:
					return Typ[UntypedComplex]
				}
			}
			return nil
		}
		 := .applyTypeFunc(, , )
		if  == nil {
			.errorf(, InvalidComplex, invalidArg+"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
		}

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

		.typ = 

	case _Copy:
		// copy(x, y []T) int
		,  := coreType(.typ).(*Slice)

		 := [1]
		 := coreString(.typ)
		if  != nil && isString() {
			 = NewSlice(universeByte)
		}
		,  := .(*Slice)

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

		if !Identical(.elem, .elem) {
			.errorf(, InvalidCopy, invalidArg+"arguments to copy %s and %s have different element types %s and %s", , , .elem, .elem)
			return
		}

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

	case _Delete:
		// delete(map_, key)
		// map_ must be a map type or a type parameter describing map types.
		// The key cannot be a type parameter for now.
		 := .typ
		var  Type
		if !underIs(, func( Type) bool {
			,  := .(*Map)
			if  == nil {
				.errorf(, InvalidDelete, invalidArg+"%s is not a map", )
				return false
			}
			if  != nil && !Identical(.key, ) {
				.errorf(, InvalidDelete, invalidArg+"maps of %s must have identical key types", )
				return false
			}
			 = .key
			return true
		}) {
			return
		}

		* = *[1] // key
		.assignment(, , "argument to delete")
		if .mode == invalid {
			return
		}

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

	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
		// (applyTypeFunc never calls f with a type parameter)
		 := func( Type) Type {
			assert(!isTypeParam())
			if ,  := under().(*Basic);  != nil {
				switch .kind {
				case Complex64:
					return Typ[Float32]
				case Complex128:
					return Typ[Float64]
				case UntypedComplex:
					return Typ[UntypedFloat]
				}
			}
			return nil
		}
		 := .applyTypeFunc(, , )
		if  == nil {
			 := InvalidImag
			if  == _Real {
				 = InvalidReal
			}
			.errorf(, , 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
		}

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

		.typ = 

	case _Make:
		// make(T, n)
		// make(T, n, m)
		// (no argument evaluated yet)
		 := [0]
		 := .varType()
		if !isValid() {
			return
		}

		var  int // minimum number of arguments
		switch coreType().(type) {
		case *Slice:
			 = 2
		case *Map, *Chan:
			 = 1
		case nil:
			.errorf(, InvalidMake, invalidArg+"cannot make %s: no core type", )
			return
		default:
			.errorf(, InvalidMake, invalidArg+"cannot make %s; type must be slice, map, or channel", )
			return
		}
		if  <  || +1 <  {
			.errorf(, WrongArgCount, invalidOp+"%v expects %d or %d arguments; found %d", , , +1, )
			return
		}

		 := []Type{}
		var  []int64 // constant integer arguments, if any
		for ,  := range [1:] {
			,  := .index(, -1) // ok to continue with typ == Typ[Invalid]
			 = append(, )
			if  >= 0 {
				 = append(, )
			}
		}
		if len() == 2 && [0] > [1] {
			.error([1], SwappedMakeArgs, invalidArg+"length and capacity swapped")
			// safe to continue
		}
		.mode = value
		.typ = 
		if .recordTypes() {
			.recordBuiltinType(.Fun, makeSig(.typ, ...))
		}

	case _Max, _Min:
		// max(x, ...)
		// min(x, ...)
		.verifyVersionf(.Fun, go1_21, .name)

		 := token.LSS
		if  == _Max {
			 = token.GTR
		}

		for ,  := range  {
			if .mode == invalid {
				return
			}

			if !allOrdered(.typ) {
				.errorf(, InvalidMinMaxOperand, invalidArg+"%s cannot be ordered", )
				return
			}

			// The first argument is already in x and there's nothing left to do.
			if  > 0 {
				.matchTypes(, )
				if .mode == invalid {
					return
				}

				if !Identical(.typ, .typ) {
					.errorf(, MismatchedTypes, invalidArg+"mismatched types %s (previous argument) and %s (type of %s)", .typ, .typ, .expr)
					return
				}

				if .mode == constant_ && .mode == constant_ {
					if constant.Compare(.val, , .val) {
						* = *
					}
				} else {
					.mode = value
				}
			}
		}

		// If nargs == 1, make sure x.mode is either a value or a constant.
		if .mode != constant_ {
			.mode = value
			// A value must not be untyped.
			.assignment(, &emptyInterface, "argument to "+.name)
			if .mode == invalid {
				return
			}
		}

		// Use the final type computed above for all arguments.
		for ,  := range  {
			.updateExprType(.expr, .typ, true)
		}

		if .recordTypes() && .mode != constant_ {
			 := make([]Type, )
			for  := range  {
				[] = .typ
			}
			.recordBuiltinType(.Fun, makeSig(.typ, ...))
		}

	case _New:
		// new(T)
		// (no argument evaluated yet)
		 := .varType([0])
		if !isValid() {
			return
		}

		.mode = value
		.typ = &Pointer{base: }
		if .recordTypes() {
			.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 .recordTypes() {
			.recordBuiltinType(.Fun, makeSig(nil, &emptyInterface))
		}

	case _Print, _Println:
		// print(x, y, ...)
		// println(x, y, ...)
		var  []Type
		if  > 0 {
			 = make([]Type, )
			for ,  := range  {
				.assignment(, nil, "argument to "+predeclaredFuncs[].name)
				if .mode == invalid {
					return
				}
				[] = .typ
			}
		}

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

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

	case _Add:
		// unsafe.Add(ptr unsafe.Pointer, len IntegerType) unsafe.Pointer
		.verifyVersionf(.Fun, go1_17, "unsafe.Add")

		.assignment(, Typ[UnsafePointer], "argument to unsafe.Add")
		if .mode == invalid {
			return
		}

		 := [1]
		if !.isValidIndex(, InvalidUnsafeAdd, "length", true) {
			return
		}

		.mode = value
		.typ = Typ[UnsafePointer]
		if .recordTypes() {
			.recordBuiltinType(.Fun, makeSig(.typ, .typ, .typ))
		}

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

		if hasVarSize(.typ, nil) {
			.mode = value
			if .recordTypes() {
				.recordBuiltinType(.Fun, makeSig(Typ[Uintptr], .typ))
			}
		} else {
			.mode = constant_
			.val = constant.MakeInt64(.conf.alignof(.typ))
			// result is constant - no need to record signature
		}
		.typ = Typ[Uintptr]

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

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

		 := derefStructPtr(.typ)
		 := .Sel.Name
		, ,  := LookupFieldOrMethod(, false, .pkg, )
		switch .(type) {
		case nil:
			.errorf(, MissingFieldOrMethod, invalidArg+"%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".
			.errorf(, InvalidOffsetof, invalidArg+"%s is a method value", )
			return
		}
		if  {
			.errorf(, InvalidOffsetof, invalidArg+"field %s is embedded via a pointer in %s", , )
			return
		}

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

		// record the selector expression (was bug - go.dev/issue/47895)
		{
			 := value
			if .mode == variable ||  {
				 = variable
			}
			.record(&operand{, , .Type(), nil, 0})
		}

		// The field offset is considered a variable even if the field is declared before
		// the part of the struct which is variable-sized. This makes both the rules
		// simpler and also permits (or at least doesn't prevent) a compiler from re-
		// arranging struct fields if it wanted to.
		if hasVarSize(, nil) {
			.mode = value
			if .recordTypes() {
				.recordBuiltinType(.Fun, makeSig(Typ[Uintptr], .Type()))
			}
		} else {
			 := .conf.offsetof(, )
			if  < 0 {
				.errorf(, TypeTooLarge, "%s is too large", )
				return
			}
			.mode = constant_
			.val = constant.MakeInt64()
			// result is constant - no need to record signature
		}
		.typ = Typ[Uintptr]

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

		if hasVarSize(.typ, nil) {
			.mode = value
			if .recordTypes() {
				.recordBuiltinType(.Fun, makeSig(Typ[Uintptr], .typ))
			}
		} else {
			 := .conf.sizeof(.typ)
			if  < 0 {
				.errorf(, TypeTooLarge, "%s is too large", )
				return
			}
			.mode = constant_
			.val = constant.MakeInt64()
			// result is constant - no need to record signature
		}
		.typ = Typ[Uintptr]

	case _Slice:
		// unsafe.Slice(ptr *T, len IntegerType) []T
		.verifyVersionf(.Fun, go1_17, "unsafe.Slice")

		,  := coreType(.typ).(*Pointer)
		if  == nil {
			.errorf(, InvalidUnsafeSlice, invalidArg+"%s is not a pointer", )
			return
		}

		 := [1]
		if !.isValidIndex(, InvalidUnsafeSlice, "length", false) {
			return
		}

		.mode = value
		.typ = NewSlice(.base)
		if .recordTypes() {
			.recordBuiltinType(.Fun, makeSig(.typ, , .typ))
		}

	case _SliceData:
		// unsafe.SliceData(slice []T) *T
		.verifyVersionf(.Fun, go1_20, "unsafe.SliceData")

		,  := coreType(.typ).(*Slice)
		if  == nil {
			.errorf(, InvalidUnsafeSliceData, invalidArg+"%s is not a slice", )
			return
		}

		.mode = value
		.typ = NewPointer(.elem)
		if .recordTypes() {
			.recordBuiltinType(.Fun, makeSig(.typ, ))
		}

	case _String:
		// unsafe.String(ptr *byte, len IntegerType) string
		.verifyVersionf(.Fun, go1_20, "unsafe.String")

		.assignment(, NewPointer(universeByte), "argument to unsafe.String")
		if .mode == invalid {
			return
		}

		 := [1]
		if !.isValidIndex(, InvalidUnsafeString, "length", false) {
			return
		}

		.mode = value
		.typ = Typ[String]
		if .recordTypes() {
			.recordBuiltinType(.Fun, makeSig(.typ, NewPointer(universeByte), .typ))
		}

	case _StringData:
		// unsafe.StringData(str string) *byte
		.verifyVersionf(.Fun, go1_20, "unsafe.StringData")

		.assignment(, Typ[String], "argument to unsafe.StringData")
		if .mode == invalid {
			return
		}

		.mode = value
		.typ = NewPointer(universeByte)
		if .recordTypes() {
			.recordBuiltinType(.Fun, makeSig(.typ, Typ[String]))
		}

	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) {
			.errorf(, Test, invalidArg+"%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  {
			.rawExpr(nil, , , nil, false) // permit trace for types, e.g.: new(trace(T))
			.dump("%v: %s", .Pos(), )
			 = & // use incoming x only for first argument
		}
		if .mode == invalid {
			return
		}
		// trace is only available in test mode - no need to record signature

	default:
		unreachable()
	}

	assert(.mode != invalid)
	return true
}

// hasVarSize reports if the size of type t is variable due to type parameters
// or if the type is infinitely-sized due to a cycle for which the type has not
// yet been checked.
func hasVarSize( Type,  map[*Named]bool) ( bool) {
	// Cycles are only possible through *Named types.
	// The seen map is used to detect cycles and track
	// the results of previously seen types.
	if  := asNamed();  != nil {
		if ,  := [];  {
			return 
		}
		if  == nil {
			 = make(map[*Named]bool)
		}
		[] = true // possibly cyclic until proven otherwise
		defer func() {
			[] =  // record final determination for named
		}()
	}

	switch u := under().(type) {
	case *Array:
		return (.elem, )
	case *Struct:
		for ,  := range .fields {
			if (.typ, ) {
				return true
			}
		}
	case *Interface:
		return isTypeParam()
	case *Named, *Union:
		unreachable()
	}
	return false
}

// applyTypeFunc applies f to x. If x is a type parameter,
// the result is a type parameter constrained by a new
// interface bound. The type bounds for that interface
// are computed by applying f to each of the type bounds
// of x. If any of these applications of f return nil,
// applyTypeFunc returns nil.
// If x is not a type parameter, the result is f(x).
func ( *Checker) ( func(Type) Type,  *operand,  builtinId) Type {
	if ,  := .typ.(*TypeParam);  != nil {
		// Test if t satisfies the requirements for the argument
		// type and collect possible result types at the same time.
		var  []*Term
		if !.is(func( *term) bool {
			if  == nil {
				return false
			}
			if  := (.typ);  != nil {
				 = append(, NewTerm(.tilde, ))
				return true
			}
			return false
		}) {
			return nil
		}

		// We can type-check this fine but we're introducing a synthetic
		// type parameter for the result. It's not clear what the API
		// implications are here. Report an error for 1.18 (see go.dev/issue/50912),
		// but continue type-checking.
		var  Code
		switch  {
		case _Real:
			 = InvalidReal
		case _Imag:
			 = InvalidImag
		case _Complex:
			 = InvalidComplex
		default:
			unreachable()
		}
		.softErrorf(, , "%s not supported as argument to %s for go1.18 (see go.dev/issue/50937)", , predeclaredFuncs[].name)

		// Construct a suitable new type parameter for the result type.
		// The type parameter is placed in the current package so export/import
		// works as expected.
		 := NewTypeName(nopos, .pkg, .obj.name, nil)
		 := .newTypeParam(, NewInterfaceType(nil, []Type{NewUnion()})) // assigns type to tpar as a side-effect
		.index = .index

		return 
	}

	return (.typ)
}

// 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(nopos, nil, "", Default())
	}
	 := NewTuple(...)
	var  *Tuple
	if  != nil {
		assert(!isUntyped())
		 = NewTuple(NewVar(nopos, nil, "", ))
	}
	return &Signature{params: , results: }
}

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

func unparen( ast.Expr) ast.Expr { return ast.Unparen() }