// Copyright 2013 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 initialization and assignment checks.

package types

import (
	
	
	. 
	
)

// assignment reports whether x can be assigned to a variable of type T,
// if necessary by attempting to convert untyped values to the appropriate
// type. context describes the context in which the assignment takes place.
// Use T == nil to indicate assignment to an untyped blank identifier.
// If the assignment check fails, x.mode is set to invalid.
func ( *Checker) ( *operand,  Type,  string) {
	.singleValue()

	switch .mode {
	case invalid:
		return // error reported before
	case constant_, variable, mapindex, value, commaok, commaerr:
		// ok
	default:
		// we may get here because of other problems (go.dev/issue/39634, crash 12)
		// TODO(gri) do we need a new "generic" error code here?
		.errorf(, IncompatibleAssign, "cannot assign %s to %s in %s", , , )
		.mode = invalid
		return
	}

	if isUntyped(.typ) {
		 := 
		// spec: "If an untyped constant is assigned to a variable of interface
		// type or the blank identifier, the constant is first converted to type
		// bool, rune, int, float64, complex128 or string respectively, depending
		// on whether the value is a boolean, rune, integer, floating-point,
		// complex, or string constant."
		if  == nil || isNonTypeParamInterface() {
			if  == nil && .typ == Typ[UntypedNil] {
				.errorf(, UntypedNilUse, "use of untyped nil in %s", )
				.mode = invalid
				return
			}
			 = Default(.typ)
		}
		, ,  := .implicitTypeAndValue(, )
		if  != 0 {
			 := .sprintf("cannot use %s as %s value in %s", , , )
			switch  {
			case TruncatedFloat:
				 += " (truncated)"
			case NumericOverflow:
				 += " (overflows)"
			default:
				 = IncompatibleAssign
			}
			.error(, , )
			.mode = invalid
			return
		}
		if  != nil {
			.val = 
			.updateExprVal(.expr, )
		}
		if  != .typ {
			.typ = 
			.updateExprType(.expr, , false)
		}
	}
	// x.typ is typed

	// A generic (non-instantiated) function value cannot be assigned to a variable.
	if ,  := under(.typ).(*Signature);  != nil && .TypeParams().Len() > 0 {
		.errorf(, WrongTypeArgCount, "cannot use generic function %s without instantiation in %s", , )
		.mode = invalid
		return
	}

	// spec: "If a left-hand side is the blank identifier, any typed or
	// non-constant value except for the predeclared identifier nil may
	// be assigned to it."
	if  == nil {
		return
	}

	 := ""
	if ,  := .assignableTo(, , &); ! {
		if  != "" {
			.errorf(, , "cannot use %s as %s value in %s: %s", , , , )
		} else {
			.errorf(, , "cannot use %s as %s value in %s", , , )
		}
		.mode = invalid
	}
}

func ( *Checker) ( *Const,  *operand) {
	if .mode == invalid || !isValid(.typ) || !isValid(.typ) {
		if .typ == nil {
			.typ = Typ[Invalid]
		}
		return
	}

	// rhs must be a constant
	if .mode != constant_ {
		.errorf(, InvalidConstInit, "%s is not constant", )
		if .typ == nil {
			.typ = Typ[Invalid]
		}
		return
	}
	assert(isConstType(.typ))

	// If the lhs doesn't have a type yet, use the type of x.
	if .typ == nil {
		.typ = .typ
	}

	.assignment(, .typ, "constant declaration")
	if .mode == invalid {
		return
	}

	.val = .val
}

