]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile/internal/syntax: always use IndexExpr node for type...
authorRobert Griesemer <gri@golang.org>
Wed, 14 Oct 2020 05:33:17 +0000 (22:33 -0700)
committerRobert Griesemer <gri@golang.org>
Wed, 14 Oct 2020 21:37:37 +0000 (21:37 +0000)
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>
src/cmd/compile/internal/syntax/nodes.go
src/cmd/compile/internal/syntax/parser.go
src/cmd/compile/internal/syntax/parser_test.go

index f2dbdcda29769f1f92587686d8a77dfb30031146..e5b69628ece56e4a8c72305dfe4e7cebabc29f1b 100644 (file)
@@ -184,6 +184,7 @@ type (
        }
 
        // X[Index]
+       // X[T1, T2, ...] (with Ti = Index.(*ListExpr).ElemList[i])
        IndexExpr struct {
                X     Expr
                Index Expr
@@ -225,10 +226,9 @@ type (
 
        // Fun(ArgList[0], ArgList[1], ...)
        CallExpr struct {
-               Fun      Expr
-               ArgList  []Expr // nil means no arguments
-               HasDots  bool   // last argument is followed by ...
-               Brackets bool   // []'s instead of ()'s
+               Fun     Expr
+               ArgList []Expr // nil means no arguments
+               HasDots bool   // last argument is followed by ...
                expr
        }
 
index 126b87b4ee99dad55c9595826a75c9d7e208be43..dbec462ab15803120906f3c3bd730917b44793eb 100644 (file)
@@ -458,15 +458,15 @@ func isEmptyFuncDecl(dcl Decl) bool {
 // ----------------------------------------------------------------------------
 // Declarations
 
-// list parses a possibly empty, sep-separated list, optionally
-// followed sep, and closed by close. sep must be one of _Comma
+// list parses a possibly empty, sep-separated list of elements, optionally
+// 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.
-// 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 } ")" |
-//        { f sep } "}" . // "," or ";" is optional before ")", "}" or "]"
+// For each list element, f is called. Specifically, unless we're at close
+// (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 {
        if debug && (sep != _Comma && sep != _Semi || close != _Rparen && close != _Rbrace && close != _Rbrack) {
@@ -1017,43 +1017,44 @@ loop:
                                break
                        }
 
-                       p.xnest++
-
                        var i Expr
                        if p.tok != _Colon {
-                               i = p.expr()
-                               if p.got(_Rbrack) {
-                                       // x[i]
-                                       t := new(IndexExpr)
-                                       t.pos = pos
-                                       t.X = x
-                                       t.Index = i
-                                       x = t
+                               if p.mode&AllowGenerics == 0 {
+                                       p.xnest++
+                                       i = p.expr()
                                        p.xnest--
-                                       break
-                               }
-
-                               if p.mode&AllowGenerics != 0 && p.tok == _Comma {
-                                       // x[i, ... (instantiated type)
-                                       // TODO(gri) Suggestion by mdempsky@: Use IndexExpr + ExprList for this case.
-                                       //           Then we can get rid of CallExpr.Brackets.
-                                       t := new(CallExpr)
-                                       t.pos = pos
-                                       t.Fun = x
-                                       t.ArgList, _ = p.argList(i, _Rbrack)
-                                       t.Brackets = true
-                                       x = t
-                                       p.xnest--
-                                       break
+                                       if p.got(_Rbrack) {
+                                               // x[i]
+                                               t := new(IndexExpr)
+                                               t.pos = pos
+                                               t.X = x
+                                               t.Index = i
+                                               x = t
+                                               break
+                                       }
+                               } else {
+                                       var comma bool
+                                       i, comma = p.typeList()
+                                       if comma || p.tok == _Rbrack {
+                                               p.want(_Rbrack)
+                                               // x[i,] or x[i, j, ...]
+                                               t := new(IndexExpr)
+                                               t.pos = pos
+                                               t.X = x
+                                               t.Index = i
+                                               x = t
+                                               break
+                                       }
                                }
                        }
 
                        // x[i:...
+                       p.want(_Colon)
+                       p.xnest++
                        t := new(SliceExpr)
                        t.pos = pos
                        t.X = x
                        t.Index[0] = i
-                       p.want(_Colon)
                        if p.tok != _Colon && p.tok != _Rbrack {
                                // x[i:j...
                                t.Index[1] = p.expr()
@@ -1074,17 +1075,16 @@ loop:
                                        t.Index[2] = p.badExpr()
                                }
                        }
+                       p.xnest--
                        p.want(_Rbrack)
-
                        x = t
-                       p.xnest--
 
                case _Lparen:
                        t := new(CallExpr)
                        t.pos = pos
                        p.next()
                        t.Fun = x
-                       t.ArgList, t.HasDots = p.argList(nil, _Rparen)
+                       t.ArgList, t.HasDots = p.argList()
                        x = t
 
                case _Lbrace:
@@ -1093,17 +1093,12 @@ loop:
                        t := unparen(x)
                        // determine if '{' belongs to a composite literal or a block statement
                        complit_ok := false
-                       switch t := t.(type) {
+                       switch t.(type) {
                        case *Name, *SelectorExpr:
                                if p.xnest >= 0 {
                                        // x is possibly a composite literal type
                                        complit_ok = true
                                }
-                       case *CallExpr:
-                               if t.Brackets && p.xnest >= 0 {
-                                       // x is possibly a composite literal type
-                                       complit_ok = true
-                               }
                        case *IndexExpr:
                                if p.xnest >= 0 {
                                        // x is possibly a composite literal type
@@ -1302,12 +1297,12 @@ func (p *parser) typeInstance(typ Expr) Expr {
                return typ
        }
 
-       call := new(CallExpr)
-       call.pos = pos
-       call.Fun = typ
-       call.ArgList, _ = p.argList(nil, _Rbrack)
-       call.Brackets = true
-       return call
+       x := new(IndexExpr)
+       x.pos = pos
+       x.X = typ
+       x.Index, _ = p.typeList()
+       p.want(_Rbrack)
+       return x
 }
 
 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.
                if p.mode&AllowGenerics != 0 && len(names) == 1 && p.tok == _Lbrack {
                        typ = p.arrayOrTArgs()
-                       if typ, ok := typ.(*CallExpr); ok {
+                       if typ, ok := typ.(*IndexExpr); ok {
                                // embedded type T[P1, P2, ...]
-                               typ.Fun = name // name == names[0]
+                               typ.X = name // name == names[0]
                                tag := p.oliteral()
                                p.addField(styp, pos, nil, typ, tag)
                                break
@@ -1589,25 +1584,25 @@ func (p *parser) arrayOrTArgs() Expr {
                return p.sliceType(pos)
        }
 
-       // x [P]E or x[P]
-       args, _ := p.argList(nil, _Rbrack)
-       if len(args) == 1 {
+       // x [n]E or x[n,], x[n1, n2], ...
+       n, comma := p.typeList()
+       p.want(_Rbrack)
+       if !comma {
                if elem := p.typeOrNil(); elem != nil {
-                       // x [P]E
+                       // x [n]E
                        t := new(ArrayType)
                        t.pos = pos
-                       t.Len = args[0]
+                       t.Len = n
                        t.Elem = elem
                        return t
                }
        }
 
-       // x[P], x[P1, P2], ...
-       t := new(CallExpr)
+       // x[n,], x[n1, n2], ...
+       t := new(IndexExpr)
        t.pos = pos
-       // t.Fun will be filled in by caller
-       t.ArgList = args
-       t.Brackets = true
+       // t.X will be filled in by caller
+       t.Index = n
        return t
 }
 
@@ -1664,7 +1659,8 @@ func (p *parser) methodDecl() *Field {
                        pos := p.pos()
                        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 {
                                // name[]
                                pos := p.pos()
@@ -1684,7 +1680,21 @@ func (p *parser) methodDecl() *Field {
                        // A type argument list looks like a parameter list with only
                        // types. Parse a parameter list and decide afterwards.
                        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
                                f.Name = name
                                f.Type = p.funcType()
@@ -1696,15 +1706,22 @@ func (p *parser) methodDecl() *Field {
                        }
 
                        // embedded instantiated type
-                       call := new(CallExpr)
-                       call.pos = pos
-                       call.Fun = name
-                       call.Brackets = true
-                       call.ArgList = make([]Expr, len(list))
-                       for i := range list {
-                               call.ArgList[i] = list[i].Type
+                       t := new(IndexExpr)
+                       t.pos = pos
+                       t.X = name
+                       if len(list) == 1 {
+                               t.Index = list[0].Type
+                       } else {
+                               // 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
                }
                fallthrough
@@ -1733,8 +1750,8 @@ func (p *parser) paramDeclOrNil(name *Name) *Field {
 
                if p.mode&AllowGenerics != 0 && p.tok == _Lbrack {
                        f.Type = p.arrayOrTArgs()
-                       if typ, ok := f.Type.(*CallExpr); ok {
-                               typ.Fun = name
+                       if typ, ok := f.Type.(*IndexExpr); ok {
+                               typ.X = name
                        } else {
                                f.Name = name
                        }
@@ -2427,22 +2444,20 @@ func (p *parser) stmtList() (l []Stmt) {
        return
 }
 
-// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
-func (p *parser) argList(arg Expr, close token) (list []Expr, hasDots bool) {
+// argList parses a possibly empty, comma-separated list of arguments,
+// 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 {
                defer p.trace("argList")()
        }
 
        p.xnest++
-       p.list(_Comma, close, func() bool {
-               if arg == nil {
-                       arg = p.expr()
-               }
-               list = append(list, arg)
-               arg = nil
-               if close == _Rparen {
-                       hasDots = p.got(_DotDotDot)
-               }
+       p.list(_Comma, _Rparen, func() bool {
+               list = append(list, p.expr())
+               hasDots = p.got(_DotDotDot)
                return hasDots
        })
        p.xnest--
@@ -2548,6 +2563,41 @@ func (p *parser) exprList() Expr {
        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.
 func unparen(x Expr) Expr {
        for {
index e27087973912b7c9a4ba91d605d4ddf4c9b6bdcc..70651efeae4e15b001cb67502a5f487d53b97ae0 100644 (file)
@@ -26,7 +26,7 @@ var (
 )
 
 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) {