cmd/compile/internal/gc: fix parsing of <-x (recv op vs recv-only chan)
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>
diff --git a/src/cmd/compile/internal/gc/parser.go b/src/cmd/compile/internal/gc/parser.go
index 4eb4339..bb2799d 100644
--- a/src/cmd/compile/internal/gc/parser.go
+++ b/src/cmd/compile/internal/gc/parser.go
@@ -89,7 +89,7 @@
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 @@
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 @@
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 @@
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
index 0000000..fa3815f
--- /dev/null
+++ b/test/fixedbugs/issue13273.go
@@ -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)
+}