go/parser: fix parsing of array or slice constraint types

Now that we allow eliding 'interface' from constraint types, we need to
be a bit more careful about not consuming a '[' when parsing the next
expression after "type T [". We want to check if the next expression is
an identifier not followed by ']', in which case we're in a generic
type, but need to avoid parsing index or slice expressions. Such
expressions aren't valid array lengths because these expressions are
never constant, so when encountering a following '[' we can instead
assume that this is a type parameter field with array or slice type
constraint.

Test cases are added for the related issues #49174 and #49175, along
with a flag to enable tracing error tests.

For #49174
For #49175

Change-Id: I0476ef20c4c134ac537118272f20caaf123ee70e
Reviewed-on: https://go-review.googlesource.com/c/go/+/359134
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Robert Findley 2021-10-27 09:19:50 -04:00
parent 5c98bcb7d4
commit a5a423e0e8
4 changed files with 79 additions and 29 deletions

View File

@ -23,6 +23,7 @@
package parser
import (
"flag"
"go/internal/typeparams"
"go/scanner"
"go/token"
@ -33,6 +34,8 @@ import (
"testing"
)
var traceErrs = flag.Bool("trace_errs", false, "whether to enable tracing for error tests")
const testdata = "testdata"
// getFile assumes that each filename occurs at most once
@ -192,6 +195,9 @@ func TestErrors(t *testing.T) {
if !strings.HasSuffix(name, ".go2") {
mode |= typeparams.DisallowParsing
}
if *traceErrs {
mode |= Trace
}
checkErrors(t, filepath.Join(testdata, name), nil, mode, true)
}
})

View File

