// Copyright 2021 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 index/slice expressions.

package types

import (
	
	
	
	. 
)

// If e is a valid function instantiation, indexExpr returns true.
// In that case x represents the uninstantiated function value and
// it is the caller's responsibility to instantiate the function.
func ( *Checker) ( *operand,  *typeparams.IndexExpr) ( bool) {
	.exprOrType(, .X, true)
	// x may be generic

	switch .mode {
	case invalid:
		.use(.Indices...)
		return false

	case typexpr:
		// type instantiation
		.mode = invalid
		// TODO(gri) here we re-evaluate e.X - try to avoid this
		.typ = .varType(.Orig)
		if isValid(.typ) {
			.mode = typexpr
		}
		return false

	case value:
		if ,  := under(.typ).(*Signature);  != nil && .TypeParams().Len() > 0 {
			// function instantiation
			return true
		}
	}

	// x should not be generic at this point, but be safe and check
	.nonGeneric(nil, )
	if .mode == invalid {
		return false
	}

	// ordinary index expression
	 := false
	 := int64(-1) // valid if >= 0
	switch typ := under(.typ).(type) {
	case *Basic:
		if isString() {
			 = true
			if .mode == constant_ {
				 = int64(len(constant.StringVal(.val)))
			}
			// an indexed string always yields a byte value
			// (not a constant) even if the string and the
			// index are constant
			.mode = value
			.typ = universeByte // use 'byte' name
		}

	case *Array:
		 = true
		 = .len
		if .mode != variable {
			.mode = value
		}
		.typ = .elem

	case *Pointer:
		if ,  := under(.base).(*Array);  != nil {
			 = true
			 = .len
			.mode = variable
			.typ = .elem
		}

	case *Slice:
		 = true
		.mode = variable
		.typ = .elem

	case *Map:
		 := .singleIndex()
		if  == nil {
			.mode = invalid
			return false
		}
		var  operand
		.expr(nil, &, )
		.assignment(&, .key, "map index")
		// ok to continue even if indexing failed - map element type is known
		.mode = mapindex
		.typ = .elem
		.expr = .Orig
		return false

	case *Interface:
		if !isTypeParam(.typ) {
			break
		}
		// TODO(gri) report detailed failure cause for better error messages
		var ,  Type // key != nil: we must have all maps
		 := variable   // non-maps result mode
		// TODO(gri) factor out closure and use it for non-typeparam cases as well
		if .typeSet().underIs(func( Type) bool {
			 := int64(-1) // valid if >= 0
			var ,  Type  // k is only set for maps
			switch t := .(type) {
			case *Basic:
				if isString() {
					 = universeByte
					 = value
				}
			case *Array:
				 = .len
				 = .elem
				if .mode != variable {
					 = value
				}
			case *Pointer:
				if ,  := under(.base).(*Array);  != nil {
					 = .len
					 = .elem
				}
			case *Slice:
				 = .elem
			case *Map:
				 = .key
				 = .elem
			}
			if  == nil {
				return false
			}
			if  == nil {
				// first type
				 = 
				,  = , 
				return true
			}
			// all map keys must be identical (incl. all nil)
			// (that is, we cannot mix maps with other types)
			if !Identical(, ) {
				return false
			}
			// all element types must be identical
			if !Identical(, ) {
				return false
			}
			// track the minimal length for arrays, if any
			if  >= 0 &&  <  {
				 = 
			}
			return true
		}) {
			// For maps, the index expression must be assignable to the map key type.
			if  != nil {
				 := .singleIndex()
				if  == nil {
					.mode = invalid
					return false
				}
				var  operand
				.expr(nil, &, )
				.assignment(&, , "map index")
				// ok to continue even if indexing failed - map element type is known
				.mode = mapindex
				.typ = 
				.expr = 
				return false
			}

			// no maps
			 = true
			.mode = 
			.typ = 
		}
	}

	if ! {
		// types2 uses the position of '[' for the error
		.errorf(, NonIndexableOperand, invalidOp+"cannot index %s", )
		.use(.Indices...)
		.mode = invalid
		return false
	}

	 := .singleIndex()
	if  == nil {
		.mode = invalid
		return false
	}

	// In pathological (invalid) cases (e.g.: type T1 [][[]T1{}[0][0]]T0)
	// the element type may be accessed before it's set. Make sure we have
	// a valid type.
	if .typ == nil {
		.typ = Typ[Invalid]
	}

	.index(, )
	return false
}

