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

package types

import (
	
	
	
	
	. 
	
)

func ( *Checker) ( *declInfo,  string,  *Signature,  *ast.BlockStmt,  constant.Value) {
	if .conf.IgnoreFuncBodies {
		panic("function body not ignored")
	}

	if .conf._Trace {
		.trace(.Pos(), "-- %s: %s", , )
	}

	// save/restore current environment and set up function environment
	// (and use 0 indentation at function start)
	defer func( environment,  int) {
		.environment = 
		.indent = 
	}(.environment, .indent)
	.environment = environment{
		decl:  ,
		scope: .scope,
		iota:  ,
		sig:   ,
	}
	.indent = 0

	.stmtList(0, .List)

	if .hasLabel {
		.labels()
	}

	if .results.Len() > 0 && !.isTerminating(, "") {
		.error(atPos(.Rbrace), MissingReturn, "missing return")
	}

	// spec: "Implementation restriction: A compiler may make it illegal to
	// declare a variable inside a function body if the variable is never used."
	.usage(.scope)
}

func ( *Checker) ( *Scope) {
	var  []*Var
	for ,  := range .elems {
		 = resolve(, )
		if ,  := .(*Var);  != nil && !.used {
			 = append(, )
		}
	}
	sort.Slice(, func(,  int) bool {
		return cmpPos([].pos, [].pos) < 0
	})
	for ,  := range  {
		.softErrorf(, UnusedVar, "%s declared and not used", .name)
	}

	for ,  := range .children {
		// Don't go inside function literal scopes a second time;
		// they are handled explicitly by funcBody.
		if !.isFunc {
			.()
		}
	}
}

// stmtContext is a bitset describing which
// control-flow statements are permissible,
// and provides additional context information
// for better error messages.
type stmtContext uint

const (
	// permissible control-flow statements
	breakOk stmtContext = 1 << iota
	continueOk
	fallthroughOk

	// additional context information
	finalSwitchCase
	inTypeSwitch
)

func ( *Checker) ( ast.Stmt) {
	if  != nil {
		.stmt(0, )
	}
}

func trimTrailingEmptyStmts( []ast.Stmt) []ast.Stmt {
	for  := len();  > 0; -- {
		if ,  := [-1].(*ast.EmptyStmt); ! {
			return [:]
		}
	}
	return nil
}

func ( *Checker) ( stmtContext,  []ast.Stmt) {
	 := &fallthroughOk != 0
	 :=  &^ fallthroughOk
	 = trimTrailingEmptyStmts() // trailing empty statements are "invisible" to fallthrough analysis
	for ,  := range  {
		 := 
		if  && +1 == len() {
			 |= fallthroughOk
		}
		.stmt(, )
	}
}

func ( *Checker) ( []ast.Stmt) {
	var  ast.Stmt
	for ,  := range  {
		var  ast.Stmt
		switch c := .(type) {
		case *ast.CaseClause:
			if len(.List) == 0 {
				 = 
			}
		case *ast.CommClause:
			if .Comm == nil {
				 = 
			}
		default:
			.error(, InvalidSyntaxTree, "case/communication clause expected")
		}
		if  != nil {
			if  != nil {
				.errorf(, DuplicateDefault, "multiple defaults (first at %s)", .fset.Position(.Pos()))
			} else {
				 = 
			}
		}
	}
}

func ( *Checker) ( ast.Node,  string) {
	 := NewScope(.scope, .Pos(), .End(), )
	.recordScope(, )
	.scope = 
}

func ( *Checker) () {
	.scope = .scope.Parent()
}

func assignOp( token.Token) token.Token {
	// token_test.go verifies the token ordering this function relies on
	if token.ADD_ASSIGN <=  &&  <= token.AND_NOT_ASSIGN {
		return  + (token.ADD - token.ADD_ASSIGN)
	}
	return token.ILLEGAL
}

func ( *Checker) ( string,  *ast.CallExpr) {
	var  operand
	var  string
	var  Code
	switch .rawExpr(nil, &, , nil, false) {
	case conversion:
		 = "requires function call, not conversion"
		 = InvalidDefer
		if  == "go" {
			 = InvalidGo
		}
	case expression:
		 = "discards result of"
		 = UnusedResults
	case statement:
		return
	default:
		unreachable()
	}
	.errorf(&, , "%s %s %s", , , &)
}

