// 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 conversions.

package types

import (
	
	. 
	
)

// conversion type-checks the conversion T(x).
// The result is in x.
func ( *Checker) ( *operand,  Type) {
	 := .mode == constant_

	 := func( Type,  *constant.Value) bool {
		switch ,  := under().(*Basic); {
		case  == nil:
			// nothing to do
		case representableConst(.val, , , ):
			return true
		case isInteger(.typ) && isString():
			 := unicode.ReplacementChar
			if ,  := constant.Uint64Val(.val);  &&  <= unicode.MaxRune {
				 = rune()
			}
			if  != nil {
				* = constant.MakeString(string())
			}
			return true
		}
		return false
	}

	var  bool
	var  string
	switch {
	case  && isConstType():
		// constant conversion
		 = (, &.val)
		// A conversion from an integer constant to an integer type
		// can only fail if there's overflow. Give a concise error.
		// (go.dev/issue/63563)
		if ! && isInteger(.typ) && isInteger() {
			.errorf(, InvalidConversion, "constant %s overflows %s", .val, )
			.mode = invalid
			return
		}
	case  && isTypeParam():
		// x is convertible to T if it is convertible
		// to each specific type in the type set of T.
		// If T's type set is empty, or if it doesn't
		// have specific types, constant x cannot be
		// converted.
		 = .(*TypeParam).underIs(func( Type) bool {
			// u is nil if there are no specific type terms
			if  == nil {
				 = .sprintf("%s does not contain specific types", )
				return false
			}
			if isString(.typ) && isBytesOrRunes() {
				return true
			}
			if !(, nil) {
				if isInteger(.typ) && isInteger() {
					// see comment above on constant conversion
					 = .sprintf("constant %s overflows %s (in %s)", .val, , )
				} else {
					 = .sprintf("cannot convert %s to type %s (in %s)", , , )
				}
				return false
			}
			return true
		})
		.mode = value // type parameters are not constants
	case .convertibleTo(, , &):
		// non-constant conversion
		 = true
		.mode = value
	}

	if ! {
		if  != "" {
			.errorf(, InvalidConversion, "cannot convert %s to type %s: %s", , , )
		} else {
			.errorf(, InvalidConversion, "cannot convert %s to type %s", , )
		}
		.mode = invalid
		return
	}

	// The conversion argument types are final. For untyped values the
	// conversion provides the type, per the spec: "A constant may be
	// given a type explicitly by a constant declaration or conversion,...".
	if isUntyped(.typ) {
		 := 
		// - For conversions to interfaces, use the argument's default type.
		// - For conversions of untyped constants to non-constant types, also
		//   use the default type (e.g., []byte("foo") should report string
		//   not []byte as type for the constant "foo").
		// - Keep untyped nil for untyped nil arguments.
		// - For constant integer to string conversions, keep the argument type.
		//   (See also the TODO below.)
		if isNonTypeParamInterface() ||  && !isConstType() || .isNil() {
			 = Default(.typ) // default type of untyped nil is untyped nil
		} else if .mode == constant_ && isInteger(.typ) && allString() {
			 = .typ
		}
		.updateExprType(.expr, , true)
	}

	.typ = 
}

// TODO(gri) convertibleTo checks if T(x) is valid. It assumes that the type
// of x is fully known, but that's not the case for say string(1<<s + 1.0):
// Here, the type of 1<<s + 1.0 will be UntypedFloat which will lead to the
// (correct!) refusal of the conversion. But the reported error is essentially
// "cannot convert untyped float value to string", yet the correct error (per
// the spec) is that we cannot shift a floating-point value: 1 in 1<<s should
// be converted to UntypedFloat because of the addition of 1.0. Fixing this
// is tricky because we'd have to run updateExprType on the argument first.
// (go.dev/issue/21982.)

// convertibleTo reports whether T(x) is valid. In the failure case, *cause
// may be set to the cause for the failure.
// The check parameter may be nil if convertibleTo is invoked through an
// exported API call, i.e., when all methods have been type-checked.
func ( *operand) ( *Checker,  Type,  *string) bool {
	// "x is assignable to T"
	if ,  := .assignableTo(, , );  {
		return true
	}

	// "V and T have identical underlying types if tags are ignored
	// and V and T are not type parameters"
	 := .typ
	 := under()
	 := under()
	,  := .(*TypeParam)
	,  := .(*TypeParam)
	if IdenticalIgnoreTags(, ) &&  == nil &&  == nil {
		return true
	}

	// "V and T are unnamed pointer types and their pointer base types
	// have identical underlying types if tags are ignored
	// and their pointer base types are not type parameters"
	if ,  := .(*Pointer);  {
		if ,  := .(*Pointer);  {
			if IdenticalIgnoreTags(under(.base), under(.base)) && !isTypeParam(.base) && !isTypeParam(.base) {
				return true
			}
		}
	}

	// "V and T are both integer or floating point types"
	if isIntegerOrFloat() && isIntegerOrFloat() {
		return true
	}

	// "V and T are both complex types"
	if isComplex() && isComplex() {
		return true
	}

	// "V is an integer or a slice of bytes or runes and T is a string type"
	if (isInteger() || isBytesOrRunes()) && isString() {
		return true
	}

	// "V is a string and T is a slice of bytes or runes"
	if isString() && isBytesOrRunes() {
		return true
	}

	// package unsafe:
	// "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
	if (isPointer() || isUintptr()) && isUnsafePointer() {
		return true
	}
	// "and vice versa"
	if isUnsafePointer() && (isPointer() || isUintptr()) {
		return true
	}

	// "V is a slice, T is an array or pointer-to-array type,
	// and the slice and array types have identical element types."
	if ,  := .(*Slice);  != nil {
		switch a := .(type) {
		case *Array:
			if Identical(.Elem(), .Elem()) {
				if  == nil || .allowVersion(.pkg, , go1_20) {
					return true
				}
				// check != nil
				if  != nil {
					// TODO(gri) consider restructuring versionErrorf so we can use it here and below
					* = "conversion of slices to arrays requires go1.20 or later"
				}
				return false
			}
		case *Pointer:
			if ,  := under(.Elem()).(*Array);  != nil {
				if Identical(.Elem(), .Elem()) {
					if  == nil || .allowVersion(.pkg, , go1_17) {
						return true
					}
					// check != nil
					if  != nil {
						* = "conversion of slices to array pointers requires go1.17 or later"
					}
					return false
				}
			}
		}
	}

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

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

	// generic cases with specific type terms
	// (generic operands cannot be constants, so we can ignore x.val)
	switch {
	case  != nil &&  != nil:
		 := * // don't clobber outer x
		return .is(func( *term) bool {
			if  == nil {
				return false // no specific types
			}
			.typ = .typ
			return .is(func( *term) bool {
				if  == nil {
					return false // no specific types
				}
				if !.(, .typ, ) {
					("cannot convert %s (in %s) to type %s (in %s)", .typ, , .typ, )
					return false
				}
				return true
			})
		})
	case  != nil:
		 := * // don't clobber outer x
		return .is(func( *term) bool {
			if  == nil {
				return false // no specific types
			}
			.typ = .typ
			if !.(, , ) {
				("cannot convert %s (in %s) to type %s", .typ, , )
				return false
			}
			return true
		})
	case  != nil:
		return .is(func( *term) bool {
			if  == nil {
				return false // no specific types
			}
			if !.(, .typ, ) {
				("cannot convert %s to type %s (in %s)", .typ, .typ, )
				return false
			}
			return true
		})
	}

	return false
}

func isUintptr( Type) bool {
	,  := under().(*Basic)
	return  != nil && .kind == Uintptr
}

func isUnsafePointer( Type) bool {
	,  := under().(*Basic)
	return  != nil && .kind == UnsafePointer
}

func isPointer( Type) bool {
	,  := under().(*Pointer)
	return 
}

func isBytesOrRunes( Type) bool {
	if ,  := under().(*Slice);  != nil {
		,  := under(.elem).(*Basic)
		return  != nil && (.kind == Byte || .kind == Rune)
	}
	return false
}