]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/gc: fix incorrect parsing of &(T{}) when followed by {
authorRobert Griesemer <gri@golang.org>
Wed, 18 Nov 2015 18:57:27 +0000 (10:57 -0800)
committerRobert Griesemer <gri@golang.org>
Wed, 18 Nov 2015 22:25:48 +0000 (22:25 +0000)
Handling of &(T{}) assumed that the parser would not introduce ()'s.

Also: Better comments around handling of OPAREN syntax tree optimization.

Fixes #13261.

Change-Id: Ifc5047a0448f5e7d74cd42f6608b87dcc9c2f2fb
Reviewed-on: https://go-review.googlesource.com/17040
Reviewed-by: Chris Manghane <cmang@golang.org>
src/cmd/compile/internal/gc/parser.go
test/fixedbugs/issue13261.go [new file with mode: 0644]

index bb2799d2e4ea1284bf9456401211279803d2a858..a3ecf92c08f29e9a064a4b6d0ed9706580ad8948 100644 (file)
@@ -1276,6 +1276,13 @@ func (p *parser) expr() *Node {
        return p.bexpr(1)
 }
 
+func unparen(x *Node) *Node {
+       for x.Op == OPAREN {
+               x = x.Left
+       }
+       return x
+}
+
 // go.y:uexpr
 func (p *parser) uexpr() *Node {
        if trace && Debug['x'] != 0 {
@@ -1289,7 +1296,9 @@ func (p *parser) uexpr() *Node {
 
        case '&':
                p.next()
-               x := p.uexpr()
+               // uexpr may have returned a parenthesized composite literal
+               // (see comment in operand) - remove parentheses if any
+               x := unparen(p.uexpr())
                if x.Op == OCOMPLIT {
                        // Special case for &T{...}: turn into (*T){...}.
                        x.Right = Nod(OIND, x.Right, nil)
@@ -1349,6 +1358,7 @@ func (p *parser) uexpr() *Node {
                                        // t is type <-chan E but <-<-chan E is not permitted
                                        // (report same error as for "type _ <-<-chan E")
                                        p.syntax_error("unexpected <-, expecting chan")
+                                       // already progressed, no need to advance
                                }
                                t.Etype = Crecv
                        }
@@ -1356,6 +1366,7 @@ func (p *parser) uexpr() *Node {
                                // channel dir is <- but channel element E is not a channel
                                // (report same error as for "type _ <-chan<-E")
                                p.syntax_error(fmt.Sprintf("unexpected %v, expecting chan", t))
+                               // already progressed, no need to advance
                        }
                        return x
                }
@@ -1380,7 +1391,9 @@ func (p *parser) pseudocall() *Node {
                defer p.trace("pseudocall")()
        }
 
-       x := p.pexpr(true)
+       // The expression in go/defer must not be parenthesized;
+       // don't drop ()'s so we can report an error.
+       x := p.pexpr(true /* keep_parens */)
        if x.Op != OCALL {
                Yyerror("argument to go/defer must be function call")
        }
@@ -1409,25 +1422,29 @@ func (p *parser) operand(keep_parens bool) *Node {
                p.nest--
                p.want(')')
 
-               // Need to know on lhs of := whether there are ( ).
-               // Don't bother with the OPAREN in other cases:
-               // it's just a waste of memory and time.
-               //
-               // But if the next token is a { , introduce OPAREN since
-               // we may have a composite literal and we need to know
-               // if there were ()'s'.
-               //
-               // TODO(gri) could simplify this if we parse complits
-               // in operand (see respective comment in pexpr).
-               //
-               // (We can probably not do this because of qualified types
-               // as in pkg.Type{}) (issue 13243).
-               if keep_parens || p.tok == '{' {
-                       return Nod(OPAREN, x, nil)
-               }
+               // Optimization: Record presence of ()'s only where needed
+               // for error reporting. Don't bother in other cases; it is
+               // just a waste of memory and time.
+
+               // Parentheses are not permitted on lhs of := .
                switch x.Op {
                case ONAME, ONONAME, OPACK, OTYPE, OLITERAL, OTYPESW:
-                       return Nod(OPAREN, x, nil)
+                       keep_parens = true
+               }
+
+               // Parentheses are not permitted around T in a composite
+               // literal T{}. If the next token is a {, assume x is a
+               // composite literal type T (it may not be, { could be
+               // the opening brace of a block, but we don't know yet).
+               if p.tok == '{' {
+                       keep_parens = true
+               }
+
+               // Parentheses are also not permitted around the expression
+               // in a go/defer statement. In that case, operand is called
+               // with keep_parens set.
+               if keep_parens {
+                       x = Nod(OPAREN, x, nil)
                }
                return x
 
diff --git a/test/fixedbugs/issue13261.go b/test/fixedbugs/issue13261.go
new file mode 100644 (file)
index 0000000..c73062c
--- /dev/null
@@ -0,0 +1,29 @@
+// compile
+
+// Copyright 2015 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Taking the address of a parenthesized composite literal is permitted.
+
+package main
+
+type T struct{}
+
+func main() {
+       _ = &T{}
+       _ = &(T{})
+       _ = &((T{}))
+
+       _ = &struct{}{}
+       _ = &(struct{}{})
+       _ = &((struct{}{}))
+
+       switch (&T{}) {}
+       switch &(T{}) {}
+       switch &((T{})) {}
+
+       switch &struct{}{} {}
+       switch &(struct{}{}) {}
+       switch &((struct{}{})) {}
+}