// goVal returns the Go value for val, or nil.
func goVal( constant.Value) any {
	// val should exist, but be conservative and check
	if  == nil {
		return nil
	}
	// Match implementation restriction of other compilers.
	// gc only checks duplicates for integer, floating-point
	// and string values, so only create Go values for these
	// types.
	switch .Kind() {
	case constant.Int:
		if ,  := constant.Int64Val();  {
			return 
		}
		if ,  := constant.Uint64Val();  {
			return 
		}
	case constant.Float:
		if ,  := constant.Float64Val();  {
			return 
		}
	case constant.String:
		return constant.StringVal()
	}
	return nil
}

// A valueMap maps a case value (of a basic Go type) to a list of positions
// where the same case value appeared, together with the corresponding case
// types.
// Since two case values may have the same "underlying" value but different
// types we need to also check the value's types (e.g., byte(1) vs myByte(1))
// when the switch expression is of interface type.
type (
	valueMap  map[any][]valueType // underlying Go value -> valueType
	valueType struct {
		pos token.Pos
		typ Type
	}
)

func ( *Checker) ( *operand,  []ast.Expr,  valueMap) {
:
	for ,  := range  {
		var  operand
		.expr(nil, &, )
		if .mode == invalid || .mode == invalid {
			continue 
		}
		.convertUntyped(&, .typ)
		if .mode == invalid {
			continue 
		}
		// Order matters: By comparing v against x, error positions are at the case values.
		 :=  // keep original v unchanged
		.comparison(&, , token.EQL, true)
		if .mode == invalid {
			continue 
		}
		if .mode != constant_ {
			continue  // we're done
		}
		// look for duplicate values
		if  := goVal(.val);  != nil {
			// look for duplicate types for a given value
			// (quadratic algorithm, but these lists tend to be very short)
			for ,  := range [] {
				if Identical(.typ, .typ) {
					.errorf(&, DuplicateCase, "duplicate case %s in expression switch", &)
					.error(atPos(.pos), DuplicateCase, "\tprevious case") // secondary error, \t indented
					continue 
				}
			}
			[] = append([], valueType{.Pos(), .typ})
		}
	}
}

// isNil reports whether the expression e denotes the predeclared value nil.
func ( *Checker) ( ast.Expr) bool {
	// The only way to express the nil value is by literally writing nil (possibly in parentheses).
	if ,  := unparen().(*ast.Ident);  != nil {
		,  := .lookup(.Name).(*Nil)
		return 
	}
	return false
}

// If the type switch expression is invalid, x is nil.
func ( *Checker) ( *operand,  []ast.Expr,  map[Type]ast.Expr) ( Type) {
	var  operand
:
	for ,  := range  {
		// The spec allows the value nil instead of a type.
		if .isNil() {
			 = nil
			.expr(nil, &, ) // run e through expr so we get the usual Info recordings
		} else {
			 = .varType()
			if !isValid() {
				continue 
			}
		}
		// look for duplicate types
		// (quadratic algorithm, but type switches tend to be reasonably small)
		for ,  := range  {
			if  == nil &&  == nil ||  != nil &&  != nil && Identical(, ) {
				// talk about "case" rather than "type" because of nil case
				 := "nil"
				if  != nil {
					 = TypeString(, .qualifier)
				}
				.errorf(, DuplicateCase, "duplicate case %s in type switch", )
				.error(, DuplicateCase, "\tprevious case") // secondary error, \t indented
				continue 
			}
		}
		[] = 
		if  != nil &&  != nil {
			.typeAssertion(, , , true)
		}
	}
	return
}

// TODO(gri) Once we are certain that typeHash is correct in all situations, use this version of caseTypes instead.
// (Currently it may be possible that different types have identical names and import paths due to ImporterFrom.)
//
// func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[string]ast.Expr) (T Type) {
// 	var dummy operand
// L:
// 	for _, e := range types {
// 		// The spec allows the value nil instead of a type.
// 		var hash string
// 		if check.isNil(e) {
// 			check.expr(nil, &dummy, e) // run e through expr so we get the usual Info recordings
// 			T = nil
// 			hash = "<nil>" // avoid collision with a type named nil
// 		} else {
// 			T = check.varType(e)
// 			if !isValid(T) {
// 				continue L
// 			}
// 			hash = typeHash(T, nil)
// 		}
// 		// look for duplicate types
// 		if other := seen[hash]; other != nil {
// 			// talk about "case" rather than "type" because of nil case
// 			Ts := "nil"
// 			if T != nil {
// 				Ts = TypeString(T, check.qualifier)
// 			}
// 			var err error_
//			err.code = DuplicateCase
// 			err.errorf(e, "duplicate case %s in type switch", Ts)
// 			err.errorf(other, "previous case")
// 			check.report(&err)
// 			continue L
// 		}
// 		seen[hash] = e
// 		if T != nil {
// 			check.typeAssertion(e.Pos(), x, xtyp, T)
// 		}
// 	}
// 	return
// }

