]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/gc: fix parsing of <-x (recv op vs recv-only chan)
authorRobert Griesemer <gri@golang.org>
Tue, 17 Nov 2015 01:27:32 +0000 (17:27 -0800)
committerRobert Griesemer <gri@golang.org>
Wed, 18 Nov 2015 22:22:56 +0000 (22:22 +0000)
Also:
- better error messages in some cases
- factored out function to produce syntax error at given line number

Fixes #13273.

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

index 4eb4339a26c7e4c1945c4c22aba815f62ae0732a..bb2799d2e4ea1284bf9456401211279803d2a858 100644 (file)
@@ -89,7 +89,7 @@ func (p *parser) got(tok int32) bool {
 
 func (p *parser) want(tok int32) {
        if !p.got(tok) {
-               p.syntax_error("")
+               p.syntax_error("expecting " + tokstring(tok))
                p.advance()
        }
 }
@@ -138,7 +138,16 @@ func (p *parser) syntax_error(msg string) {
                tok = tokstring(p.tok)
        }
 
-       Yyerror("syntax error: unexpected %s", tok + msg)
+       Yyerror("syntax error: unexpected %s", tok+msg)
+}
+
+// Like syntax_error, but reports error at given line rather than current lexer line.
+func (p *parser) syntax_error_at(lineno int32, msg string) {
+       defer func(lineno int32) {
+               lexlineno = lineno
+       }(lexlineno)
+       lexlineno = lineno
+       p.syntax_error(msg)
 }
 
 // Advance consumes tokens until it finds a token of the stoplist.
@@ -737,11 +746,8 @@ func (p *parser) labeled_stmt(label *Node) *Node {
                ls = p.stmt()
                if ls == missing_stmt {
                        // report error at line of ':' token
-                       saved := lexlineno
-                       lexlineno = prevlineno
-                       p.syntax_error("missing statement after label")
+                       p.syntax_error_at(prevlineno, "missing statement after label")
                        // we are already at the end of the labeled statement - no need to advance
-                       lexlineno = saved
                        return missing_stmt
                }
        }
@@ -1313,15 +1319,49 @@ func (p *parser) uexpr() *Node {
                op = OCOM
 
        case LCOMM:
-               // receive operation (<-s2) or receive-only channel type (<-chan s3)
+               // receive op (<-x) or receive-only channel (<-chan E)
                p.next()
-               if p.got(LCHAN) {
-                       // <-chan T
-                       t := Nod(OTCHAN, p.chan_elem(), nil)
-                       t.Etype = Crecv
-                       return t
+
+               // If the next token is LCHAN we still don't know if it is
+               // a channel (<-chan int) or a receive op (<-chan int(ch)).
+               // We only know once we have found the end of the uexpr.
+
+               x := p.uexpr()
+
+               // There are two cases:
+               //
+               //   <-chan...  => <-x is a channel type
+               //   <-x        => <-x is a receive operation
+               //
+               // In the first case, <- must be re-associated with
+               // the channel type parsed already:
+               //
+               //   <-(chan E)   =>  (<-chan E)
+               //   <-(chan<-E)  =>  (<-chan (<-E))
+
+               if x.Op == OTCHAN {
+                       // x is a channel type => re-associate <-
+                       dir := EType(Csend)
+                       t := x
+                       for ; t.Op == OTCHAN && dir == Csend; t = t.Left {
+                               dir = t.Etype
+                               if dir == Crecv {
+                                       // 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")
+                               }
+                               t.Etype = Crecv
+                       }
+                       if dir == Csend {
+                               // 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))
+                       }
+                       return x
                }
-               return Nod(ORECV, p.uexpr(), nil)
+
+               // x is not a channel type => we have a receive op
+               return Nod(ORECV, x, nil)
 
        default:
                return p.pexpr(false)
diff --git a/test/fixedbugs/issue13273.go b/test/fixedbugs/issue13273.go
new file mode 100644 (file)
index 0000000..fa3815f
--- /dev/null
@@ -0,0 +1,55 @@
+// errorcheck
+
+// 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.
+
+// Check that we correctly construct (and report errors)
+// for unary expressions of the form <-x where we only
+// know after parsing x whether <-x is a receive operation
+// or a channel type.
+
+package n
+
+func f() {
+       // test case from issue 13273
+       <-chan int((chan int)(nil))
+
+       <-chan int(nil)
+       <-chan chan int(nil)
+       <-chan chan chan int(nil)
+       <-chan chan chan chan int(nil)
+       <-chan chan chan chan chan int(nil)
+
+       <-chan<-chan int(nil)
+       <-chan<-chan<-chan int(nil)
+       <-chan<-chan<-chan<-chan int(nil)
+       <-chan<-chan<-chan<-chan<-chan int(nil)
+
+       <-chan (<-chan int)(nil)
+       <-chan (<-chan (<-chan int))(nil)
+       <-chan (<-chan (<-chan (<-chan int)))(nil)
+       <-chan (<-chan (<-chan (<-chan (<-chan int))))(nil)
+
+       <-(<-chan int)(nil)
+       <-(<-chan chan int)(nil)
+       <-(<-chan chan chan int)(nil)
+       <-(<-chan chan chan chan int)(nil)
+       <-(<-chan chan chan chan chan int)(nil)
+
+       <-(<-chan<-chan int)(nil)
+       <-(<-chan<-chan<-chan int)(nil)
+       <-(<-chan<-chan<-chan<-chan int)(nil)
+       <-(<-chan<-chan<-chan<-chan<-chan int)(nil)
+
+       <-(<-chan (<-chan int))(nil)
+       <-(<-chan (<-chan (<-chan int)))(nil)
+       <-(<-chan (<-chan (<-chan (<-chan int))))(nil)
+       <-(<-chan (<-chan (<-chan (<-chan (<-chan int)))))(nil)
+
+       type _ <-<-chan int // ERROR "unexpected <-, expecting chan"
+       <-<-chan int // ERROR "unexpected <-, expecting chan|expecting {" (new parser: same error as for type decl)
+
+       type _ <-chan<-int // ERROR "unexpected int, expecting chan|expecting chan"
+       <-chan<-int // ERROR "unexpected int, expecting chan|expecting {" (new parser: same error as for type decl)
+}