package types
import (
"go/ast"
"go/constant"
"go/token"
. "internal/types/errors"
)
func (check *Checker ) indexExpr (x *operand , e *indexedExpr ) (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 underIs (x .typ , 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 *indexedExpr ) 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
}
type indexedExpr struct {
orig ast .Expr
x ast .Expr
lbrack token .Pos
indices []ast .Expr
rbrack token .Pos
}
func (x *indexedExpr ) Pos () token .Pos {
return x .orig .Pos ()
}
func unpackIndexedExpr(n ast .Node ) *indexedExpr {
switch e := n .(type ) {
case *ast .IndexExpr :
return &indexedExpr {
orig : e ,
x : e .X ,
lbrack : e .Lbrack ,
indices : []ast .Expr {e .Index },
rbrack : e .Rbrack ,
}
case *ast .IndexListExpr :
return &indexedExpr {
orig : e ,
x : e .X ,
lbrack : e .Lbrack ,
indices : e .Indices ,
rbrack : e .Rbrack ,
}
}
return nil
}
The pages are generated with Golds v0.7.3-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 .