]> Cypherpunks repositories - gostls13.git/commitdiff
go/*,cmd/gofmt: guard AST changes with the typeparams build tag
authorRob Findley <rfindley@google.com>
Fri, 5 Mar 2021 15:57:48 +0000 (10:57 -0500)
committerRobert Findley <rfindley@google.com>
Tue, 13 Apr 2021 22:24:31 +0000 (22:24 +0000)
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>
35 files changed:
src/cmd/gofmt/gofmt.go
src/cmd/gofmt/gofmt_test.go
src/cmd/gofmt/gofmt_typeparams_test.go [moved from src/cmd/gofmt/gofmt_go1.18.go with 73% similarity]
src/go/ast/ast.go
src/go/ast/ast_notypeparams.go [new file with mode: 0644]
src/go/ast/ast_typeparams.go [new file with mode: 0644]
src/go/ast/walk.go
src/go/ast/walk_notypeparams.go [new file with mode: 0644]
src/go/ast/walk_typeparams.go [new file with mode: 0644]
src/go/build/deps_test.go
src/go/internal/typeparams/common.go [new file with mode: 0644]
src/go/internal/typeparams/notypeparams.go [new file with mode: 0644]
src/go/internal/typeparams/typeparams.go [new file with mode: 0644]
src/go/parser/error_test.go
src/go/parser/interface.go
src/go/parser/parser.go
src/go/parser/resolver.go
src/go/parser/resolver_test.go
src/go/parser/short_test.go
src/go/printer/nodes.go
src/go/printer/printer_test.go
src/go/printer/testdata/declarations.golden
src/go/printer/testdata/declarations.input
src/go/printer/testdata/generics.golden
src/go/printer/testdata/generics.input
src/go/types/api_test.go
src/go/types/api_typeparams.go [moved from src/go/types/api_go1.18.go with 90% similarity]
src/go/types/api_typeparams_test.go [moved from src/go/types/api_go1.18_test.go with 98% similarity]
src/go/types/assignments.go
src/go/types/call.go
src/go/types/check_test.go
src/go/types/decl.go
src/go/types/exprstring.go
src/go/types/resolver.go
src/go/types/typexpr.go

index cd867bba155a35fa0460f83e7453f0b7a9d7978f..2793c2c2a43f0891f9d91c0cdb94515c72572e50 100644 (file)
@@ -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 {
index 9e2239a6926c9df7da6f1fd54cfe91603f375c88..f0d3f8780f40e135f2e86f8d21e1feb61dbfecbd 100644 (file)
@@ -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)
                }
similarity index 73%
rename from src/cmd/gofmt/gofmt_go1.18.go
rename to src/cmd/gofmt/gofmt_typeparams_test.go
index be7b46b5edefc767944e69a270b3560b28da2482..10641a77cb2f8762dfd10c2dfbb25313adab225a 100644 (file)
@@ -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
 }
index 6eb4d13f4d0fbcec4da61cc4d24de46b3dee83bc..c87529ec77dda9399b803cfe632b7b9fc2f90904 100644 (file)
@@ -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 (file)
index 0000000..fa132fb
--- /dev/null
@@ -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 (file)
index 0000000..24fdc5f
--- /dev/null
@@ -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
+}
index a57ff25ac5afed4ed1ed77f2e28bc65ac0d64051..9224264e291e2fc8904fc208d94d0915f2e626f4 100644 (file)
@@ -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 (file)
index 0000000..d43e13d
--- /dev/null
@@ -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 (file)
index 0000000..77267a5
--- /dev/null
@@ -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))
+       }
+}
index a1a4324331c4f7f5698e05f8d4e45943eefc61d7..a1ed834c7150f135a8ce13fd509400da7aec6b66 100644 (file)
@@ -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 (file)
index 0000000..47b8f7c
--- /dev/null
@@ -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 (file)
index 0000000..7bd62c9
--- /dev/null
@@ -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 (file)
index 0000000..0332b6b
--- /dev/null
@@ -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))
+       }
+}
index 3caa3571e6a9f984243f3bac08342beae325383c..5d45a7b817b62af0cd10a12128fbd8c48c7a76f6 100644 (file)
@@ -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)
                }
index 8f306adaabdd7c9149674329e768aa5235cb8346..dcc5fa6616c4349d91bd41f1415cfea88b22c338 100644 (file)
@@ -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
index 93ab4a4600fc63d3c8a2c2abfab6e32aa745d42e..aff5838780df97b19c5bf96fe96ba61b55aa63e6 100644 (file)
@@ -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
 }
 
index dd77b685e39d5ccf766d3622ed2c58b6dff6be48..1e357e26dfe20c8ee0a42a80c53d496a64527780 100644 (file)
@@ -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)
 }
index 9ae2844d730f7684a8b8407daf99b748846ce143..80a6638210dd55288d3e0b39f804460b156353dd 100644 (file)
@@ -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 {
index b21dd0aa60f4f2e11d04a32eebda08d29b3d6b8c..67fef15665583db4e4a1ba7aad93a081b8c9bfc9 100644 (file)
@@ -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)
                }
        })
 }
index 1c0a14ec157756a9b09ec800efc679e9ae43ef2b..913281ea6c14b7b13ef6e95f9233a1c808e518b1 100644 (file)
@@ -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)
index e03c2df0633d6221ab4615b80556d0db3ea4386a..20c97b8c08173065e23a2be9795b90beb904201f 100644 (file)
@@ -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
index 74ffce7d73f867aa3392c47e7a28b1f552d32de7..fe0f7838de50617b2ead0e15a3d65d53c736454f 100644 (file)
@@ -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{}
index ab2022142a6d48bbeb50d6aa9af5bcb0dbe446fd..f34395b505dbd7be2b808acc9fd7ed817ba10b42 100644 (file)
@@ -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{}
index 88c461622ece99c44cc6ba36a32bee46bb68b2de..cc7fbbe1d8360403db9181d0303e7df12a18780b 100644 (file)
@@ -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
+}
index 5fdf8cdb8713262ce2b7b71adb2c4071c4dfa82f..f4571ad33693cb260dd3c0afb4420eba1ac11cf4 100644 (file)
@@ -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 }
index 427aa04e47bf85adf2c8de6cc2d59224091b231d..a8e29d3fdab7a434aad2958ec1ec95ad3390e4a3 100644 (file)
@@ -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),
                }
similarity index 90%
rename from src/go/types/api_go1.18.go
rename to src/go/types/api_typeparams.go
index d98f4ef0dc17d202d1f491161c1907fd3b1e6738..108e526fbf5b569b234cfac6d1fb4a22c6d5ff3d 100644 (file)
@@ -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
 
similarity index 98%
rename from src/go/types/api_go1.18_test.go
rename to src/go/types/api_typeparams_test.go
index e60fe232012224b578ae2f00dd18afcadbfba48b..e6cccf8691a8016a7519e38118b86bcd9893d5fc 100644 (file)
@@ -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
 
index f223cb7574361902da3027a967b43160a0910288..3aa06e8939fca76084b0217202c4f2eb8c4b4509 100644 (file)
@@ -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
index ae0a245b2b703986a66a8866d18c78b5f26a58e6..642eb5e3915d61f050101d76d87c6413b924ed05 100644 (file)
@@ -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
index 327fb4c5f19cbe0226ee9b96fb9b8bf7711a5b7a..8a15841e3791cbf0723ae4dcf7ed9f0adc654b71 100644 (file)
@@ -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
index 2eb2c39745935f2c570c6e0e0745cc1a8a88ba8a..b5b9d35b243a7b7efa6657c2814b600d1158b15e 100644 (file)
@@ -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
index 9e073b1de02e92445bf501afbb822b698f23be38..f05e6424d44d88a89e4fecde277b15f34344320b 100644 (file)
@@ -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)
index 8e67237446bd44017950bbeb0a78fe6b8bc78546..43d2c739a55d55f1d5d118dfbde737c260a2248e 100644 (file)
@@ -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:
index 60a42b0426c360ca714082f41fdc7ef8f686a3a2..e6846545c63c1034343e483750be72d5c1b8e65d 100644 (file)
@@ -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)