[dev.typeparams] cmd/compile/internal/syntax: always use IndexExpr node for type instantiation

Per @mdempsky's suggestion: Instead of representing a type instantiation T[P]
by an IndexExpr node, and a type instantiation with multiple type arguments
T[P1, P2] by a CallExpr node with special Brackets flag, always use an IndexExpr.
Use a ListExpr as index in the (less common) case of multiple type arguments.

This removes the need for the CallExpr.Brackets field and cleans up the parser
code around type instantiations.

Backport of syntax package changes from https://golang.org/cl/262020.

Change-Id: I32e8bc4eafac5b3ef2e7eb40fa8c790a5a905b69
Reviewed-on: https://go-review.googlesource.com/c/go/+/262137
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Robert Griesemer 2020-10-13 22:33:17 -07:00
parent 48755e06aa
commit 73f529845c
3 changed files with 139 additions and 89 deletions

View file

@ -184,6 +184,7 @@ type (
} }
// X[Index] // X[Index]
// X[T1, T2, ...] (with Ti = Index.(*ListExpr).ElemList[i])
IndexExpr struct { IndexExpr struct {
X Expr X Expr
Index Expr Index Expr
@ -225,10 +226,9 @@ type (
// Fun(ArgList[0], ArgList[1], ...) // Fun(ArgList[0], ArgList[1], ...)
CallExpr struct { CallExpr struct {
Fun Expr Fun Expr
ArgList []Expr // nil means no arguments ArgList []Expr // nil means no arguments
HasDots bool // last argument is followed by ... HasDots bool // last argument is followed by ...
Brackets bool // []'s instead of ()'s
expr expr
} }

View file

@ -458,15 +458,15 @@ func isEmptyFuncDecl(dcl Decl) bool {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Declarations // Declarations
// list parses a possibly empty, sep-separated list, optionally // list parses a possibly empty, sep-separated list of elements, optionally
// followed sep, and closed by close. sep must be one of _Comma // followed by sep, and closed by close (or EOF). sep must be one of _Comma
// or _Semi, and close must be one of _Rparen, _Rbrace, or _Rbrack. // or _Semi, and close must be one of _Rparen, _Rbrace, or _Rbrack.
// For each list element, f is called. After f returns true, no
// more list elements are accepted. list returns the position
// of the closing token.
// //
// list = { f sep } ")" | // For each list element, f is called. Specifically, unless we're at close
// { f sep } "}" . // "," or ";" is optional before ")", "}" or "]" // (or EOF), f is called at least once. After f returns true, no more list
// elements are accepted. list returns the position of the closing token.
//
// list = [ f { sep f } [sep] ] close .
// //
func (p *parser) list(sep, close token, f func() bool) Pos { func (p *parser) list(sep, close token, f func() bool) Pos {
if debug && (sep != _Comma && sep != _Semi || close != _Rparen && close != _Rbrace && close != _Rbrack) { if debug && (sep != _Comma && sep != _Semi || close != _Rparen && close != _Rbrace && close != _Rbrack) {
@ -1017,43 +1017,44 @@ loop:
break break
} }
p.xnest++
var i Expr var i Expr
if p.tok != _Colon { if p.tok != _Colon {
i = p.expr() if p.mode&AllowGenerics == 0 {
if p.got(_Rbrack) { p.xnest++
// x[i] i = p.expr()
t := new(IndexExpr)
t.pos = pos
t.X = x
t.Index = i
x = t
p.xnest-- p.xnest--
break if p.got(_Rbrack) {
} // x[i]
t := new(IndexExpr)
if p.mode&AllowGenerics != 0 && p.tok == _Comma { t.pos = pos
// x[i, ... (instantiated type) t.X = x
// TODO(gri) Suggestion by mdempsky@: Use IndexExpr + ExprList for this case. t.Index = i
// Then we can get rid of CallExpr.Brackets. x = t
t := new(CallExpr) break
t.pos = pos }
t.Fun = x } else {
t.ArgList, _ = p.argList(i, _Rbrack) var comma bool
t.Brackets = true i, comma = p.typeList()
x = t if comma || p.tok == _Rbrack {
p.xnest-- p.want(_Rbrack)
break // x[i,] or x[i, j, ...]
t := new(IndexExpr)
t.pos = pos
t.X = x
t.Index = i
x = t
break
}
} }
} }
// x[i:... // x[i:...
p.want(_Colon)
p.xnest++
t := new(SliceExpr) t := new(SliceExpr)
t.pos = pos t.pos = pos
t.X = x t.X = x
t.Index[0] = i t.Index[0] = i
p.want(_Colon)
if p.tok != _Colon && p.tok != _Rbrack { if p.tok != _Colon && p.tok != _Rbrack {
// x[i:j... // x[i:j...
t.Index[1] = p.expr() t.Index[1] = p.expr()
@ -1074,17 +1075,16 @@ loop:
t.Index[2] = p.badExpr() t.Index[2] = p.badExpr()
} }
} }
p.want(_Rbrack)
x = t
p.xnest-- p.xnest--
p.want(_Rbrack)
x = t
case _Lparen: case _Lparen:
t := new(CallExpr) t := new(CallExpr)
t.pos = pos t.pos = pos
p.next() p.next()
t.Fun = x t.Fun = x
t.ArgList, t.HasDots = p.argList(nil, _Rparen) t.ArgList, t.HasDots = p.argList()
x = t x = t
case _Lbrace: case _Lbrace:
@ -1093,17 +1093,12 @@ loop:
t := unparen(x) t := unparen(x)
// determine if '{' belongs to a composite literal or a block statement // determine if '{' belongs to a composite literal or a block statement
complit_ok := false complit_ok := false
switch t := t.(type) { switch t.(type) {
case *Name, *SelectorExpr: case *Name, *SelectorExpr:
if p.xnest >= 0 { if p.xnest >= 0 {
// x is possibly a composite literal type // x is possibly a composite literal type
complit_ok = true complit_ok = true
} }
case *CallExpr:
if t.Brackets && p.xnest >= 0 {
// x is possibly a composite literal type
complit_ok = true
}
case *IndexExpr: case *IndexExpr:
if p.xnest >= 0 { if p.xnest >= 0 {
// x is possibly a composite literal type // x is possibly a composite literal type
@ -1302,12 +1297,12 @@ func (p *parser) typeInstance(typ Expr) Expr {
return typ return typ
} }
call := new(CallExpr) x := new(IndexExpr)
call.pos = pos x.pos = pos
call.Fun = typ x.X = typ
call.ArgList, _ = p.argList(nil, _Rbrack) x.Index, _ = p.typeList()
call.Brackets = true p.want(_Rbrack)
return call return x
} }
func (p *parser) funcType() *FuncType { func (p *parser) funcType() *FuncType {
@ -1521,9 +1516,9 @@ func (p *parser) fieldDecl(styp *StructType) {
// type T[P1, P2, ...] or a field T of array/slice type [P]E or []E. // type T[P1, P2, ...] or a field T of array/slice type [P]E or []E.
if p.mode&AllowGenerics != 0 && len(names) == 1 && p.tok == _Lbrack { if p.mode&AllowGenerics != 0 && len(names) == 1 && p.tok == _Lbrack {
typ = p.arrayOrTArgs() typ = p.arrayOrTArgs()
if typ, ok := typ.(*CallExpr); ok { if typ, ok := typ.(*IndexExpr); ok {
// embedded type T[P1, P2, ...] // embedded type T[P1, P2, ...]
typ.Fun = name // name == names[0] typ.X = name // name == names[0]
tag := p.oliteral() tag := p.oliteral()
p.addField(styp, pos, nil, typ, tag) p.addField(styp, pos, nil, typ, tag)
break break
@ -1589,25 +1584,25 @@ func (p *parser) arrayOrTArgs() Expr {
return p.sliceType(pos) return p.sliceType(pos)
} }
// x [P]E or x[P] // x [n]E or x[n,], x[n1, n2], ...
args, _ := p.argList(nil, _Rbrack) n, comma := p.typeList()
if len(args) == 1 { p.want(_Rbrack)
if !comma {
if elem := p.typeOrNil(); elem != nil { if elem := p.typeOrNil(); elem != nil {
// x [P]E // x [n]E
t := new(ArrayType) t := new(ArrayType)
t.pos = pos t.pos = pos
t.Len = args[0] t.Len = n
t.Elem = elem t.Elem = elem
return t return t
} }
} }
// x[P], x[P1, P2], ... // x[n,], x[n1, n2], ...
t := new(CallExpr) t := new(IndexExpr)
t.pos = pos t.pos = pos
// t.Fun will be filled in by caller // t.X will be filled in by caller
t.ArgList = args t.Index = n
t.Brackets = true
return t return t
} }
@ -1664,7 +1659,8 @@ func (p *parser) methodDecl() *Field {
pos := p.pos() pos := p.pos()
p.next() p.next()
// empty type parameter or argument lists are not permitted // Empty type parameter or argument lists are not permitted.
// Treat as if [] were absent.
if p.tok == _Rbrack { if p.tok == _Rbrack {
// name[] // name[]
pos := p.pos() pos := p.pos()
@ -1684,7 +1680,21 @@ func (p *parser) methodDecl() *Field {
// A type argument list looks like a parameter list with only // A type argument list looks like a parameter list with only
// types. Parse a parameter list and decide afterwards. // types. Parse a parameter list and decide afterwards.
list := p.paramList(nil, _Rbrack) list := p.paramList(nil, _Rbrack)
if len(list) > 0 && list[0].Name != nil { if len(list) == 0 {
// The type parameter list is not [] but we got nothing
// due to other errors (reported by paramList). Treat
// as if [] were absent.
if p.tok == _Lparen {
f.Name = name
f.Type = p.funcType()
} else {
f.Type = name
}
break
}
// len(list) > 0
if list[0].Name != nil {
// generic method // generic method
f.Name = name f.Name = name
f.Type = p.funcType() f.Type = p.funcType()
@ -1696,15 +1706,22 @@ func (p *parser) methodDecl() *Field {
} }
// embedded instantiated type // embedded instantiated type
call := new(CallExpr) t := new(IndexExpr)
call.pos = pos t.pos = pos
call.Fun = name t.X = name
call.Brackets = true if len(list) == 1 {
call.ArgList = make([]Expr, len(list)) t.Index = list[0].Type
for i := range list { } else {
call.ArgList[i] = list[i].Type // len(list) > 1
l := new(ListExpr)
l.pos = list[0].Pos()
l.ElemList = make([]Expr, len(list))
for i := range list {
l.ElemList[i] = list[i].Type
}
t.Index = l
} }
f.Type = call f.Type = t
break break
} }
fallthrough fallthrough
@ -1733,8 +1750,8 @@ func (p *parser) paramDeclOrNil(name *Name) *Field {
if p.mode&AllowGenerics != 0 && p.tok == _Lbrack { if p.mode&AllowGenerics != 0 && p.tok == _Lbrack {
f.Type = p.arrayOrTArgs() f.Type = p.arrayOrTArgs()
if typ, ok := f.Type.(*CallExpr); ok { if typ, ok := f.Type.(*IndexExpr); ok {
typ.Fun = name typ.X = name
} else { } else {
f.Name = name f.Name = name
} }
@ -2427,22 +2444,20 @@ func (p *parser) stmtList() (l []Stmt) {
return return
} }
// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" . // argList parses a possibly empty, comma-separated list of arguments,
func (p *parser) argList(arg Expr, close token) (list []Expr, hasDots bool) { // optionally followed by a comma (if not empty), and closed by ")".
// The last argument may be followed by "...".
//
// argList = [ arg { "," arg } [ "..." ] [ "," ] ] ")" .
func (p *parser) argList() (list []Expr, hasDots bool) {
if trace { if trace {
defer p.trace("argList")() defer p.trace("argList")()
} }
p.xnest++ p.xnest++
p.list(_Comma, close, func() bool { p.list(_Comma, _Rparen, func() bool {
if arg == nil { list = append(list, p.expr())
arg = p.expr() hasDots = p.got(_DotDotDot)
}
list = append(list, arg)
arg = nil
if close == _Rparen {
hasDots = p.got(_DotDotDot)
}
return hasDots return hasDots
}) })
p.xnest-- p.xnest--
@ -2548,6 +2563,41 @@ func (p *parser) exprList() Expr {
return x return x
} }
// typeList parses a non-empty, comma-separated list of expressions,
// optionally followed by a comma. The first list element may be any
// expression, all other list elements must be type expressions.
// If there is more than one argument, the result is a *ListExpr.
// The comma result indicates whether there was a (separating or
// trailing) comma.
//
// typeList = arg { "," arg } [ "," ] .
func (p *parser) typeList() (x Expr, comma bool) {
if trace {
defer p.trace("typeList")()
}
p.xnest++
x = p.expr()
if p.got(_Comma) {
comma = true
if t := p.typeOrNil(); t != nil {
list := []Expr{x, t}
for p.got(_Comma) {
if t = p.typeOrNil(); t == nil {
break
}
list = append(list, t)
}
l := new(ListExpr)
l.pos = x.Pos() // == list[0].Pos()
l.ElemList = list
x = l
}
}
p.xnest--
return
}
// unparen removes all parentheses around an expression. // unparen removes all parentheses around an expression.
func unparen(x Expr) Expr { func unparen(x Expr) Expr {
for { for {

View file

@ -26,7 +26,7 @@ var (
) )
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
ParseFile(*src_, func(err error) { t.Error(err) }, nil, 0) ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics)
} }
func TestParseGo2(t *testing.T) { func TestParseGo2(t *testing.T) {