// initVar checks the initialization lhs = x in a variable declaration.
// If lhs doesn't have a type yet, it is given the type of x,
// or Typ[Invalid] in case of an error.
// If the initialization check fails, x.mode is set to invalid.
func ( *Checker) ( *Var,  *operand,  string) {
	if .mode == invalid || !isValid(.typ) || !isValid(.typ) {
		if .typ == nil {
			.typ = Typ[Invalid]
		}
		.mode = invalid
		return
	}

	// If lhs doesn't have a type yet, use the type of x.
	if .typ == nil {
		 := .typ
		if isUntyped() {
			// convert untyped types to default types
			if  == Typ[UntypedNil] {
				.errorf(, UntypedNilUse, "use of untyped nil in %s", )
				.typ = Typ[Invalid]
				.mode = invalid
				return
			}
			 = Default()
		}
		.typ = 
	}

	.assignment(, .typ, )
}

// lhsVar checks a lhs variable in an assignment and returns its type.
// lhsVar takes care of not counting a lhs identifier as a "use" of
// that identifier. The result is nil if it is the blank identifier,
// and Typ[Invalid] if it is an invalid lhs expression.
func ( *Checker) ( ast.Expr) Type {
	// Determine if the lhs is a (possibly parenthesized) identifier.
	,  := unparen().(*ast.Ident)

	// Don't evaluate lhs if it is the blank identifier.
	if  != nil && .Name == "_" {
		.recordDef(, nil)
		return nil
	}

	// If the lhs is an identifier denoting a variable v, this reference
	// is not a 'use' of v. Remember current value of v.used and restore
	// after evaluating the lhs via check.expr.
	var  *Var
	var  bool
	if  != nil {
		if  := .lookup(.Name);  != nil {
			// It's ok to mark non-local variables, but ignore variables
			// from other packages to avoid potential race conditions with
			// dot-imported variables.
			if ,  := .(*Var);  != nil && .pkg == .pkg {
				 = 
				 = .used
			}
		}
	}

	var  operand
	.expr(nil, &, )

	if  != nil {
		.used =  // restore v.used
	}

	if .mode == invalid || !isValid(.typ) {
		return Typ[Invalid]
	}

	// spec: "Each left-hand side operand must be addressable, a map index
	// expression, or the blank identifier. Operands may be parenthesized."
	switch .mode {
	case invalid:
		return Typ[Invalid]
	case variable, mapindex:
		// ok
	default:
		if ,  := .expr.(*ast.SelectorExpr);  {
			var  operand
			.expr(nil, &, .X)
			if .mode == mapindex {
				.errorf(&, UnaddressableFieldAssign, "cannot assign to struct field %s in map", ExprString(.expr))
				return Typ[Invalid]
			}
		}
		.errorf(&, UnassignableOperand, "cannot assign to %s (neither addressable nor a map index expression)", .expr)
		return Typ[Invalid]
	}

	return .typ
}

// assignVar checks the assignment lhs = rhs (if x == nil), or lhs = x (if x != nil).
// If x != nil, it must be the evaluation of rhs (and rhs will be ignored).
// If the assignment check fails and x != nil, x.mode is set to invalid.
func ( *Checker) (,  ast.Expr,  *operand,  string) {
	 := .lhsVar() // nil if lhs is _
	if !isValid() {
		if  != nil {
			.mode = invalid
		} else {
			.use()
		}
		return
	}

	if  == nil {
		var  *target
		// avoid calling ExprString if not needed
		if  != nil {
			if ,  := under().(*Signature);  {
				 = newTarget(, ExprString())
			}
		}
		 = new(operand)
		.expr(, , )
	}

	if  == nil &&  == "assignment" {
		 = "assignment to _ identifier"
	}
	.assignment(, , )
}

// operandTypes returns the list of types for the given operands.
func operandTypes( []*operand) ( []Type) {
	for ,  := range  {
		 = append(, .typ)
	}
	return 
}

// varTypes returns the list of types for the given variables.
func varTypes( []*Var) ( []Type) {
	for ,  := range  {
		 = append(, .typ)
	}
	return 
}

