[dev.typeparams] go/*: switch from ListExpr to MultiIndexExpr

When instantiating a generic type or function with multiple type
arguments, we need to represent an index expression with multiple
indexes in the AST. Previous to this CL this was done with a new
ast.ListExpr node, which allowed packing multiple expressions into a
single ast.Expr. This compositional pattern can be both inefficient and
cumbersome to work with, and introduces a new node type that only exists
to augment the meaning of an existing node type.

By comparison, other specializations of syntax are given distinct nodes
in go/ast, for example variations of switch or for statements, so the
use of ListExpr was also (arguably) inconsistent.

This CL removes ListExpr, and instead adds a MultiIndexExpr node, which
is exactly like IndexExpr but allows for multiple index arguments. This
requires special handling for this new node type, but a new wrapper in
the typeparams helper package largely mitigates this special handling.

Change-Id: I65eb29c025c599bae37501716284dc7eb953b2ad
Reviewed-on: https://go-review.googlesource.com/c/go/+/327149
Trust: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
Rob Findley 2021-06-11 10:58:43 -04:00 committed by Robert Findley
parent 6b85a218b8
commit 334f2fc045
14 changed files with 170 additions and 167 deletions

View file

@ -344,6 +344,15 @@ type (
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
@ -374,13 +383,6 @@ type (
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.
//
@ -494,21 +496,16 @@ func (x *CompositeLit) Pos() token.Pos {
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
@ -533,21 +530,16 @@ func (x *CompositeLit) End() token.Pos { return x.Rbrace + 1 }
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()
@ -570,10 +562,10 @@ func (*CompositeLit) exprNode() {}
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() {}

View file

@ -116,6 +116,12 @@ func Walk(v Visitor, node Node) {
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 {
@ -138,11 +144,6 @@ func Walk(v Visitor, node Node) {
Walk(v, n.Fun)
walkExprList(v, n.Args)
case *ListExpr:
for _, elem := range n.ElemList {
Walk(v, elem)
}
case *StarExpr:
Walk(v, n.X)

View file

@ -7,42 +7,56 @@ package typeparams
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}
// 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 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 IsListExpr(n ast.Node) bool {
_, ok := n.(*ast.ListExpr)
return ok
}
func Get(n ast.Node) *ast.FieldList {
switch n := n.(type) {
case *ast.TypeSpec:

View file

@ -600,7 +600,7 @@ func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident) (*ast.Ident, ast.Ex
}
// 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 {
@ -991,7 +991,7 @@ func (p *parser) parseMethodSpec() *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
@ -1178,7 +1178,6 @@ func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr {
}
opening := p.expect(token.LBRACK)
p.exprLev++
var list []ast.Expr
for p.tok != token.RBRACK && p.tok != token.EOF {
@ -1192,7 +1191,17 @@ func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr {
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 {
@ -1455,7 +1464,7 @@ func (p *parser) parseIndexOrSliceOrInstance(x ast.Expr) 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 {
@ -1557,6 +1566,7 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr {
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
@ -1646,7 +1656,7 @@ func (p *parser) parsePrimaryExpr() (x ast.Expr) {
return
}
// x is possibly a composite literal type
case *ast.IndexExpr:
case *ast.IndexExpr, *ast.MultiIndexExpr:
if p.exprLev < 0 {
return
}

View file

@ -871,17 +871,15 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
// 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:

View file

@ -16,23 +16,22 @@ import (
// 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
}
@ -40,11 +39,11 @@ func (check *Checker) funcInst(x *operand, inst *ast.IndexExpr) {
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)
@ -55,8 +54,8 @@ func (check *Checker) funcInst(x *operand, inst *ast.IndexExpr) {
// 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()
}
@ -64,25 +63,27 @@ func (check *Checker) funcInst(x *operand, inst *ast.IndexExpr) {
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)
}
@ -149,21 +150,20 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
// 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

View file

@ -1331,9 +1331,10 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
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
@ -1423,12 +1424,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
// 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

View file

@ -67,11 +67,11 @@ func WriteExpr(buf *bytes.Buffer, x ast.Expr) {
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(", ")
}

View file

@ -15,18 +15,18 @@ 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 (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
}
@ -77,7 +77,7 @@ func (check *Checker) indexExpr(x *operand, e *ast.IndexExpr) (isFuncInst bool)
x.typ = typ.elem
case *Map:
index := check.singleIndex(e)
index := check.singleIndex(expr)
if index == nil {
x.mode = invalid
return
@ -88,7 +88,7 @@ func (check *Checker) indexExpr(x *operand, e *ast.IndexExpr) (isFuncInst bool)
// 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:
@ -137,7 +137,7 @@ func (check *Checker) indexExpr(x *operand, e *ast.IndexExpr) (isFuncInst bool)
// 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
@ -151,7 +151,7 @@ func (check *Checker) indexExpr(x *operand, e *ast.IndexExpr) (isFuncInst bool)
if nmaps == typ.NumTerms() {
x.mode = mapindex
x.typ = telem
x.expr = e
x.expr = expr.Orig
return
}
@ -180,7 +180,7 @@ func (check *Checker) indexExpr(x *operand, e *ast.IndexExpr) (isFuncInst bool)
return
}
index := check.singleIndex(e)
index := check.singleIndex(expr)
if index == nil {
x.mode = invalid
return
@ -311,23 +311,16 @@ L:
// 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)
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
}
indexes := typeparams.UnpackExpr(index)
if len(indexes) == 0 {
check.invalidAST(index, "index expression %v with 0 indices", index)
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.

View file

@ -499,10 +499,12 @@ L: // unpack receiver type
}
// 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:
@ -510,7 +512,7 @@ L: // unpack receiver type
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)
}

View file

@ -244,24 +244,21 @@ func isubst(x ast.Expr, smap map[*ast.Ident]*ast.Ident) ast.Expr {
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

View file

@ -33,11 +33,11 @@ var _ A3
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]

View file

@ -10,7 +10,7 @@ func main() {
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

View file

@ -261,13 +261,13 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
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.
@ -403,8 +403,8 @@ func (check *Checker) typeOrNil(e ast.Expr) Type {
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
}
@ -420,19 +420,19 @@ func (check *Checker) instantiatedType(x ast.Expr, targs []ast.Expr, def *Named)
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()
}