Rbrack token.Pos // position of "]"
}
+ // A MultiIndexExpr node represents an expression followed by multiple
+ // indices.
+ MultiIndexExpr struct {
+ X Expr // expression
+ Lbrack token.Pos // position of "["
+ Indices []Expr // index expressions
+ Rbrack token.Pos // position of "]"
+ }
+
// A SliceExpr node represents an expression followed by slice indices.
SliceExpr struct {
X Expr // expression
Rparen token.Pos // position of ")"
}
- // A ListExpr node represents a list of expressions separated by commas.
- // ListExpr nodes are used as index in IndexExpr nodes representing type
- // or function instantiations with more than one type argument.
- ListExpr struct {
- ElemList []Expr
- }
-
// A StarExpr node represents an expression of the form "*" Expression.
// Semantically it could be a unary "*" expression, or a pointer type.
//
func (x *ParenExpr) Pos() token.Pos { return x.Lparen }
func (x *SelectorExpr) Pos() token.Pos { return x.X.Pos() }
func (x *IndexExpr) Pos() token.Pos { return x.X.Pos() }
+func (x *MultiIndexExpr) Pos() token.Pos { return x.X.Pos() }
func (x *SliceExpr) Pos() token.Pos { return x.X.Pos() }
func (x *TypeAssertExpr) Pos() token.Pos { return x.X.Pos() }
func (x *CallExpr) Pos() token.Pos { return x.Fun.Pos() }
-func (x *ListExpr) Pos() token.Pos {
- if len(x.ElemList) > 0 {
- return x.ElemList[0].Pos()
- }
- return token.NoPos
-}
-func (x *StarExpr) Pos() token.Pos { return x.Star }
-func (x *UnaryExpr) Pos() token.Pos { return x.OpPos }
-func (x *BinaryExpr) Pos() token.Pos { return x.X.Pos() }
-func (x *KeyValueExpr) Pos() token.Pos { return x.Key.Pos() }
-func (x *ArrayType) Pos() token.Pos { return x.Lbrack }
-func (x *StructType) Pos() token.Pos { return x.Struct }
+func (x *StarExpr) Pos() token.Pos { return x.Star }
+func (x *UnaryExpr) Pos() token.Pos { return x.OpPos }
+func (x *BinaryExpr) Pos() token.Pos { return x.X.Pos() }
+func (x *KeyValueExpr) Pos() token.Pos { return x.Key.Pos() }
+func (x *ArrayType) Pos() token.Pos { return x.Lbrack }
+func (x *StructType) Pos() token.Pos { return x.Struct }
func (x *FuncType) Pos() token.Pos {
if x.Func.IsValid() || x.Params == nil { // see issue 3870
return x.Func
func (x *ParenExpr) End() token.Pos { return x.Rparen + 1 }
func (x *SelectorExpr) End() token.Pos { return x.Sel.End() }
func (x *IndexExpr) End() token.Pos { return x.Rbrack + 1 }
+func (x *MultiIndexExpr) End() token.Pos { return x.Rbrack + 1 }
func (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 }
func (x *TypeAssertExpr) End() token.Pos { return x.Rparen + 1 }
func (x *CallExpr) End() token.Pos { return x.Rparen + 1 }
-func (x *ListExpr) End() token.Pos {
- if len(x.ElemList) > 0 {
- return x.ElemList[len(x.ElemList)-1].End()
- }
- return token.NoPos
-}
-func (x *StarExpr) End() token.Pos { return x.X.End() }
-func (x *UnaryExpr) End() token.Pos { return x.X.End() }
-func (x *BinaryExpr) End() token.Pos { return x.Y.End() }
-func (x *KeyValueExpr) End() token.Pos { return x.Value.End() }
-func (x *ArrayType) End() token.Pos { return x.Elt.End() }
-func (x *StructType) End() token.Pos { return x.Fields.End() }
+func (x *StarExpr) End() token.Pos { return x.X.End() }
+func (x *UnaryExpr) End() token.Pos { return x.X.End() }
+func (x *BinaryExpr) End() token.Pos { return x.Y.End() }
+func (x *KeyValueExpr) End() token.Pos { return x.Value.End() }
+func (x *ArrayType) End() token.Pos { return x.Elt.End() }
+func (x *StructType) End() token.Pos { return x.Fields.End() }
func (x *FuncType) End() token.Pos {
if x.Results != nil {
return x.Results.End()
func (*ParenExpr) exprNode() {}
func (*SelectorExpr) exprNode() {}
func (*IndexExpr) exprNode() {}
+func (*MultiIndexExpr) exprNode() {}
func (*SliceExpr) exprNode() {}
func (*TypeAssertExpr) exprNode() {}
func (*CallExpr) exprNode() {}
-func (*ListExpr) exprNode() {}
func (*StarExpr) exprNode() {}
func (*UnaryExpr) exprNode() {}
func (*BinaryExpr) exprNode() {}
Walk(v, n.X)
Walk(v, n.Index)
+ case *MultiIndexExpr:
+ Walk(v, n.X)
+ for _, index := range n.Indices {
+ Walk(v, index)
+ }
+
case *SliceExpr:
Walk(v, n.X)
if n.Low != nil {
Walk(v, n.Fun)
walkExprList(v, n.Args)
- case *ListExpr:
- for _, elem := range n.ElemList {
- Walk(v, elem)
- }
-
case *StarExpr:
Walk(v, n.X)
import (
"fmt"
"go/ast"
+ "go/token"
)
const Enabled = true
-func PackExpr(list []ast.Expr) ast.Expr {
- switch len(list) {
+func PackIndexExpr(x ast.Expr, lbrack token.Pos, exprs []ast.Expr, rbrack token.Pos) ast.Expr {
+ switch len(exprs) {
case 0:
- // Return an empty ListExpr here, rather than nil, as IndexExpr.Index must
- // never be nil.
- // TODO(rFindley) would a BadExpr be more appropriate here?
- return &ast.ListExpr{}
+ panic("internal error: PackIndexExpr with empty expr slice")
case 1:
- return list[0]
+ return &ast.IndexExpr{
+ X: x,
+ Lbrack: lbrack,
+ Index: exprs[0],
+ Rbrack: rbrack,
+ }
default:
- return &ast.ListExpr{ElemList: list}
+ return &ast.MultiIndexExpr{
+ X: x,
+ Lbrack: lbrack,
+ Indices: exprs,
+ Rbrack: rbrack,
+ }
}
}
-// TODO(gri) Should find a more efficient solution that doesn't
-// require introduction of a new slice for simple
-// expressions.
-func UnpackExpr(x ast.Expr) []ast.Expr {
- if x, _ := x.(*ast.ListExpr); x != nil {
- return x.ElemList
- }
- if x != nil {
- return []ast.Expr{x}
- }
- return nil
+// IndexExpr wraps an ast.IndexExpr or ast.MultiIndexExpr into the
+// MultiIndexExpr interface.
+//
+// Orig holds the original ast.Expr from which this IndexExpr was derived.
+type IndexExpr struct {
+ Orig ast.Expr // the wrapped expr, which may be distinct from MultiIndexExpr below.
+ *ast.MultiIndexExpr
}
-func IsListExpr(n ast.Node) bool {
- _, ok := n.(*ast.ListExpr)
- return ok
+func UnpackIndexExpr(n ast.Node) *IndexExpr {
+ switch e := n.(type) {
+ case *ast.IndexExpr:
+ return &IndexExpr{e, &ast.MultiIndexExpr{
+ X: e.X,
+ Lbrack: e.Lbrack,
+ Indices: []ast.Expr{e.Index},
+ Rbrack: e.Rbrack,
+ }}
+ case *ast.MultiIndexExpr:
+ return &IndexExpr{e, e}
+ }
+ return nil
}
func Get(n ast.Node) *ast.FieldList {
}
// x[P], x[P1, P2], ...
- return nil, &ast.IndexExpr{X: x, Lbrack: lbrack, Index: typeparams.PackExpr(args), Rbrack: rbrack}
+ return nil, typeparams.PackIndexExpr(x, lbrack, args, rbrack)
}
func (p *parser) parseFieldDecl() *ast.Field {
p.exprLev--
}
rbrack := p.expectClosing(token.RBRACK, "type argument list")
- typ = &ast.IndexExpr{X: ident, Lbrack: lbrack, Index: typeparams.PackExpr(list), Rbrack: rbrack}
+ typ = typeparams.PackIndexExpr(ident, lbrack, list, rbrack)
}
case p.tok == token.LPAREN:
// ordinary method
}
opening := p.expect(token.LBRACK)
-
p.exprLev++
var list []ast.Expr
for p.tok != token.RBRACK && p.tok != token.EOF {
closing := p.expectClosing(token.RBRACK, "type argument list")
- return &ast.IndexExpr{X: typ, Lbrack: opening, Index: typeparams.PackExpr(list), Rbrack: closing}
+ if len(list) == 0 {
+ p.errorExpected(closing, "type argument list")
+ return &ast.IndexExpr{
+ X: typ,
+ Lbrack: opening,
+ Index: &ast.BadExpr{From: opening + 1, To: closing},
+ Rbrack: closing,
+ }
+ }
+
+ return typeparams.PackIndexExpr(typ, opening, list, closing)
}
func (p *parser) tryIdentOrType() ast.Expr {
}
// instance expression
- return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: typeparams.PackExpr(args), Rbrack: rbrack}
+ return typeparams.PackIndexExpr(x, lbrack, args, rbrack)
}
func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
panic("unreachable")
case *ast.SelectorExpr:
case *ast.IndexExpr:
+ case *ast.MultiIndexExpr:
case *ast.SliceExpr:
case *ast.TypeAssertExpr:
// If t.Type == nil we have a type assertion of the form
return
}
// x is possibly a composite literal type
- case *ast.IndexExpr:
+ case *ast.IndexExpr, *ast.MultiIndexExpr:
if p.exprLev < 0 {
return
}
// TODO(gri): should treat[] like parentheses and undo one level of depth
p.expr1(x.X, token.HighestPrec, 1)
p.print(x.Lbrack, token.LBRACK)
- // Note: we're a bit defensive here to handle the case of a ListExpr of
- // length 1.
- if list := typeparams.UnpackExpr(x.Index); len(list) > 0 {
- if len(list) > 1 {
- p.exprList(x.Lbrack, list, depth+1, commaTerm, x.Rbrack, false)
- } else {
- p.expr0(list[0], depth+1)
- }
- } else {
- p.expr0(x.Index, depth+1)
- }
+ p.expr0(x.Index, depth+1)
+ p.print(x.Rbrack, token.RBRACK)
+
+ case *ast.MultiIndexExpr:
+ // TODO(gri): as for IndexExpr, should treat [] like parentheses and undo
+ // one level of depth
+ p.expr1(x.X, token.HighestPrec, 1)
+ p.print(x.Lbrack, token.LBRACK)
+ p.exprList(x.Lbrack, x.Indices, depth+1, commaTerm, x.Rbrack, false)
p.print(x.Rbrack, token.RBRACK)
case *ast.SliceExpr:
// funcInst type-checks a function instantiation inst and returns the result in x.
// The operand x must be the evaluation of inst.X and its type must be a signature.
-func (check *Checker) funcInst(x *operand, inst *ast.IndexExpr) {
- xlist := typeparams.UnpackExpr(inst.Index)
- targs := check.typeList(xlist)
+func (check *Checker) funcInst(x *operand, ix *typeparams.IndexExpr) {
+ targs := check.typeList(ix.Indices)
if targs == nil {
x.mode = invalid
- x.expr = inst
+ x.expr = ix.Orig
return
}
- assert(len(targs) == len(xlist))
+ assert(len(targs) == len(ix.Indices))
// check number of type arguments (got) vs number of type parameters (want)
sig := x.typ.(*Signature)
got, want := len(targs), len(sig.tparams)
if got > want {
- check.errorf(xlist[got-1], _Todo, "got %d type arguments but want %d", got, want)
+ check.errorf(ix.Indices[got-1], _Todo, "got %d type arguments but want %d", got, want)
x.mode = invalid
- x.expr = inst
+ x.expr = ix.Orig
return
}
inferred := false
if got < want {
- targs = check.infer(inst, sig.tparams, targs, nil, nil, true)
+ targs = check.infer(ix.Orig, sig.tparams, targs, nil, nil, true)
if targs == nil {
// error was already reported
x.mode = invalid
- x.expr = inst
+ x.expr = ix.Orig
return
}
got = len(targs)
// determine argument positions (for error reporting)
// TODO(rFindley) use a positioner here? instantiate would need to be
// updated accordingly.
- poslist := make([]token.Pos, len(xlist))
- for i, x := range xlist {
+ poslist := make([]token.Pos, len(ix.Indices))
+ for i, x := range ix.Indices {
poslist[i] = x.Pos()
}
res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature)
assert(res.tparams == nil) // signature is not generic anymore
if inferred {
- check.recordInferred(inst, targs, res)
+ check.recordInferred(ix.Orig, targs, res)
}
x.typ = res
x.mode = value
- x.expr = inst
+ x.expr = ix.Orig
}
func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
- var inst *ast.IndexExpr
- if iexpr, _ := call.Fun.(*ast.IndexExpr); iexpr != nil {
- if check.indexExpr(x, iexpr) {
+ ix := typeparams.UnpackIndexExpr(call.Fun)
+ if ix != nil {
+ if check.indexExpr(x, ix) {
// Delay function instantiation to argument checking,
// where we combine type and value arguments for type
// inference.
assert(x.mode == value)
- inst = iexpr
+ } else {
+ ix = nil
}
- x.expr = iexpr
+ x.expr = call.Fun
check.record(x)
+
} else {
check.exprOrType(x, call.Fun)
}
// evaluate type arguments, if any
var targs []Type
- if inst != nil {
- xlist := typeparams.UnpackExpr(inst.Index)
- targs = check.typeList(xlist)
+ if ix != nil {
+ targs = check.typeList(ix.Indices)
if targs == nil {
check.use(call.Args...)
x.mode = invalid
x.expr = call
return statement
}
- assert(len(targs) == len(xlist))
+ assert(len(targs) == len(ix.Indices))
// check number of type arguments (got) vs number of type parameters (want)
got, want := len(targs), len(sig.tparams)
if got > want {
- check.errorf(xlist[want], _Todo, "got %d type arguments but want %d", got, want)
+ check.errorf(ix.Indices[want], _Todo, "got %d type arguments but want %d", got, want)
check.use(call.Args...)
x.mode = invalid
x.expr = call
case *ast.SelectorExpr:
check.selector(x, e)
- case *ast.IndexExpr:
- if check.indexExpr(x, e) {
- check.funcInst(x, e)
+ case *ast.IndexExpr, *ast.MultiIndexExpr:
+ ix := typeparams.UnpackIndexExpr(e)
+ if check.indexExpr(x, ix) {
+ check.funcInst(x, ix)
}
if x.mode == invalid {
goto Error
// types, which are comparatively rare.
default:
- if typeparams.IsListExpr(e) {
- // catch-all for unexpected expression lists
- check.errorf(e, _Todo, "unexpected list of expressions")
- } else {
- panic(fmt.Sprintf("%s: unknown expression type %T", check.fset.Position(e.Pos()), e))
- }
+ panic(fmt.Sprintf("%s: unknown expression type %T", check.fset.Position(e.Pos()), e))
}
// everything went well
buf.WriteByte('.')
buf.WriteString(x.Sel.Name)
- case *ast.IndexExpr:
- WriteExpr(buf, x.X)
+ case *ast.IndexExpr, *ast.MultiIndexExpr:
+ ix := typeparams.UnpackIndexExpr(x)
+ WriteExpr(buf, ix.X)
buf.WriteByte('[')
- exprs := typeparams.UnpackExpr(x.Index)
- for i, e := range exprs {
+ for i, e := range ix.Indices {
if i > 0 {
buf.WriteString(", ")
}
// 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 (check *Checker) indexExpr(x *operand, e *ast.IndexExpr) (isFuncInst bool) {
- check.exprOrType(x, e.X)
+func (check *Checker) indexExpr(x *operand, expr *typeparams.IndexExpr) (isFuncInst bool) {
+ check.exprOrType(x, expr.X)
switch x.mode {
case invalid:
- check.use(typeparams.UnpackExpr(e.Index)...)
+ check.use(expr.Indices...)
return false
case typexpr:
// type instantiation
x.mode = invalid
- x.typ = check.varType(e)
+ x.typ = check.varType(expr.Orig)
if x.typ != Typ[Invalid] {
x.mode = typexpr
}
x.typ = typ.elem
case *Map:
- index := check.singleIndex(e)
+ index := check.singleIndex(expr)
if index == nil {
x.mode = invalid
return
// ok to continue even if indexing failed - map element type is known
x.mode = mapindex
x.typ = typ.elem
- x.expr = e
+ x.expr = expr.Orig
return
case *Union:
// If there are maps, the index expression must be assignable
// to the map key type (as for simple map index expressions).
if nmaps > 0 {
- index := check.singleIndex(e)
+ index := check.singleIndex(expr)
if index == nil {
x.mode = invalid
return
if nmaps == typ.NumTerms() {
x.mode = mapindex
x.typ = telem
- x.expr = e
+ x.expr = expr.Orig
return
}
return
}
- index := check.singleIndex(e)
+ index := check.singleIndex(expr)
if index == nil {
x.mode = invalid
return
// 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 (check *Checker) singleIndex(e *ast.IndexExpr) ast.Expr {
- index := e.Index
- if index == nil {
- check.invalidAST(e, "missing index for %s", e)
- return nil
- }
-
- indexes := typeparams.UnpackExpr(index)
- if len(indexes) == 0 {
- check.invalidAST(index, "index expression %v with 0 indices", index)
+func (check *Checker) singleIndex(expr *typeparams.IndexExpr) ast.Expr {
+ if len(expr.Indices) == 0 {
+ check.invalidAST(expr.Orig, "index expression %v with 0 indices", expr)
return nil
}
- if len(indexes) > 1 {
+ if len(expr.Indices) > 1 {
// TODO(rFindley) should this get a distinct error code?
- check.invalidOp(indexes[1], _InvalidIndex, "more than one index")
+ check.invalidOp(expr.Indices[1], _InvalidIndex, "more than one index")
}
- return indexes[0]
+ return expr.Indices[0]
}
// index checks an index expression for validity.
}
// unpack type parameters, if any
- if ptyp, _ := rtyp.(*ast.IndexExpr); ptyp != nil {
- rtyp = ptyp.X
+ switch rtyp.(type) {
+ case *ast.IndexExpr, *ast.MultiIndexExpr:
+ ix := typeparams.UnpackIndexExpr(rtyp)
+ rtyp = ix.X
if unpackParams {
- for _, arg := range typeparams.UnpackExpr(ptyp.Index) {
+ for _, arg := range ix.Indices {
var par *ast.Ident
switch arg := arg.(type) {
case *ast.Ident:
case *ast.BadExpr:
// ignore - error already reported by parser
case nil:
- check.invalidAST(ptyp, "parameterized receiver contains nil parameters")
+ check.invalidAST(ix.Orig, "parameterized receiver contains nil parameters")
default:
check.errorf(arg, _Todo, "receiver type parameter %s must be an identifier", arg)
}
new.X = X
return &new
}
- case *ast.IndexExpr:
- elems := typeparams.UnpackExpr(n.Index)
- var newElems []ast.Expr
- for i, elem := range elems {
- new := isubst(elem, smap)
- if new != elem {
- if newElems == nil {
- newElems = make([]ast.Expr, len(elems))
- copy(newElems, elems)
+ case *ast.IndexExpr, *ast.MultiIndexExpr:
+ ix := typeparams.UnpackIndexExpr(x)
+ var newIndexes []ast.Expr
+ for i, index := range ix.Indices {
+ new := isubst(index, smap)
+ if new != index {
+ if newIndexes == nil {
+ newIndexes = make([]ast.Expr, len(ix.Indices))
+ copy(newIndexes, ix.Indices)
}
- newElems[i] = new
+ newIndexes[i] = new
}
}
- if newElems != nil {
- index := typeparams.PackExpr(newElems)
- new := *n
- new.Index = index
- return &new
+ if newIndexes != nil {
+ return typeparams.PackIndexExpr(ix.X, ix.Lbrack, newIndexes, ix.Rbrack)
}
case *ast.ParenExpr:
return isubst(n.X, smap) // no need to keep parentheses
var x int
type _ x /* ERROR not a type */ [int]
-type _ int /* ERROR not a generic type */ []
-type _ myInt /* ERROR not a generic type */ []
+type _ int /* ERROR not a generic type */ [] // ERROR expected type argument list
+type _ myInt /* ERROR not a generic type */ [] // ERROR expected type argument list
// TODO(gri) better error messages
-type _ T1 /* ERROR got 0 arguments but 1 type parameters */ []
+type _ T1[] // ERROR expected type argument list
type _ T1[x /* ERROR not a type */ ]
type _ T1 /* ERROR got 2 arguments but 1 type parameters */ [int, float32]
type N[T any] struct{}
-var _ N /* ERROR "0 arguments but 1 type parameters" */ []
+var _ N [] // ERROR expected type argument list
type I interface {
~map[int]int | ~[]int
check.errorf(&x, _NotAType, "%s is not a type", &x)
}
- case *ast.IndexExpr:
+ case *ast.IndexExpr, *ast.MultiIndexExpr:
+ ix := typeparams.UnpackIndexExpr(e)
if typeparams.Enabled {
- exprs := typeparams.UnpackExpr(e.Index)
- return check.instantiatedType(e.X, exprs, def)
+ return check.instantiatedType(ix, def)
}
check.errorf(e0, _NotAType, "%s is not a type", e0)
- check.use(e.X)
+ check.use(ix.X)
case *ast.ParenExpr:
// Generic types must be instantiated before they can be used in any form.
return Typ[Invalid]
}
-func (check *Checker) instantiatedType(x ast.Expr, targs []ast.Expr, def *Named) Type {
- b := check.genericType(x, true) // TODO(gri) what about cycles?
+func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) Type {
+ b := check.genericType(ix.X, true) // TODO(gri) what about cycles?
if b == Typ[Invalid] {
return b // error already reported
}
def.setUnderlying(typ)
typ.check = check
- typ.pos = x.Pos()
+ typ.pos = ix.X.Pos()
typ.base = base
// evaluate arguments (always)
- typ.targs = check.typeList(targs)
+ typ.targs = check.typeList(ix.Indices)
if typ.targs == nil {
def.setUnderlying(Typ[Invalid]) // avoid later errors due to lazy instantiation
return Typ[Invalid]
}
// determine argument positions (for error reporting)
- typ.poslist = make([]token.Pos, len(targs))
- for i, arg := range targs {
+ typ.poslist = make([]token.Pos, len(ix.Indices))
+ for i, arg := range ix.Indices {
typ.poslist[i] = arg.Pos()
}