// stmt typechecks statement s.
func ( *Checker) ( stmtContext,  ast.Stmt) {
	// statements must end with the same top scope as they started with
	if debug {
		defer func( *Scope) {
			// don't check if code is panicking
			if  := recover();  != nil {
				panic()
			}
			assert( == .scope)
		}(.scope)
	}

	// process collected function literals before scope changes
	defer .processDelayed(len(.delayed))

	// reset context for statements of inner blocks
	 :=  &^ (fallthroughOk | finalSwitchCase | inTypeSwitch)

	switch s := .(type) {
	case *ast.BadStmt, *ast.EmptyStmt:
		// ignore

	case *ast.DeclStmt:
		.declStmt(.Decl)

	case *ast.LabeledStmt:
		.hasLabel = true
		.(, .Stmt)

	case *ast.ExprStmt:
		// spec: "With the exception of specific built-in functions,
		// function and method calls and receive operations can appear
		// in statement context. Such statements may be parenthesized."
		var  operand
		 := .rawExpr(nil, &, .X, nil, false)
		var  string
		var  Code
		switch .mode {
		default:
			if  == statement {
				return
			}
			 = "is not used"
			 = UnusedExpr
		case builtin:
			 = "must be called"
			 = UncalledBuiltin
		case typexpr:
			 = "is not an expression"
			 = NotAnExpr
		}
		.errorf(&, , "%s %s", &, )

	case *ast.SendStmt:
		var ,  operand
		.expr(nil, &, .Chan)
		.expr(nil, &, .Value)
		if .mode == invalid || .mode == invalid {
			return
		}
		 := coreType(.typ)
		if  == nil {
			.errorf(inNode(, .Arrow), InvalidSend, invalidOp+"cannot send to %s: no core type", &)
			return
		}
		,  := .(*Chan)
		if  == nil {
			.errorf(inNode(, .Arrow), InvalidSend, invalidOp+"cannot send to non-channel %s", &)
			return
		}
		if .dir == RecvOnly {
			.errorf(inNode(, .Arrow), InvalidSend, invalidOp+"cannot send to receive-only channel %s", &)
			return
		}
		.assignment(&, .elem, "send")

	case *ast.IncDecStmt:
		var  token.Token
		switch .Tok {
		case token.INC:
			 = token.ADD
		case token.DEC:
			 = token.SUB
		default:
			.errorf(inNode(, .TokPos), InvalidSyntaxTree, "unknown inc/dec operation %s", .Tok)
			return
		}

		var  operand
		.expr(nil, &, .X)
		if .mode == invalid {
			return
		}
		if !allNumeric(.typ) {
			.errorf(.X, NonNumericIncDec, invalidOp+"%s%s (non-numeric type %s)", .X, .Tok, .typ)
			return
		}

		 := &ast.BasicLit{ValuePos: .X.Pos(), Kind: token.INT, Value: "1"} // use x's position
		.binary(&, nil, .X, , , .TokPos)
		if .mode == invalid {
			return
		}
		.assignVar(.X, nil, &, "assignment")

	case *ast.AssignStmt:
		switch .Tok {
		case token.ASSIGN, token.DEFINE:
			if len(.Lhs) == 0 {
				.error(, InvalidSyntaxTree, "missing lhs in assignment")
				return
			}
			if .Tok == token.DEFINE {
				.shortVarDecl(inNode(, .TokPos), .Lhs, .Rhs)
			} else {
				// regular assignment
				.assignVars(.Lhs, .Rhs)
			}

		default:
			// assignment operations
			if len(.Lhs) != 1 || len(.Rhs) != 1 {
				.errorf(inNode(, .TokPos), MultiValAssignOp, "assignment operation %s requires single-valued expressions", .Tok)
				return
			}
			 := assignOp(.Tok)
			if  == token.ILLEGAL {
				.errorf(atPos(.TokPos), InvalidSyntaxTree, "unknown assignment operation %s", .Tok)
				return
			}
			var  operand
			.binary(&, nil, .Lhs[0], .Rhs[0], , .TokPos)
			if .mode == invalid {
				return
			}
			.assignVar(.Lhs[0], nil, &, "assignment")
		}

	case *ast.GoStmt:
		.suspendedCall("go", .Call)

	case *ast.DeferStmt:
		.suspendedCall("defer", .Call)

	case *ast.ReturnStmt:
		 := .sig.results
		// Return with implicit results allowed for function with named results.
		// (If one is named, all are named.)
		if len(.Results) == 0 && .Len() > 0 && .vars[0].name != "" {
			// spec: "Implementation restriction: A compiler may disallow an empty expression
			// list in a "return" statement if a different entity (constant, type, or variable)
			// with the same name as a result parameter is in scope at the place of the return."
			for ,  := range .vars {
				if  := .lookup(.name);  != nil &&  !=  {
					.errorf(, OutOfScopeResult, "result parameter %s not in scope at return", .name)
					.errorf(, OutOfScopeResult, "\tinner declaration of %s", )
					// ok to continue
				}
			}
		} else {
			var  []*Var
			if .Len() > 0 {
				 = .vars
			}
			.initVars(, .Results, )
		}

	case *ast.BranchStmt:
		if .Label != nil {
			.hasLabel = true
			return // checked in 2nd pass (check.labels)
		}
		switch .Tok {
		case token.BREAK:
			if &breakOk == 0 {
				.error(, MisplacedBreak, "break not in for, switch, or select statement")
			}
		case token.CONTINUE:
			if &continueOk == 0 {
				.error(, MisplacedContinue, "continue not in for statement")
			}
		case token.FALLTHROUGH:
			if &fallthroughOk == 0 {
				var  string
				switch {
				case &finalSwitchCase != 0:
					 = "cannot fallthrough final case in switch"
				case &inTypeSwitch != 0:
					 = "cannot fallthrough in type switch"
				default:
					 = "fallthrough statement out of place"
				}
				.error(, MisplacedFallthrough, )
			}
		default:
			.errorf(, InvalidSyntaxTree, "branch statement: %s", .Tok)
		}

	case *ast.BlockStmt:
		.openScope(, "block")
		defer .closeScope()

		.stmtList(, .List)

	case *ast.IfStmt:
		.openScope(, "if")
		defer .closeScope()

		.simpleStmt(.Init)
		var  operand
		.expr(nil, &, .Cond)
		if .mode != invalid && !allBoolean(.typ) {
			.error(.Cond, InvalidCond, "non-boolean condition in if statement")
		}
		.(, .Body)
		// The parser produces a correct AST but if it was modified
		// elsewhere the else branch may be invalid. Check again.
		switch .Else.(type) {
		case nil, *ast.BadStmt:
			// valid or error already reported
		case *ast.IfStmt, *ast.BlockStmt:
			.(, .Else)
		default:
			.error(.Else, InvalidSyntaxTree, "invalid else branch in if statement")
		}

	case *ast.SwitchStmt:
		 |= breakOk
		.openScope(, "switch")
		defer .closeScope()

		.simpleStmt(.Init)
		var  operand
		if .Tag != nil {
			.expr(nil, &, .Tag)
			// By checking assignment of x to an invisible temporary
			// (as a compiler would), we get all the relevant checks.
			.assignment(&, nil, "switch expression")
			if .mode != invalid && !Comparable(.typ) && !hasNil(.typ) {
				.errorf(&, InvalidExprSwitch, "cannot switch on %s (%s is not comparable)", &, .typ)
				.mode = invalid
			}
		} else {
			// spec: "A missing switch expression is
			// equivalent to the boolean value true."
			.mode = constant_
			.typ = Typ[Bool]
			.val = constant.MakeBool(true)
			.expr = &ast.Ident{NamePos: .Body.Lbrace, Name: "true"}
		}

		.multipleDefaults(.Body.List)

		 := make(valueMap) // map of seen case values to positions and types
		for ,  := range .Body.List {
			,  := .(*ast.CaseClause)
			if  == nil {
				.error(, InvalidSyntaxTree, "incorrect expression switch case")
				continue
			}
			.caseValues(&, .List, )
			.openScope(, "case")
			 := 
			if +1 < len(.Body.List) {
				 |= fallthroughOk
			} else {
				 |= finalSwitchCase
			}
			.stmtList(, .Body)
			.closeScope()
		}

	case *ast.TypeSwitchStmt:
		 |= breakOk | inTypeSwitch
		.openScope(, "type switch")
		defer .closeScope()

		.simpleStmt(.Init)

		// A type switch guard must be of the form:
		//
		//     TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
		//
		// The parser is checking syntactic correctness;
		// remaining syntactic errors are considered AST errors here.
		// TODO(gri) better factoring of error handling (invalid ASTs)
		//
		var  *ast.Ident // lhs identifier or nil
		var  ast.Expr
		switch guard := .Assign.(type) {
		case *ast.ExprStmt:
			 = .X
		case *ast.AssignStmt:
			if len(.Lhs) != 1 || .Tok != token.DEFINE || len(.Rhs) != 1 {
				.error(, InvalidSyntaxTree, "incorrect form of type switch guard")
				return
			}

			, _ = .Lhs[0].(*ast.Ident)
			if  == nil {
				.error(, InvalidSyntaxTree, "incorrect form of type switch guard")
				return
			}

			if .Name == "_" {
				// _ := x.(type) is an invalid short variable declaration
				.softErrorf(, NoNewVar, "no new variable on left side of :=")
				 = nil // avoid declared and not used error below
			} else {
				.recordDef(, nil) // lhs variable is implicitly declared in each cause clause
			}

			 = .Rhs[0]

		default:
			.error(, InvalidSyntaxTree, "incorrect form of type switch guard")
			return
		}

		// rhs must be of the form: expr.(type) and expr must be an ordinary interface
		,  := .(*ast.TypeAssertExpr)
		if  == nil || .Type != nil {
			.error(, InvalidSyntaxTree, "incorrect form of type switch guard")
			return
		}
		var  operand
		.expr(nil, &, .X)
		if .mode == invalid {
			return
		}
		// TODO(gri) we may want to permit type switches on type parameter values at some point
		var  *operand // switch expression against which cases are compared against; nil if invalid
		if isTypeParam(.typ) {
			.errorf(&, InvalidTypeSwitch, "cannot use type switch on type parameter value %s", &)
		} else {
			if ,  := under(.typ).(*Interface);  {
				 = &
			} else {
				.errorf(&, InvalidTypeSwitch, "%s is not an interface", &)
			}
		}

		.multipleDefaults(.Body.List)

		var  []*Var              // list of implicitly declared lhs variables
		 := make(map[Type]ast.Expr) // map of seen types to positions
		for ,  := range .Body.List {
			,  := .(*ast.CaseClause)
			if  == nil {
				.error(, InvalidSyntaxTree, "incorrect type switch case")
				continue
			}
			// Check each type in this type switch case.
			 := .caseTypes(, .List, )
			.openScope(, "case")
			// If lhs exists, declare a corresponding variable in the case-local scope.
			if  != nil {
				// spec: "The TypeSwitchGuard may include a short variable declaration.
				// When that form is used, the variable is declared at the beginning of
				// the implicit block in each clause. In clauses with a case listing
				// exactly one type, the variable has that type; otherwise, the variable
				// has the type of the expression in the TypeSwitchGuard."
				if len(.List) != 1 ||  == nil {
					 = .typ
				}
				 := NewVar(.Pos(), .pkg, .Name, )
				 := .Pos() + token.Pos(len("default")) // for default clause (len(List) == 0)
				if  := len(.List);  > 0 {
					 = .List[-1].End()
				}
				.declare(.scope, nil, , )
				.recordImplicit(, )
				// For the "declared and not used" error, all lhs variables act as
				// one; i.e., if any one of them is 'used', all of them are 'used'.
				// Collect them for later analysis.
				 = append(, )
			}
			.stmtList(, .Body)
			.closeScope()
		}

		// If lhs exists, we must have at least one lhs variable that was used.
		if  != nil {
			var  bool
			for ,  := range  {
				if .used {
					 = true
				}
				.used = true // avoid usage error when checking entire function
			}
			if ! {
				.softErrorf(, UnusedVar, "%s declared and not used", .Name)
			}
		}

	case *ast.SelectStmt:
		 |= breakOk

		.multipleDefaults(.Body.List)

		for ,  := range .Body.List {
			,  := .(*ast.CommClause)
			if  == nil {
				continue // error reported before
			}

			// clause.Comm must be a SendStmt, RecvStmt, or default case
			 := false
			var  ast.Expr // rhs of RecvStmt, or nil
			switch s := .Comm.(type) {
			case nil, *ast.SendStmt:
				 = true
			case *ast.AssignStmt:
				if len(.Rhs) == 1 {
					 = .Rhs[0]
				}
			case *ast.ExprStmt:
				 = .X
			}

			// if present, rhs must be a receive operation
			if  != nil {
				if ,  := unparen().(*ast.UnaryExpr);  != nil && .Op == token.ARROW {
					 = true
				}
			}

			if ! {
				.error(.Comm, InvalidSelectCase, "select case must be send or receive (possibly with assignment)")
				continue
			}

			.openScope(, "case")
			if .Comm != nil {
				.(, .Comm)
			}
			.stmtList(, .Body)
			.closeScope()
		}

	case *ast.ForStmt:
		 |= breakOk | continueOk
		.openScope(, "for")
		defer .closeScope()

		.simpleStmt(.Init)
		if .Cond != nil {
			var  operand
			.expr(nil, &, .Cond)
			if .mode != invalid && !allBoolean(.typ) {
				.error(.Cond, InvalidCond, "non-boolean condition in for statement")
			}
		}
		.simpleStmt(.Post)
		// spec: "The init statement may be a short variable
		// declaration, but the post statement must not."
		if ,  := .Post.(*ast.AssignStmt);  != nil && .Tok == token.DEFINE {
			.softErrorf(, InvalidPostDecl, "cannot declare in post statement")
			// Don't call useLHS here because we want to use the lhs in
			// this erroneous statement so that we don't get errors about
			// these lhs variables being declared and not used.
			.use(.Lhs...) // avoid follow-up errors
		}
		.(, .Body)

	case *ast.RangeStmt:
		 |= breakOk | continueOk
		.rangeStmt(, )

	default:
		.error(, InvalidSyntaxTree, "invalid statement")
	}
}

