// 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 typesimport (.)// 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, *indexedExpr) ( bool) { .exprOrType(, .x, true)// x may be genericswitch .mode() {caseinvalid: .use(.indices...)returnfalsecasetypexpr:// type instantiation .invalidate()// TODO(gri) here we re-evaluate e.X - try to avoid this .typ_ = .varType(.orig)ifisValid(.typ()) { .mode_ = typexpr }returnfalsecasevalue:if , := .typ().Underlying().(*Signature); != nil && .TypeParams().Len() > 0 {// function instantiationreturntrue } }// x should not be generic at this point, but be safe and check .nonGeneric(nil, )if !.isValid() {returnfalse }// We cannot index on an incomplete type; make sure it's complete.if !.isComplete(.typ()) { .invalidate()returnfalse }switch typ := .typ().Underlying().(type) {case *Pointer:// Additionally, if x.typ is a pointer to an array type, indexing implicitly dereferences the value, meaning // its base type must also be complete.if !.isComplete(.base) { .invalidate()returnfalse }case *Map:// Lastly, if x.typ is a map type, indexing must produce a value of a complete type, meaning // its element type must also be complete.if !.isComplete(.elem) { .invalidate()returnfalse } }// ordinary index expression := false := int64(-1) // valid if >= 0switch typ := .typ().Underlying().(type) {case *Basic:ifisString() { = trueif .mode() == constant_ { = constant.StringLen(.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 = .lenif .mode() != variable { .mode_ = value } .typ_ = .elemcase *Pointer:if , := .base.Underlying().(*Array); != nil { = true = .len .mode_ = variable .typ_ = .elem }case *Slice: = true .mode_ = variable .typ_ = .elemcase *Map: := .singleIndex()if == nil { .invalidate()returnfalse }varoperand .genericExpr(&, , nil) .assignment(&, .key, "map index")// ok to continue even if indexing failed - map element type is known .mode_ = mapindex .typ_ = .elem .expr = .origreturnfalsecase *Interface:if !isTypeParam(.typ()) {break }// TODO(gri) report detailed failure cause for better error messagesvar , 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 wellifunderIs(.typ(), func( Type) bool { := int64(-1) // valid if >= 0var , Type// k is only set for mapsswitch t := .(type) {case *Basic:ifisString() { = universeByte = value }case *Array: = .len = .elemif .mode() != variable { = value }case *Pointer:if , := .base.Underlying().(*Array); != nil { = .len = .elem }case *Slice: = .elemcase *Map: = .key = .elem }if == nil {returnfalse }if == nil {// first type = , = , returntrue }// all map keys must be identical (incl. all nil) // (that is, we cannot mix maps with other types)if !Identical(, ) {returnfalse }// all element types must be identicalif !Identical(, ) {returnfalse }// track the minimal length for arrays, if anyif >= 0 && < { = }returntrue }) {// For maps, the index expression must be assignable to the map key type.if != nil { := .singleIndex()if == nil { .invalidate()returnfalse }varoperand .genericExpr(&, , nil) .assignment(&, , "map index")// ok to continue even if indexing failed - map element type is known .mode_ = mapindex .typ_ = .expr = .origreturnfalse }// no maps = true .mode_ = .typ_ = } }if ! {// types2 uses the position of '[' for the error .errorf(, NonIndexableOperand, "cannot index %s", ) .use(.indices...) .invalidate()returnfalse } := .singleIndex()if == nil { .invalidate()returnfalse }// 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(, )returnfalse}func ( *Checker) ( *operand, *ast.SliceExpr) { .expr(nil, , .X)if !.isValid() { .use(.Low, .High, .Max)return }// determine common underlying type cuvar , Type// type and respective common underlying typevarbool// TODO(adonovan): use go1.23 "range typeset()".typeset(.typ())(func(, Type) bool {if == nil { .errorf(, NonSliceableOperand, "cannot slice %s: no specific type in %s", , .typ()) = nilreturnfalse }// Treat strings like byte slices but remember that we saw a string.ifisString() { = NewSlice(universeByte) = true }// If this is the first type we're seeing, we're done.if == nil { , = , returntrue }// Otherwise, the current type must have the same underlying type as all previous types.if !Identical(, ) { .errorf(, NonSliceableOperand, "cannot slice %s: %s and %s have different underlying types", , , ) = nilreturnfalse }returntrue })if {// If we saw a string, proceed with string type, // but don't go from untyped string to string. = Typ[String]if !isTypeParam(.typ()) { = .typ().Underlying() // untyped string remains untyped } }// Note that we don't permit slice expressions where x is a type expression, so we don't check for that here. // However, if x.typ is a pointer to an array type, slicing implicitly dereferences the value, meaning // its base type must also be complete.if , := .typ().Underlying().(*Pointer); && !.isComplete(.base) { .invalidate()return } := false := int64(-1) // valid if >= 0switch u := .(type) {casenil:// error reported above .invalidate()returncase *Basic:ifisString() {if .Slice3 { := .Maxif == nil { = // e.Index[2] should be present but be careful } .error(, InvalidSliceExpr, invalidOp+"3-index slice of string") .invalidate()return } = trueif .mode() == constant_ { = constant.StringLen(.val) }// spec: "For untyped string operands the result // is a non-constant value of type string."ifisUntyped(.typ()) { .typ_ = Typ[String] } }case *Array: = true = .lenif .mode() != variable { .errorf(, NonSliceableOperand, "cannot slice unaddressable value %s", ) .invalidate()return } .typ_ = &Slice{elem: .elem}case *Pointer:if , := .base.Underlying().(*Array); != nil { = true = .len .typ_ = &Slice{elem: .elem} }case *Slice: = true// x.typ doesn't change }if ! { .errorf(, NonSliceableOperand, "cannot slice %s", ) .invalidate()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") .invalidate()return }// check indicesvar [3]int64for , := 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 = 0case >= 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) ( *indexedExpr) ast.Expr {iflen(.indices) == 0 { .errorf(.orig, InvalidSyntaxTree, "index expression %v with 0 indices", )returnnil }iflen(.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] = -1varoperand .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 !.isValid() {returnfalse }// spec: "a constant index that is untyped is given type int" .convertUntyped(, Typ[Int])if !.isValid() {returnfalse }// spec: "the index x must be of integer type or an untyped constant"if !allInteger(.typ()) { .errorf(, , invalidArg+"%s %s must be integer", , )returnfalse }if .mode() == constant_ {// spec: "a constant index must be non-negative ..."if ! && constant.Sign(.val) < 0 { .errorf(, , invalidArg+"%s %s must not be negative", , )returnfalse }// spec: "... and representable by a value of type int"if !representableConst(.val, , Typ[Int], &.val) { .errorf(, , invalidArg+"%s %s overflows int", , )returnfalse } }returntrue}// indexedExpr wraps an ast.IndexExpr or ast.IndexListExpr.//// Orig holds the original ast.Expr from which this indexedExpr was derived.//// Note: indexedExpr (intentionally) does not wrap ast.Expr, as that leads to// accidental misuse such as encountered in golang/go#63933.//// TODO(rfindley): remove this helper, in favor of just having a helper// function that returns indices.type indexedExpr struct { orig ast.Expr// the wrapped expr, which may be distinct from the IndexListExpr below. x ast.Expr// expression lbrack token.Pos// position of "[" indices []ast.Expr// index expressions rbrack token.Pos// position of "]"}func ( *indexedExpr) () token.Pos {return .orig.Pos()}func unpackIndexedExpr( ast.Node) *indexedExpr {switch e := .(type) {case *ast.IndexExpr:return &indexedExpr{orig: ,x: .X,lbrack: .Lbrack,indices: []ast.Expr{.Index},rbrack: .Rbrack, }case *ast.IndexListExpr:return &indexedExpr{orig: ,x: .X,lbrack: .Lbrack,indices: .Indices,rbrack: .Rbrack, } }returnnil}
The pages are generated with Goldsv0.8.7. (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.