func ( *Checker) ( *operand,  *ast.SliceExpr) {
	.expr(nil, , .X)
	if .mode == invalid {
		.use(.Low, .High, .Max)
		return
	}

	 := false
	 := int64(-1) // valid if >= 0
	switch u := coreString(.typ).(type) {
	case nil:
		.errorf(, NonSliceableOperand, invalidOp+"cannot slice %s: %s has no core type", , .typ)
		.mode = invalid
		return

	case *Basic:
		if isString() {
			if .Slice3 {
				 := .Max
				if  == nil {
					 =  // e.Index[2] should be present but be careful
				}
				.error(, InvalidSliceExpr, invalidOp+"3-index slice of string")
				.mode = invalid
				return
			}
			 = true
			if .mode == constant_ {
				 = int64(len(constant.StringVal(.val)))
			}
			// spec: "For untyped string operands the result
			// is a non-constant value of type string."
			if isUntyped(.typ) {
				.typ = Typ[String]
			}
		}

	case *Array:
		 = true
		 = .len
		if .mode != variable {
			.errorf(, NonSliceableOperand, invalidOp+"cannot slice %s (value not addressable)", )
			.mode = invalid
			return
		}
		.typ = &Slice{elem: .elem}

	case *Pointer:
		if ,  := under(.base).(*Array);  != nil {
			 = true
			 = .len
			.typ = &Slice{elem: .elem}
		}

	case *Slice:
		 = true
		// x.typ doesn't change
	}

	if ! {
		.errorf(, NonSliceableOperand, invalidOp+"cannot slice %s", )
		.mode = invalid
		return
	}

	.mode = value

	// spec: "Only the first index may be omitted; it defaults to 0."
	if .Slice3 && (.High == nil || .Max == nil) {
		.error(inNode(, .Rbrack), InvalidSyntaxTree, "2nd and 3rd index required in 3-index slice")
		.mode = invalid
		return
	}

	// check indices
	var  [3]int64
	for ,  := range []ast.Expr{.Low, .High, .Max} {
		 := int64(-1)
		switch {
		case  != nil:
			// The "capacity" is only known statically for strings, arrays,
			// and pointers to arrays, and it is the same as the length for
			// those types.
			 := int64(-1)
			if  >= 0 {
				 =  + 1
			}
			if ,  := .index(, );  >= 0 {
				 = 
			}
		case  == 0:
			// default is 0 for the first index
			 = 0
		case  >= 0:
			// default is length (== capacity) otherwise
			 = 
		}
		[] = 
	}

	// constant indices must be in range
	// (check.index already checks that existing indices >= 0)
:
	for ,  := range [:len()-1] {
		if  > 0 {
			for ,  := range [+1:] {
				if  >= 0 &&  <  {
					// The value y corresponds to the expression e.Index[i+1+j].
					// Because y >= 0, it must have been set from the expression
					// when checking indices and thus e.Index[i+1+j] is not nil.
					 := []ast.Expr{.Low, .High, .Max}[+1+]
					.errorf(, SwappedSliceIndices, "invalid slice indices: %d < %d", , )
					break  // only report one error, ok to continue
				}
			}
		}
	}
}

// singleIndex returns the (single) index from the index expression e.
// If the index is missing, or if there are multiple indices, an error
// is reported and the result is nil.
func ( *Checker) ( *typeparams.IndexExpr) ast.Expr {
	if len(.Indices) == 0 {
		.errorf(.Orig, InvalidSyntaxTree, "index expression %v with 0 indices", )
		return nil
	}
	if len(.Indices) > 1 {
		// TODO(rFindley) should this get a distinct error code?
		.error(.Indices[1], InvalidIndex, invalidOp+"more than one index")
	}
	return .Indices[0]
}

// index checks an index expression for validity.
// If max >= 0, it is the upper bound for index.
// If the result typ is != Typ[Invalid], index is valid and typ is its (possibly named) integer type.
// If the result val >= 0, index is valid and val is its constant int value.
func ( *Checker) ( ast.Expr,  int64) ( Type,  int64) {
	 = Typ[Invalid]
	 = -1

	var  operand
	.expr(nil, &, )
	if !.isValidIndex(&, InvalidIndex, "index", false) {
		return
	}

	if .mode != constant_ {
		return .typ, -1
	}

	if .val.Kind() == constant.Unknown {
		return
	}

	,  := constant.Int64Val(.val)
	assert()
	if  >= 0 &&  >=  {
		.errorf(&, InvalidIndex, invalidArg+"index %s out of bounds [0:%d]", .val.String(), )
		return
	}

	// 0 <= v [ && v < max ]
	return .typ, 
}

func ( *Checker) ( *operand,  Code,  string,  bool) bool {
	if .mode == invalid {
		return false
	}

	// spec: "a constant index that is untyped is given type int"
	.convertUntyped(, Typ[Int])
	if .mode == invalid {
		return false
	}

	// spec: "the index x must be of integer type or an untyped constant"
	if !allInteger(.typ) {
		.errorf(, , invalidArg+"%s %s must be integer", , )
		return false
	}

	if .mode == constant_ {
		// spec: "a constant index must be non-negative ..."
		if ! && constant.Sign(.val) < 0 {
			.errorf(, , invalidArg+"%s %s must not be negative", , )
			return false
		}

		// spec: "... and representable by a value of type int"
		if !representableConst(.val, , Typ[Int], &.val) {
			.errorf(, , invalidArg+"%s %s overflows int", , )
			return false
		}
	}

	return true
}

// indexedElts checks the elements (elts) of an array or slice composite literal
// against the literal's element type (typ), and the element indices against
// the literal length if known (length >= 0). It returns the length of the
// literal (maximum index value + 1).
func ( *Checker) ( []ast.Expr,  Type,  int64) int64 {
	 := make(map[int64]bool, len())
	var ,  int64
	for ,  := range  {
		// determine and check index
		 := false
		 := 
		if ,  := .(*ast.KeyValueExpr);  != nil {
			if ,  := .index(.Key, ); isValid() {
				if  >= 0 {
					 = 
					 = true
				} else {
					.errorf(, InvalidLitIndex, "index %s must be integer constant", .Key)
				}
			}
			 = .Value
		} else if  >= 0 &&  >=  {
			.errorf(, OversizeArrayLit, "index %d is out of bounds (>= %d)", , )
		} else {
			 = true
		}

		// if we have a valid index, check for duplicate entries
		if  {
			if [] {
				.errorf(, DuplicateLitKey, "duplicate index %d in array or slice literal", )
			}
			[] = true
		}
		++
		if  >  {
			 = 
		}

		// check element against composite literal element type
		var  operand
		.exprWithHint(&, , )
		.assignment(&, , "array or slice literal")
	}
	return 
}