@ -455,10 +455,10 @@ func (p *parser) parseExprList() (list []ast.Expr) {
defer un(trace(p, "ExpressionList"))
}
list = append(list, p.checkExpr(p.parseExpr()))
list = append(list, p.checkExpr(p.parseExpr(nil)))
for p.tok == token.COMMA {
p.next()
list = append(list, p.checkExpr(p.parseExpr()))
list = append(list, p.checkExpr(p.parseExpr(nil)))
}
return
@ -996,7 +996,7 @@ func (p *parser) parseMethodSpec() *ast.Field {
lbrack := p.pos
p.next()
p.exprLev++
x := p.parseExpr()
x := p.parseExpr(nil)
p.exprLev--
if name0, _ := x.(*ast.Ident); name0 != nil && p.tok != token.COMMA && p.tok != token.RBRACK {
// generic method m[T any]
@ -1538,7 +1538,7 @@ func (p *parser) parseValue() ast.Expr {
return p.parseLiteralValue(nil)
}
x := p.checkExpr(p.parseExpr())
x := p.checkExpr(p.parseExpr(nil))
return x
}
@ -1648,12 +1648,14 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
return x
}
func (p *parser) parsePrimaryExpr() (x ast.Expr) {
func (p *parser) parsePrimaryExpr(x ast.Expr) ast.Expr {
if p.trace {
defer un(trace(p, "PrimaryExpr"))
}
x = p.parseOperand()
if x == nil {
x = p.parseOperand()
}
for {
switch p.tok {
case token.PERIOD:
@ -1689,18 +1691,18 @@ func (p *parser) parsePrimaryExpr() (x ast.Expr) {
switch t.(type) {
case *ast.BadExpr, *ast.Ident, *ast.SelectorExpr:
if p.exprLev < 0 {
return
return x
}
// x is possibly a composite literal type
case *ast.IndexExpr, *ast.IndexListExpr:
if p.exprLev < 0 {
return
return x
}
// x is possibly a composite literal type
case *ast.ArrayType, *ast.StructType, *ast.MapType:
// x is a composite literal type
default:
return
return x
}
if t != x {
p.error(t.Pos(), "cannot parenthesize type in composite literal")
@ -1708,7 +1710,7 @@ func (p *parser) parsePrimaryExpr() (x ast.Expr) {
}
x = p.parseLiteralValue(x)
default:
return
return x
}
}
}
@ -1779,7 +1781,7 @@ func (p *parser) parseUnaryExpr() ast.Expr {
return &ast.StarExpr{Star: pos, X: p.checkExprOrType(x)}
}
return p.parsePrimaryExpr()
return p.parsePrimaryExpr(nil)
}
func (p *parser) tokPrec() (token.Token, int) {
@ -1790,19 +1792,21 @@ func (p *parser) tokPrec() (token.Token, int) {
return tok, tok.Precedence()
}
func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
func (p *parser) parseBinaryExpr(x ast.Expr, prec1 int) ast.Expr {
if p.trace {
defer un(trace(p, "BinaryExpr"))
}
x := p.parseUnaryExpr()
if x == nil {
x = p.parseUnaryExpr()
}
for {
op, oprec := p.tokPrec()
if oprec < prec1 {
return x
}
pos := p.expect(op)
y := p.parseBinaryExpr(oprec + 1)
y := p.parseBinaryExpr(nil, oprec+1)
x = &ast.BinaryExpr{X: p.checkExpr(x), OpPos: pos, Op: op, Y: p.checkExpr(y)}
}
}
@ -1810,18 +1814,18 @@ func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
// The result may be a type or even a raw type ([...]int). Callers must
// check the result (using checkExpr or checkExprOrType), depending on
// context.
func (p *parser) parseExpr() ast.Expr {
func (p *parser) parseExpr(lhs ast.Expr) ast.Expr {
if p.trace {
defer un(trace(p, "Expression"))
}
return p.parseBinaryExpr(token.LowestPrec + 1)
return p.parseBinaryExpr(lhs, token.LowestPrec+1)
}
func (p *parser) parseRhs() ast.Expr {
old := p.inRhs
p.inRhs = true
x := p.checkExpr(p.parseExpr())
x := p.checkExpr(p.parseExpr(nil))
p.inRhs = old
return x
}
@ -1829,7 +1833,7 @@ func (p *parser) parseRhs() ast.Expr {
func (p *parser) parseRhsOrType() ast.Expr {
old := p.inRhs
p.inRhs = true
x := p.checkExprOrType(p.parseExpr())
x := p.checkExprOrType(p.parseExpr(nil))
p.inRhs = old
return x
}
@ -2539,6 +2543,10 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, _ token.Pos, keyword toke
}
func (p *parser) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 *ast.Ident) {
if p.trace {
defer un(trace(p, "parseGenericType"))
}
list := p.parseParameterList(name0, token.RBRACK)
closePos := p.expect(token.RBRACK)
spec.TypeParams = &ast.FieldList{Opening: openPos, List: list, Closing: closePos}
@ -2563,19 +2571,34 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token
lbrack := p.pos
p.next()
if p.tok == token.IDENT {
// array type or generic type [T any]
p.exprLev++
x := p.parseExpr()
p.exprLev--
if name0, _ := x.(*ast.Ident); p.allowGenerics() && name0 != nil && p.tok != token.RBRACK {
// generic type [T any];
// array type or generic type: [name0...
name0 := p.parseIdent()
if p.allowGenerics() && p.tok == token.LBRACK {
// Index or slice expressions are not valid array lengths, so we can
// parse as though we are in a generic type with array or slice
// constraint: [T [...
p.parseGenericType(spec, lbrack, name0)
break
} else {
// array type
// TODO(rfindley) should resolve all identifiers in x.
p.expect(token.RBRACK)
elt := p.parseType()
spec.Type = &ast.ArrayType{Lbrack: lbrack, Len: x, Elt: elt}
// We may still have either an array type or generic type -- check if
// name0 is the entire expr.
p.exprLev++
lhs := p.parsePrimaryExpr(name0)
x := p.parseExpr(lhs)
p.exprLev--
if name1, _ := x.(*ast.Ident); p.allowGenerics() && name1 != nil && p.tok != token.RBRACK {
// generic type [T any];
p.parseGenericType(spec, lbrack, name1)
} else {
// array type
// TODO(rfindley) should resolve all identifiers in x.
p.expect(token.RBRACK)
elt := p.parseType()
spec.Type = &ast.ArrayType{Lbrack: lbrack, Len: x, Elt: elt}
}
}
} else {
// array type

8
src/go/parser/testdata/issue49174.go2 vendored Normal file
View File

@ -0,0 +1,8 @@
// Copyright 2021 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.
package p
func _[_ []int | int]() {}
func _[_ int | []int]() {}

13
src/go/parser/testdata/issue49175.go2 vendored Normal file
View File

@ -0,0 +1,13 @@
// Copyright 2021 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.
package p
type _[_ []t]t
type _[_ [1]t]t
func _[_ []t]() {}
func _[_ [1]t]() {}
type t [t /* ERROR "all type parameters must be named" */ [0]]t