From: Robert Griesemer Date: Sat, 30 Oct 2021 17:50:59 +0000 (-0700) Subject: cmd/compile/internal/syntax: fix parsing of array or slice constraint types X-Git-Tag: go1.18beta1~656 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=0bef30d28ac0b9654e5daef0fef731d7a9495214;p=gostls13.git cmd/compile/internal/syntax: fix parsing of array or slice constraint types 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 Run-TryBot: Robert Griesemer TryBot-Result: Go Bot Reviewed-by: Robert Findley --- diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go index af5a505cdb..9f02cb6c2c 100644 --- a/src/cmd/compile/internal/syntax/parser.go +++ b/src/cmd/compile/internal/syntax/parser.go @@ -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 { diff --git a/src/cmd/compile/internal/syntax/testdata/typeset.go2 b/src/cmd/compile/internal/syntax/testdata/typeset.go2 index 78d3fe1ae5..19b74f28ea 100644 --- a/src/cmd/compile/internal/syntax/testdata/typeset.go2 +++ b/src/cmd/compile/internal/syntax/testdata/typeset.go2 @@ -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