// 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.
// x.mode is set to invalid if the assignment failed.
func ( *Checker) ( *operand,  Type,  string) {
	.singleValue()

	switch .mode {
	case invalid:
		return // error reported before
	case constant_, variable, mapindex, value, commaok, commaerr:
		// ok
	default:
		unreachable()
	}

	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 || IsInterface() {
			if  == nil && .typ == Typ[UntypedNil] {
				.errorf(, _UntypedNil, "use of untyped nil in %s", )
				.mode = invalid
				return
			}
			 = Default(.typ)
		}
		if  := .canConvertUntyped(, );  != nil {
			 := .sprintf("cannot use %s as %s value in %s", , , )
			 := _IncompatibleAssign
			var  Error
			if errors.As(, &) {
				// Preserve these inner errors, as they are informative.
				switch .go116code {
				case _TruncatedFloat:
					 += " (truncated)"
					 = .go116code
				case _NumericOverflow:
					 += " (overflows)"
					 = .go116code
				}
			}
			.error(, , )
			.mode = invalid
			return
		}
	}
	// x.typ is typed

	// 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 || .typ == Typ[Invalid] || .typ == Typ[Invalid] {
		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
}

func ( *Checker) ( *Var,  *operand,  string) Type {
	if .mode == invalid || .typ == Typ[Invalid] || .typ == Typ[Invalid] {
		if .typ == nil {
			.typ = Typ[Invalid]
		}
		return nil
	}

	// If the 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(, _UntypedNil, "use of untyped nil in %s", )
				.typ = Typ[Invalid]
				return nil
			}
			 = Default()
		}
		.typ = 
	}

	.assignment(, .typ, )
	if .mode == invalid {
		return nil
	}

	return .typ
}

func ( *Checker) ( ast.Expr,  *operand) Type {
	if .mode == invalid || .typ == Typ[Invalid] {
		return nil
	}

	// 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)
		.assignment(, nil, "assignment to _ identifier")
		if .mode == invalid {
			return nil
		}
		return .typ
	}

	// If the lhs is an identifier denoting a variable v, this assignment
	// 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(&, )
	if  != nil {
		.used =  // restore v.used
	}

	if .mode == invalid || .typ == Typ[Invalid] {
		return nil
	}

	// 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 nil
	case variable, mapindex:
		// ok
	default:
		if ,  := .expr.(*ast.SelectorExpr);  {
			var  operand
			.expr(&, .X)
			if .mode == mapindex {
				.errorf(&, _UnaddressableFieldAssign, "cannot assign to struct field %s in map", ExprString(.expr))
				return nil
			}
		}
		.errorf(&, _UnassignableOperand, "cannot assign to %s", &)
		return nil
	}

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

	return .typ
}

// If returnPos is valid, initVars is called to type-check the assignment of
// return expressions, and returnPos is the position of the return statement.
func ( *Checker) ( []*Var,  []ast.Expr,  token.Pos) {
	 := len()
	, ,  := unpack(func( *operand,  int) { .multiExpr(, []) }, len(),  == 2 && !.IsValid())
	if  == nil ||  !=  {
		// invalidate lhs and use rhs
		for ,  := range  {
			if .typ == nil {
				.typ = Typ[Invalid]
			}
		}
		if  == nil {
			return // error reported by unpack
		}
		.useGetter(, )
		if .IsValid() {
			.errorf(atPos(), _WrongResultCount, "wrong number of return values (want %d, got %d)", , )
			return
		}
		.errorf([0], _WrongAssignCount, "cannot initialize %d variables with %d values", , )
		return
	}

	 := "assignment"
	if .IsValid() {
		 = "return statement"
	}

	var  operand
	if  {
		var  [2]Type
		for  := range  {
			(&, )
			[] = .initVar([], &, )
		}
		.recordCommaOkTypes([0], )
		return
	}

	for ,  := range  {
		(&, )
		.initVar(, &, )
	}
}

func ( *Checker) (,  []ast.Expr) {
	 := len()
	, ,  := unpack(func( *operand,  int) { .multiExpr(, []) }, len(),  == 2)
	if  == nil {
		.useLHS(...)
		return // error reported by unpack
	}
	if  !=  {
		.useGetter(, )
		.errorf([0], _WrongAssignCount, "cannot assign %d values to %d variables", , )
		return
	}

	var  operand
	if  {
		var  [2]Type
		for  := range  {
			(&, )
			[] = .assignVar([], &)
		}
		.recordCommaOkTypes([0], )
		return
	}

	for ,  := range  {
		(&, )
		.assignVar(, &)
	}
}

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

	// collect lhs variables
	var  []*Var
	var  = make([]*Var, len())
	for ,  := range  {
		var  *Var
		if ,  := .(*ast.Ident);  != nil {
			// 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.
			 := .Name
			if  := .Lookup();  != nil {
				// redeclared object must be a variable
				if ,  := .(*Var);  != nil {
					 = 
				} else {
					.errorf(, _UnassignableOperand, "cannot assign to %s", )
				}
				.recordUse(, )
			} else {
				// declare new variable, possibly a blank (_) variable
				 = NewVar(.Pos(), .pkg, , nil)
				if  != "_" {
					 = append(, )
				}
				.recordDef(, )
			}
		} else {
			.useLHS()
			.invalidAST(, "cannot declare %s", )
		}
		if  == nil {
			 = NewVar(.Pos(), .pkg, "_", nil) // dummy variable
		}
		[] = 
	}

	.initVars(, , token.NoPos)

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

	// declare new variables
	if len() > 0 {
		// 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, , ) // recordObject already called
		}
	} else {
		.softErrorf(, _NoNewVar, "no new variables on left side of :=")
	}
}