// typesSummary returns a string of the form "(t1, t2, ...)" where the
// ti's are user-friendly string representations for the given types.
// If variadic is set and the last type is a slice, its string is of
// the form "...E" where E is the slice's element type.
func ( *Checker) ( []Type,  bool) string {
	var  []string
	for ,  := range  {
		var  string
		switch {
		case  == nil:
			fallthrough // should not happen but be cautious
		case !isValid():
			 = "unknown type"
		case isUntyped():
			if isNumeric() {
				// Do not imply a specific type requirement:
				// "have number, want float64" is better than
				// "have untyped int, want float64" or
				// "have int, want float64".
				 = "number"
			} else {
				// If we don't have a number, omit the "untyped" qualifier
				// for compactness.
				 = strings.Replace(.(*Basic).name, "untyped ", "", -1)
			}
		case  &&  == len()-1:
			 = .sprintf("...%s", .(*Slice).elem)
		}
		if  == "" {
			 = .sprintf("%s", )
		}
		 = append(, )
	}
	return "(" + strings.Join(, ", ") + ")"
}

func measure( int,  string) string {
	if  != 1 {
		 += "s"
	}
	return fmt.Sprintf("%d %s", , )
}

func ( *Checker) ( []ast.Expr, ,  int) {
	 := measure(, "variable")
	 := measure(, "value")
	 := [0]

	if len() == 1 {
		if ,  := unparen().(*ast.CallExpr);  != nil {
			.errorf(, WrongAssignCount, "assignment mismatch: %s but %s returns %s", , .Fun, )
			return
		}
	}
	.errorf(, WrongAssignCount, "assignment mismatch: %s but %s", , )
}

func ( *Checker) ( positioner,  []*Var,  []*operand) {
	,  := len(), len()
	 := "not enough"
	if  >  {
		 = [] // report at first extra value
		 = "too many"
	} else if  > 0 {
		 = [-1] // report at last value
	}
	var  error_
	.code = WrongResultCount
	.errorf(.Pos(), "%s return values", )
	.errorf(nopos, "have %s", .typesSummary(operandTypes(), false))
	.errorf(nopos, "want %s", .typesSummary(varTypes(), false))
	.report(&)
}

// initVars type-checks assignments of initialization expressions orig_rhs
// to variables lhs.
// If returnStmt is non-nil, initVars type-checks the implicit assignment
// of result expressions orig_rhs to function result parameters lhs.
func ( *Checker) ( []*Var,  []ast.Expr,  ast.Stmt) {
	 := "assignment"
	if  != nil {
		 = "return statement"
	}

	,  := len(), len()

	// If l == 1 and the rhs is a single call, for a better
	// error message don't handle it as n:n mapping below.
	 := false
	if  == 1 {
		_,  = unparen([0]).(*ast.CallExpr)
	}

	// If we have a n:n mapping from lhs variable to rhs expression,
	// each value can be assigned to its corresponding variable.
	if  ==  && ! {
		var  operand
		for ,  := range  {
			 := .name
			if  != nil &&  == "" {
				 = "result variable"
			}
			.expr(newTarget(.typ, ), &, [])
			.initVar(, &, )
		}
		return
	}

	// If we don't have an n:n mapping, the rhs must be a single expression
	// resulting in 2 or more values; otherwise we have an assignment mismatch.
	if  != 1 {
		// Only report a mismatch error if there are no other errors on the rhs.
		if .use(...) {
			if  != nil {
				 := .exprList()
				.returnError(, , )
			} else {
				.assignError(, , )
			}
		}
		// ensure that LHS variables have a type
		for ,  := range  {
			if .typ == nil {
				.typ = Typ[Invalid]
			}
		}
		return
	}

	,  := .multiExpr([0],  == 2 &&  == nil)
	 = len()
	if  ==  {
		for ,  := range  {
			.initVar(, [], )
		}
		// Only record comma-ok expression if both initializations succeeded
		// (go.dev/issue/59371).
		if  && [0].mode != invalid && [1].mode != invalid {
			.recordCommaOkTypes([0], )
		}
		return
	}

	// In all other cases we have an assignment mismatch.
	// Only report a mismatch error if there are no other errors on the rhs.
	if [0].mode != invalid {
		if  != nil {
			.returnError(, , )
		} else {
			.assignError(, , )
		}
	}
	// ensure that LHS variables have a type
	for ,  := range  {
		if .typ == nil {
			.typ = Typ[Invalid]
		}
	}
	// orig_rhs[0] was already evaluated
}

