]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/syntax: fix parsing of array or slice constraint types
authorRobert Griesemer <gri@golang.org>
Sat, 30 Oct 2021 17:50:59 +0000 (10:50 -0700)
committerRobert Griesemer <gri@golang.org>
Sun, 31 Oct 2021 02:24:29 +0000 (02:24 +0000)
This is a port of the idea used in CL 359134 from go/parser to syntax,
with adjustments due to the slightly different structure of the two
parsers, and some refactoring to simplify the logic.

Fixes #49175.

Change-Id: Ib4955bde708f2b08345f35523e6094c03ab3076c
Reviewed-on: https://go-review.googlesource.com/c/go/+/360135
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/syntax/parser.go
src/cmd/compile/internal/syntax/testdata/typeset.go2

index af5a505cdbe642d4b8842aae81a07b1f9458a441..9f02cb6c2ca1a11a1f91e2f8697ce657485e1465 100644 (file)
@@ -586,42 +586,54 @@ func (p *parser) typeDecl(group *Group) Decl {
        d.Pragma = p.takePragma()
 
        d.Name = p.name()
-       if p.tok == _Lbrack {
-               // array/slice or generic type
-               // name "[" ...
+       if p.allowGenerics() && p.tok == _Lbrack {
+               // d.Name "[" ...
+               // array/slice or type parameter list
                pos := p.pos()
                p.next()
                switch p.tok {
-               case _Rbrack:
-                       // name "[" "]" ...
-                       p.next()
-                       d.Type = p.sliceType(pos)
                case _Name:
-                       // array or generic type
-                       // name "[" name ...
-                       p.xnest++
-                       // TODO(gri) p.expr may consume an opening "[" when it shouldn't (issue #49175)
-                       x := p.expr()
-                       p.xnest--
-                       if name0, ok := x.(*Name); p.allowGenerics() && ok && p.tok != _Rbrack {
-                               // generic type
-                               // name "[" name ...
-                               d.TParamList = p.paramList(name0, _Rbrack, true)
-                               pos := p.pos()
-                               if p.gotAssign() {
-                                       p.syntaxErrorAt(pos, "generic type cannot be alias")
+                       // d.Name "[" name ...
+                       // array or type parameter list
+                       name := p.name()
+                       // Index or slice expressions are never constant and thus invalid
+                       // array length expressions. Thus, if we see a "[" following name
+                       // we can safely assume that "[" name starts a type parameter list.
+                       var x Expr // x != nil means x is the array length expression
+                       if p.tok != _Lbrack {
+                               // d.Name "[" name ...
+                               // If we reach here, the next token is not a "[", and we need to
+                               // parse the expression starting with name. If that expression is
+                               // just that name, not followed by a "]" (in which case we might
+                               // have the array length "[" name "]"), we can also safely assume
+                               // a type parameter list.
+                               p.xnest++
+                               // To parse the expression starting with name, expand the call
+                               // sequence we would get by passing in name to parser.expr, and
+                               // pass in name to parser.pexpr.
+                               x = p.binaryExpr(p.pexpr(name, false), 0)
+                               p.xnest--
+                               if x == name && p.tok != _Rbrack {
+                                       x = nil
                                }
+                       }
+                       if x == nil {
+                               // d.Name "[" name ...
+                               // type parameter list
+                               d.TParamList = p.paramList(name, _Rbrack, true)
+                               d.Alias = p.gotAssign()
                                d.Type = p.typeOrNil()
                        } else {
+                               // d.Name "[" x "]" ...
                                // x is the array length expression
-                               // name "[" x ...
-                               if debug && x == nil {
-                                       panic("length expression is nil")
-                               }
                                d.Type = p.arrayType(pos, x)
                        }
+               case _Rbrack:
+                       // d.Name "[" "]" ...
+                       p.next()
+                       d.Type = p.sliceType(pos)
                default:
-                       // name "[" ...
+                       // d.Name "[" ...
                        d.Type = p.arrayType(pos, nil)
                }
        } else {
@@ -736,14 +748,16 @@ func (p *parser) expr() Expr {
                defer p.trace("expr")()
        }
 
-       return p.binaryExpr(0)
+       return p.binaryExpr(nil, 0)
 }
 
 // Expression = UnaryExpr | Expression binary_op Expression .
-func (p *parser) binaryExpr(prec int) Expr {
+func (p *parser) binaryExpr(x Expr, prec int) Expr {
        // don't trace binaryExpr - only leads to overly nested trace output
 
-       x := p.unaryExpr()
+       if x == nil {
+               x = p.unaryExpr()
+       }
        for (p.tok == _Operator || p.tok == _Star) && p.prec > prec {
                t := new(Operation)
                t.pos = p.pos()
@@ -751,7 +765,7 @@ func (p *parser) binaryExpr(prec int) Expr {
                tprec := p.prec
                p.next()
                t.X = x
-               t.Y = p.binaryExpr(tprec)
+               t.Y = p.binaryExpr(nil, tprec)
                x = t
        }
        return x
@@ -846,7 +860,7 @@ func (p *parser) unaryExpr() Expr {
        // TODO(mdempsky): We need parens here so we can report an
        // error for "(x) := true". It should be possible to detect
        // and reject that more efficiently though.
-       return p.pexpr(true)
+       return p.pexpr(nil, true)
 }
 
 // callStmt parses call-like statements that can be preceded by 'defer' and 'go'.
@@ -860,7 +874,7 @@ func (p *parser) callStmt() *CallStmt {
        s.Tok = p.tok // _Defer or _Go
        p.next()
 
-       x := p.pexpr(p.tok == _Lparen) // keep_parens so we can report error below
+       x := p.pexpr(nil, p.tok == _Lparen) // keep_parens so we can report error below
        if t := unparen(x); t != x {
                p.errorAt(x.Pos(), fmt.Sprintf("expression in %s must not be parenthesized", s.Tok))
                // already progressed, no need to advance
@@ -976,12 +990,14 @@ func (p *parser) operand(keep_parens bool) Expr {
 //                  "]" .
 // TypeAssertion  = "." "(" Type ")" .
 // Arguments      = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
-func (p *parser) pexpr(keep_parens bool) Expr {
+func (p *parser) pexpr(x Expr, keep_parens bool) Expr {
        if trace {
                defer p.trace("pexpr")()
        }
 
-       x := p.operand(keep_parens)
+       if x == nil {
+               x = p.operand(keep_parens)
+       }
 
 loop:
        for {
index 78d3fe1ae59a6d3b0f17b45c24a6846d5a1c5fcc..19b74f28eacdb8e24ceea65735167ec1c5bd5369 100644 (file)
@@ -44,17 +44,23 @@ type (
         _[_ t|~struct{}] t
         _[_ ~t|~struct{}] t
 
-        // TODO(gri) fix this (issue #49175)
-        // _[_ []t]t
+        // test cases for issue #49175
+        _[_ []t]t
+        _[_ [1]t]t
         _[_ ~[]t]t
+        _[_ ~[1]t]t
+        t [ /* ERROR type parameters must be named */ t[0]]t
 )
 
 // test cases for issue #49174
 func _[_ t]() {}
 func _[_ []t]() {}
+func _[_ [1]t]() {}
 func _[_ []t | t]() {}
+func _[_ [1]t | t]() {}
 func _[_ t | []t]() {}
 func _[_ []t | []t]() {}
+func _[_ [1]t | [1]t]() {}
 func _[_ t[t] | t[t]]() {}
 
 // Single-expression type parameter lists and those that don't start