func ( *Checker) ( stmtContext,  *ast.RangeStmt) {
	// Convert go/ast form to local variables.
	type  = ast.Expr
	type  = ast.Ident
	 := func( *) string { return .Name }
	,  := .Key, .Value
	var  ast.Expr = nil
	 := .Tok == token.DEFINE
	 := .X
	 := inNode(, .TokPos)

	// Everything from here on is shared between cmd/compile/internal/types2 and go/types.

	// check expression to iterate over
	var  operand
	.expr(nil, &, )

	// determine key/value types
	var ,  Type
	if .mode != invalid {
		// Ranging over a type parameter is permitted if it has a core type.
		, , , ,  := rangeKeyVal(.typ, func( goVersion) bool {
			return .allowVersion(.pkg, .expr, )
		})
		switch {
		case ! &&  != "":
			.softErrorf(&, InvalidRangeExpr, "cannot range over %s: %s", &, )
		case !:
			.softErrorf(&, InvalidRangeExpr, "cannot range over %s", &)
		case  == nil &&  != nil:
			.softErrorf(, InvalidIterVar, "range over %s permits no iteration variables", &)
		case  == nil &&  != nil:
			.softErrorf(, InvalidIterVar, "range over %s permits only one iteration variable", &)
		case  != nil:
			.softErrorf(, InvalidIterVar, "range clause permits at most two iteration variables")
		case  && (( == nil) != ( == nil) || ( == nil) != ( == nil)):
			var  string
			switch {
			case  == nil:
				 = "no iteration variables"
			case  == nil:
				 = "one iteration variable"
			default:
				 = "two iteration variables"
			}
			.softErrorf(&, InvalidIterVar, "range over %s must have %s", &, )
		}
		,  = , 
	}

	// Open the for-statement block scope now, after the range clause.
	// Iteration variables declared with := need to go in this scope (was go.dev/issue/51437).
	.openScope(, "range")
	defer .closeScope()

	// check assignment to/declaration of iteration variables
	// (irregular assignment, cannot easily map to existing assignment checks)

	// lhs expressions and initialization value (rhs) types
	 := [2]{, } // sKey, sValue may be nil
	 := [2]Type{, }     // key, val may be nil

	 := .mode == constant_ && isInteger(.typ)

	if  {
		// short variable declaration
		var  []*Var
		for ,  := range  {
			if  == nil {
				continue
			}

			// determine lhs variable
			var  *Var
			if ,  := .(*);  != nil {
				// declare new variable
				 := ()
				 = NewVar(.Pos(), .pkg, , nil)
				.recordDef(, )
				// _ variables don't count as new variables
				if  != "_" {
					 = append(, )
				}
			} else {
				.errorf(, InvalidSyntaxTree, "cannot declare %s", )
				 = NewVar(.Pos(), .pkg, "_", nil) // dummy variable
			}

			// initialize lhs variable
			if  {
				.initVar(, &, "range clause")
			} else if  := [];  != nil {
				.mode = value
				.expr =  // we don't have a better rhs expression to use here
				.typ = 
				.initVar(, &, "assignment") // error is on variable, use "assignment" not "range clause"
			} else {
				.typ = Typ[Invalid]
				.used = true // don't complain about unused variable
			}
		}

		// declare variables
		if len() > 0 {
			 := .Body.Pos()
			for ,  := range  {
				.declare(.scope, nil /* recordDef already called */, , )
			}
		} else {
			.error(, NoNewVar, "no new variables on left side of :=")
		}
	} else if  != nil /* lhs[0] != nil */ {
		// ordinary assignment
		for ,  := range  {
			if  == nil {
				continue
			}

			if  {
				.assignVar(, nil, &, "range clause")
			} else if  := [];  != nil {
				.mode = value
				.expr =  // we don't have a better rhs expression to use here
				.typ = 
				.assignVar(, nil, &, "assignment") // error is on variable, use "assignment" not "range clause"
			}
		}
	} else if  {
		// If we don't have any iteration variables, we still need to
		// check that a (possibly untyped) integer range expression x
		// is valid.
		// We do this by checking the assignment _ = x. This ensures
		// that an untyped x can be converted to a value of type int.
		.assignment(&, nil, "range clause")
	}

	.stmt(, .Body)
}