// assignVars type-checks assignments of expressions orig_rhs to variables lhs.
func ( *Checker) (,  []ast.Expr) {
	,  := len(), len()

	// If l == 1 and the rhs is a single call, for a better
	// error message don't handle it as n:n mapping below.
	 := false
	if  == 1 {
		_,  = unparen([0]).(*ast.CallExpr)
	}

	// If we have a n:n mapping from lhs variable to rhs expression,
	// each value can be assigned to its corresponding variable.
	if  ==  && ! {
		for ,  := range  {
			.assignVar(, [], nil, "assignment")
		}
		return
	}

	// If we don't have an n:n mapping, the rhs must be a single expression
	// resulting in 2 or more values; otherwise we have an assignment mismatch.
	if  != 1 {
		// Only report a mismatch error if there are no other errors on the lhs or rhs.
		 := .useLHS(...)
		 := .use(...)
		if  &&  {
			.assignError(, , )
		}
		return
	}

	,  := .multiExpr([0],  == 2)
	 = len()
	if  ==  {
		for ,  := range  {
			.assignVar(, nil, [], "assignment")
		}
		// Only record comma-ok expression if both assignments succeeded
		// (go.dev/issue/59371).
		if  && [0].mode != invalid && [1].mode != invalid {
			.recordCommaOkTypes([0], )
		}
		return
	}

	// In all other cases we have an assignment mismatch.
	// Only report a mismatch error if there are no other errors on the rhs.
	if [0].mode != invalid {
		.assignError(, , )
	}
	.useLHS(...)
	// orig_rhs[0] was already evaluated
}

func ( *Checker) ( positioner, ,  []ast.Expr) {
	 := len(.delayed)
	 := .scope

	// collect lhs variables
	 := make(map[string]bool, len())
	 := make([]*Var, len())
	 := make([]*Var, 0, len())
	 := false
	for ,  := range  {
		,  := .(*ast.Ident)
		if  == nil {
			.useLHS()
			// TODO(rFindley) this is redundant with a parser error. Consider omitting?
			.errorf(, BadDecl, "non-name %s on left side of :=", )
			 = true
			continue
		}

		 := .Name
		if  != "_" {
			if [] {
				.errorf(, RepeatedDecl, "%s repeated on left side of :=", )
				 = true
				continue
			}
			[] = true
		}

		// Use the correct obj if the ident is redeclared. The
		// variable's scope starts after the declaration; so we
		// must use Scope.Lookup here and call Scope.Insert
		// (via check.declare) later.
		if  := .Lookup();  != nil {
			.recordUse(, )
			// redeclared object must be a variable
			if ,  := .(*Var);  != nil {
				[] = 
			} else {
				.errorf(, UnassignableOperand, "cannot assign to %s", )
				 = true
			}
			continue
		}

		// declare new variable
		 := NewVar(.Pos(), .pkg, , nil)
		[] = 
		if  != "_" {
			 = append(, )
		}
		.recordDef(, )
	}

	// create dummy variables where the lhs is invalid
	for ,  := range  {
		if  == nil {
			[] = NewVar([].Pos(), .pkg, "_", nil)
		}
	}

	.initVars(, , nil)

	// process function literals in rhs expressions before scope changes
	.processDelayed()

	if len() == 0 && ! {
		.softErrorf(, NoNewVar, "no new variables on left side of :=")
		return
	}

	// declare new variables
	// spec: "The scope of a constant or variable identifier declared inside
	// a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
	// for short variable declarations) and ends at the end of the innermost
	// containing block."
	 := [len()-1].End()
	for ,  := range  {
		.declare(, nil, , ) // id = nil: recordDef already called
	}
}