package types
import (
"go/ast"
"go/constant"
"go/internal/typeparams"
. "internal/types/errors"
)
func (check *Checker ) indexExpr (x *operand , e *typeparams .IndexExpr ) (isFuncInst bool ) {
check .exprOrType (x , e .X , true )
switch x .mode {
case invalid :
check .use (e .Indices ...)
return false
case typexpr :
x .mode = invalid
x .typ = check .varType (e .Orig )
if isValid (x .typ ) {
x .mode = typexpr
}
return false
case value :
if sig , _ := under (x .typ ).(*Signature ); sig != nil && sig .TypeParams ().Len () > 0 {
return true
}
}
check .nonGeneric (nil , x )
if x .mode == invalid {
return false
}
valid := false
length := int64 (-1 )
switch typ := under (x .typ ).(type ) {
case *Basic :
if isString (typ ) {
valid = true
if x .mode == constant_ {
length = int64 (len (constant .StringVal (x .val )))
}
x .mode = value
x .typ = universeByte
}
case *Array :
valid = true
length = typ .len
if x .mode != variable {
x .mode = value
}
x .typ = typ .elem
case *Pointer :
if typ , _ := under (typ .base ).(*Array ); typ != nil {
valid = true
length = typ .len
x .mode = variable
x .typ = typ .elem
}
case *Slice :
valid = true
x .mode = variable
x .typ = typ .elem
case *Map :
index := check .singleIndex (e )
if index == nil {
x .mode = invalid
return false
}
var key operand
check .expr (nil , &key , index )
check .assignment (&key , typ .key , "map index" )
x .mode = mapindex
x .typ = typ .elem
x .expr = e .Orig
return false
case *Interface :
if !isTypeParam (x .typ ) {
break
}
var key , elem Type
mode := variable
if typ .typeSet ().underIs (func (u Type ) bool {
l := int64 (-1 )
var k , e Type
switch t := u .(type ) {
case *Basic :
if isString (t ) {
e = universeByte
mode = value
}
case *Array :
l = t .len
e = t .elem
if x .mode != variable {
mode = value
}
case *Pointer :
if t , _ := under (t .base ).(*Array ); t != nil {
l = t .len
e = t .elem
}
case *Slice :
e = t .elem
case *Map :
k = t .key
e = t .elem
}
if e == nil {
return false
}
if elem == nil {
length = l
key , elem = k , e
return true
}
if !Identical (key , k ) {
return false
}
if !Identical (elem , e ) {
return false
}
if l >= 0 && l < length {
length = l
}
return true
}) {
if key != nil {
index := check .singleIndex (e )
if index == nil {
x .mode = invalid
return false
}
var k operand
check .expr (nil , &k , index )
check .assignment (&k , key , "map index" )
x .mode = mapindex
x .typ = elem
x .expr = e .Orig
return false
}
valid = true
x .mode = mode
x .typ = elem
}
}
if !valid {
check .errorf (x , NonIndexableOperand , invalidOp +"cannot index %s" , x )
check .use (e .Indices ...)
x .mode = invalid
return false
}
index := check .singleIndex (e )
if index == nil {
x .mode = invalid
return false
}
if x .typ == nil {
x .typ = Typ [Invalid ]
}
check .index (index , length )
return false
}
func (check *Checker ) sliceExpr (x *operand , e *ast .SliceExpr ) {
check .expr (nil , x , e .X )
if x .mode == invalid {
check .use (e .Low , e .High , e .Max )
return
}
valid := false
length := int64 (-1 )
switch u := coreString (x .typ ).(type ) {
case nil :
check .errorf (x , NonSliceableOperand , invalidOp +"cannot slice %s: %s has no core type" , x , x .typ )
x .mode = invalid
return
case *Basic :
if isString (u ) {
if e .Slice3 {
at := e .Max
if at == nil {
at = e
}
check .error (at , InvalidSliceExpr , invalidOp +"3-index slice of string" )
x .mode = invalid
return
}
valid = true
if x .mode == constant_ {
length = int64 (len (constant .StringVal (x .val )))
}
if isUntyped (x .typ ) {
x .typ = Typ [String ]
}
}
case *Array :
valid = true
length = u .len
if x .mode != variable {
check .errorf (x , NonSliceableOperand , invalidOp +"cannot slice %s (value not addressable)" , x )
x .mode = invalid
return
}
x .typ = &Slice {elem : u .elem }
case *Pointer :
if u , _ := under (u .base ).(*Array ); u != nil {
valid = true
length = u .len
x .typ = &Slice {elem : u .elem }
}
case *Slice :
valid = true
}
if !valid {
check .errorf (x , NonSliceableOperand , invalidOp +"cannot slice %s" , x )
x .mode = invalid
return
}
x .mode = value
if e .Slice3 && (e .High == nil || e .Max == nil ) {
check .error (inNode (e , e .Rbrack ), InvalidSyntaxTree , "2nd and 3rd index required in 3-index slice" )
x .mode = invalid
return
}
var ind [3 ]int64
for i , expr := range []ast .Expr {e .Low , e .High , e .Max } {
x := int64 (-1 )
switch {
case expr != nil :
max := int64 (-1 )
if length >= 0 {
max = length + 1
}
if _ , v := check .index (expr , max ); v >= 0 {
x = v
}
case i == 0 :
x = 0
case length >= 0 :
x = length
}
ind [i ] = x
}
L :
for i , x := range ind [:len (ind )-1 ] {
if x > 0 {
for j , y := range ind [i +1 :] {
if y >= 0 && y < x {
at := []ast .Expr {e .Low , e .High , e .Max }[i +1 +j ]
check .errorf (at , SwappedSliceIndices , "invalid slice indices: %d < %d" , y , x )
break L
}
}
}
}
}
func (check *Checker ) singleIndex (expr *typeparams .IndexExpr ) ast .Expr {
if len (expr .Indices ) == 0 {
check .errorf (expr .Orig , InvalidSyntaxTree , "index expression %v with 0 indices" , expr )
return nil
}
if len (expr .Indices ) > 1 {
check .error (expr .Indices [1 ], InvalidIndex , invalidOp +"more than one index" )
}
return expr .Indices [0 ]
}
func (check *Checker ) index (index ast .Expr , max int64 ) (typ Type , val int64 ) {
typ = Typ [Invalid ]
val = -1
var x operand
check .expr (nil , &x , index )
if !check .isValidIndex (&x , InvalidIndex , "index" , false ) {
return
}
if x .mode != constant_ {
return x .typ , -1
}
if x .val .Kind () == constant .Unknown {
return
}
v , ok := constant .Int64Val (x .val )
assert (ok )
if max >= 0 && v >= max {
check .errorf (&x , InvalidIndex , invalidArg +"index %s out of bounds [0:%d]" , x .val .String (), max )
return
}
return x .typ , v
}
func (check *Checker ) isValidIndex (x *operand , code Code , what string , allowNegative bool ) bool {
if x .mode == invalid {
return false
}
check .convertUntyped (x , Typ [Int ])
if x .mode == invalid {
return false
}
if !allInteger (x .typ ) {
check .errorf (x , code , invalidArg +"%s %s must be integer" , what , x )
return false
}
if x .mode == constant_ {
if !allowNegative && constant .Sign (x .val ) < 0 {
check .errorf (x , code , invalidArg +"%s %s must not be negative" , what , x )
return false
}
if !representableConst (x .val , check , Typ [Int ], &x .val ) {
check .errorf (x , code , invalidArg +"%s %s overflows int" , what , x )
return false
}
}
return true
}
func (check *Checker ) indexedElts (elts []ast .Expr , typ Type , length int64 ) int64 {
visited := make (map [int64 ]bool , len (elts ))
var index , max int64
for _ , e := range elts {
validIndex := false
eval := e
if kv , _ := e .(*ast .KeyValueExpr ); kv != nil {
if typ , i := check .index (kv .Key , length ); isValid (typ ) {
if i >= 0 {
index = i
validIndex = true
} else {
check .errorf (e , InvalidLitIndex , "index %s must be integer constant" , kv .Key )
}
}
eval = kv .Value
} else if length >= 0 && index >= length {
check .errorf (e , OversizeArrayLit , "index %d is out of bounds (>= %d)" , index , length )
} else {
validIndex = true
}
if validIndex {
if visited [index ] {
check .errorf (e , DuplicateLitKey , "duplicate index %d in array or slice literal" , index )
}
visited [index ] = true
}
index ++
if index > max {
max = index
}
var x operand
check .exprWithHint (&x , eval , typ )
check .assignment (&x , typ , "array or slice literal" )
}
return max
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .