doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
allErrors = flag.Bool("e", false, "report all errors (not just the first 10 on different lines)")
- // allowTypeParams controls whether type parameters are allowed in the code
- // being formatted. It is enabled for go1.18 in gofmt_go1.18.go.
- allowTypeParams = false
-
// debugging
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
)
printerNormalizeNumbers = 1 << 30
)
-// parseTypeParams tells go/parser to parse type parameters. Must be kept in
-// sync with go/parser/interface.go.
-const parseTypeParams parser.Mode = 1 << 30
-
var (
fileSet = token.NewFileSet() // per process FileSet
exitCode = 0
if *allErrors {
parserMode |= parser.AllErrors
}
- if allowTypeParams {
- parserMode |= parseTypeParams
- }
}
func isGoFile(f fs.DirEntry) bool {
case scanner.EOF:
return ""
}
-
}
return ""
}
+var typeParamsEnabled = false
+
func runTest(t *testing.T, in, out string) {
// process flags
*simplifyAST = false
// fake flag - pretend input is from stdin
stdin = true
case "-G":
- // fake flag - allow parsing type parameters
- allowTypeParams = true
+ // fake flag - test is for generic code
+ if !typeParamsEnabled {
+ return
+ }
default:
t.Errorf("unrecognized flag name: %s", name)
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build go1.18
-// +build go1.18
+//go:build typeparams
+// +build typeparams
package main
func init() {
- allowTypeParams = true
+ typeParamsEnabled = true
}
Rparen token.Pos // position of ")"
}
- // A ListExpr node represents a list of expressions separated by commas.
- // ListExpr nodes are used as index in IndexExpr nodes representing type
- // or function instantiations with more than one type argument.
- ListExpr struct {
- ElemList []Expr
- }
-
// A StarExpr node represents an expression of the form "*" Expression.
// Semantically it could be a unary "*" expression, or a pointer type.
//
// Pointer types are represented via StarExpr nodes.
- // A FuncType node represents a function type.
- FuncType struct {
- Func token.Pos // position of "func" keyword (token.NoPos if there is no "func")
- TParams *FieldList // type parameters; or nil
- Params *FieldList // (incoming) parameters; non-nil
- Results *FieldList // (outgoing) results; or nil
- }
-
// An InterfaceType node represents an interface type.
InterfaceType struct {
Interface token.Pos // position of "interface" keyword
func (x *SliceExpr) Pos() token.Pos { return x.X.Pos() }
func (x *TypeAssertExpr) Pos() token.Pos { return x.X.Pos() }
func (x *CallExpr) Pos() token.Pos { return x.Fun.Pos() }
-func (x *ListExpr) Pos() token.Pos {
- if len(x.ElemList) > 0 {
- return x.ElemList[0].Pos()
- }
- return token.NoPos
-}
-func (x *StarExpr) Pos() token.Pos { return x.Star }
-func (x *UnaryExpr) Pos() token.Pos { return x.OpPos }
-func (x *BinaryExpr) Pos() token.Pos { return x.X.Pos() }
-func (x *KeyValueExpr) Pos() token.Pos { return x.Key.Pos() }
-func (x *ArrayType) Pos() token.Pos { return x.Lbrack }
-func (x *StructType) Pos() token.Pos { return x.Struct }
+func (x *StarExpr) Pos() token.Pos { return x.Star }
+func (x *UnaryExpr) Pos() token.Pos { return x.OpPos }
+func (x *BinaryExpr) Pos() token.Pos { return x.X.Pos() }
+func (x *KeyValueExpr) Pos() token.Pos { return x.Key.Pos() }
+func (x *ArrayType) Pos() token.Pos { return x.Lbrack }
+func (x *StructType) Pos() token.Pos { return x.Struct }
func (x *FuncType) Pos() token.Pos {
if x.Func.IsValid() || x.Params == nil { // see issue 3870
return x.Func
func (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 }
func (x *TypeAssertExpr) End() token.Pos { return x.Rparen + 1 }
func (x *CallExpr) End() token.Pos { return x.Rparen + 1 }
-func (x *ListExpr) End() token.Pos {
- if len(x.ElemList) > 0 {
- return x.ElemList[len(x.ElemList)-1].End()
- }
- return token.NoPos
-}
-func (x *StarExpr) End() token.Pos { return x.X.End() }
-func (x *UnaryExpr) End() token.Pos { return x.X.End() }
-func (x *BinaryExpr) End() token.Pos { return x.Y.End() }
-func (x *KeyValueExpr) End() token.Pos { return x.Value.End() }
-func (x *ArrayType) End() token.Pos { return x.Elt.End() }
-func (x *StructType) End() token.Pos { return x.Fields.End() }
+func (x *StarExpr) End() token.Pos { return x.X.End() }
+func (x *UnaryExpr) End() token.Pos { return x.X.End() }
+func (x *BinaryExpr) End() token.Pos { return x.Y.End() }
+func (x *KeyValueExpr) End() token.Pos { return x.Value.End() }
+func (x *ArrayType) End() token.Pos { return x.Elt.End() }
+func (x *StructType) End() token.Pos { return x.Fields.End() }
func (x *FuncType) End() token.Pos {
if x.Results != nil {
return x.Results.End()
func (*SliceExpr) exprNode() {}
func (*TypeAssertExpr) exprNode() {}
func (*CallExpr) exprNode() {}
-func (*ListExpr) exprNode() {}
func (*StarExpr) exprNode() {}
func (*UnaryExpr) exprNode() {}
func (*BinaryExpr) exprNode() {}
Values []Expr // initial values; or nil
Comment *CommentGroup // line comments; or nil
}
-
- // A TypeSpec node represents a type declaration (TypeSpec production).
- TypeSpec struct {
- Doc *CommentGroup // associated documentation; or nil
- Name *Ident // type name
- TParams *FieldList // type parameters; or nil
- Assign token.Pos // position of '=', if any
- Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
- Comment *CommentGroup // line comments; or nil
- }
)
// Pos and End implementations for spec nodes.
--- /dev/null
+// 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.
+
+//go:build !typeparams
+// +build !typeparams
+
+package ast
+
+import "go/token"
+
+type (
+ // A FuncType node represents a function type.
+ FuncType struct {
+ Func token.Pos // position of "func" keyword (token.NoPos if there is no "func")
+ Params *FieldList // (incoming) parameters; non-nil
+ Results *FieldList // (outgoing) results; or nil
+ }
+
+ // A TypeSpec node represents a type declaration (TypeSpec production).
+ TypeSpec struct {
+ Doc *CommentGroup // associated documentation; or nil
+ Name *Ident // type name
+ Assign token.Pos // position of '=', if any
+ Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
+ Comment *CommentGroup // line comments; or nil
+ }
+)
--- /dev/null
+// 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.
+
+//go:build typeparams
+// +build typeparams
+
+package ast
+
+import "go/token"
+
+type (
+ // A FuncType node represents a function type.
+ FuncType struct {
+ Func token.Pos // position of "func" keyword (token.NoPos if there is no "func")
+ TParams *FieldList // type parameters; or nil
+ Params *FieldList // (incoming) parameters; non-nil
+ Results *FieldList // (outgoing) results; or nil
+ }
+
+ // A TypeSpec node represents a type declaration (TypeSpec production).
+ TypeSpec struct {
+ Doc *CommentGroup // associated documentation; or nil
+ Name *Ident // type name
+ TParams *FieldList // type parameters; or nil
+ Assign token.Pos // position of '=', if any
+ Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
+ Comment *CommentGroup // line comments; or nil
+ }
+
+ // A ListExpr node represents a list of expressions separated by commas.
+ // ListExpr nodes are used as index in IndexExpr nodes representing type
+ // or function instantiations with more than one type argument.
+ ListExpr struct {
+ ElemList []Expr
+ }
+)
+
+func (*ListExpr) exprNode() {}
+func (x *ListExpr) Pos() token.Pos {
+ if len(x.ElemList) > 0 {
+ return x.ElemList[0].Pos()
+ }
+ return token.NoPos
+}
+func (x *ListExpr) End() token.Pos {
+ if len(x.ElemList) > 0 {
+ return x.ElemList[len(x.ElemList)-1].End()
+ }
+ return token.NoPos
+}
package ast
-import "fmt"
-
// A Visitor's Visit method is invoked for each node encountered by Walk.
// If the result visitor w is not nil, Walk visits each of the children
// of node with the visitor w, followed by a call of w.Visit(nil).
Walk(v, n.X)
Walk(v, n.Index)
- case *ListExpr:
- walkExprList(v, n.ElemList)
-
case *SliceExpr:
Walk(v, n.X)
if n.Low != nil {
Walk(v, n.Fields)
case *FuncType:
- if n.TParams != nil {
- Walk(v, n.TParams)
- }
+ walkFuncTypeParams(v, n)
if n.Params != nil {
Walk(v, n.Params)
}
Walk(v, n.Doc)
}
Walk(v, n.Name)
- if n.TParams != nil {
- Walk(v, n.TParams)
- }
+ walkTypeSpecParams(v, n)
Walk(v, n.Type)
if n.Comment != nil {
Walk(v, n.Comment)
}
default:
- panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n))
+ walkOtherNodes(v, n)
}
v.Visit(nil)
--- /dev/null
+// 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.
+
+//go:build !typeparams
+// +build !typeparams
+
+package ast
+
+import "fmt"
+
+func walkFuncTypeParams(v Visitor, n *FuncType) {}
+func walkTypeSpecParams(v Visitor, n *TypeSpec) {}
+
+func walkOtherNodes(v Visitor, n Node) {
+ panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n))
+}
--- /dev/null
+// 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.
+
+//go:build typeparams
+// +build typeparams
+
+package ast
+
+func walkFuncTypeParams(v Visitor, n *FuncType) {
+ if n.TParams != nil {
+ Walk(v, n.TParams)
+ }
+}
+
+func walkTypeSpecParams(v Visitor, n *TypeSpec) {
+ if n.TParams != nil {
+ Walk(v, n.TParams)
+ }
+}
+
+func walkOtherNodes(v Visitor, n Node) {
+ if e, ok := n.(*ast.ListExpr); ok {
+ if e != nil {
+ Walk(v, e)
+ }
+ } else {
+ panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n))
+ }
+}
< go/token
< go/scanner
< go/ast
+ < go/internal/typeparams
< go/parser;
FMT
--- /dev/null
+// 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
+
+// DisallowParsing is the numeric value of a parsing mode that disallows type
+// parameters. This only matters if the typeparams experiment is active, and
+// may be used for running tests that disallow generics.
+const DisallowParsing = 1 << 30
--- /dev/null
+// 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.
+
+//go:build !typeparams
+// +build !typeparams
+
+package typeparams
+
+import (
+ "go/ast"
+)
+
+const Enabled = false
+
+func PackExpr(list []ast.Expr) ast.Expr {
+ switch len(list) {
+ case 0:
+ return nil
+ case 1:
+ return list[0]
+ default:
+ // The parser should not attempt to pack multiple expressions into an
+ // IndexExpr if type params are disabled.
+ panic("multiple index expressions are unsupported without type params")
+ }
+}
+
+func UnpackExpr(expr ast.Expr) []ast.Expr {
+ return []ast.Expr{expr}
+}
+
+func Get(ast.Node) *ast.FieldList {
+ return nil
+}
+
+func Set(node ast.Node, params *ast.FieldList) {
+}
--- /dev/null
+// 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.
+
+//go:build typeparams
+// +build typeparams
+
+package typeparams
+
+import (
+ "fmt"
+ "go/ast"
+)
+
+const Enabled = true
+
+func PackExpr(list []ast.Expr) ast.Expr {
+ switch len(list) {
+ case 0:
+ return nil
+ case 1:
+ return list[0]
+ default:
+ return &ast.ListExpr{ElemList: list}
+ }
+}
+
+// TODO(gri) Should find a more efficient solution that doesn't
+// require introduction of a new slice for simple
+// expressions.
+func UnpackExpr(x ast.Expr) []ast.Expr {
+ if x, _ := x.(*ast.ListExpr); x != nil {
+ return x.ElemList
+ }
+ if x != nil {
+ return []ast.Expr{x}
+ }
+ return nil
+}
+
+func Get(n ast.Node) *ast.FieldList {
+ switch n := n.(type) {
+ case *ast.TypeSpec:
+ return n.TParams
+ case *ast.FuncType:
+ return n.TParams
+ default:
+ panic(fmt.Sprintf("node type %T has no type parameters", n))
+ }
+}
+
+func Set(n ast.Node, params *ast.FieldList) {
+ switch n := n.(type) {
+ case *ast.TypeSpec:
+ n.TParams = params
+ case *ast.FuncType:
+ n.TParams = params
+ default:
+ panic(fmt.Sprintf("node type %T has no type parameters", n))
+ }
+}
package parser
import (
+ "go/internal/typeparams"
"go/scanner"
"go/token"
"os"
if !d.IsDir() && !strings.HasPrefix(name, ".") && (strings.HasSuffix(name, ".src") || strings.HasSuffix(name, ".go2")) {
mode := DeclarationErrors | AllErrors
if strings.HasSuffix(name, ".go2") {
- mode |= parseTypeParams
+ if !typeparams.Enabled {
+ continue
+ }
+ } else {
+ mode |= typeparams.DisallowParsing
}
checkErrors(t, filepath.Join(testdata, name), nil, mode, true)
}
DeclarationErrors // report declaration errors
SpuriousErrors // same as AllErrors, for backward-compatibility
AllErrors = SpuriousErrors // report all errors (not just the first 10 on different lines)
-
- // parseTypeParams controls the parsing of type parameters. Must be
- // kept in sync with:
- // go/printer/printer_test.go
- // go/types/check_test.go
- // cmd/gofmt/gofmt.go
- parseTypeParams = 1 << 30
)
// ParseFile parses the source code of a single Go source file and returns
import (
"fmt"
"go/ast"
+ "go/internal/typeparams"
"go/scanner"
"go/token"
"strconv"
p.next()
}
+func (p *parser) parseTypeParams() bool {
+ return typeparams.Enabled && p.mode&typeparams.DisallowParsing == 0
+}
+
// ----------------------------------------------------------------------------
// Parsing support
}
typ := p.parseTypeName(ident)
- if p.tok == token.LBRACK && p.mode&parseTypeParams != 0 {
+ if p.tok == token.LBRACK && p.parseTypeParams() {
typ = p.parseTypeInstance(typ)
}
// TODO(rfindley): consider changing parseRhsOrType so that this function variable
// is not needed.
argparser := p.parseRhsOrType
- if p.mode&parseTypeParams == 0 {
+ if !p.parseTypeParams() {
argparser = p.parseRhs
}
if p.tok != token.RBRACK {
// x [P]E
return x, &ast.ArrayType{Lbrack: lbrack, Len: args[0], Elt: elt}
}
- if p.mode&parseTypeParams == 0 {
+ if !p.parseTypeParams() {
p.error(rbrack, "missing element type in array type expression")
return nil, &ast.BadExpr{From: args[0].Pos(), To: args[0].End()}
}
}
- if p.mode&parseTypeParams == 0 {
+ if !p.parseTypeParams() {
p.error(firstComma, "expected ']', found ','")
return x, &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()}
}
// x[P], x[P1, P2], ...
- return nil, &ast.IndexExpr{X: x, Lbrack: lbrack, Index: &ast.ListExpr{ElemList: args}, Rbrack: rbrack}
+ return nil, &ast.IndexExpr{X: x, Lbrack: lbrack, Index: typeparams.PackExpr(args), Rbrack: rbrack}
}
func (p *parser) parseFieldDecl() *ast.Field {
defer un(trace(p, "Parameters"))
}
- if p.mode&parseTypeParams != 0 && acceptTParams && p.tok == token.LBRACK {
+ if p.parseTypeParams() && acceptTParams && p.tok == token.LBRACK {
opening := p.pos
p.next()
// [T any](params) syntax
x := p.parseTypeName(nil)
if ident, _ := x.(*ast.Ident); ident != nil {
switch {
- case p.tok == token.LBRACK && p.mode&parseTypeParams != 0:
+ case p.tok == token.LBRACK && p.parseTypeParams():
// generic method or embedded instantiated type
lbrack := p.pos
p.next()
_, params := p.parseParameters(false)
results := p.parseResult()
idents = []*ast.Ident{ident}
- typ = &ast.FuncType{Func: token.NoPos, TParams: tparams, Params: params, Results: results}
+ typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results}
+ typeparams.Set(typ, tparams)
} else {
// embedded instantiated type
// TODO(rfindley) should resolve all identifiers in x.
p.exprLev--
}
rbrack := p.expectClosing(token.RBRACK, "type argument list")
- typ = &ast.IndexExpr{X: ident, Lbrack: lbrack, Index: &ast.ListExpr{ElemList: list}, Rbrack: rbrack}
+ typ = &ast.IndexExpr{X: ident, Lbrack: lbrack, Index: typeparams.PackExpr(list), Rbrack: rbrack}
}
case p.tok == token.LPAREN:
// ordinary method
} else {
// embedded, possibly instantiated type
typ = x
- if p.tok == token.LBRACK && p.mode&parseTypeParams != 0 {
+ if p.tok == token.LBRACK && p.parseTypeParams() {
// embedded instantiated interface
typ = p.parseTypeInstance(typ)
}
pos := p.expect(token.INTERFACE)
lbrace := p.expect(token.LBRACE)
var list []*ast.Field
- for p.tok == token.IDENT || p.mode&parseTypeParams != 0 && p.tok == token.TYPE {
+ for p.tok == token.IDENT || p.parseTypeParams() && p.tok == token.TYPE {
if p.tok == token.IDENT {
list = append(list, p.parseMethodSpec())
} else {
closing := p.expectClosing(token.RBRACK, "type argument list")
- return &ast.IndexExpr{X: typ, Lbrack: opening, Index: &ast.ListExpr{ElemList: list}, Rbrack: closing}
+ return &ast.IndexExpr{X: typ, Lbrack: opening, Index: typeparams.PackExpr(list), Rbrack: closing}
}
func (p *parser) tryIdentOrType() ast.Expr {
switch p.tok {
case token.IDENT:
typ := p.parseTypeName(nil)
- if p.tok == token.LBRACK && p.mode&parseTypeParams != 0 {
+ if p.tok == token.LBRACK && p.parseTypeParams() {
typ = p.parseTypeInstance(typ)
}
return typ
return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}
}
- if p.mode&parseTypeParams == 0 {
+ if !p.parseTypeParams() {
p.error(firstComma, "expected ']' or ':', found ','")
return &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()}
}
// instance expression
- return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: &ast.ListExpr{ElemList: args}, Rbrack: rbrack}
+ return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: typeparams.PackExpr(args), Rbrack: rbrack}
}
func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
func (p *parser) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 *ast.Ident, closeTok token.Token) {
list := p.parseParameterList(name0, closeTok, p.parseParamDecl, true)
closePos := p.expect(closeTok)
- spec.TParams = &ast.FieldList{Opening: openPos, List: list, Closing: closePos}
+ typeparams.Set(spec, &ast.FieldList{Opening: openPos, List: list, Closing: closePos})
// Type alias cannot have type parameters. Accept them for robustness but complain.
if p.tok == token.ASSIGN {
p.error(p.pos, "generic type cannot be alias")
p.exprLev++
x := p.parseExpr()
p.exprLev--
- if name0, _ := x.(*ast.Ident); p.mode&parseTypeParams != 0 && name0 != nil && p.tok != token.RBRACK {
+ if name0, _ := x.(*ast.Ident); p.parseTypeParams() && name0 != nil && p.tok != token.RBRACK {
// generic type [T any];
p.parseGenericType(spec, lbrack, name0, token.RBRACK)
} else {
Name: ident,
Type: &ast.FuncType{
Func: pos,
- TParams: tparams,
Params: params,
Results: results,
},
Body: body,
}
+ typeparams.Set(decl.Type, tparams)
return decl
}
import (
"fmt"
"go/ast"
+ "go/internal/typeparams"
"go/token"
)
// at the identifier in the TypeSpec and ends at the end of the innermost
// containing block.
r.declare(spec, nil, r.topScope, ast.Typ, spec.Name)
- if spec.TParams != nil {
+ if tparams := typeparams.Get(spec); tparams != nil {
r.openScope(spec.Pos())
defer r.closeScope()
- r.walkFieldList(r.topScope, spec.TParams, ast.Typ)
+ r.walkFieldList(r.topScope, tparams, ast.Typ)
}
ast.Walk(r, spec.Type)
}
}
func (r *resolver) walkFuncType(scope *ast.Scope, typ *ast.FuncType) {
- r.walkFieldList(scope, typ.TParams, ast.Typ)
r.walkFieldList(scope, typ.Params, ast.Var)
r.walkFieldList(scope, typ.Results, ast.Var)
}
import (
"fmt"
"go/ast"
+ "go/internal/typeparams"
"go/scanner"
"go/token"
"os"
src := readFile(path) // panics on failure
var mode Mode
if strings.HasSuffix(path, ".go2") {
- mode = parseTypeParams
+ if !typeparams.Enabled {
+ t.Skip("type params are not enabled")
+ }
+ } else {
+ mode |= typeparams.DisallowParsing
}
file, err := ParseFile(fset, path, src, mode)
if err != nil {
package parser
-import "testing"
+import (
+ "go/internal/typeparams"
+ "testing"
+)
var valids = []string{
"package p\n",
}
})
t.Run("tparams", func(t *testing.T) {
+ if !typeparams.Enabled {
+ t.Skip("type params are not enabled")
+ }
for _, src := range valids {
- checkErrors(t, src, src, DeclarationErrors|AllErrors|parseTypeParams, false)
+ checkErrors(t, src, src, DeclarationErrors|AllErrors, false)
}
for _, src := range validWithTParamsOnly {
- checkErrors(t, src, src, DeclarationErrors|AllErrors|parseTypeParams, false)
+ checkErrors(t, src, src, DeclarationErrors|AllErrors, false)
}
})
}
// TestSingle is useful to track down a problem with a single short test program.
func TestSingle(t *testing.T) {
- const src = `package p; var _ = T[P]{}`
- checkErrors(t, src, src, DeclarationErrors|AllErrors|parseTypeParams, true)
+ const src = `package p; var _ = T{}`
+ checkErrors(t, src, src, DeclarationErrors|AllErrors, true)
}
var invalids = []string{
func TestInvalid(t *testing.T) {
t.Run("no tparams", func(t *testing.T) {
for _, src := range invalids {
- checkErrors(t, src, src, DeclarationErrors|AllErrors, true)
+ checkErrors(t, src, src, DeclarationErrors|AllErrors|typeparams.DisallowParsing, true)
}
for _, src := range validWithTParamsOnly {
- checkErrors(t, src, src, DeclarationErrors|AllErrors, true)
+ checkErrors(t, src, src, DeclarationErrors|AllErrors|typeparams.DisallowParsing, true)
}
for _, src := range invalidNoTParamErrs {
- checkErrors(t, src, src, DeclarationErrors|AllErrors, true)
+ checkErrors(t, src, src, DeclarationErrors|AllErrors|typeparams.DisallowParsing, true)
}
})
t.Run("tparams", func(t *testing.T) {
+ if !typeparams.Enabled {
+ t.Skip("type params are not enabled")
+ }
for _, src := range invalids {
- checkErrors(t, src, src, DeclarationErrors|AllErrors|parseTypeParams, true)
+ checkErrors(t, src, src, DeclarationErrors|AllErrors, true)
}
for _, src := range invalidTParamErrs {
- checkErrors(t, src, src, DeclarationErrors|AllErrors|parseTypeParams, true)
+ checkErrors(t, src, src, DeclarationErrors|AllErrors, true)
}
})
}
import (
"bytes"
"go/ast"
+ "go/internal/typeparams"
"go/token"
"math"
"strconv"
}
func (p *printer) signature(sig *ast.FuncType) {
- if sig.TParams != nil {
- p.parameters(sig.TParams, true)
+ if tparams := typeparams.Get(sig); tparams != nil {
+ p.parameters(tparams, true)
}
if sig.Params != nil {
p.parameters(sig.Params, false)
// TODO(gri): should treat[] like parentheses and undo one level of depth
p.expr1(x.X, token.HighestPrec, 1)
p.print(x.Lbrack, token.LBRACK)
- if e, _ := x.Index.(*ast.ListExpr); e != nil {
- p.exprList(x.Lbrack, e.ElemList, depth+1, commaTerm, x.Rbrack, false)
+ // Note: we're a bit defensive here to handle the case of a ListExpr of
+ // length 1.
+ if list := typeparams.UnpackExpr(x.Index); len(list) > 0 {
+ if len(list) > 1 {
+ p.exprList(x.Lbrack, list, depth+1, commaTerm, x.Rbrack, false)
+ } else {
+ p.expr0(list[0], depth+1)
+ }
} else {
p.expr0(x.Index, depth+1)
}
case *ast.TypeSpec:
p.setComment(s.Doc)
p.expr(s.Name)
- if s.TParams != nil {
- p.parameters(s.TParams, true)
+ if tparams := typeparams.Get(s); tparams != nil {
+ p.parameters(tparams, true)
}
if n == 1 {
p.print(blank)
"flag"
"fmt"
"go/ast"
+ "go/internal/typeparams"
"go/parser"
"go/token"
"io"
"time"
)
-// parseTypeParams tells go/parser to parse type parameters. Must be kept in
-// sync with go/parser/interface.go.
-const parseTypeParams parser.Mode = 1 << 30
-
const (
dataDir = "testdata"
tabwidth = 8
// if any.
func format(src []byte, mode checkMode) ([]byte, error) {
// parse src
- parseMode := parser.ParseComments
- if mode&allowTypeParams != 0 {
- parseMode |= parseTypeParams
- }
- f, err := parser.ParseFile(fset, "", src, parseMode)
+ f, err := parser.ParseFile(fset, "", src, parser.ParseComments)
if err != nil {
return nil, fmt.Errorf("parse: %s\n%s", err, src)
}
// make sure formatted output is syntactically correct
res := buf.Bytes()
- if _, err := parser.ParseFile(fset, "", res, parseTypeParams); err != nil {
+ if _, err := parser.ParseFile(fset, "", res, parser.ParseComments); err != nil {
return nil, fmt.Errorf("re-parse: %s\n%s", err, buf.Bytes())
}
{"linebreaks.input", "linebreaks.golden", idempotent},
{"expressions.input", "expressions.golden", idempotent},
{"expressions.input", "expressions.raw", rawFormat | idempotent},
- {"declarations.input", "declarations.golden", allowTypeParams},
+ {"declarations.input", "declarations.golden", 0},
{"statements.input", "statements.golden", 0},
{"slow.input", "slow.golden", idempotent},
{"complit.input", "complit.x", export},
func TestFiles(t *testing.T) {
t.Parallel()
for _, e := range data {
+ if !typeparams.Enabled && e.mode&allowTypeParams != 0 {
+ continue
+ }
source := filepath.Join(dataDir, e.source)
golden := filepath.Join(dataDir, e.golden)
mode := e.mode
x ...int)
}
-// properly format one-line type lists
-type _ interface{ type a }
-
-type _ interface {
- type a, b, c
-}
-
// omit superfluous parentheses in parameter lists
func _(int)
func _(int)
y int
}) // no extra comma between } and )
-// type parameters
-func _[A, B any](a A, b B) int {}
-func _[T any](x, y T) T
-
// alias declarations
type c0 struct{}
x ...int)
}
-// properly format one-line type lists
-type _ interface { type a }
-
-type _ interface { type a,b,c }
-
// omit superfluous parentheses in parameter lists
func _((int))
func _((((((int))))))
y int
}) // no extra comma between } and )
-// type parameters
-func _[A, B any](a A, b B) int {}
-func _[T any](x, y T) T
-
// alias declarations
type c0 struct{}
package generics
+func _[A, B any](a A, b B) int {}
+func _[T any](x, y T) T
+
type T[P any] struct{}
type T[P1, P2, P3 any] struct{}
var _ []T[P]
_ = []T[P]{}
}
+
+// properly format one-line type lists
+type _ interface{ type a }
+
+type _ interface {
+ type a, b, c
+}
package generics
+func _[A, B any](a A, b B) int {}
+func _[T any](x, y T) T
+
type T[P any] struct{}
type T[P1, P2, P3 any] struct{}
var _ []T[P]
_ = []T[P]{}
}
+
+// properly format one-line type lists
+type _ interface { type a }
+
+type _ interface { type a,b,c }
"fmt"
"go/ast"
"go/importer"
+ "go/internal/typeparams"
"go/parser"
"go/token"
"internal/testenv"
const genericPkg = "package generic_"
func modeForSource(src string) parser.Mode {
- if strings.HasPrefix(src, genericPkg) {
- return parseTypeParams
+ if !strings.HasPrefix(src, genericPkg) {
+ return typeparams.DisallowParsing
}
return 0
}
}
for _, test := range tests {
+ if strings.HasPrefix(test.src, genericPkg) && !typeparams.Enabled {
+ continue
+ }
info := Info{Types: make(map[ast.Expr]TypeAndValue)}
var name string
if strings.HasPrefix(test.src, broken) {
}
for _, test := range tests {
+ if strings.HasPrefix(test.src, genericPkg) && !typeparams.Enabled {
+ continue
+ }
info := Info{
Defs: make(map[*ast.Ident]Object),
}
}
for _, test := range tests {
+ if strings.HasPrefix(test.src, genericPkg) && !typeparams.Enabled {
+ continue
+ }
info := Info{
Uses: make(map[*ast.Ident]Object),
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build go1.18
-// +build go1.18
+//go:build typeparams
+// +build typeparams
package types
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build go1.18
-// +build go1.18
+//go:build typeparams
+// +build typeparams
package types_test
}
}
-// unpack unpacks an *ast.ListExpr into a list of ast.Expr.
-// TODO(gri) Should find a more efficient solution that doesn't
-// require introduction of a new slice for simple
-// expressions.
-func unpackExpr(x ast.Expr) []ast.Expr {
- if x, _ := x.(*ast.ListExpr); x != nil {
- return x.ElemList
- }
- if x != nil {
- return []ast.Expr{x}
- }
- return nil
-}
-
func (check *Checker) shortVarDecl(pos positioner, lhs, rhs []ast.Expr) {
top := len(check.delayed)
scope := check.scope
import (
"go/ast"
+ "go/internal/typeparams"
"go/token"
"strings"
"unicode"
// funcInst type-checks a function instantiaton inst and returns the result in x.
// The operand x must be the evaluation of inst.X and its type must be a signature.
func (check *Checker) funcInst(x *operand, inst *ast.IndexExpr) {
- args, ok := check.exprOrTypeList(unpackExpr(inst.Index))
+ exprs := typeparams.UnpackExpr(inst.Index)
+ args, ok := check.exprOrTypeList(exprs)
if !ok {
x.mode = invalid
x.expr = inst
"fmt"
"go/ast"
"go/importer"
+ "go/internal/typeparams"
"go/parser"
"go/scanner"
"go/token"
. "go/types"
)
-// parseTypeParams tells go/parser to parse type parameters. Must be kept in
-// sync with go/parser/interface.go.
-const parseTypeParams parser.Mode = 1 << 30
-
var (
haltOnError = flag.Bool("halt", false, "halt on error")
listErrors = flag.Bool("errlist", false, "list errors")
mode := parser.AllErrors
if strings.HasSuffix(filenames[0], ".go2") {
- mode |= parseTypeParams
+ if !typeparams.Enabled {
+ t.Skip("type params are not enabled")
+ }
+ } else {
+ mode |= typeparams.DisallowParsing
}
// parse files and collect parser errors
"fmt"
"go/ast"
"go/constant"
+ "go/internal/typeparams"
"go/token"
)
})
alias := tdecl.Assign.IsValid()
- if alias && tdecl.TParams != nil {
+ if alias && typeparams.Get(tdecl) != nil {
// The parser will ensure this but we may still get an invalid AST.
// Complain and continue as regular type definition.
check.error(atPos(tdecl.Assign), 0, "generic type cannot be alias")
def.setUnderlying(named)
obj.typ = named // make sure recursive type declarations terminate
- if tdecl.TParams != nil {
+ if tparams := typeparams.Get(tdecl); tparams != nil {
check.openScope(tdecl, "type parameters")
defer check.closeScope()
- named.tparams = check.collectTypeParams(tdecl.TParams)
+ named.tparams = check.collectTypeParams(tparams)
}
// determine underlying type of named
"bytes"
"fmt"
"go/ast"
+ "go/internal/typeparams"
)
// ExprString returns the (possibly shortened) string representation for x.
case *ast.IndexExpr:
WriteExpr(buf, x.X)
buf.WriteByte('[')
- WriteExpr(buf, x.Index)
- buf.WriteByte(']')
-
- case *ast.ListExpr:
- for i, e := range x.ElemList {
+ exprs := typeparams.UnpackExpr(x.Index)
+ for i, e := range exprs {
if i > 0 {
buf.WriteString(", ")
}
WriteExpr(buf, e)
}
+ buf.WriteByte(']')
case *ast.SliceExpr:
WriteExpr(buf, x.X)
"fmt"
"go/ast"
"go/constant"
+ "go/internal/typeparams"
"go/token"
"sort"
"strconv"
if name == "main" {
code = _InvalidMainDecl
}
- if d.decl.Type.TParams != nil {
- check.softErrorf(d.decl.Type.TParams, code, "func %s must have no type parameters", name)
+ if tparams := typeparams.Get(d.decl.Type); tparams != nil {
+ check.softErrorf(tparams, code, "func %s must have no type parameters", name)
}
if t := d.decl.Type; t.Params.NumFields() != 0 || t.Results != nil {
// TODO(rFindley) Should this be a hard error?
if ptyp, _ := rtyp.(*ast.IndexExpr); ptyp != nil {
rtyp = ptyp.X
if unpackParams {
- for _, arg := range unpackExpr(ptyp.Index) {
+ for _, arg := range typeparams.UnpackExpr(ptyp.Index) {
var par *ast.Ident
switch arg := arg.(type) {
case *ast.Ident:
"fmt"
"go/ast"
"go/constant"
+ "go/internal/typeparams"
"go/token"
"sort"
"strconv"
return &new
}
case *ast.IndexExpr:
- index := isubst(n.Index, smap)
- if index != n.Index {
- new := *n
- new.Index = index
- return &new
- }
- case *ast.ListExpr:
- var elems []ast.Expr
- for i, elem := range n.ElemList {
+ elems := typeparams.UnpackExpr(n.Index)
+ var newElems []ast.Expr
+ for i, elem := range elems {
new := isubst(elem, smap)
if new != elem {
- if elems == nil {
- elems = make([]ast.Expr, len(n.ElemList))
- copy(elems, n.ElemList)
+ if newElems == nil {
+ newElems = make([]ast.Expr, len(elems))
+ copy(newElems, elems)
}
- elems[i] = new
+ newElems[i] = new
}
}
- if elems != nil {
+ if newElems != nil {
+ index := typeparams.PackExpr(newElems)
new := *n
- new.ElemList = elems
+ new.Index = index
return &new
}
case *ast.ParenExpr:
}
}
- if ftyp.TParams != nil {
- sig.tparams = check.collectTypeParams(ftyp.TParams)
+ if tparams := typeparams.Get(ftyp); tparams != nil {
+ sig.tparams = check.collectTypeParams(tparams)
// Always type-check method type parameters but complain that they are not allowed.
// (A separate check is needed when type-checking interface method signatures because
// they don't have a receiver specification.)
if recvPar != nil {
- check.errorf(ftyp.TParams, _Todo, "methods cannot have type parameters")
+ check.errorf(tparams, _Todo, "methods cannot have type parameters")
}
}
}
case *ast.IndexExpr:
- return check.instantiatedType(e.X, unpackExpr(e.Index), def)
+ exprs := typeparams.UnpackExpr(e.Index)
+ return check.instantiatedType(e.X, exprs, def)
case *ast.ParenExpr:
// Generic types must be instantiated before they can be used in any form.
// (This extra check is needed here because interface method signatures don't have
// a receiver specification.)
if sig.tparams != nil {
- check.errorf(f.Type.(*ast.FuncType).TParams, _Todo, "methods cannot have type parameters")
+ var at positioner = f.Type
+ if tparams := typeparams.Get(f.Type); tparams != nil {
+ at = tparams
+ }
+ check.errorf(at, _Todo, "methods cannot have type parameters")
}
// use named receiver type if available (for better error messages)