From: Robert Findley Date: Wed, 27 Oct 2021 13:19:50 +0000 (-0400) Subject: go/parser: fix parsing of array or slice constraint types X-Git-Tag: go1.18beta1~707 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=a5a423e0e809e451b06771eb6a7b95cc9255f9fd;p=gostls13.git 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 Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- diff --git a/src/go/parser/error_test.go b/src/go/parser/error_test.go index f35ba0b501..a45c897da3 100644 --- a/src/go/parser/error_test.go +++ b/src/go/parser/error_test.go @@ -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) } }) diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index 4f7a498780..999663b98c 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -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 diff --git a/src/go/parser/testdata/issue49174.go2 b/src/go/parser/testdata/issue49174.go2 new file mode 100644 index 0000000000..77c195083f --- /dev/null +++ b/src/go/parser/testdata/issue49174.go2 @@ -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]() {} diff --git a/src/go/parser/testdata/issue49175.go2 b/src/go/parser/testdata/issue49175.go2 new file mode 100644 index 0000000000..a5ad30f14c --- /dev/null +++ b/src/go/parser/testdata/issue49175.go2 @@ -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