// rangeKeyVal returns the key and value type produced by a range clause
// over an expression of type typ.
// If allowVersion != nil, it is used to check the required language version.
// If the range clause is not permitted, rangeKeyVal returns ok = false.
// When ok = false, rangeKeyVal may also return a reason in cause.
func rangeKeyVal( Type,  func(goVersion) bool) (,  Type,  string, ,  bool) {
	 := func( string) (Type, Type, string, bool, bool) {
		return Typ[Invalid], Typ[Invalid], , false, false
	}
	 := func( Type) *Signature {
		,  := coreType().(*Signature)
		return 
	}

	 := 
	switch typ := arrayPtrDeref(coreType()).(type) {
	case nil:
		return ("no core type")
	case *Basic:
		if isString() {
			return Typ[Int], universeRune, "", false, true // use 'rune' name
		}
		if isInteger() {
			if  != nil && !(go1_22) {
				return ("requires go1.22 or later")
			}
			return , nil, "", false, true
		}
	case *Array:
		return Typ[Int], .elem, "", false, true
	case *Slice:
		return Typ[Int], .elem, "", false, true
	case *Map:
		return .key, .elem, "", false, true
	case *Chan:
		if .dir == SendOnly {
			return ("receive from send-only channel")
		}
		return .elem, nil, "", false, true
	case *Signature:
		// TODO(gri) when this becomes enabled permanently, add version check
		if !buildcfg.Experiment.RangeFunc {
			break
		}
		assert(.Recv() == nil)
		switch {
		case .Params().Len() != 1:
			return ("func must be func(yield func(...) bool): wrong argument count")
		case (.Params().At(0).Type()) == nil:
			return ("func must be func(yield func(...) bool): argument is not func")
		case .Results().Len() != 0:
			return ("func must be func(yield func(...) bool): unexpected results")
		}
		 := (.Params().At(0).Type())
		assert(.Recv() == nil)
		switch {
		case .Params().Len() > 2:
			return ("func must be func(yield func(...) bool): yield func has too many parameters")
		case .Results().Len() != 1 || !isBoolean(.Results().At(0).Type()):
			return ("func must be func(yield func(...) bool): yield func does not return bool")
		}
		if .Params().Len() >= 1 {
			 = .Params().At(0).Type()
		}
		if .Params().Len() >= 2 {
			 = .Params().At(1).Type()
		}
		return , , "", true, true
	}
	return
}