aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Findley <rfindley@google.com>2021-03-05 10:57:48 -0500
committerRobert Findley <rfindley@google.com>2021-04-13 22:24:31 +0000
commitefaf75a2166d397b2050cc0bb1b60b0c80698e2d (patch)
treec1ec333957953f7ed90c0883b85fe786fb3f7596
parent693859542e71fdd9186fff759bf121e9df197fed (diff)
downloadgo-efaf75a2166d397b2050cc0bb1b60b0c80698e2d.tar.gz
go-efaf75a2166d397b2050cc0bb1b60b0c80698e2d.zip
go/*,cmd/gofmt: guard AST changes with the typeparams build tag
This CL changes our approach to guarding type parameter functionality and API. Previously, we guarded type parameter functionality with the parser.parseTypeParams parser mode, and were in the process of hiding the type parameter API behind the go1.18 build constraint. These mechanisms had several limitations: + Requiring the parser.parseTypeParams mode to be set meant that existing tooling would have to opt-in to type parameters in all places where it parses Go files. + The parseTypeParams mode value had to be copied in several places. + go1.18 is not specific to typeparams, making it difficult to set up the builders to run typeparams tests. This CL addresses the above limitations, and completes the task of hiding the AST API, by switching to a new 'typeparams' build constraint and adding a new go/internal/typeparams helper package. The typeparams build constraint is used to conditionally compile the new AST changes. The typeparams package provides utilities for accessing and writing the new AST data, so that we don't have to fragment our parser or type checker logic across build constraints. The typeparams.Enabled const is used to guard tests that require type parameter support. The parseTypeParams parser mode is gone, replaced by a new typeparams.DisableParsing mode with the opposite sense. Now, type parameters are only parsed if go/parser is compiled with the typeparams build constraint set AND typeparams.DisableParsing not set. This new parser mode allows opting out of type parameter parsing for tests. How exactly to run tests on builders is left to a follow-up CL. Updates #44933 Change-Id: I3091e42a2e5e2f23e8b2ae584f415a784b9fbd65 Reviewed-on: https://go-review.googlesource.com/c/go/+/300649 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>
-rw-r--r--src/cmd/gofmt/gofmt.go11
-rw-r--r--src/cmd/gofmt/gofmt_test.go9
-rw-r--r--src/cmd/gofmt/gofmt_typeparams_test.go (renamed from src/cmd/gofmt/gofmt_go1.18.go)6
-rw-r--r--src/go/ast/ast.go62
-rw-r--r--src/go/ast/ast_notypeparams.go28
-rw-r--r--src/go/ast/ast_typeparams.go51
-rw-r--r--src/go/ast/walk.go15
-rw-r--r--src/go/ast/walk_notypeparams.go17
-rw-r--r--src/go/ast/walk_typeparams.go30
-rw-r--r--src/go/build/deps_test.go1
-rw-r--r--src/go/internal/typeparams/common.go13
-rw-r--r--src/go/internal/typeparams/notypeparams.go38
-rw-r--r--src/go/internal/typeparams/typeparams.go61
-rw-r--r--src/go/parser/error_test.go7
-rw-r--r--src/go/parser/interface.go7
-rw-r--r--src/go/parser/parser.go42
-rw-r--r--src/go/parser/resolver.go6
-rw-r--r--src/go/parser/resolver_test.go7
-rw-r--r--src/go/parser/short_test.go29
-rw-r--r--src/go/printer/nodes.go19
-rw-r--r--src/go/printer/printer_test.go18
-rw-r--r--src/go/printer/testdata/declarations.golden11
-rw-r--r--src/go/printer/testdata/declarations.input9
-rw-r--r--src/go/printer/testdata/generics.golden10
-rw-r--r--src/go/printer/testdata/generics.input8
-rw-r--r--src/go/types/api_test.go14
-rw-r--r--src/go/types/api_typeparams.go (renamed from src/go/types/api_go1.18.go)4
-rw-r--r--src/go/types/api_typeparams_test.go (renamed from src/go/types/api_go1.18_test.go)4
-rw-r--r--src/go/types/assignments.go14
-rw-r--r--src/go/types/call.go4
-rw-r--r--src/go/types/check_test.go11
-rw-r--r--src/go/types/decl.go7
-rw-r--r--src/go/types/exprstring.go9
-rw-r--r--src/go/types/resolver.go7
-rw-r--r--src/go/types/typexpr.go41
35 files changed, 417 insertions, 213 deletions
diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go
index cd867bba15..2793c2c2a4 100644
--- a/src/cmd/gofmt/gofmt.go
+++ b/src/cmd/gofmt/gofmt.go
@@ -33,10 +33,6 @@ var (
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")
)
@@ -53,10 +49,6 @@ const (
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
@@ -79,9 +71,6 @@ func initParserMode() {
if *allErrors {
parserMode |= parser.AllErrors
}
- if allowTypeParams {
- parserMode |= parseTypeParams
- }
}
func isGoFile(f fs.DirEntry) bool {
diff --git a/src/cmd/gofmt/gofmt_test.go b/src/cmd/gofmt/gofmt_test.go
index 9e2239a692..f0d3f8780f 100644
--- a/src/cmd/gofmt/gofmt_test.go
+++ b/src/cmd/gofmt/gofmt_test.go
@@ -49,12 +49,13 @@ func gofmtFlags(filename string, maxLines int) string {
case scanner.EOF:
return ""
}
-
}
return ""
}
+var typeParamsEnabled = false
+
func runTest(t *testing.T, in, out string) {
// process flags
*simplifyAST = false
@@ -78,8 +79,10 @@ func runTest(t *testing.T, in, out string) {
// 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)
}
diff --git a/src/cmd/gofmt/gofmt_go1.18.go b/src/cmd/gofmt/gofmt_typeparams_test.go
index be7b46b5ed..10641a77cb 100644
--- a/src/cmd/gofmt/gofmt_go1.18.go
+++ b/src/cmd/gofmt/gofmt_typeparams_test.go
@@ -2,11 +2,11 @@
// 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
}
diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go
index 6eb4d13f4d..c87529ec77 100644
--- a/src/go/ast/ast.go
+++ b/src/go/ast/ast.go
@@ -374,13 +374,6 @@ type (
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.
//
@@ -447,14 +440,6 @@ 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
@@ -497,18 +482,12 @@ func (x *IndexExpr) Pos() token.Pos { return x.X.Pos() }
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
@@ -536,18 +515,12 @@ func (x *IndexExpr) End() token.Pos { return x.Rbrack + 1 }
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()
@@ -573,7 +546,6 @@ func (*IndexExpr) exprNode() {}
func (*SliceExpr) exprNode() {}
func (*TypeAssertExpr) exprNode() {}
func (*CallExpr) exprNode() {}
-func (*ListExpr) exprNode() {}
func (*StarExpr) exprNode() {}
func (*UnaryExpr) exprNode() {}
func (*BinaryExpr) exprNode() {}
@@ -920,16 +892,6 @@ type (
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.
diff --git a/src/go/ast/ast_notypeparams.go b/src/go/ast/ast_notypeparams.go
new file mode 100644
index 0000000000..fa132fba85
--- /dev/null
+++ b/src/go/ast/ast_notypeparams.go
@@ -0,0 +1,28 @@
+// 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
+ }
+)
diff --git a/src/go/ast/ast_typeparams.go b/src/go/ast/ast_typeparams.go
new file mode 100644
index 0000000000..24fdc5f131
--- /dev/null
+++ b/src/go/ast/ast_typeparams.go
@@ -0,0 +1,51 @@
+// 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
+}
diff --git a/src/go/ast/walk.go b/src/go/ast/walk.go
index a57ff25ac5..9224264e29 100644
--- a/src/go/ast/walk.go
+++ b/src/go/ast/walk.go
@@ -4,8 +4,6 @@
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).
@@ -116,9 +114,6 @@ func Walk(v Visitor, node Node) {
Walk(v, n.X)
Walk(v, n.Index)
- case *ListExpr:
- walkExprList(v, n.ElemList)
-
case *SliceExpr:
Walk(v, n.X)
if n.Low != nil {
@@ -166,9 +161,7 @@ func Walk(v Visitor, node Node) {
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)
}
@@ -323,9 +316,7 @@ func Walk(v Visitor, node Node) {
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)
@@ -372,7 +363,7 @@ func Walk(v Visitor, node Node) {
}
default:
- panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n))
+ walkOtherNodes(v, n)
}
v.Visit(nil)
diff --git a/src/go/ast/walk_notypeparams.go b/src/go/ast/walk_notypeparams.go
new file mode 100644
index 0000000000..d43e13dd11
--- /dev/null
+++ b/src/go/ast/walk_notypeparams.go
@@ -0,0 +1,17 @@
+// 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))
+}
diff --git a/src/go/ast/walk_typeparams.go b/src/go/ast/walk_typeparams.go
new file mode 100644
index 0000000000..77267a5b8c
--- /dev/null
+++ b/src/go/ast/walk_typeparams.go
@@ -0,0 +1,30 @@
+// 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))
+ }
+}
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index a1a4324331..a1ed834c71 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -278,6 +278,7 @@ var depsRules = `
< go/token
< go/scanner
< go/ast
+ < go/internal/typeparams
< go/parser;
FMT
diff --git a/src/go/internal/typeparams/common.go b/src/go/internal/typeparams/common.go
new file mode 100644
index 0000000000..47b8f7cf02
--- /dev/null
+++ b/src/go/internal/typeparams/common.go
@@ -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 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
diff --git a/src/go/internal/typeparams/notypeparams.go b/src/go/internal/typeparams/notypeparams.go
new file mode 100644
index 0000000000..7bd62c9efe
--- /dev/null
+++ b/src/go/internal/typeparams/notypeparams.go
@@ -0,0 +1,38 @@
+// 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) {
+}
diff --git a/src/go/internal/typeparams/typeparams.go b/src/go/internal/typeparams/typeparams.go
new file mode 100644
index 0000000000..0332b6b816
--- /dev/null
+++ b/src/go/internal/typeparams/typeparams.go
@@ -0,0 +1,61 @@
+// 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))
+ }
+}
diff --git a/src/go/parser/error_test.go b/src/go/parser/error_test.go
index 3caa3571e6..5d45a7b817 100644
--- a/src/go/parser/error_test.go
+++ b/src/go/parser/error_test.go
@@ -23,6 +23,7 @@
package parser
import (
+ "go/internal/typeparams"
"go/scanner"
"go/token"
"os"
@@ -188,7 +189,11 @@ func TestErrors(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 |= parseTypeParams
+ if !typeparams.Enabled {
+ continue
+ }
+ } else {
+ mode |= typeparams.DisallowParsing
}
checkErrors(t, filepath.Join(testdata, name), nil, mode, true)
}
diff --git a/src/go/parser/interface.go b/src/go/parser/interface.go
index 8f306adaab..dcc5fa6616 100644
--- a/src/go/parser/interface.go
+++ b/src/go/parser/interface.go
@@ -56,13 +56,6 @@ const (
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
diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go
index 93ab4a4600..aff5838780 100644
--- a/src/go/parser/parser.go
+++ b/src/go/parser/parser.go
@@ -19,6 +19,7 @@ package parser
import (
"fmt"
"go/ast"
+ "go/internal/typeparams"
"go/scanner"
"go/token"
"strconv"
@@ -75,6 +76,10 @@ func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mod
p.next()
}
+func (p *parser) parseTypeParams() bool {
+ return typeparams.Enabled && p.mode&typeparams.DisallowParsing == 0
+}
+
// ----------------------------------------------------------------------------
// Parsing support
@@ -494,7 +499,7 @@ func (p *parser) parseQualifiedIdent(ident *ast.Ident) ast.Expr {
}
typ := p.parseTypeName(ident)
- if p.tok == token.LBRACK && p.mode&parseTypeParams != 0 {
+ if p.tok == token.LBRACK && p.parseTypeParams() {
typ = p.parseTypeInstance(typ)
}
@@ -553,7 +558,7 @@ func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident) (*ast.Ident, ast.Ex
// 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 {
@@ -583,19 +588,19 @@ 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.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 {
@@ -878,7 +883,7 @@ func (p *parser) parseParameters(acceptTParams bool) (tparams, params *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
@@ -951,7 +956,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.mode&parseTypeParams != 0:
+ case p.tok == token.LBRACK && p.parseTypeParams():
// generic method or embedded instantiated type
lbrack := p.pos
p.next()
@@ -967,7 +972,8 @@ func (p *parser) parseMethodSpec() *ast.Field {
_, 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.
@@ -984,7 +990,7 @@ func (p *parser) parseMethodSpec() *ast.Field {
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
@@ -1000,7 +1006,7 @@ func (p *parser) parseMethodSpec() *ast.Field {
} 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)
}
@@ -1020,7 +1026,7 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
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 {
@@ -1108,14 +1114,14 @@ func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr {
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
@@ -1360,13 +1366,13 @@ func (p *parser) parseIndexOrSliceOrInstance(x ast.Expr) ast.Expr {
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 {
@@ -2406,7 +2412,7 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, _ token.Pos, keyword toke
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")
@@ -2432,7 +2438,7 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token
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 {
@@ -2537,12 +2543,12 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
Name: ident,
Type: &ast.FuncType{
Func: pos,
- TParams: tparams,
Params: params,
Results: results,
},
Body: body,
}
+ typeparams.Set(decl.Type, tparams)
return decl
}
diff --git a/src/go/parser/resolver.go b/src/go/parser/resolver.go
index dd77b685e3..1e357e26df 100644
--- a/src/go/parser/resolver.go
+++ b/src/go/parser/resolver.go
@@ -7,6 +7,7 @@ package parser
import (
"fmt"
"go/ast"
+ "go/internal/typeparams"
"go/token"
)
@@ -450,10 +451,10 @@ func (r *resolver) Visit(node ast.Node) ast.Visitor {
// 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)
}
@@ -476,7 +477,6 @@ func (r *resolver) Visit(node ast.Node) ast.Visitor {
}
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)
}
diff --git a/src/go/parser/resolver_test.go b/src/go/parser/resolver_test.go
index 9ae2844d73..80a6638210 100644
--- a/src/go/parser/resolver_test.go
+++ b/src/go/parser/resolver_test.go
@@ -7,6 +7,7 @@ package parser
import (
"fmt"
"go/ast"
+ "go/internal/typeparams"
"go/scanner"
"go/token"
"os"
@@ -41,7 +42,11 @@ func TestResolution(t *testing.T) {
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 {
diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go
index b21dd0aa60..67fef15665 100644
--- a/src/go/parser/short_test.go
+++ b/src/go/parser/short_test.go
@@ -6,7 +6,10 @@
package parser
-import "testing"
+import (
+ "go/internal/typeparams"
+ "testing"
+)
var valids = []string{
"package p\n",
@@ -130,19 +133,22 @@ func TestValid(t *testing.T) {
}
})
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{
@@ -250,21 +256,24 @@ 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, 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)
}
})
}
diff --git a/src/go/printer/nodes.go b/src/go/printer/nodes.go
index 1c0a14ec15..913281ea6c 100644
--- a/src/go/printer/nodes.go
+++ b/src/go/printer/nodes.go
@@ -11,6 +11,7 @@ package printer
import (
"bytes"
"go/ast"
+ "go/internal/typeparams"
"go/token"
"math"
"strconv"
@@ -382,8 +383,8 @@ func (p *printer) parameters(fields *ast.FieldList, isTypeParam bool) {
}
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)
@@ -870,8 +871,14 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
// 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)
}
@@ -1628,8 +1635,8 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool) {
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)
diff --git a/src/go/printer/printer_test.go b/src/go/printer/printer_test.go
index e03c2df063..20c97b8c08 100644
--- a/src/go/printer/printer_test.go
+++ b/src/go/printer/printer_test.go
@@ -10,6 +10,7 @@ import (
"flag"
"fmt"
"go/ast"
+ "go/internal/typeparams"
"go/parser"
"go/token"
"io"
@@ -19,10 +20,6 @@ import (
"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
@@ -47,11 +44,7 @@ const (
// 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)
}
@@ -79,7 +72,7 @@ func format(src []byte, mode checkMode) ([]byte, error) {
// 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())
}
@@ -210,7 +203,7 @@ var data = []entry{
{"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},
@@ -229,6 +222,9 @@ var data = []entry{
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
diff --git a/src/go/printer/testdata/declarations.golden b/src/go/printer/testdata/declarations.golden
index 74ffce7d73..fe0f7838de 100644
--- a/src/go/printer/testdata/declarations.golden
+++ b/src/go/printer/testdata/declarations.golden
@@ -942,13 +942,6 @@ type _ interface {
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)
@@ -999,10 +992,6 @@ func _(struct {
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{}
diff --git a/src/go/printer/testdata/declarations.input b/src/go/printer/testdata/declarations.input
index ab2022142a..f34395b505 100644
--- a/src/go/printer/testdata/declarations.input
+++ b/src/go/printer/testdata/declarations.input
@@ -955,11 +955,6 @@ r string,
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))))))
@@ -1010,10 +1005,6 @@ func _(struct {
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{}
diff --git a/src/go/printer/testdata/generics.golden b/src/go/printer/testdata/generics.golden
index 88c461622e..cc7fbbe1d8 100644
--- a/src/go/printer/testdata/generics.golden
+++ b/src/go/printer/testdata/generics.golden
@@ -4,6 +4,9 @@
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{}
@@ -31,3 +34,10 @@ func _() {
var _ []T[P]
_ = []T[P]{}
}
+
+// properly format one-line type lists
+type _ interface{ type a }
+
+type _ interface {
+ type a, b, c
+}
diff --git a/src/go/printer/testdata/generics.input b/src/go/printer/testdata/generics.input
index 5fdf8cdb87..f4571ad336 100644
--- a/src/go/printer/testdata/generics.input
+++ b/src/go/printer/testdata/generics.input
@@ -4,6 +4,9 @@
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{}
@@ -28,3 +31,8 @@ func _() {
var _ []T[P]
_ = []T[P]{}
}
+
+// properly format one-line type lists
+type _ interface { type a }
+
+type _ interface { type a,b,c }
diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go
index 427aa04e47..a8e29d3fda 100644
--- a/src/go/types/api_test.go
+++ b/src/go/types/api_test.go
@@ -9,6 +9,7 @@ import (
"fmt"
"go/ast"
"go/importer"
+ "go/internal/typeparams"
"go/parser"
"go/token"
"internal/testenv"
@@ -48,8 +49,8 @@ func mustTypecheck(t *testing.T, path, source string, info *Info) string {
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
}
@@ -347,6 +348,9 @@ func TestTypesInfo(t *testing.T) {
}
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) {
@@ -401,6 +405,9 @@ func TestDefsInfo(t *testing.T) {
}
for _, test := range tests {
+ if strings.HasPrefix(test.src, genericPkg) && !typeparams.Enabled {
+ continue
+ }
info := Info{
Defs: make(map[*ast.Ident]Object),
}
@@ -446,6 +453,9 @@ func TestUsesInfo(t *testing.T) {
}
for _, test := range tests {
+ if strings.HasPrefix(test.src, genericPkg) && !typeparams.Enabled {
+ continue
+ }
info := Info{
Uses: make(map[*ast.Ident]Object),
}
diff --git a/src/go/types/api_go1.18.go b/src/go/types/api_typeparams.go
index d98f4ef0dc..108e526fbf 100644
--- a/src/go/types/api_go1.18.go
+++ b/src/go/types/api_typeparams.go
@@ -2,8 +2,8 @@
// 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
diff --git a/src/go/types/api_go1.18_test.go b/src/go/types/api_typeparams_test.go
index e60fe23201..e6cccf8691 100644
--- a/src/go/types/api_go1.18_test.go
+++ b/src/go/types/api_typeparams_test.go
@@ -2,8 +2,8 @@
// 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
diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go
index f223cb7574..3aa06e8939 100644
--- a/src/go/types/assignments.go
+++ b/src/go/types/assignments.go
@@ -303,20 +303,6 @@ func (check *Checker) assignVars(lhs, origRHS []ast.Expr) {
}
}
-// 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
diff --git a/src/go/types/call.go b/src/go/types/call.go
index ae0a245b2b..642eb5e391 100644
--- a/src/go/types/call.go
+++ b/src/go/types/call.go
@@ -8,6 +8,7 @@ package types
import (
"go/ast"
+ "go/internal/typeparams"
"go/token"
"strings"
"unicode"
@@ -16,7 +17,8 @@ import (
// 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
diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go
index 327fb4c5f1..8a15841e37 100644
--- a/src/go/types/check_test.go
+++ b/src/go/types/check_test.go
@@ -30,6 +30,7 @@ import (
"fmt"
"go/ast"
"go/importer"
+ "go/internal/typeparams"
"go/parser"
"go/scanner"
"go/token"
@@ -43,10 +44,6 @@ import (
. "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")
@@ -213,7 +210,11 @@ func checkFiles(t *testing.T, goVersion string, filenames []string, srcs [][]byt
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
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
index 2eb2c39745..b5b9d35b24 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -8,6 +8,7 @@ import (
"fmt"
"go/ast"
"go/constant"
+ "go/internal/typeparams"
"go/token"
)
@@ -645,7 +646,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
})
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")
@@ -668,10 +669,10 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
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
diff --git a/src/go/types/exprstring.go b/src/go/types/exprstring.go
index 9e073b1de0..f05e6424d4 100644
--- a/src/go/types/exprstring.go
+++ b/src/go/types/exprstring.go
@@ -10,6 +10,7 @@ import (
"bytes"
"fmt"
"go/ast"
+ "go/internal/typeparams"
)
// ExprString returns the (possibly shortened) string representation for x.
@@ -69,16 +70,14 @@ func WriteExpr(buf *bytes.Buffer, x ast.Expr) {
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)
diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go
index 8e67237446..43d2c739a5 100644
--- a/src/go/types/resolver.go
+++ b/src/go/types/resolver.go
@@ -8,6 +8,7 @@ import (
"fmt"
"go/ast"
"go/constant"
+ "go/internal/typeparams"
"go/token"
"sort"
"strconv"
@@ -389,8 +390,8 @@ func (check *Checker) collectObjects() {
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?
@@ -497,7 +498,7 @@ L: // unpack receiver type
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:
diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go
index 60a42b0426..e6846545c6 100644
--- a/src/go/types/typexpr.go
+++ b/src/go/types/typexpr.go
@@ -10,6 +10,7 @@ import (
"fmt"
"go/ast"
"go/constant"
+ "go/internal/typeparams"
"go/token"
"sort"
"strconv"
@@ -209,27 +210,22 @@ func isubst(x ast.Expr, smap map[*ast.Ident]*ast.Ident) ast.Expr {
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:
@@ -316,13 +312,13 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
}
}
- 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")
}
}
@@ -467,7 +463,8 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
}
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.
@@ -801,7 +798,11 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
// (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)