From: Robert Griesemer Date: Thu, 18 Aug 2022 19:54:28 +0000 (-0700) Subject: go/parser: remove (internal) ability to disable generic code X-Git-Tag: go1.20rc1~1507 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=4a530b8837222dc926f72684f63fa4b5f476e31b;p=gostls13.git go/parser: remove (internal) ability to disable generic code Generics are part of the language now; there's no need anymore to switch back to a syntax without generics. Remove the associated machinery and adjust short tests accordingly. Change-Id: I6b16c5c75fd9354ee87e3b9bee110f49f514565a Reviewed-on: https://go-review.googlesource.com/c/go/+/424857 Reviewed-by: Robert Griesemer Run-TryBot: Robert Griesemer TryBot-Result: Gopher Robot Reviewed-by: Robert Findley --- diff --git a/src/go/internal/typeparams/common.go b/src/go/internal/typeparams/common.go deleted file mode 100644 index 9b82e6061a..0000000000 --- a/src/go/internal/typeparams/common.go +++ /dev/null @@ -1,15 +0,0 @@ -// 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 typeparams provides functions to work with type parameter data -// stored in the AST, while these AST changes are guarded by a build -// constraint. -package typeparams - -// 'Hidden' parser modes to control the parsing of type-parameter related -// features. -const ( - DisallowTypeSets = 1 << 29 // Disallow eliding 'interface' in constraint type sets. - DisallowParsing = 1 << 30 // Disallow type parameters entirely. -) diff --git a/src/go/parser/error_test.go b/src/go/parser/error_test.go index c3a8ec6ad8..c2b9ca0b72 100644 --- a/src/go/parser/error_test.go +++ b/src/go/parser/error_test.go @@ -24,7 +24,6 @@ package parser import ( "flag" - "go/internal/typeparams" "go/scanner" "go/token" "os" @@ -189,9 +188,6 @@ func TestErrors(t *testing.T) { t.Run(name, func(t *testing.T) { if !d.IsDir() && !strings.HasPrefix(name, ".") && (strings.HasSuffix(name, ".src") || strings.HasSuffix(name, ".go2")) { mode := DeclarationErrors | AllErrors - if !strings.HasSuffix(name, ".go2") { - mode |= typeparams.DisallowParsing - } if *traceErrs { mode |= Trace } diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index 564846a2e8..c55b026005 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -76,9 +76,6 @@ func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mod p.next() } -func (p *parser) allowGenerics() bool { return p.mode&typeparams.DisallowParsing == 0 } -func (p *parser) allowTypeSets() bool { return p.mode&typeparams.DisallowTypeSets == 0 } - // ---------------------------------------------------------------------------- // Parsing support @@ -516,7 +513,7 @@ func (p *parser) parseQualifiedIdent(ident *ast.Ident) ast.Expr { } typ := p.parseTypeName(ident) - if p.tok == token.LBRACK && p.allowGenerics() { + if p.tok == token.LBRACK { typ = p.parseTypeInstance(typ) } @@ -582,22 +579,12 @@ func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident) (*ast.Ident, ast.Ex // list such as T[P,]? (We do in parseTypeInstance). lbrack := p.expect(token.LBRACK) var args []ast.Expr - var firstComma token.Pos - // TODO(rfindley): consider changing parseRhsOrType so that this function variable - // is not needed. - argparser := p.parseRhsOrType - if !p.allowGenerics() { - argparser = p.parseRhs - } if p.tok != token.RBRACK { p.exprLev++ - args = append(args, argparser()) + args = append(args, p.parseRhsOrType()) for p.tok == token.COMMA { - if !firstComma.IsValid() { - firstComma = p.pos - } p.next() - args = append(args, argparser()) + args = append(args, p.parseRhsOrType()) } p.exprLev-- } @@ -616,15 +603,6 @@ func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident) (*ast.Ident, ast.Ex // x [P]E return x, &ast.ArrayType{Lbrack: lbrack, Len: args[0], Elt: elt} } - if !p.allowGenerics() { - p.error(rbrack, "missing element type in array type expression") - return nil, &ast.BadExpr{From: args[0].Pos(), To: args[0].End()} - } - } - - if !p.allowGenerics() { - p.error(firstComma, "expected ']', found ','") - return x, &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()} } // x[P], x[P1, P2], ... @@ -866,7 +844,7 @@ func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing tok // Type parameters are the only parameter list closed by ']'. tparams := closing == token.RBRACK // Type set notation is ok in type parameter lists. - typeSetsOK := tparams && p.allowTypeSets() + typeSetsOK := tparams pos := p.pos if name0 != nil { @@ -992,7 +970,7 @@ func (p *parser) parseParameters(acceptTParams bool) (tparams, params *ast.Field defer un(trace(p, "Parameters")) } - if p.allowGenerics() && acceptTParams && p.tok == token.LBRACK { + if acceptTParams && p.tok == token.LBRACK { opening := p.pos p.next() // [T any](params) syntax @@ -1065,7 +1043,7 @@ func (p *parser) parseMethodSpec() *ast.Field { x := p.parseTypeName(nil) if ident, _ := x.(*ast.Ident); ident != nil { switch { - case p.tok == token.LBRACK && p.allowGenerics(): + case p.tok == token.LBRACK: // generic method or embedded instantiated type lbrack := p.pos p.next() @@ -1123,7 +1101,7 @@ func (p *parser) parseMethodSpec() *ast.Field { } else { // embedded, possibly instantiated type typ = x - if p.tok == token.LBRACK && p.allowGenerics() { + if p.tok == token.LBRACK { // embedded instantiated interface typ = p.parseTypeInstance(typ) } @@ -1194,18 +1172,18 @@ parseElements: switch { case p.tok == token.IDENT: f := p.parseMethodSpec() - if f.Names == nil && p.allowGenerics() { + if f.Names == nil { f.Type = p.embeddedElem(f.Type) } p.expectSemi() f.Comment = p.lineComment list = append(list, f) - case p.tok == token.TILDE && p.allowGenerics(): + case p.tok == token.TILDE: typ := p.embeddedElem(nil) p.expectSemi() comment := p.lineComment list = append(list, &ast.Field{Type: typ, Comment: comment}) - case p.allowGenerics(): + default: if t := p.tryIdentOrType(); t != nil { typ := p.embeddedElem(t) p.expectSemi() @@ -1214,8 +1192,6 @@ parseElements: } else { break parseElements } - default: - break parseElements } } @@ -1273,7 +1249,6 @@ func (p *parser) parseChanType() *ast.ChanType { } func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr { - assert(p.allowGenerics(), "parseTypeInstance while not parsing type params") if p.trace { defer un(trace(p, "TypeInstance")) } @@ -1311,7 +1286,7 @@ func (p *parser) tryIdentOrType() ast.Expr { switch p.tok { case token.IDENT: typ := p.parseTypeName(nil) - if p.tok == token.LBRACK && p.allowGenerics() { + if p.tok == token.LBRACK { typ = p.parseTypeInstance(typ) } return typ @@ -1500,7 +1475,6 @@ func (p *parser) parseIndexOrSliceOrInstance(x ast.Expr) ast.Expr { var args []ast.Expr var index [N]ast.Expr var colons [N - 1]token.Pos - var firstComma token.Pos if p.tok != token.COLON { // We can't know if we have an index expression or a type instantiation; // so even if we see a (named) type we are not going to be in type context. @@ -1519,7 +1493,6 @@ func (p *parser) parseIndexOrSliceOrInstance(x ast.Expr) ast.Expr { } } case token.COMMA: - firstComma = p.pos // instance expression args = append(args, index[0]) for p.tok == token.COMMA { @@ -1557,11 +1530,6 @@ func (p *parser) parseIndexOrSliceOrInstance(x ast.Expr) ast.Expr { return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack} } - if !p.allowGenerics() { - p.error(firstComma, "expected ']' or ':', found ','") - return &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()} - } - // instance expression return typeparams.PackIndexExpr(x, lbrack, args, rbrack) } @@ -2643,7 +2611,7 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token name := p.parseIdent() spec := &ast.TypeSpec{Doc: doc, Name: name} - if p.tok == token.LBRACK && p.allowGenerics() { + if p.tok == token.LBRACK { // spec.Name "[" ... // array/slice type or type parameter list lbrack := p.pos diff --git a/src/go/parser/resolver_test.go b/src/go/parser/resolver_test.go index 0c06c592d5..38739a33fe 100644 --- a/src/go/parser/resolver_test.go +++ b/src/go/parser/resolver_test.go @@ -7,7 +7,6 @@ package parser import ( "fmt" "go/ast" - "go/internal/typeparams" "go/scanner" "go/token" "os" @@ -41,9 +40,6 @@ func TestResolution(t *testing.T) { path := filepath.Join(dir, fi.Name()) src := readFile(path) // panics on failure var mode Mode - if !strings.HasSuffix(path, ".go2") { - mode |= typeparams.DisallowParsing - } file, err := ParseFile(fset, path, src, mode) if err != nil { t.Fatal(err) diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go index be8be6450c..6e28e23377 100644 --- a/src/go/parser/short_test.go +++ b/src/go/parser/short_test.go @@ -6,10 +6,7 @@ package parser -import ( - "go/internal/typeparams" - "testing" -) +import "testing" var valids = []string{ "package p\n", @@ -64,80 +61,65 @@ var valids = []string{ `package p; type _ struct{ f [n]E }`, `package p; type _ struct{ f [a+b+c+d]E }`, `package p; type I1 interface{}; type I2 interface{ I1 }`, -} -// validWithTParamsOnly holds source code examples that are valid if -// parseTypeParams is set, but invalid if not. When checking with the -// parseTypeParams set, errors are ignored. -var validWithTParamsOnly = []string{ - `package p; type _ []T[ /* ERROR "expected ';', found '\['" */ int]`, - `package p; type T[P any /* ERROR "expected ']', found any" */ ] struct { P }`, - `package p; type T[P comparable /* ERROR "expected ']', found comparable" */ ] struct { P }`, - `package p; type T[P comparable /* ERROR "expected ']', found comparable" */ [P]] struct { P }`, - `package p; type T[P1, /* ERROR "unexpected comma" */ P2 any] struct { P1; f []P2 }`, - `package p; func _[ /* ERROR "expected '\(', found '\['" */ T any]()()`, + // generic code + `package p; type _ []T[int]`, + `package p; type T[P any] struct { P }`, + `package p; type T[P comparable] struct { P }`, + `package p; type T[P comparable[P]] struct { P }`, + `package p; type T[P1, P2 any] struct { P1; f []P2 }`, + `package p; func _[T any]()()`, `package p; func _(T (P))`, - `package p; func f[ /* ERROR "expected '\(', found '\['" */ A, B any](); func _() { _ = f[int, int] }`, - `package p; func _(x /* ERROR "mixed named and unnamed parameters" */ T[P1, P2, P3])`, - `package p; func _(x /* ERROR "mixed named and unnamed parameters" */ p.T[Q])`, - `package p; func _(p.T[ /* ERROR "missing ',' in parameter list" */ Q])`, - `package p; type _[A interface /* ERROR "expected ']', found 'interface'" */ {},] struct{}`, - `package p; type _[A interface /* ERROR "expected ']', found 'interface'" */ {}] struct{}`, - `package p; type _[A, /* ERROR "unexpected comma" */ B any,] struct{}`, - `package p; type _[A, /* ERROR "unexpected comma" */ B any] struct{}`, - `package p; type _[A any /* ERROR "expected ']', found any" */,] struct{}`, - `package p; type _[A any /* ERROR "expected ']', found any" */ ]struct{}`, - `package p; type _[A any /* ERROR "expected ']', found any" */ ] struct{ A }`, - `package p; func _[ /* ERROR "expected '\(', found '\['" */ T any]()`, - `package p; func _[ /* ERROR "expected '\(', found '\['" */ T any](x T)`, - `package p; func _[ /* ERROR "expected '\(', found '\['" */ T1, T2 any](x T)`, - `package p; func _[ /* ERROR "expected '\(', found '\['" */ A, B any](a A) B`, - `package p; func _[ /* ERROR "expected '\(', found '\['" */ A, B C](a A) B`, - `package p; func _[ /* ERROR "expected '\(', found '\['" */ A, B C[A, B]](a A) B`, - - `package p; type _[A, /* ERROR "unexpected comma" */ B any] interface { _(a A) B }`, - `package p; type _[A, /* ERROR "unexpected comma" */ B C[A, B]] interface { _(a A) B }`, - `package p; func _[ /* ERROR "expected '\(', found '\['" */ T1, T2 interface{}](x T1) T2`, - `package p; func _[ /* ERROR "expected '\(', found '\['" */ T1 interface{ m() }, T2, T3 interface{}](x T1, y T3) T2`, - `package p; var _ = [ /* ERROR "expected expression" */ ]T[int]{}`, - `package p; var _ = [ /* ERROR "expected expression" */ 10]T[int]{}`, - `package p; var _ = func /* ERROR "expected expression" */ ()T[int]{}`, - `package p; var _ = map /* ERROR "expected expression" */ [T[int]]T[int]{}`, - `package p; var _ = chan /* ERROR "expected expression" */ T[int](x)`, - `package p; func _(_ T[ /* ERROR "missing ',' in parameter list" */ P], T P) T[P]`, - `package p; var _ T[ /* ERROR "expected ';', found '\['" */ chan int]`, - - // TODO(rfindley) this error message could be improved. - `package p; func (_ /* ERROR "mixed named and unnamed parameters" */ R[P]) _(x T)`, - `package p; func (_ /* ERROR "mixed named and unnamed parameters" */ R[ P, Q]) _(x T)`, - - `package p; func (R[P] /* ERROR "missing element type" */ ) _()`, - `package p; func _(T[P] /* ERROR "missing element type" */ )`, - `package p; func _(T[P1, /* ERROR "expected ']', found ','" */ P2, P3 ])`, - `package p; func _(T[P] /* ERROR "missing element type" */ ) T[P]`, - `package p; type _ struct{ T[P] /* ERROR "missing element type" */ }`, - `package p; type _ struct{ T[struct /* ERROR "expected expression" */ {a, b, c int}] }`, - `package p; type _ interface{int| /* ERROR "expected ';'" */ float32; bool; m(); string;}`, - `package p; type I1[T any /* ERROR "expected ']', found any" */ ] interface{}; type I2 interface{ I1[int] }`, - `package p; type I1[T any /* ERROR "expected ']', found any" */ ] interface{}; type I2[T any] interface{ I1[T] }`, - `package p; type _ interface { N[ /* ERROR "expected ';', found '\['" */ T] }`, - `package p; type T[P any /* ERROR "expected ']'" */ ] = T0`, + `package p; func f[A, B any](); func _() { _ = f[int, int] }`, + `package p; func _(x T[P1, P2, P3])`, + `package p; func _(x p.T[Q])`, + `package p; func _(p.T[Q])`, + `package p; type _[A interface{},] struct{}`, + `package p; type _[A interface{}] struct{}`, + `package p; type _[A, B any,] struct{}`, + `package p; type _[A, B any] struct{}`, + `package p; type _[A any,] struct{}`, + `package p; type _[A any]struct{}`, + `package p; type _[A any] struct{ A }`, + `package p; func _[T any]()`, + `package p; func _[T any](x T)`, + `package p; func _[T1, T2 any](x T)`, + `package p; func _[A, B any](a A) B`, + `package p; func _[A, B C](a A) B`, + `package p; func _[A, B C[A, B]](a A) B`, + + `package p; type _[A, B any] interface { _(a A) B }`, + `package p; type _[A, B C[A, B]] interface { _(a A) B }`, + `package p; func _[T1, T2 interface{}](x T1) T2`, + `package p; func _[T1 interface{ m() }, T2, T3 interface{}](x T1, y T3) T2`, + `package p; var _ = []T[int]{}`, + `package p; var _ = [10]T[int]{}`, + `package p; var _ = func()T[int]{}`, + `package p; var _ = map[T[int]]T[int]{}`, + `package p; var _ = chan T[int](x)`, + `package p; func _(_ T[P], T P) T[P]`, + `package p; var _ T[chan int]`, + + `package p; func (_ R[P]) _(x T)`, + `package p; func (_ R[ P, Q]) _(x T)`, + + `package p; func (R[P]) _()`, + `package p; func _(T[P])`, + `package p; func _(T[P1, P2, P3 ])`, + `package p; func _(T[P]) T[P]`, + `package p; type _ struct{ T[P]}`, + `package p; type _ struct{ T[struct{a, b, c int}] }`, + `package p; type _ interface{int|float32; bool; m(); string;}`, + `package p; type I1[T any] interface{}; type I2 interface{ I1[int] }`, + `package p; type I1[T any] interface{}; type I2[T any] interface{ I1[T] }`, + `package p; type _ interface { N[T] }`, + `package p; type T[P any] = T0`, } func TestValid(t *testing.T) { - t.Run("no tparams", func(t *testing.T) { - for _, src := range valids { - checkErrors(t, src, src, DeclarationErrors|AllErrors, false) - } - }) - t.Run("tparams", func(t *testing.T) { - for _, src := range valids { - checkErrors(t, src, src, DeclarationErrors|AllErrors, false) - } - for _, src := range validWithTParamsOnly { - checkErrors(t, src, src, DeclarationErrors|AllErrors, false) - } - }) + for _, src := range valids { + checkErrors(t, src, src, DeclarationErrors|AllErrors, false) + } } // TestSingle is useful to track down a problem with a single short test program. @@ -228,28 +210,8 @@ var invalids = []string{ // issue 13475 `package p; func f() { if true {} else ; /* ERROR "expected if statement or block" */ }`, `package p; func f() { if true {} else defer /* ERROR "expected if statement or block" */ f() }`, -} - -// invalidNoTParamErrs holds invalid source code examples annotated with the -// error messages produced when ParseTypeParams is not set. -var invalidNoTParamErrs = []string{ - `package p; type _[_ any /* ERROR "expected ']', found any" */ ] int; var _ = T[]{}`, - `package p; type T[P any /* ERROR "expected ']', found any" */ ] = T0`, - `package p; var _ func[ /* ERROR "expected '\(', found '\['" */ T any](T)`, - `package p; func _[ /* ERROR "expected '\(', found '\['" */ ]()`, - `package p; type _[A, /* ERROR "unexpected comma" */] struct{ A }`, - `package p; func _[ /* ERROR "expected '\(', found '\['" */ type P, *Q interface{}]()`, - - `package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B any](a A) B`, - `package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B C](a A) B`, - `package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B C[A, B]](a A) B`, - - `package p; func(*T[ /* ERROR "missing ',' in parameter list" */ e, e]) _()`, -} -// invalidTParamErrs holds invalid source code examples annotated with the -// error messages produced when ParseTypeParams is set. -var invalidTParamErrs = []string{ + // generic code `package p; type _[_ any] int; var _ = T[] /* ERROR "expected operand" */ {}`, `package p; var _ func[ /* ERROR "must have no type parameters" */ T any](T)`, `package p; func _[]/* ERROR "empty type parameter list" */()`, @@ -268,23 +230,7 @@ var invalidTParamErrs = []string{ } func TestInvalid(t *testing.T) { - t.Run("no tparams", func(t *testing.T) { - for _, src := range invalids { - checkErrors(t, src, src, DeclarationErrors|AllErrors|typeparams.DisallowParsing, true) - } - for _, src := range validWithTParamsOnly { - checkErrors(t, src, src, DeclarationErrors|AllErrors|typeparams.DisallowParsing, true) - } - for _, src := range invalidNoTParamErrs { - checkErrors(t, src, src, DeclarationErrors|AllErrors|typeparams.DisallowParsing, true) - } - }) - t.Run("tparams", func(t *testing.T) { - for _, src := range invalids { - checkErrors(t, src, src, DeclarationErrors|AllErrors, true) - } - for _, src := range invalidTParamErrs { - checkErrors(t, src, src, DeclarationErrors|AllErrors, true) - } - }) + for _, src := range invalids { + checkErrors(t, src, src, DeclarationErrors|AllErrors, true) + } } diff --git a/src/go/parser/testdata/typeparams.src b/src/go/parser/testdata/typeparams.src deleted file mode 100644 index 479cb96871..0000000000 --- a/src/go/parser/testdata/typeparams.src +++ /dev/null @@ -1,17 +0,0 @@ -// 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. - -// Test cases for error messages produced while parsing code that uses type -// parameters, without ParseTypeParams being enabled. - -package p - -type List[E any /* ERROR "expected ']', found any" */ ] []E - -type Pair[L, /* ERROR "unexpected comma" */ R any] struct { - Left L - Right R -} - -var _ = Pair[int, /* ERROR "expected ']' or ':', found ','" */ string]{} diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index db2ace5feb..8bd34ba39d 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -10,7 +10,6 @@ import ( "fmt" "go/ast" "go/importer" - "go/internal/typeparams" "go/parser" "go/token" "internal/testenv" @@ -57,12 +56,10 @@ func mustTypecheck(t testing.TB, path, source string, info *Info) string { // genericPkg is a prefix for packages that should be type checked with // generics. +// TODO(gri) remove this machinery now that all source accepts generics. const genericPkg = "package generic_" func modeForSource(src string) parser.Mode { - if !strings.HasPrefix(src, genericPkg) { - return typeparams.DisallowParsing - } return 0 } @@ -361,9 +358,7 @@ func TestTypesInfo(t *testing.T) { {genericPkg + `t1; type t[P any] int; var _ t[int]`, `t`, `generic_t1.t[P any]`}, {genericPkg + `t2; type t[P interface{}] int; var _ t[int]`, `t`, `generic_t2.t[P interface{}]`}, {genericPkg + `t3; type t[P, Q interface{}] int; var _ t[int, int]`, `t`, `generic_t3.t[P, Q interface{}]`}, - - // TODO (rFindley): compare with types2, which resolves the type broken_t4.t[P₁, Q₂ interface{m()}] here - {broken + `t4; type t[P, Q interface{ m() }] int; var _ t[int, int]`, `t`, `broken_t4.t`}, + {broken + `t4; type t[P, Q interface{ m() }] int; var _ t[int, int]`, `t`, `broken_t4.t[P, Q interface{m()}]`}, // instantiated types must be sanitized {genericPkg + `g0; type t[P any] int; var x struct{ f t[int] }; var _ = x.f`, `x.f`, `generic_g0.t[int]`},