[dev.go2go] go/*: merge parser and types changes from dev.typeparams
Merge just the conflicting changes from dev.typeparams (ast, parser,
types, etc.), excluding io/fs changes.
The rest of the dev.typeparams merge will be done in a later CL, once
the compiler changes have stabilized.
Change-Id: Ic24c5902e4cacc85338ab23b100e5a245a5148bc
Reviewed-on: https://go-review.googlesource.com/c/go/+/286774
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Robert Findley <rfindley@google.com>
diff --git a/api/except.txt b/api/except.txt
index 79ffd91..ccfdf06 100644
--- a/api/except.txt
+++ b/api/except.txt
@@ -1,5 +1,4 @@
pkg encoding/json, method (*RawMessage) MarshalJSON() ([]uint8, error)
-pkg go/types, type Type interface { String, Underlying }
pkg math/big, const MaxBase = 36
pkg math/big, type Word uintptr
pkg net, func ListenUnixgram(string, *UnixAddr) (*UDPConn, error)
diff --git a/api/go1.15.txt b/api/go1.15.txt
index b51837c..dd90506 100644
--- a/api/go1.15.txt
+++ b/api/go1.15.txt
@@ -112,8 +112,6 @@
pkg debug/pe, const IMAGE_SUBSYSTEM_WINDOWS_GUI ideal-int
pkg debug/pe, const IMAGE_SUBSYSTEM_XBOX = 14
pkg debug/pe, const IMAGE_SUBSYSTEM_XBOX ideal-int
-pkg go/printer, const StdFormat = 16
-pkg go/printer, const StdFormat Mode
pkg math/big, method (*Int) FillBytes([]uint8) []uint8
pkg net, method (*Resolver) LookupIP(context.Context, string, string) ([]IP, error)
pkg net/url, method (*URL) EscapedFragment() string
diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go
index 92bfd46..8a6f4cc 100644
--- a/src/cmd/gofmt/gofmt.go
+++ b/src/cmd/gofmt/gofmt.go
@@ -40,7 +40,13 @@
// Keep these in sync with go/format/format.go.
const (
tabWidth = 8
- printerMode = printer.UseSpaces | printer.TabIndent | printer.StdFormat | printer.UseBrackets
+ printerMode = printer.UseSpaces | printer.TabIndent | printerNormalizeNumbers
+
+ // printerNormalizeNumbers means to canonicalize number literal prefixes
+ // and exponents while printing. See https://golang.org/doc/go1.13#gofmt.
+ //
+ // This value is defined in go/printer specifically for go/format and cmd/gofmt.
+ printerNormalizeNumbers = 1 << 30
)
var (
@@ -62,7 +68,7 @@
func initParserMode() {
// Keep this in sync with go/format/format.go.
- parserMode = parser.ParseComments | parser.UnifiedParamLists
+ parserMode = parser.ParseComments | parser.ParseTypeParams
if *allErrors {
parserMode |= parser.AllErrors
}
diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go
index d6e691c..2456020 100644
--- a/src/go/ast/ast.go
+++ b/src/go/ast/ast.go
@@ -57,6 +57,11 @@
// Comments
// A Comment node represents a single //-style or /*-style comment.
+//
+// The Text field contains the comment text without carriage returns (\r) that
+// may have been present in the source. Because a comment's end position is
+// computed using len(Text), the position reported by End() does not match the
+// true source end position for comments containing carriage returns.
type Comment struct {
Slash token.Pos // position of "/" starting the comment
Text string // comment text (excluding '\n' for //-style comments)
@@ -367,7 +372,9 @@
Args []Expr // function arguments; or nil
Ellipsis token.Pos // position of "..." (token.NoPos if there is no "...")
Rparen token.Pos // position of ")"
- Brackets bool // if set, "[" and "]" are used instead of "(" and ")"
+ // TODO(rFindley) use a new ListExpr type rather than overloading CallExpr
+ // via Brackets, as is done in the syntax package
+ Brackets bool // if set, "[" and "]" are used instead of "(" and ")"
}
// A StarExpr node represents an expression of the form "*" Expression.
@@ -982,6 +989,8 @@
Name *Ident // function/method name
Type *FuncType // function signature: type and value parameters, results, and position of "func" keyword
Body *BlockStmt // function body; or nil for external (non-Go) function
+ // TODO(rFindley) consider storing TParams here, rather than FuncType, as
+ // they are only valid for declared functions
}
)
@@ -1039,15 +1048,14 @@
// are "free-floating" (see also issues #18593, #20744).
//
type File struct {
- Doc *CommentGroup // associated documentation; or nil
- Package token.Pos // position of "package" keyword
- Name *Ident // package name
- Decls []Decl // top-level declarations; or nil
- Scope *Scope // package scope (this file only)
- Imports []*ImportSpec // imports in this file
- Unresolved []*Ident // unresolved identifiers in this file
- Comments []*CommentGroup // list of all comments in the source file
- UseBrackets bool // indicates that type parameters use []'s
+ Doc *CommentGroup // associated documentation; or nil
+ Package token.Pos // position of "package" keyword
+ Name *Ident // package name
+ Decls []Decl // top-level declarations; or nil
+ Scope *Scope // package scope (this file only)
+ Imports []*ImportSpec // imports in this file
+ Unresolved []*Ident // unresolved identifiers in this file
+ Comments []*CommentGroup // list of all comments in the source file
}
func (f *File) Pos() token.Pos { return f.Package }
diff --git a/src/go/ast/commentmap_test.go b/src/go/ast/commentmap_test.go
index e372eab..38c62b0 100644
--- a/src/go/ast/commentmap_test.go
+++ b/src/go/ast/commentmap_test.go
@@ -140,4 +140,31 @@
}
}
-// TODO(gri): add tests for Filter.
+func TestFilter(t *testing.T) {
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "", src, parser.ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+ cmap := NewCommentMap(fset, f, f.Comments)
+
+ // delete variable declaration
+ for i, decl := range f.Decls {
+ if gen, ok := decl.(*GenDecl); ok && gen.Tok == token.VAR {
+ copy(f.Decls[i:], f.Decls[i+1:])
+ f.Decls = f.Decls[:len(f.Decls)-1]
+ break
+ }
+ }
+
+ // check if comments are filtered correctly
+ cc := cmap.Filter(f)
+ for n, list := range cc {
+ key := fmt.Sprintf("%2d: %T", fset.Position(n.Pos()).Line, n)
+ got := ctext(list)
+ want := res[key]
+ if key == "25: *ast.GenDecl" || got != want {
+ t.Errorf("%s: got %q; want %q", key, got, want)
+ }
+ }
+}
diff --git a/src/go/ast/example_test.go b/src/go/ast/example_test.go
index 6e8d2ee..c2b3520 100644
--- a/src/go/ast/example_test.go
+++ b/src/go/ast/example_test.go
@@ -135,8 +135,7 @@
// 56 . Unresolved: []*ast.Ident (len = 1) {
// 57 . . 0: *(obj @ 29)
// 58 . }
- // 59 . UseBrackets: false
- // 60 }
+ // 59 }
}
// This example illustrates how to remove a variable declaration
diff --git a/src/go/ast/filter.go b/src/go/ast/filter.go
index 90a997b..c398e6e 100644
--- a/src/go/ast/filter.go
+++ b/src/go/ast/filter.go
@@ -493,5 +493,5 @@
}
// TODO(gri) need to compute unresolved identifiers!
- return &File{doc, pos, NewIdent(pkg.Name), decls, pkg.Scope, imports, nil, comments, false}
+ return &File{doc, pos, NewIdent(pkg.Name), decls, pkg.Scope, imports, nil, comments}
}
diff --git a/src/go/constant/value.go b/src/go/constant/value.go
index 08bcb3b..78cb3f8 100644
--- a/src/go/constant/value.go
+++ b/src/go/constant/value.go
@@ -17,6 +17,7 @@
"go/token"
"math"
"math/big"
+ "math/bits"
"strconv"
"strings"
"sync"
@@ -66,6 +67,22 @@
// The spec requires at least 256 bits; typical implementations use 512 bits.
const prec = 512
+// TODO(gri) Consider storing "error" information in an unknownVal so clients
+// can provide better error messages. For instance, if a number is
+// too large (incl. infinity), that could be recorded in unknownVal.
+// See also #20583 and #42695 for use cases.
+
+// Representation of values:
+//
+// Values of Int and Float Kind have two different representations each: int64Val
+// and intVal, and ratVal and floatVal. When possible, the "smaller", respectively
+// more precise (for Floats) representation is chosen. However, once a Float value
+// is represented as a floatVal, any subsequent results remain floatVals (unless
+// explicitly converted); i.e., no attempt is made to convert a floatVal back into
+// a ratVal. The reasoning is that all representations but floatVal are mathematically
+// exact, but once that precision is lost (by moving to floatVal), moving back to
+// a different representation implies a precision that's not actually there.
+
type (
unknownVal struct{}
boolVal bool
@@ -257,14 +274,8 @@
func i64tof(x int64Val) floatVal { return floatVal{newFloat().SetInt64(int64(x))} }
func itor(x intVal) ratVal { return ratVal{newRat().SetInt(x.val)} }
func itof(x intVal) floatVal { return floatVal{newFloat().SetInt(x.val)} }
-
-func rtof(x ratVal) floatVal {
- a := newFloat().SetInt(x.val.Num())
- b := newFloat().SetInt(x.val.Denom())
- return floatVal{a.Quo(a, b)}
-}
-
-func vtoc(x Value) complexVal { return complexVal{x, int64Val(0)} }
+func rtof(x ratVal) floatVal { return floatVal{newFloat().SetRat(x.val)} }
+func vtoc(x Value) complexVal { return complexVal{x, int64Val(0)} }
func makeInt(x *big.Int) Value {
if x.IsInt64() {
@@ -273,21 +284,15 @@
return intVal{x}
}
-// Permit fractions with component sizes up to maxExp
-// before switching to using floating-point numbers.
-const maxExp = 4 << 10
-
func makeRat(x *big.Rat) Value {
a := x.Num()
b := x.Denom()
- if a.BitLen() < maxExp && b.BitLen() < maxExp {
+ if smallInt(a) && smallInt(b) {
// ok to remain fraction
return ratVal{x}
}
// components too large => switch to float
- fa := newFloat().SetInt(a)
- fb := newFloat().SetInt(b)
- return floatVal{fa.Quo(fa, fb)}
+ return floatVal{newFloat().SetRat(x)}
}
var floatVal0 = floatVal{newFloat()}
@@ -297,16 +302,25 @@
if x.Sign() == 0 {
return floatVal0
}
+ if x.IsInf() {
+ return unknownVal{}
+ }
+ // No attempt is made to "go back" to ratVal, even if possible,
+ // to avoid providing the illusion of a mathematically exact
+ // representation.
return floatVal{x}
}
func makeComplex(re, im Value) Value {
+ if re.Kind() == Unknown || im.Kind() == Unknown {
+ return unknownVal{}
+ }
return complexVal{re, im}
}
func makeFloatFromLiteral(lit string) Value {
if f, ok := newFloat().SetString(lit); ok {
- if smallRat(f) {
+ if smallFloat(f) {
// ok to use rationals
if f.Sign() == 0 {
// Issue 20228: If the float underflowed to zero, parse just "0".
@@ -325,14 +339,34 @@
return nil
}
-// smallRat reports whether x would lead to "reasonably"-sized fraction
+// Permit fractions with component sizes up to maxExp
+// before switching to using floating-point numbers.
+const maxExp = 4 << 10
+
+// smallInt reports whether x would lead to "reasonably"-sized fraction
// if converted to a *big.Rat.
-func smallRat(x *big.Float) bool {
- if !x.IsInf() {
- e := x.MantExp(nil)
- return -maxExp < e && e < maxExp
+func smallInt(x *big.Int) bool {
+ return x.BitLen() < maxExp
+}
+
+// smallFloat64 reports whether x would lead to "reasonably"-sized fraction
+// if converted to a *big.Rat.
+func smallFloat64(x float64) bool {
+ if math.IsInf(x, 0) {
+ return false
}
- return false
+ _, e := math.Frexp(x)
+ return -maxExp < e && e < maxExp
+}
+
+// smallFloat reports whether x would lead to "reasonably"-sized fraction
+// if converted to a *big.Rat.
+func smallFloat(x *big.Float) bool {
+ if x.IsInf() {
+ return false
+ }
+ e := x.MantExp(nil)
+ return -maxExp < e && e < maxExp
}
// ----------------------------------------------------------------------------
@@ -359,16 +393,16 @@
}
// MakeFloat64 returns the Float value for x.
+// If x is -0.0, the result is 0.0.
// If x is not finite, the result is an Unknown.
func MakeFloat64(x float64) Value {
if math.IsInf(x, 0) || math.IsNaN(x) {
return unknownVal{}
}
- // convert -0 to 0
- if x == 0 {
- return int64Val(0)
+ if smallFloat64(x) {
+ return ratVal{newRat().SetFloat64(x + 0)} // convert -0 to 0
}
- return ratVal{newRat().SetFloat64(x)}
+ return floatVal{newFloat().SetFloat64(x + 0)}
}
// MakeFromLiteral returns the corresponding integer, floating-point,
@@ -583,11 +617,11 @@
case int64:
return int64Val(x)
case *big.Int:
- return intVal{x}
+ return makeInt(x)
case *big.Rat:
- return ratVal{x}
+ return makeRat(x)
case *big.Float:
- return floatVal{x}
+ return makeFloat(x)
default:
return unknownVal{}
}
@@ -599,7 +633,11 @@
func BitLen(x Value) int {
switch x := x.(type) {
case int64Val:
- return i64toi(x).val.BitLen()
+ u := uint64(x)
+ if x < 0 {
+ u = uint64(-x)
+ }
+ return 64 - bits.LeadingZeros64(u)
case intVal:
return x.val.BitLen()
case unknownVal:
@@ -720,7 +758,7 @@
case ratVal:
return makeInt(x.val.Num())
case floatVal:
- if smallRat(x.val) {
+ if smallFloat(x.val) {
r, _ := x.val.Rat(nil)
return makeInt(r.Num())
}
@@ -742,7 +780,7 @@
case ratVal:
return makeInt(x.val.Denom())
case floatVal:
- if smallRat(x.val) {
+ if smallFloat(x.val) {
r, _ := x.val.Rat(nil)
return makeInt(r.Denom())
}
@@ -815,7 +853,7 @@
// avoid creation of huge integers
// (Existing tests require permitting exponents of at least 1024;
// allow any value that would also be permissible as a fraction.)
- if smallRat(x.val) {
+ if smallFloat(x.val) {
i := newInt()
if _, acc := x.val.Int(i); acc == big.Exact {
return makeInt(i)
@@ -858,14 +896,16 @@
func ToFloat(x Value) Value {
switch x := x.(type) {
case int64Val:
- return i64tof(x)
+ return i64tor(x) // x is always a small int
case intVal:
+ if smallInt(x.val) {
+ return itor(x)
+ }
return itof(x)
case ratVal, floatVal:
return x
case complexVal:
- if im := ToInt(x.im); im.Kind() == Int && Sign(im) == 0 {
- // imaginary component is 0
+ if Sign(x.im) == 0 {
return ToFloat(x.re)
}
}
@@ -876,13 +916,7 @@
// Otherwise it returns an Unknown.
func ToComplex(x Value) Value {
switch x := x.(type) {
- case int64Val:
- return vtoc(i64tof(x))
- case intVal:
- return vtoc(itof(x))
- case ratVal:
- return vtoc(x)
- case floatVal:
+ case int64Val, intVal, ratVal, floatVal:
return vtoc(x)
case complexVal:
return x
@@ -1001,59 +1035,45 @@
// or invalid (say, nil) both results are that value.
//
func match(x, y Value) (_, _ Value) {
- if ord(x) > ord(y) {
- y, x = match(y, x)
- return x, y
+ switch ox, oy := ord(x), ord(y); {
+ case ox < oy:
+ x, y = match0(x, y)
+ case ox > oy:
+ y, x = match0(y, x)
}
- // ord(x) <= ord(y)
+ return x, y
+}
- switch x := x.(type) {
- case boolVal, *stringVal, complexVal:
- return x, y
+// match0 must only be called by match.
+// Invariant: ord(x) < ord(y)
+func match0(x, y Value) (_, _ Value) {
+ // Prefer to return the original x and y arguments when possible,
+ // to avoid unnecessary heap allocations.
- case int64Val:
- switch y := y.(type) {
- case int64Val:
- return x, y
- case intVal:
- return i64toi(x), y
- case ratVal:
- return i64tor(x), y
- case floatVal:
- return i64tof(x), y
- case complexVal:
- return vtoc(x), y
- }
-
+ switch y.(type) {
case intVal:
- switch y := y.(type) {
- case intVal:
- return x, y
- case ratVal:
- return itor(x), y
- case floatVal:
- return itof(x), y
- case complexVal:
- return vtoc(x), y
+ switch x1 := x.(type) {
+ case int64Val:
+ return i64toi(x1), y
}
-
case ratVal:
- switch y := y.(type) {
- case ratVal:
- return x, y
- case floatVal:
- return rtof(x), y
- case complexVal:
- return vtoc(x), y
+ switch x1 := x.(type) {
+ case int64Val:
+ return i64tor(x1), y
+ case intVal:
+ return itor(x1), y
}
-
case floatVal:
- switch y := y.(type) {
- case floatVal:
- return x, y
- case complexVal:
- return vtoc(x), y
+ switch x1 := x.(type) {
+ case int64Val:
+ return i64tof(x1), y
+ case intVal:
+ return itof(x1), y
+ case ratVal:
+ return rtof(x1), y
}
+ case complexVal:
+ return vtoc(x), y
}
// force unknown and invalid values into "x position" in callers of match
diff --git a/src/go/constant/value_test.go b/src/go/constant/value_test.go
index a319039f..91ad0b0 100644
--- a/src/go/constant/value_test.go
+++ b/src/go/constant/value_test.go
@@ -7,6 +7,7 @@
import (
"fmt"
"go/token"
+ "math"
"math/big"
"strings"
"testing"
@@ -82,6 +83,11 @@
`1_2_3.123 = 123.123`,
`0123.01_23 = 123.0123`,
+ `1e-1000000000 = 0`,
+ `1e+1000000000 = ?`,
+ `6e5518446744 = ?`,
+ `-6e5518446744 = ?`,
+
// hexadecimal floats
`0x0.p+0 = 0.`,
`0Xdeadcafe.p-10 = 0xdeadcafe/1024`,
@@ -117,6 +123,11 @@
`0.e+1i = 0i`,
`123.E-1_0i = 123e-10i`,
`01_23.e123i = 123e123i`,
+
+ `1e-1000000000i = 0i`,
+ `1e+1000000000i = ?`,
+ `6e5518446744i = ?`,
+ `-6e5518446744i = ?`,
}
func testNumbers(t *testing.T, kind token.Token, tests []string) {
@@ -129,21 +140,32 @@
x := MakeFromLiteral(a[0], kind, 0)
var y Value
- if i := strings.Index(a[1], "/"); i >= 0 && kind == token.FLOAT {
- n := MakeFromLiteral(a[1][:i], token.INT, 0)
- d := MakeFromLiteral(a[1][i+1:], token.INT, 0)
- y = BinaryOp(n, token.QUO, d)
+ if a[1] == "?" {
+ y = MakeUnknown()
} else {
- y = MakeFromLiteral(a[1], kind, 0)
+ if i := strings.Index(a[1], "/"); i >= 0 && kind == token.FLOAT {
+ n := MakeFromLiteral(a[1][:i], token.INT, 0)
+ d := MakeFromLiteral(a[1][i+1:], token.INT, 0)
+ y = BinaryOp(n, token.QUO, d)
+ } else {
+ y = MakeFromLiteral(a[1], kind, 0)
+ }
+ if y.Kind() == Unknown {
+ panic(fmt.Sprintf("invalid test case: %s %d", test, y.Kind()))
+ }
}
xk := x.Kind()
yk := y.Kind()
- if xk != yk || xk == Unknown {
+ if xk != yk {
t.Errorf("%s: got kind %d != %d", test, xk, yk)
continue
}
+ if yk == Unknown {
+ continue
+ }
+
if !Compare(x, token.EQL, y) {
t.Errorf("%s: %s != %s", test, x, y)
}
@@ -200,6 +222,7 @@
`1i * 1i = -1`,
`? * 0 = ?`,
`0 * ? = ?`,
+ `0 * 1e+1000000000 = ?`,
`0 / 0 = "division_by_zero"`,
`10 / 2 = 5`,
@@ -207,6 +230,7 @@
`5i / 3i = 5/3`,
`? / 0 = ?`,
`0 / ? = ?`,
+ `0 * 1e+1000000000i = ?`,
`0 % 0 = "runtime_error:_integer_divide_by_zero"`, // TODO(gri) should be the same as for /
`10 % 3 = 1`,
@@ -597,18 +621,68 @@
}
}
-func TestMake(t *testing.T) {
- for _, want := range []interface{}{
- false,
- "hello",
- int64(1),
- big.NewInt(10),
- big.NewFloat(2.0),
- big.NewRat(1, 3),
+func TestMakeFloat64(t *testing.T) {
+ var zero float64
+ for _, arg := range []float64{
+ -math.MaxFloat32,
+ -10,
+ -0.5,
+ -zero,
+ zero,
+ 1,
+ 10,
+ 123456789.87654321e-23,
+ 1e10,
+ math.MaxFloat64,
} {
- got := Val(Make(want))
- if got != want {
- t.Errorf("got %v; want %v", got, want)
+ val := MakeFloat64(arg)
+ if val.Kind() != Float {
+ t.Errorf("%v: got kind = %d; want %d", arg, val.Kind(), Float)
+ }
+
+ // -0.0 is mapped to 0.0
+ got, exact := Float64Val(val)
+ if !exact || math.Float64bits(got) != math.Float64bits(arg+0) {
+ t.Errorf("%v: got %v (exact = %v)", arg, got, exact)
+ }
+ }
+
+ // infinity
+ for sign := range []int{-1, 1} {
+ arg := math.Inf(sign)
+ val := MakeFloat64(arg)
+ if val.Kind() != Unknown {
+ t.Errorf("%v: got kind = %d; want %d", arg, val.Kind(), Unknown)
+ }
+ }
+}
+
+type makeTestCase struct {
+ kind Kind
+ arg, want interface{}
+}
+
+func dup(k Kind, x interface{}) makeTestCase { return makeTestCase{k, x, x} }
+
+func TestMake(t *testing.T) {
+ for _, test := range []makeTestCase{
+ {Bool, false, false},
+ {String, "hello", "hello"},
+
+ {Int, int64(1), int64(1)},
+ {Int, big.NewInt(10), int64(10)},
+ {Int, new(big.Int).Lsh(big.NewInt(1), 62), int64(1 << 62)},
+ dup(Int, new(big.Int).Lsh(big.NewInt(1), 63)),
+
+ {Float, big.NewFloat(0), floatVal0.val},
+ dup(Float, big.NewFloat(2.0)),
+ dup(Float, big.NewRat(1, 3)),
+ } {
+ val := Make(test.arg)
+ got := Val(val)
+ if val.Kind() != test.kind || got != test.want {
+ t.Errorf("got %v (%T, kind = %d); want %v (%T, kind = %d)",
+ got, got, val.Kind(), test.want, test.want, test.kind)
}
}
}
@@ -632,3 +706,24 @@
})
}
}
+
+var bitLenTests = []struct {
+ val int64
+ want int
+}{
+ {0, 0},
+ {1, 1},
+ {-16, 5},
+ {1 << 61, 62},
+ {1 << 62, 63},
+ {-1 << 62, 63},
+ {-1 << 63, 64},
+}
+
+func TestBitLen(t *testing.T) {
+ for _, test := range bitLenTests {
+ if got := BitLen(MakeInt64(test.val)); got != test.want {
+ t.Errorf("%v: got %v, want %v", test.val, got, test.want)
+ }
+ }
+}
diff --git a/src/go/format/format.go b/src/go/format/format.go
index 3613702..279fc2e 100644
--- a/src/go/format/format.go
+++ b/src/go/format/format.go
@@ -27,12 +27,18 @@
// Keep these in sync with cmd/gofmt/gofmt.go.
const (
tabWidth = 8
- printerMode = printer.UseSpaces | printer.TabIndent | printer.StdFormat | printer.UseBrackets
+ printerMode = printer.UseSpaces | printer.TabIndent | printerNormalizeNumbers
+
+ // printerNormalizeNumbers means to canonicalize number literal prefixes
+ // and exponents while printing. See https://golang.org/doc/go1.13#gofmt.
+ //
+ // This value is defined in go/printer specifically for go/format and cmd/gofmt.
+ printerNormalizeNumbers = 1 << 30
)
var config = printer.Config{Mode: printerMode, Tabwidth: tabWidth}
-const parserMode = parser.ParseComments | parser.UnifiedParamLists
+const parserMode = parser.ParseComments | parser.ParseTypeParams
// Node formats node in canonical gofmt style and writes the result to dst.
//
diff --git a/src/go/format/format_test.go b/src/go/format/format_test.go
index aee51e2..58e088e 100644
--- a/src/go/format/format_test.go
+++ b/src/go/format/format_test.go
@@ -58,8 +58,8 @@
diff(t, buf.Bytes(), src)
}
-// Node is documented to not modify the AST. Test that it is so, even when
-// formatting changes are applied due to printer.StdFormat mode being used.
+// Node is documented to not modify the AST.
+// Test that it is so even when numbers are normalized.
func TestNodeNoModify(t *testing.T) {
const (
src = "package p\n\nconst _ = 0000000123i\n"
diff --git a/src/go/go2go/go2go.go b/src/go/go2go/go2go.go
index a2265f1..b0c4873 100644
--- a/src/go/go2go/go2go.go
+++ b/src/go/go2go/go2go.go
@@ -83,9 +83,8 @@
var merr multiErr
conf := types.Config{
- InferFromConstraints: true,
- Importer: importer,
- Error: merr.add,
+ Importer: importer,
+ Error: merr.add,
}
path := importPath
if path == "" {
@@ -126,15 +125,14 @@
// for error messages.
func RewriteBuffer(importer *Importer, filename string, file []byte) ([]byte, error) {
fset := token.NewFileSet()
- pf, err := parser.ParseFile(fset, filename, file, parser.UnifiedParamLists)
+ pf, err := parser.ParseFile(fset, filename, file, parser.ParseTypeParams)
if err != nil {
return nil, err
}
var merr multiErr
conf := types.Config{
- InferFromConstraints: true,
- Importer: importer,
- Error: merr.add,
+ Importer: importer,
+ Error: merr.add,
}
tpkg, err := conf.Check(pf.Name.Name, fset, []*ast.File{pf}, importer.info)
if err != nil {
@@ -222,7 +220,7 @@
func parseFiles(importer *Importer, dir string, go2files []string, fset *token.FileSet) ([]*ast.Package, error) {
pkgs := make(map[string]*ast.Package)
for _, go2f := range go2files {
- mode := parser.UnifiedParamLists // overrides UseBrackets
+ mode := parser.ParseTypeParams
filename := filepath.Join(dir, go2f)
pf, err := parser.ParseFile(fset, filename, nil, mode)
diff --git a/src/go/parser/error_test.go b/src/go/parser/error_test.go
index c7f93c9a..15c1b50 100644
--- a/src/go/parser/error_test.go
+++ b/src/go/parser/error_test.go
@@ -114,6 +114,7 @@
// of found errors and reports discrepancies.
//
func compareErrors(t *testing.T, fset *token.FileSet, expected map[token.Pos]string, found scanner.ErrorList) {
+ t.Helper()
for _, error := range found {
// error.Pos is a token.Position, but we want
// a token.Pos so we can do a map lookup
@@ -149,7 +150,8 @@
}
}
-func checkErrors(t *testing.T, filename string, input interface{}) {
+func checkErrors(t *testing.T, filename string, input interface{}, mode Mode, expectErrors bool) {
+ t.Helper()
src, err := readSource(filename, input)
if err != nil {
t.Error(err)
@@ -157,7 +159,7 @@
}
fset := token.NewFileSet()
- _, err = ParseFile(fset, filename, src, DeclarationErrors|AllErrors)
+ _, err = ParseFile(fset, filename, src, mode)
found, ok := err.(scanner.ErrorList)
if err != nil && !ok {
t.Error(err)
@@ -165,9 +167,12 @@
}
found.RemoveMultiples()
- // we are expecting the following errors
- // (collect these after parsing a file so that it is found in the file set)
- expected := expectedErrors(fset, filename, src)
+ expected := map[token.Pos]string{}
+ if expectErrors {
+ // we are expecting the following errors
+ // (collect these after parsing a file so that it is found in the file set)
+ expected = expectedErrors(fset, filename, src)
+ }
// verify errors returned by the parser
compareErrors(t, fset, expected, found)
@@ -178,10 +183,14 @@
if err != nil {
t.Fatal(err)
}
- for _, fi := range list {
- name := fi.Name()
- if !fi.IsDir() && !strings.HasPrefix(name, ".") && (strings.HasSuffix(name, ".src") || strings.HasSuffix(name, ".go2")) {
- checkErrors(t, filepath.Join(testdata, name), nil)
+ for _, d := range list {
+ name := d.Name()
+ if !d.IsDir() && !strings.HasPrefix(name, ".") && (strings.HasSuffix(name, ".src") || strings.HasSuffix(name, ".go2")) {
+ mode := DeclarationErrors | AllErrors
+ if strings.HasSuffix(name, ".go2") {
+ mode |= ParseTypeParams
+ }
+ checkErrors(t, filepath.Join(testdata, name), nil, mode, true)
}
}
}
diff --git a/src/go/parser/interface.go b/src/go/parser/interface.go
index e8d218e..58c57a1 100644
--- a/src/go/parser/interface.go
+++ b/src/go/parser/interface.go
@@ -55,8 +55,7 @@
Trace // print a trace of parsed productions
DeclarationErrors // report declaration errors
SpuriousErrors // same as AllErrors, for backward-compatibility
- UseBrackets // use square brackets with type parameters
- UnifiedParamLists // use same syntax for type and ordinary parameter lists (implies UseBrackets)
+ ParseTypeParams // Placeholder. Will control the parsing of type parameters.
AllErrors = SpuriousErrors // report all errors (not just the first 10 on different lines)
)
@@ -142,23 +141,29 @@
pkgs = make(map[string]*ast.Package)
for _, d := range list {
- if strings.HasSuffix(d.Name(), ".go") && (filter == nil || filter(d)) {
- filename := filepath.Join(path, d.Name())
- if src, err := ParseFile(fset, filename, nil, mode); err == nil {
- name := src.Name.Name
- pkg, found := pkgs[name]
- if !found {
- pkg = &ast.Package{
- Name: name,
- Files: make(map[string]*ast.File),
- }
- pkgs[name] = pkg
- }
- pkg.Files[filename] = src
- } else if first == nil {
- first = err
+ if d.IsDir() || !strings.HasSuffix(d.Name(), ".go") {
+ continue
+ }
+ if filter != nil {
+ if !filter(d) {
+ continue
}
}
+ filename := filepath.Join(path, d.Name())
+ if src, err := ParseFile(fset, filename, nil, mode); err == nil {
+ name := src.Name.Name
+ pkg, found := pkgs[name]
+ if !found {
+ pkg = &ast.Package{
+ Name: name,
+ Files: make(map[string]*ast.File),
+ }
+ pkgs[name] = pkg
+ }
+ pkg.Files[filename] = src
+ } else if first == nil {
+ first = err
+ }
}
return
diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go
index 797ef25..ccbcef8 100644
--- a/src/go/parser/parser.go
+++ b/src/go/parser/parser.go
@@ -35,7 +35,6 @@
// Tracing/debugging
mode Mode // parsing mode
trace bool // == (mode&Trace != 0)
- brack bool // use square brackets to enclose type parameters
indent int // indentation used for tracing output
// Comments
@@ -82,7 +81,6 @@
p.mode = mode
p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)
- p.brack = mode&UseBrackets != 0 || mode&UnifiedParamLists != 0
p.next()
}
@@ -630,12 +628,12 @@
// ----------------------------------------------------------------------------
// Types
-func (p *parser) parseType(typeContext bool) ast.Expr {
+func (p *parser) parseType() ast.Expr {
if p.trace {
defer un(trace(p, "Type"))
}
- typ := p.tryType(typeContext)
+ typ := p.tryType()
if typ == nil {
pos := p.pos
@@ -647,6 +645,19 @@
return typ
}
+func (p *parser) parseQualifiedIdent(ident *ast.Ident) ast.Expr {
+ if p.trace {
+ defer un(trace(p, "QualifiedIdent"))
+ }
+
+ typ := p.parseTypeName(ident)
+ if p.tok == token.LBRACK && p.mode&ParseTypeParams != 0 {
+ typ = p.parseTypeInstance(typ)
+ }
+
+ return typ
+}
+
// If the result is an identifier, it is not resolved.
func (p *parser) parseTypeName(ident *ast.Ident) ast.Expr {
if p.trace {
@@ -697,12 +708,22 @@
// list such as T[P,]? (We do in parseTypeInstance).
lbrack := p.expect(token.LBRACK)
var args []ast.Expr
+ var firstComma token.Pos
+ // TODO(rfindley): consider changing parseRhsOrType so that this function variable
+ // is not needed.
+ argparser := p.parseRhsOrType
+ if p.mode&ParseTypeParams == 0 {
+ argparser = p.parseRhs
+ }
if p.tok != token.RBRACK {
p.exprLev++
- args = append(args, p.parseRhsOrType())
+ args = append(args, argparser())
for p.tok == token.COMMA {
+ if !firstComma.IsValid() {
+ firstComma = p.pos
+ }
p.next()
- args = append(args, p.parseRhsOrType())
+ args = append(args, argparser())
}
p.exprLev--
}
@@ -710,36 +731,32 @@
if len(args) == 0 {
// x []E
- elt := p.parseType(true)
+ elt := p.parseType()
return x, &ast.ArrayType{Lbrack: lbrack, Elt: elt}
}
// x [P]E or x[P]
if len(args) == 1 {
- elt := p.tryType(true)
+ elt := p.tryType()
if elt != nil {
// x [P]E
return x, &ast.ArrayType{Lbrack: lbrack, Len: args[0], Elt: elt}
}
+ if p.mode&ParseTypeParams == 0 {
+ 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 {
+ 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.CallExpr{Fun: x, Lparen: lbrack, Args: args, Rparen: rbrack, Brackets: true}
}
-type lookAhead struct {
- pos token.Pos
- tok token.Token
-}
-
-func (l *lookAhead) consume(p *parser) {
- if l.tok == 0 {
- l.pos = p.pos
- l.tok = p.tok
- p.next()
- }
-}
-
func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
if p.trace {
defer un(trace(p, "FieldDecl"))
@@ -755,11 +772,7 @@
// embedded type
typ = name
if p.tok == token.PERIOD {
- typ = p.parseTypeName(name)
- // A [ or ( indicates a type parameter (or a syntax error).
- if p.brack && p.tok == token.LBRACK || !p.brack && p.tok == token.LPAREN {
- typ = p.parseTypeInstance(typ, lookAhead{})
- }
+ typ = p.parseQualifiedIdent(name)
} else {
p.resolve(typ)
}
@@ -772,21 +785,21 @@
}
// Careful dance: We don't know if we have an embedded instantiated
// type T[P1, P2, ...] or a field T of array type []E or [P]E.
- if len(names) == 1 && p.brack && p.tok == token.LBRACK {
+ if len(names) == 1 && p.tok == token.LBRACK {
name, typ = p.parseArrayFieldOrTypeInstance(name)
if name == nil {
names = nil
}
} else {
// T P
- typ = p.parseType(true)
+ typ = p.parseType()
}
}
} else {
// embedded, possibly generic type
// (using the enclosing parentheses to distinguish it from a named field declaration)
- // TODO(gri) if p.brack is set, don't allow parenthesized embedded type
- typ = p.parseType(true)
+ // TODO(rFindley) confirm that this doesn't allow parenthesized embedded type
+ typ = p.parseType()
}
var tag *ast.BasicLit
@@ -795,7 +808,7 @@
p.next()
}
- p.expectSemi()
+ p.expectSemi() // call before accessing p.linecomment
field := &ast.Field{Doc: doc, Names: names, Type: typ, Tag: tag, Comment: p.lineComment}
p.declare(field, nil, scope, ast.Var, names...)
@@ -812,6 +825,9 @@
scope := ast.NewScope(nil) // struct scope
var list []*ast.Field
for p.tok == token.IDENT || p.tok == token.MUL || p.tok == token.LPAREN {
+ // a field declaration cannot start with a '(' but we accept
+ // it here for more robust parsing and better error messages
+ // (parseFieldDecl will check and complain if necessary)
list = append(list, p.parseFieldDecl(scope))
}
rbrace := p.expect(token.RBRACE)
@@ -826,13 +842,13 @@
}
}
-func (p *parser) parsePointerType(typeContext bool) *ast.StarExpr {
+func (p *parser) parsePointerType() *ast.StarExpr {
if p.trace {
defer un(trace(p, "PointerType"))
}
star := p.expect(token.MUL)
- base := p.parseType(typeContext)
+ base := p.parseType()
return &ast.StarExpr{Star: star, X: base}
}
@@ -843,7 +859,7 @@
}
pos := p.expect(token.ELLIPSIS)
- elt := p.parseType(true)
+ elt := p.parseType()
return &ast.Ellipsis{Ellipsis: pos, Elt: elt}
}
@@ -854,6 +870,7 @@
}
func (p *parser) parseParamDecl(name *ast.Ident) (f field) {
+ // TODO(rFindley) compare with parser.paramDeclOrNil in the syntax package
if p.trace {
defer un(trace(p, "ParamDeclOrNil"))
}
@@ -874,15 +891,11 @@
switch p.tok {
case token.IDENT, token.MUL, token.ARROW, token.FUNC, token.CHAN, token.MAP, token.STRUCT, token.INTERFACE, token.LPAREN:
// name type
- f.typ = p.parseType(true)
+ f.typ = p.parseType()
case token.LBRACK:
- if p.brack {
- // name[type1, type2, ...] or name []type or name [len]type
- f.name, f.typ = p.parseArrayFieldOrTypeInstance(f.name)
- } else {
- f.typ = p.parseType(true)
- }
+ // name[type1, type2, ...] or name []type or name [len]type
+ f.name, f.typ = p.parseArrayFieldOrTypeInstance(f.name)
case token.ELLIPSIS:
// name ...type
@@ -890,16 +903,13 @@
case token.PERIOD:
// qualified.typename
- f.typ = p.parseTypeName(f.name)
- if p.brack && p.tok == token.LBRACK || !p.brack && p.tok == token.LPAREN {
- f.typ = p.parseTypeInstance(f.typ, lookAhead{})
- }
+ f.typ = p.parseQualifiedIdent(f.name)
f.name = nil
}
case token.MUL, token.ARROW, token.FUNC, token.LBRACK, token.CHAN, token.MAP, token.STRUCT, token.INTERFACE, token.LPAREN:
// type
- f.typ = p.parseType(true)
+ f.typ = p.parseType()
case token.ELLIPSIS:
// ...type
@@ -914,35 +924,6 @@
return
}
-func (p *parser) parseTParamDecl(name *ast.Ident) (f field) {
- if p.trace {
- defer un(trace(p, "TParamDeclOrNil"))
- }
-
- ptr := false
- if name == nil && p.tok == token.MUL {
- ptr = true
- p.next()
- }
-
- if name == nil {
- name = p.parseIdent()
- }
- f.name = name
- if ptr {
- // encode pointer designation in the name
- f.name.Name = "*" + f.name.Name
- }
-
- switch p.tok {
- case token.IDENT, token.INTERFACE, token.LPAREN:
- // type bound
- f.typ = p.parseType(true)
- }
-
- return
-}
-
func (p *parser) parseParameterList(scope *ast.Scope, name0 *ast.Ident, closing token.Token, parseParamDecl func(*ast.Ident) field, tparams bool) (params []*ast.Field) {
if p.trace {
defer un(trace(p, "ParameterList"))
@@ -1059,96 +1040,49 @@
return
}
-func (p *parser) parseTypeParams(scope *ast.Scope, name0 *ast.Ident, closing token.Token) []*ast.Field {
- if p.trace {
- defer un(trace(p, "TypeParams"))
- }
-
- params := p.parseParameterList(scope, name0, closing, p.parseTParamDecl, false)
-
- // determine which form we have (list of type parameters with optional
- // type bound, or type parameters, all with interfaces as type bounds)
- for _, f := range params {
- if len(f.Names) == 0 {
- assert(f.Type != nil, "expected non-nil type")
- f.Names = []*ast.Ident{f.Type.(*ast.Ident)}
- f.Type = nil
- }
- }
-
- return params
-}
-
-func (p *parser) parseParameters(scope *ast.Scope, opening lookAhead, acceptTParams bool) (tparams, params *ast.FieldList) {
+func (p *parser) parseParameters(scope *ast.Scope, acceptTParams bool) (tparams, params *ast.FieldList) {
if p.trace {
defer un(trace(p, "Parameters"))
}
- opening.consume(p)
- lparen := opening.pos
- if acceptTParams {
- if opening.tok == token.LBRACK {
- var list []*ast.Field
- if p.mode&UnifiedParamLists != 0 {
- // assume [T any](params) syntax
- list = p.parseParameterList(scope, nil, token.RBRACK, p.parseParamDecl, true)
- } else {
- // assume [type T](params) syntax
- p.brack = true
- if p.tok == token.TYPE {
- p.next()
- }
- list = p.parseTypeParams(scope, nil, token.RBRACK)
- }
- rbrack := p.expect(token.RBRACK)
- tparams = &ast.FieldList{Opening: lparen, List: list, Closing: rbrack}
- lparen = p.expect(token.LPAREN)
- } else {
- // assume (type T)(params) syntax
- if opening.tok != token.LPAREN {
- p.errorExpected(opening.pos, "'('")
- }
- if !p.brack && p.tok == token.TYPE {
- p.next()
- list := p.parseTypeParams(scope, nil, token.RPAREN)
- rparen := p.expect(token.RPAREN)
- tparams = &ast.FieldList{Opening: lparen, List: list, Closing: rparen}
- lparen = p.expect(token.LPAREN)
- }
- }
- // type parameter lists must not be empty when we have unified parameter lists
- if p.mode&UnifiedParamLists != 0 && tparams != nil && tparams.NumFields() == 0 {
+ if p.mode&ParseTypeParams != 0 && acceptTParams && p.tok == token.LBRACK {
+ opening := p.pos
+ p.next()
+ // [T any](params) syntax
+ list := p.parseParameterList(scope, nil, token.RBRACK, p.parseParamDecl, true)
+ rbrack := p.expect(token.RBRACK)
+ tparams = &ast.FieldList{Opening: opening, List: list, Closing: rbrack}
+ // Type parameter lists must not be empty.
+ if tparams != nil && tparams.NumFields() == 0 {
p.error(tparams.Closing, "empty type parameter list")
tparams = nil // avoid follow-on errors
}
- } else {
- if opening.tok != token.LPAREN {
- p.errorExpected(lparen, "'('")
- }
}
+ opening := p.expect(token.LPAREN)
+
var fields []*ast.Field
if p.tok != token.RPAREN {
fields = p.parseParameterList(scope, nil, token.RPAREN, p.parseParamDecl, false)
}
rparen := p.expect(token.RPAREN)
- params = &ast.FieldList{Opening: lparen, List: fields, Closing: rparen}
+ params = &ast.FieldList{Opening: opening, List: fields, Closing: rparen}
return
}
-func (p *parser) parseResult(scope *ast.Scope, typeContext bool) *ast.FieldList {
+func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList {
if p.trace {
defer un(trace(p, "Result"))
}
if p.tok == token.LPAREN {
- _, results := p.parseParameters(scope, lookAhead{}, false)
+ _, results := p.parseParameters(scope, false)
return results
}
- typ := p.tryType(typeContext)
+ typ := p.tryType()
if typ != nil {
list := make([]*ast.Field, 1)
list[0] = &ast.Field{Type: typ}
@@ -1158,18 +1092,18 @@
return nil
}
-func (p *parser) parseFuncType(typeContext bool) (*ast.FuncType, *ast.Scope) {
+func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) {
if p.trace {
defer un(trace(p, "FuncType"))
}
pos := p.expect(token.FUNC)
scope := ast.NewScope(p.topScope) // function scope
- tparams, params := p.parseParameters(scope, lookAhead{}, true)
+ tparams, params := p.parseParameters(scope, true)
if tparams != nil {
p.error(tparams.Pos(), "function type cannot have type parameters")
}
- results := p.parseResult(scope, typeContext)
+ results := p.parseResult(scope)
return &ast.FuncType{Func: pos, Params: params, Results: results}, scope
}
@@ -1182,99 +1116,64 @@
doc := p.leadComment
var idents []*ast.Ident
var typ ast.Expr
- if p.mode&UnifiedParamLists != 0 {
- x := p.parseTypeName(nil)
- if ident, _ := x.(*ast.Ident); ident != nil {
- switch p.tok {
- case token.LBRACK:
- // generic method or embedded instantiated type
- lbrack := p.pos
- p.next()
- p.exprLev++
- x := p.parseExpr(true) // we don't know yet if we're a lhs or rhs expr
- p.exprLev--
- if name0, _ := x.(*ast.Ident); name0 != nil && p.tok != token.COMMA && p.tok != token.RBRACK {
- // generic method m[T any]
- scope := ast.NewScope(nil) // method scope
- list := p.parseParameterList(scope, name0, token.RBRACK, p.parseParamDecl, true)
- rbrack := p.expect(token.RBRACK)
- tparams := &ast.FieldList{Opening: lbrack, List: list, Closing: rbrack}
- _, params := p.parseParameters(scope, lookAhead{}, false)
- results := p.parseResult(scope, true)
- idents = []*ast.Ident{ident}
- typ = &ast.FuncType{Func: token.NoPos, TParams: tparams, Params: params, Results: results}
- } else {
- // embedded instantiated type
- // TODO(gri) should resolve all identifiers in x (doesn't matter for this prototype)
- list := []ast.Expr{x}
- if p.atComma("type argument list", token.RBRACK) {
- p.exprLev++
- for p.tok != token.RBRACK && p.tok != token.EOF {
- list = append(list, p.parseType(true))
- if !p.atComma("type argument list", token.RBRACK) {
- break
- }
- p.next()
- }
- p.exprLev--
- }
- rbrack := p.expectClosing(token.RBRACK, "type argument list")
- typ = &ast.CallExpr{Fun: ident, Lparen: lbrack, Args: list, Rparen: rbrack, Brackets: true}
- }
- case token.LPAREN:
- // ordinary method
+ x := p.parseTypeName(nil)
+ if ident, _ := x.(*ast.Ident); ident != nil {
+ switch {
+ case p.tok == token.LBRACK && p.mode&ParseTypeParams != 0:
+ // generic method or embedded instantiated type
+ lbrack := p.pos
+ p.next()
+ p.exprLev++
+ x := p.parseExpr(true) // we don't know yet if we're a lhs or rhs expr
+ p.exprLev--
+ if name0, _ := x.(*ast.Ident); name0 != nil && p.tok != token.COMMA && p.tok != token.RBRACK {
+ // generic method m[T any]
scope := ast.NewScope(nil) // method scope
- _, params := p.parseParameters(scope, lookAhead{}, false)
- results := p.parseResult(scope, true)
+ list := p.parseParameterList(scope, name0, token.RBRACK, p.parseParamDecl, true)
+ rbrack := p.expect(token.RBRACK)
+ tparams := &ast.FieldList{Opening: lbrack, List: list, Closing: rbrack}
+ // TODO(rfindley) refactor to share code with parseFuncType.
+ _, params := p.parseParameters(scope, false)
+ results := p.parseResult(scope)
idents = []*ast.Ident{ident}
- typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results}
- default:
- // embedded type
- typ = x
- p.resolve(typ)
+ typ = &ast.FuncType{Func: token.NoPos, TParams: tparams, Params: params, Results: results}
+ } else {
+ // embedded instantiated type
+ // TODO(rfindley) should resolve all identifiers in x.
+ list := []ast.Expr{x}
+ if p.atComma("type argument list", token.RBRACK) {
+ p.exprLev++
+ for p.tok != token.RBRACK && p.tok != token.EOF {
+ list = append(list, p.parseType())
+ if !p.atComma("type argument list", token.RBRACK) {
+ break
+ }
+ p.next()
+ }
+ p.exprLev--
+ }
+ rbrack := p.expectClosing(token.RBRACK, "type argument list")
+ typ = &ast.CallExpr{Fun: ident, Lparen: lbrack, Args: list, Rparen: rbrack, Brackets: true}
}
- } else {
- // embedded, possibly instantiated type
+ case p.tok == token.LPAREN:
+ // ordinary method
+ // TODO(rfindley) refactor to share code with parseFuncType.
+ scope := ast.NewScope(nil) // method scope
+ _, params := p.parseParameters(scope, false)
+ results := p.parseResult(scope)
+ idents = []*ast.Ident{ident}
+ typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results}
+ default:
+ // embedded type
typ = x
- if p.tok == token.LBRACK {
- // embedded instantiated interface
- typ = p.parseTypeInstance(typ, lookAhead{})
- }
+ p.resolve(typ)
}
} else {
- if p.tok == token.IDENT {
- x := p.parseTypeName(nil)
- if ident, _ := x.(*ast.Ident); ident != nil && (p.tok == token.LPAREN || p.brack && p.tok == token.LBRACK) {
- // method or embedded instantiated type
- opening := lookAhead{p.pos, p.tok}
- p.next()
- if opening.tok == token.LBRACK && p.tok != token.TYPE {
- // embedded instantiated type
- typ = x
- p.resolve(typ)
- typ = p.parseTypeInstance(typ, opening)
- } else {
- // method
- idents = []*ast.Ident{ident}
- scope := ast.NewScope(nil) // method scope
- tparams, params := p.parseParameters(scope, opening, true)
- results := p.parseResult(scope, true)
- typ = &ast.FuncType{Func: token.NoPos, TParams: tparams, Params: params, Results: results}
- }
- } else {
- // embedded, possibly instantiated type
- typ = x
- p.resolve(typ)
- if p.brack && p.tok == token.LBRACK {
- // embedded instantiated interface
- typ = p.parseTypeInstance(typ, lookAhead{})
- }
- }
- } else {
- // embedded, possibly instantiated type
- // (using enclosing parentheses to distinguish it from a method declaration)
- // TODO(gri) if p.brack is set, don't allow parenthesized embedded type
- typ = p.parseType(true)
+ // embedded, possibly instantiated type
+ typ = x
+ if p.tok == token.LBRACK && p.mode&ParseTypeParams != 0 {
+ // embedded instantiated interface
+ typ = p.parseTypeInstance(typ)
}
}
p.expectSemi() // call before accessing p.linecomment
@@ -1294,12 +1193,10 @@
lbrace := p.expect(token.LBRACE)
scope := ast.NewScope(nil) // interface scope
var list []*ast.Field
-L:
- for {
- switch p.tok {
- case token.IDENT, token.LPAREN:
+ for p.tok == token.IDENT || p.mode&ParseTypeParams != 0 && p.tok == token.TYPE {
+ if p.tok == token.IDENT {
list = append(list, p.parseMethodSpec(scope))
- case token.TYPE:
+ } else {
// all types in a type list share the same field name "type"
// (since type is a keyword, a Go program cannot have that field name)
name := []*ast.Ident{{NamePos: p.pos, Name: "type"}}
@@ -1309,10 +1206,10 @@
list = append(list, &ast.Field{Names: name, Type: typ})
}
p.expectSemi()
- default:
- break L
}
}
+ // TODO(rfindley): the error produced here could be improved, since we could
+ // accept a identifier, 'type', or a '}' at this point.
rbrace := p.expect(token.RBRACE)
return &ast.InterfaceType{
@@ -1325,21 +1222,21 @@
}
}
-func (p *parser) parseMapType(typeContext bool) *ast.MapType {
+func (p *parser) parseMapType() *ast.MapType {
if p.trace {
defer un(trace(p, "MapType"))
}
pos := p.expect(token.MAP)
p.expect(token.LBRACK)
- key := p.parseType(true)
+ key := p.parseType()
p.expect(token.RBRACK)
- value := p.parseType(typeContext)
+ value := p.parseType()
return &ast.MapType{Map: pos, Key: key, Value: value}
}
-func (p *parser) parseChanType(typeContext bool) *ast.ChanType {
+func (p *parser) parseChanType() *ast.ChanType {
if p.trace {
defer un(trace(p, "ChanType"))
}
@@ -1359,90 +1256,66 @@
p.expect(token.CHAN)
dir = ast.RECV
}
- value := p.parseType(typeContext)
+ value := p.parseType()
return &ast.ChanType{Begin: pos, Arrow: arrow, Dir: dir, Value: value}
}
-func (p *parser) parseTypeInstance(typ ast.Expr, opening lookAhead) ast.Expr {
+func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr {
if p.trace {
- defer un(trace(p, "TypeInstantiation"))
+ defer un(trace(p, "TypeInstance"))
}
- opening.consume(p)
-
- closingTok := token.RBRACK
- if !p.brack || opening.tok != token.LBRACK {
- if opening.tok != token.LPAREN {
- p.errorExpected(opening.pos, "'('")
- }
- closingTok = token.RPAREN
- }
+ opening := p.expect(token.LBRACK)
p.exprLev++
var list []ast.Expr
- for p.tok != closingTok && p.tok != token.EOF {
- list = append(list, p.parseType(true))
- if !p.atComma("type argument list", closingTok) {
+ for p.tok != token.RBRACK && p.tok != token.EOF {
+ list = append(list, p.parseType())
+ if !p.atComma("type argument list", token.RBRACK) {
break
}
p.next()
}
p.exprLev--
- closing := p.expectClosing(closingTok, "type argument list")
+ closing := p.expectClosing(token.RBRACK, "type argument list")
- return &ast.CallExpr{Fun: typ, Lparen: opening.pos, Args: list, Rparen: closing, Brackets: opening.tok == token.LBRACK}
+ return &ast.CallExpr{Fun: typ, Lparen: opening, Args: list, Rparen: closing, Brackets: true}
}
// If the result is an identifier, it is not resolved.
-// typeContext controls whether a trailing type parameter list (opening "(")
-// following a type is consumed. We need this to disambiguate an expression
-// such as []T(x) between the slice type [](T(x)) and the conversion ([]T)(x).
-// In typeContext, []T(x) is parsed as a slice type; otherwise it is parsed
-// as a conversion.
-// If we use [] for instantiation, when need this to disambiguate an expression
-// such as T[x] between the instantiated type (T[x]) and the index expression (T)(x).
-// But because an index expression requires a value and not a type before the index,
-// we can be more aggressive: whenever we are in a literal type ending in an element
-// type, we know for sure that we are in type context.
-func (p *parser) tryIdentOrType(typeContext bool) ast.Expr {
- // When []'s are used for type instantiation, there
- // are no ambiguities that prevent us from moving
- // forward with instantiation when seeing a "[".
- if p.brack {
- typeContext = true
- }
+func (p *parser) tryIdentOrType() ast.Expr {
switch p.tok {
case token.IDENT:
typ := p.parseTypeName(nil)
- if typeContext && (p.brack && p.tok == token.LBRACK || !p.brack && p.tok == token.LPAREN) {
- typ = p.parseTypeInstance(typ, lookAhead{})
+ if p.tok == token.LBRACK && p.mode&ParseTypeParams != 0 {
+ typ = p.parseTypeInstance(typ)
}
return typ
case token.LBRACK:
lbrack := p.expect(token.LBRACK)
alen := p.parseArrayLen()
p.expect(token.RBRACK)
- elt := p.parseType(typeContext)
+ elt := p.parseType()
return &ast.ArrayType{Lbrack: lbrack, Len: alen, Elt: elt}
case token.STRUCT:
return p.parseStructType()
case token.MUL:
- return p.parsePointerType(typeContext)
+ return p.parsePointerType()
case token.FUNC:
- typ, _ := p.parseFuncType(typeContext)
+ typ, _ := p.parseFuncType()
return typ
case token.INTERFACE:
return p.parseInterfaceType()
case token.MAP:
- return p.parseMapType(typeContext)
+ return p.parseMapType()
case token.CHAN, token.ARROW:
- return p.parseChanType(typeContext)
+ return p.parseChanType()
case token.LPAREN:
lparen := p.pos
p.next()
- typ := p.parseType(true)
+ typ := p.parseType()
rparen := p.expect(token.RPAREN)
return &ast.ParenExpr{Lparen: lparen, X: typ, Rparen: rparen}
}
@@ -1451,8 +1324,8 @@
return nil
}
-func (p *parser) tryType(typeContext bool) ast.Expr {
- typ := p.tryIdentOrType(typeContext)
+func (p *parser) tryType() ast.Expr {
+ typ := p.tryIdentOrType()
if typ != nil {
p.resolve(typ)
}
@@ -1512,7 +1385,7 @@
defer un(trace(p, "FuncTypeOrLit"))
}
- typ, scope := p.parseFuncType(false)
+ typ, scope := p.parseFuncType()
if p.tok != token.LBRACE {
// function type only
return typ
@@ -1560,7 +1433,7 @@
return p.parseFuncTypeOrLit()
}
- if typ := p.tryIdentOrType(false); typ != nil { // do not consume trailing type parameters
+ if typ := p.tryIdentOrType(); typ != nil { // do not consume trailing type parameters
// could be type for composite literal or conversion
_, isIdent := typ.(*ast.Ident)
assert(!isIdent, "type cannot be identifier")
@@ -1595,7 +1468,7 @@
// type switch: typ == nil
p.next()
} else {
- typ = p.parseType(true)
+ typ = p.parseType()
}
rparen := p.expect(token.RPAREN)
@@ -1608,7 +1481,7 @@
}
lbrack := p.expect(token.LBRACK)
- if p.brack && p.tok == token.RBRACK {
+ if p.tok == token.RBRACK {
// empty index, slice or index expressions are not permitted;
// accept them for parsing tolerance, but complain
p.errorExpected(p.pos, "operand")
@@ -1621,6 +1494,7 @@
var args []ast.Expr
var index [N]ast.Expr
var colons [N - 1]token.Pos
+ var firstComma token.Pos
if p.tok != token.COLON {
// We can't know if we have an index expression or a type instantiation;
// so even if we see a (named) type we are not going to be in type context.
@@ -1639,12 +1513,13 @@
}
}
case token.COMMA:
+ firstComma = p.pos
// instance expression
args = append(args, index[0])
for p.tok == token.COMMA {
p.next()
if p.tok != token.RBRACK && p.tok != token.EOF {
- args = append(args, p.parseType(true))
+ args = append(args, p.parseType())
}
}
}
@@ -1676,57 +1551,15 @@
return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}
}
+ if p.mode&ParseTypeParams == 0 {
+ p.error(firstComma, "expected ']' or ':', found ','")
+ return &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()}
+ }
+
// instance expression
return &ast.CallExpr{Fun: x, Lparen: lbrack, Args: args, Rparen: rbrack, Brackets: true}
}
-func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
- if p.trace {
- defer un(trace(p, "IndexOrSlice"))
- }
-
- const N = 3 // change the 3 to 2 to disable 3-index slices
- lbrack := p.expect(token.LBRACK)
- p.exprLev++
- var index [N]ast.Expr
- var colons [N - 1]token.Pos
- if p.tok != token.COLON {
- index[0] = p.parseRhs()
- }
- ncolons := 0
- for p.tok == token.COLON && ncolons < len(colons) {
- colons[ncolons] = p.pos
- ncolons++
- p.next()
- if p.tok != token.COLON && p.tok != token.RBRACK && p.tok != token.EOF {
- index[ncolons] = p.parseRhs()
- }
- }
- p.exprLev--
- rbrack := p.expect(token.RBRACK)
-
- if ncolons > 0 {
- // slice expression
- slice3 := false
- if ncolons == 2 {
- slice3 = true
- // Check presence of 2nd and 3rd index here rather than during type-checking
- // to prevent erroneous programs from passing through gofmt (was issue 7305).
- if index[1] == nil {
- p.error(colons[0], "2nd index required in 3-index slice")
- index[1] = &ast.BadExpr{From: colons[0] + 1, To: colons[1]}
- }
- if index[2] == nil {
- p.error(colons[1], "3rd index required in 3-index slice")
- index[2] = &ast.BadExpr{From: colons[1] + 1, To: rbrack}
- }
- }
- return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: index[0], High: index[1], Max: index[2], Slice3: slice3, Rbrack: rbrack}
- }
-
- return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}
-}
-
func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
if p.trace {
defer un(trace(p, "CallOrConversion"))
@@ -1873,28 +1706,6 @@
return x
}
-// isTypeName reports whether x is a (qualified) TypeName.
-func isTypeName(x ast.Expr) bool {
- switch t := x.(type) {
- case *ast.BadExpr:
- case *ast.Ident:
- case *ast.SelectorExpr:
- _, isIdent := t.X.(*ast.Ident)
- return isIdent
- default:
- return false // all other nodes are not type names
- }
- return true
-}
-
-// If x is of the form *T, deref returns T, otherwise it returns x.
-func deref(x ast.Expr) ast.Expr {
- if p, isPtr := x.(*ast.StarExpr); isPtr {
- x = p.X
- }
- return x
-}
-
// If x is of the form (T), unparen returns unparen(T), otherwise it returns x.
func unparen(x ast.Expr) ast.Expr {
if p, isParen := x.(*ast.ParenExpr); isParen {
@@ -1951,11 +1762,7 @@
if lhs {
p.resolve(x)
}
- if p.brack {
- x = p.parseIndexOrSliceOrInstance(p.checkExpr(x))
- } else {
- x = p.parseIndexOrSlice(p.checkExpr(x))
- }
+ x = p.parseIndexOrSliceOrInstance(p.checkExpr(x))
case token.LPAREN:
if lhs {
p.resolve(x)
@@ -1973,12 +1780,12 @@
}
// x is possibly a composite literal type
case *ast.CallExpr:
- if p.brack != t.Brackets || p.exprLev < 0 {
+ if !t.Brackets || p.exprLev < 0 {
return
}
// x is possibly a composite literal type
case *ast.IndexExpr:
- if !p.brack || p.exprLev < 0 {
+ if p.exprLev < 0 {
return
}
// x is possibly a composite literal type
@@ -2328,7 +2135,7 @@
// accept potential variable declaration but complain
if p.tok == token.VAR {
p.next()
- p.error(p.pos, fmt.Sprintf("var declaration not allowed in 'IF' initializer"))
+ p.error(p.pos, "var declaration not allowed in 'IF' initializer")
}
init, _ = p.parseSimpleStmt(basic)
}
@@ -2410,10 +2217,10 @@
defer un(trace(p, "TypeList"))
}
- list = append(list, p.parseType(true))
+ list = append(list, p.parseType())
for p.tok == token.COMMA {
p.next()
- list = append(list, p.parseType(true))
+ list = append(list, p.parseType())
}
return
@@ -2809,7 +2616,7 @@
pos := p.pos
idents := p.parseIdentList()
- typ := p.tryType(true)
+ typ := p.tryType()
var values []ast.Expr
// always permit optional initialization for more tolerant parsing
if p.tok == token.ASSIGN {
@@ -2850,16 +2657,8 @@
}
func (p *parser) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 *ast.Ident, closeTok token.Token) {
- if closeTok == token.RBRACK {
- p.brack = true // switch to using []'s instead of ()'s from now on
- }
p.openScope()
- var list []*ast.Field
- if p.mode&UnifiedParamLists != 0 {
- list = p.parseParameterList(p.topScope, name0, closeTok, p.parseParamDecl, true)
- } else {
- list = p.parseTypeParams(p.topScope, name0, closeTok)
- }
+ list := p.parseParameterList(p.topScope, name0, closeTok, p.parseParamDecl, true)
closePos := p.expect(closeTok)
spec.TParams = &ast.FieldList{Opening: openPos, List: list, Closing: closePos}
// Type alias cannot have type parameters. Accept them for robustness but complain.
@@ -2867,7 +2666,7 @@
p.error(p.pos, "generic type cannot be alias")
p.next()
}
- spec.Type = p.parseType(true)
+ spec.Type = p.parseType()
p.closeScope()
}
@@ -2889,80 +2688,27 @@
case token.LBRACK:
lbrack := p.pos
p.next()
- if p.mode&UnifiedParamLists != 0 {
- if p.tok == token.IDENT {
- // array type or generic type [T any]
- p.exprLev++
- x := p.parseExpr(true) // we don't know yet if we're a lhs or rhs expr
- p.exprLev--
- if name0, _ := x.(*ast.Ident); name0 != nil && p.tok != token.RBRACK {
- // generic type [T any];
- p.parseGenericType(spec, lbrack, name0, token.RBRACK)
- } else {
- // array type
- // TODO(gri) should resolve all identifiers in x (doesn't matter for this prototype)
- p.expect(token.RBRACK)
- elt := p.parseType(true)
- spec.Type = &ast.ArrayType{Lbrack: lbrack, Len: x, Elt: elt}
- }
+ if p.tok == token.IDENT {
+ // array type or generic type [T any]
+ p.exprLev++
+ x := p.parseExpr(true) // we don't know yet if we're a lhs or rhs expr
+ p.exprLev--
+ if name0, _ := x.(*ast.Ident); p.mode&ParseTypeParams != 0 && name0 != nil && p.tok != token.RBRACK {
+ // generic type [T any];
+ p.parseGenericType(spec, lbrack, name0, token.RBRACK)
} else {
// array type
- alen := p.parseArrayLen()
+ // TODO(rfindley) should resolve all identifiers in x.
p.expect(token.RBRACK)
- elt := p.parseType(true)
- spec.Type = &ast.ArrayType{Lbrack: lbrack, Len: alen, Elt: elt}
+ elt := p.parseType()
+ spec.Type = &ast.ArrayType{Lbrack: lbrack, Len: x, Elt: elt}
}
} else {
- switch p.tok {
- case token.TYPE:
- // generic type
- p.next()
- p.parseGenericType(spec, lbrack, nil, token.RBRACK)
- case token.IDENT:
- // array type or generic type without "type" keyword
- p.exprLev++
- x := p.parseExpr(true) // we don't know yet if we're a lhs or rhs expr
- p.exprLev--
- if name0, _ := x.(*ast.Ident); name0 != nil {
- // array type or generic type without "type" keyword;
- // assume a generic type and then decide based on next token and use of name0
- next := p.tok
- pbrack := p.brack // changed by parseGenericType; restore if we have an array type
- p.parseGenericType(spec, lbrack, name0, token.RBRACK)
- if next == token.RBRACK && !usesName(spec.Type, name0.Name) {
- // assume array type
- p.brack = pbrack
- spec.TParams = nil
- spec.Type = &ast.ArrayType{Lbrack: lbrack, Len: x, Elt: spec.Type}
- }
- } else {
- // array type
- // TODO(gri) should resolve all identifiers in x (doesn't matter for this prototype)
- p.expect(token.RBRACK)
- elt := p.parseType(true)
- spec.Type = &ast.ArrayType{Lbrack: lbrack, Len: x, Elt: elt}
- }
- default:
- // array type
- alen := p.parseArrayLen()
- p.expect(token.RBRACK)
- elt := p.parseType(true)
- spec.Type = &ast.ArrayType{Lbrack: lbrack, Len: alen, Elt: elt}
- }
- }
-
- case token.LPAREN:
- lparen := p.pos
- p.next()
- if !p.brack && p.tok == token.TYPE {
- // generic type
- p.next()
- p.parseGenericType(spec, lparen, nil, token.RPAREN)
- } else {
- // parenthesized type
- typ := p.parseType(true)
- rparen := p.expect(token.RPAREN)
- spec.Type = &ast.ParenExpr{Lparen: lparen, X: typ, Rparen: rparen}
+ // array type
+ alen := p.parseArrayLen()
+ p.expect(token.RBRACK)
+ elt := p.parseType()
+ spec.Type = &ast.ArrayType{Lbrack: lbrack, Len: alen, Elt: elt}
}
default:
@@ -2972,7 +2718,7 @@
spec.Assign = p.pos
p.next()
}
- spec.Type = p.parseType(true)
+ spec.Type = p.parseType()
}
p.expectSemi() // call before accessing p.linecomment
@@ -2981,27 +2727,6 @@
return spec
}
-// uses reports whether name appears anywhere in x except as array length.
-func usesName(x ast.Expr, name string) (used bool) {
- ast.Inspect(x, func(n ast.Node) bool {
- switch n := n.(type) {
- case *ast.Ident:
- if n.Name == name {
- used = true
- }
- return false
- case *ast.ArrayType:
- // ignore n.Len (and for slices, n.Len == nil)
- if usesName(n.Elt, name) {
- used = true
- }
- return false
- }
- return true
- })
- return
-}
-
func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.GenDecl {
if p.trace {
defer un(trace(p, "GenDecl("+keyword.String()+")"))
@@ -3044,13 +2769,13 @@
var recv *ast.FieldList
if p.tok == token.LPAREN {
- _, recv = p.parseParameters(scope, lookAhead{}, false)
+ _, recv = p.parseParameters(scope, false)
}
ident := p.parseIdent()
- tparams, params := p.parseParameters(scope, lookAhead{}, true)
- results := p.parseResult(scope, true)
+ tparams, params := p.parseParameters(scope, true)
+ results := p.parseResult(scope)
var body *ast.BlockStmt
if p.tok == token.LBRACE {
@@ -3185,14 +2910,13 @@
}
return &ast.File{
- Doc: doc,
- Package: pos,
- Name: ident,
- Decls: decls,
- Scope: p.pkgScope,
- Imports: p.imports,
- Unresolved: p.unresolved[0:i],
- Comments: p.comments,
- UseBrackets: p.brack,
+ Doc: doc,
+ Package: pos,
+ Name: ident,
+ Decls: decls,
+ Scope: p.pkgScope,
+ Imports: p.imports,
+ Unresolved: p.unresolved[0:i],
+ Comments: p.comments,
}
}
diff --git a/src/go/parser/parser_test.go b/src/go/parser/parser_test.go
index 25a374e..e204854 100644
--- a/src/go/parser/parser_test.go
+++ b/src/go/parser/parser_test.go
@@ -82,6 +82,14 @@
}
}
+func TestIssue42951(t *testing.T) {
+ path := "./testdata/issue42951"
+ _, err := ParseDir(token.NewFileSet(), path, nil, 0)
+ if err != nil {
+ t.Errorf("ParseDir(%s): %v", path, err)
+ }
+}
+
func TestParseExpr(t *testing.T) {
// just kicking the tires:
// a valid arithmetic expression
diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go
index bc7caa2..3676c27 100644
--- a/src/go/parser/short_test.go
+++ b/src/go/parser/short_test.go
@@ -49,168 +49,100 @@
`package p; type T = int`,
`package p; type (T = p.T; _ = struct{}; x = *T)`,
`package p; type T (*int)`,
-
- // structs with parameterized embedded fields (for symmetry with interfaces)
`package p; type _ struct{ ((int)) }`,
`package p; type _ struct{ (*(int)) }`,
`package p; type _ struct{ ([]byte) }`, // disallowed by type-checker
-
- // type parameters
- `package p; type T(type P) struct { P }`,
- `package p; type T(type P comparable) struct { P }`,
- `package p; type T(type P comparable(P)) struct { P }`,
- `package p; type T(type P1, P2) struct { P1; f []P2 }`,
- `package p; type _ []T(int)`,
-
- `package p; type T[type P] struct { P }`,
- `package p; type T[type P comparable] struct { P }`,
- `package p; type T[type P comparable[P]] struct { P }`,
- `package p; type T[type P1, P2] struct { P1; f []P2 }`,
- `package p; type _[type] int; type _ []T[int]`,
-
`package p; var _ = func()T(nil)`,
- `package p; func _(type)()`,
- `package p; func _(type)()()`,
`package p; func _(T (P))`,
`package p; func _(T []E)`,
`package p; func _(T [P]E)`,
- `package p; func _(x T(P1, P2, P3))`,
- `package p; func _(x p.T(Q))`,
- `package p; func _(p.T(Q))`,
-
- `package p; var _ = func()T(nil)`,
- `package p; func _[type]()`,
- `package p; func _[type]()()`,
- `package p; func _(T (P))`,
- `package p; func _[type](); func _(T []E)`,
- `package p; func _[type](); func _(T [P]E)`,
- `package p; func _[type](); func _(x T[P1, P2, P3])`,
- `package p; func _[type](); func _(x p.T[Q])`,
- `package p; func _[type](); func _(p.T[Q])`,
-
- `package p; type _[type] int; var _ T[chan int]`,
- `package p; func f[A, B](); func _() { _ = f[int, int] }`,
-
- // optional "type" keyword for generic types using square brackets
- `package p; type _[A interface{},] struct{}`,
- `package p; type _[A interface{}] struct{}`,
- `package p; type _[A, B,] struct{}`,
- `package p; type _[A, B] struct{}`,
- `package p; type _[A,] struct{}`,
- `package p; type _ [A+B]struct{}`, // this is an array!
-
- `package p; type _[A]struct{}`, // this is an array
- `package p; type _[A] struct{ A }`, // this is not an array!
-
- // optional "type" keyword for generic functions using square brackets
- `package p; func _[]()`,
- `package p; func _[T]()`,
- `package p; func _[T](x T)`,
- `package p; func _[T1, T2](x T)`,
-
- `package p; func _[](); func (R) _[]()`,
- `package p; func _[](); func (R[P]) _[T]()`,
- `package p; func _[](); func (_ R[P]) _[T](x T)`,
- `package p; func _[](); func (_ R[P, Q]) _[T1, T2](x T)`,
-
- // need for parentheses to disambiguate
- `package p; var _ = [](T(int)){}`,
- `package p; var _ = [10](T(int)){}`,
- `package p; var _ = func()(T(int)){}`,
- `package p; var _ = map[T(int)](T(int)){}`,
- `package p; var _ = chan (T(int))(x)`,
- `package p; func _((T(P)))`,
- `package p; func _((T(P1, P2, P3)))`,
-
- `package p; func _(type *P)()`,
- `package p; func _(type *P B)()`,
- `package p; func _(type P, *Q interface{})()`,
-
- `package p; func _((T(P))) T(P)`,
- `package p; func _(_ T(P), T (P)) T(P)`,
-
- // no need for parentheses to disambiguate
- `package p; type _[type] int; var _ = []T[int]{}`,
- `package p; type _[type] int; var _ = [10]T[int]{}`,
- `package p; type _[type] int; var _ = func()T[int]{}`,
- `package p; type _[type] int; var _ = map[T[int]]T[int]{}`,
- `package p; type _[type] int; var _ = chan T[int](x)`,
- `package p; type _[type] int; func _(T[P])`,
- `package p; type _[type] int; func _(T[P1, P2, P3])`,
-
- `package p; type _[type] int; func _[type *P]()`,
- `package p; type _[type] int; func _[type *P B]()`,
- `package p; type _[type] int; func _[type P, *Q interface{}]()`,
-
- `package p; type _[type] int; func _(T[P]) T[P]`,
- `package p; type _[type] int; func _(_ T[P], T (P)) T[P]`,
-
- // method type parameters (if methodTypeParamsOk)
- `package p; func _(type A, B)(a A) B`,
- `package p; func _(type A, B C)(a A) B`,
- `package p; func _(type A, B C(A, B))(a A) B`,
- `package p; func (T) _(type A, B)(a A) B`,
- `package p; func (T) _(type A, B C)(a A) B`,
- `package p; func (T) _(type A, B C(A, B))(a A) B`,
- `package p; type _ interface { _(type A, B)(a A) B }`,
- `package p; type _ interface { _(type A, B C)(a A) B }`,
- `package p; type _ interface { _(type A, B C(A, B))(a A) B }`,
-
- `package p; type _[type] int; func _[type A, B](a A) B`,
- `package p; type _[type] int; func _[type A, B C](a A) B`,
- `package p; type _[type] int; func _[type A, B C[A, B]](a A) B`,
- `package p; type _[type] int; func (T) _[type A, B](a A) B`,
- `package p; type _[type] int; func (T) _[type A, B C](a A) B`,
- `package p; type _[type] int; func (T) _[type A, B C[A, B]](a A) B`,
- `package p; type _[type] int; type _ interface { _[type A, B](a A) B }`,
- `package p; type _[type] int; type _ interface { _[type A, B C](a A) B }`,
- `package p; type _[type] int; type _ interface { _[type A, B C[A, B]](a A) B }`,
-
- // type bounds
- `package p; func _(type T1, T2 interface{})(x T1) T2`,
- `package p; func _(type T1 interface{ m() }, T2, T3 interface{})(x T1, y T3) T2`,
-
- `package p; type _[type] int; func _[type T1, T2 interface{}](x T1) T2`,
- `package p; type _[type] int; func _[type T1 interface{ m() }, T2, T3 interface{}](x T1, y T3) T2`,
-
- // embedded types
- `package p; type _ struct{ (T(P)) }`,
- `package p; type _ struct{ (T(struct{a, b, c int})) }`,
+ `package p; type _ [A+B]struct{}`,
+ `package p; func (R) _()`,
`package p; type _ struct{ f [n]E }`,
`package p; type _ struct{ f [a+b+c+d]E }`,
+ `package p; type I1 interface{}; type I2 interface{ I1 }`,
+}
- `package p; type _[type] int; type _ struct{ T[P] }`,
- `package p; type _[type] int; type _ struct{ T[struct{a, b, c int}] }`,
- `package p; type _[type] int; type _ struct{ f [n]E }`,
- `package p; type _[type] int; type _ struct{ f [a+b+c+d]E }`,
+// validWithTParamsOnly holds source code examples that are valid if
+// ParseTypeParams is set, but invalid if not. When checking with the
+// ParseTypeParams set, errors are ignored.
+var validWithTParamsOnly = []string{
+ `package p; type _ []T[ /* ERROR "expected ';', found '\['" */ int]`,
+ `package p; type T[P any /* ERROR "expected ']', found any" */ ] struct { P }`,
+ `package p; type T[P comparable /* ERROR "expected ']', found comparable" */ ] struct { P }`,
+ `package p; type T[P comparable /* ERROR "expected ']', found comparable" */ [P]] struct { P }`,
+ `package p; type T[P1, /* ERROR "expected ']', found ','" */ P2 any] struct { P1; f []P2 }`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ T any]()()`,
+ `package p; func _(T (P))`,
+ `package p; func f[ /* ERROR "expected '\(', found '\['" */ A, B any](); func _() { _ = f[int, int] }`,
+ `package p; func _(x /* ERROR "mixed named and unnamed parameters" */ T[P1, P2, P3])`,
+ `package p; func _(x /* ERROR "mixed named and unnamed parameters" */ p.T[Q])`,
+ `package p; func _(p.T[ /* ERROR "missing ',' in parameter list" */ Q])`,
+ `package p; type _[A interface /* ERROR "expected ']', found 'interface'" */ {},] struct{}`,
+ `package p; type _[A interface /* ERROR "expected ']', found 'interface'" */ {}] struct{}`,
+ `package p; type _[A, /* ERROR "expected ']', found ','" */ B any,] struct{}`,
+ `package p; type _[A, /* ERROR "expected ']', found ','" */ B any] struct{}`,
+ `package p; type _[A any /* ERROR "expected ']', found any" */,] struct{}`,
+ `package p; type _[A any /* ERROR "expected ']', found any" */ ]struct{}`,
+ `package p; type _[A any /* ERROR "expected ']', found any" */ ] struct{ A }`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ T any]()`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ T any](x T)`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ T1, T2 any](x T)`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ A, B any](a A) B`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ A, B C](a A) B`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ A, B C[A, B]](a A) B`,
+ `package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B any](a A) B`,
+ `package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B C](a A) B`,
+ `package p; func (T) _[ /* ERROR "expected '\(', found '\['" */ A, B C[A, B]](a A) B`,
+ `package p; type _[A, /* ERROR "expected ']', found ','" */ B any] interface { _(a A) B }`,
+ `package p; type _[A, /* ERROR "expected ']', found ','" */ B C[A, B]] interface { _(a A) B }`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ T1, T2 interface{}](x T1) T2`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ T1 interface{ m() }, T2, T3 interface{}](x T1, y T3) T2`,
+ `package p; var _ = [ /* ERROR "expected expression" */ ]T[int]{}`,
+ `package p; var _ = [ /* ERROR "expected expression" */ 10]T[int]{}`,
+ `package p; var _ = func /* ERROR "expected expression" */ ()T[int]{}`,
+ `package p; var _ = map /* ERROR "expected expression" */ [T[int]]T[int]{}`,
+ `package p; var _ = chan /* ERROR "expected expression" */ T[int](x)`,
+ `package p; func _(_ T[ /* ERROR "missing ',' in parameter list" */ P], T P) T[P]`,
+ `package p; var _ T[ /* ERROR "expected ';', found '\['" */ chan int]`,
- // interfaces with type lists
- `package p; type _ interface{type int}`,
- `package p; type _ interface{type int, float32; type bool; m(); type string;}`,
+ // TODO(rfindley) this error message could be improved.
+ `package p; func (_ /* ERROR "mixed named and unnamed parameters" */ R[P]) _[T any](x T)`,
+ `package p; func (_ /* ERROR "mixed named and unnamed parameters" */ R[ P, Q]) _[T1, T2 any](x T)`,
- `package p; type _[type] int; type _ interface{type int}`,
- `package p; type _[type] int; type _ interface{type int, float32; type bool; m(); type string;}`,
-
- // interfaces with parenthesized embedded and possibly parameterized interfaces
- `package p; type I1 interface{}; type I2 interface{ (I1) }`,
- `package p; type I1(type T) interface{}; type I2 interface{ (I1(int)) }`,
- `package p; type I1(type T) interface{}; type I2(type T) interface{ (I1(T)) }`,
-
- `package p; type _[type] int; type I1 interface{}; type I2 interface{ (I1) }`,
- `package p; type I1[type T] interface{}; type I2 interface{ I1[int] }`,
- `package p; type I1[type T] interface{}; type I2[type T] interface{ I1[T] }`,
+ `package p; func (R[P] /* ERROR "missing element type" */ ) _[T any]()`,
+ `package p; func _(T[P] /* ERROR "missing element type" */ )`,
+ `package p; func _(T[P1, /* ERROR "expected ']', found ','" */ P2, P3 ])`,
+ `package p; func _(T[P] /* ERROR "missing element type" */ ) T[P]`,
+ `package p; type _ struct{ T[P] /* ERROR "missing element type" */ }`,
+ `package p; type _ struct{ T[struct /* ERROR "expected expression" */ {a, b, c int}] }`,
+ `package p; type _ interface{type /* ERROR "expected '}', found 'type'" */ int}`,
+ `package p; type _ interface{type /* ERROR "expected '}', found 'type'" */ int, float32; type bool; m(); type string;}`,
+ `package p; type I1[T any /* ERROR "expected ']', found any" */ ] interface{}; type I2 interface{ I1[int] }`,
+ `package p; type I1[T any /* ERROR "expected ']', found any" */ ] interface{}; type I2[T any] interface{ I1[T] }`,
+ `package p; type _ interface { f[ /* ERROR "expected ';', found '\['" */ T any]() }`,
}
func TestValid(t *testing.T) {
- for _, src := range valids {
- checkErrors(t, src, src)
- }
+ t.Run("no tparams", func(t *testing.T) {
+ for _, src := range valids {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors, false)
+ }
+ })
+ t.Run("tparams", func(t *testing.T) {
+ for _, src := range valids {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors|ParseTypeParams, false)
+ }
+ for _, src := range validWithTParamsOnly {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors|ParseTypeParams, 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)
+ const src = `package p; var _ = T[P]{}`
+ checkErrors(t, src, src, DeclarationErrors|AllErrors|ParseTypeParams, true)
}
var invalids = []string{
@@ -236,7 +168,6 @@
`package p; var a = chan /* ERROR "expected expression" */ int;`,
`package p; var a = []int{[ /* ERROR "expected expression" */ ]int};`,
`package p; var a = ( /* ERROR "expected expression" */ []int);`,
- `package p; var a = a[[ /* ERROR "expected expression" */ ]int:[]int];`,
`package p; var a = <- /* ERROR "expected expression" */ chan int;`,
`package p; func f() { select { case _ <- chan /* ERROR "expected expression" */ int: } };`,
`package p; func f() { _ = (<-<- /* ERROR "expected 'chan'" */ chan int)(nil) };`,
@@ -259,15 +190,14 @@
`package p; func f() { go f /* ERROR HERE "function must be invoked" */ }`,
`package p; func f() { defer func() {} /* ERROR HERE "function must be invoked" */ }`,
`package p; func f() { go func() { func() { f(x func /* ERROR "missing ','" */ (){}) } } }`,
- //`package p; func f(x func(), u v func /* ERROR "missing ','" */ ()){}`,
-
- // type parameters
- `package p; var _ func( /* ERROR "cannot have type parameters" */ type T)(T)`,
`package p; func _() (type /* ERROR "found 'type'" */ T)(T)`,
`package p; func (type /* ERROR "found 'type'" */ T)(T) _()`,
`package p; type _[A+B, /* ERROR "expected ']'" */ ] int`,
- `package p; type _[type _] int; var _ = T[] /* ERROR "expected operand" */ {}`,
- `package p; type T[P any] = /* ERROR "cannot be alias" */ T0`,
+
+ // TODO: this error should be positioned on the ':'
+ `package p; var a = a[[]int:[ /* ERROR "expected expression" */ ]int];`,
+ // TODO: the compiler error is better here: "cannot parenthesize embedded type"
+ `package p; type I1 interface{}; type I2 interface{ (/* ERROR "expected '}', found '\('" */ I1) }`,
// issue 8656
`package p; func f() (a b string /* ERROR "missing ','" */ , ok bool)`,
@@ -285,17 +215,56 @@
// issue 11611
`package p; type _ struct { int, } /* ERROR "expected 'IDENT', found '}'" */ ;`,
`package p; type _ struct { int, float } /* ERROR "expected type, found '}'" */ ;`,
- //`package p; type _ struct { ( /* ERROR "cannot parenthesize embedded type" */ int) };`,
- //`package p; func _()(x, y, z ... /* ERROR "expected '\)', found '...'" */ int){}`,
- //`package p; func _()(... /* ERROR "expected type, found '...'" */ int){}`,
// issue 13475
`package p; func f() { if true {} else ; /* ERROR "expected if statement or block" */ }`,
`package p; func f() { if true {} else defer /* ERROR "expected if statement or block" */ f() }`,
}
+// invalidNoTParamErrs holds invalid source code examples annotated with the
+// error messages produced when ParseTypeParams is not set.
+var invalidNoTParamErrs = []string{
+ `package p; type _[_ any /* ERROR "expected ']', found any" */ ] int; var _ = T[]{}`,
+ `package p; type T[P any /* ERROR "expected ']', found any" */ ] = T0`,
+ `package p; var _ func[ /* ERROR "expected '\(', found '\['" */ T any](T)`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ ]()`,
+ `package p; type _[A, /* ERROR "expected ']', found ','" */] struct{ A }`,
+ `package p; func _[ /* ERROR "expected '\(', found '\['" */ type P, *Q interface{}]()`,
+}
+
+// invalidTParamErrs holds invalid source code examples annotated with the
+// error messages produced when ParseTypeParams is set.
+var invalidTParamErrs = []string{
+ `package p; type _[_ any] int; var _ = T[] /* ERROR "expected operand" */ {}`,
+ `package p; type T[P any] = /* ERROR "cannot be alias" */ T0`,
+ `package p; var _ func[ /* ERROR "cannot have type parameters" */ T any](T)`,
+ `package p; func _[]/* ERROR "empty type parameter list" */()`,
+
+ // TODO(rfindley) a better location would be after the ']'
+ `package p; type _[A/* ERROR "all type parameters must be named" */,] struct{ A }`,
+
+ // TODO(rfindley) this error is confusing.
+ `package p; func _[type /* ERROR "all type parameters must be named" */P, *Q interface{}]()`,
+}
+
func TestInvalid(t *testing.T) {
- for _, src := range invalids {
- checkErrors(t, src, src)
- }
+ t.Run("no tparams", func(t *testing.T) {
+ for _, src := range invalids {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors, true)
+ }
+ for _, src := range validWithTParamsOnly {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors, true)
+ }
+ for _, src := range invalidNoTParamErrs {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors, true)
+ }
+ })
+ t.Run("tparams", func(t *testing.T) {
+ for _, src := range invalids {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors|ParseTypeParams, true)
+ }
+ for _, src := range invalidTParamErrs {
+ checkErrors(t, src, src, DeclarationErrors|AllErrors|ParseTypeParams, true)
+ }
+ })
}
diff --git a/src/go/parser/testdata/chans.go2 b/src/go/parser/testdata/chans.go2
index d6ca4df..fad2bce 100644
--- a/src/go/parser/testdata/chans.go2
+++ b/src/go/parser/testdata/chans.go2
@@ -10,24 +10,24 @@
//
// This is a convenient way to exit a goroutine sending values when
// the receiver stops reading them.
-func Ranger(type T)() (*Sender(T), *Receiver(T)) {
+func Ranger[T any]() (*Sender[T], *Receiver[T]) {
c := make(chan T)
d := make(chan bool)
- s := &Sender(T){values: c, done: d}
- r := &Receiver(T){values: c, done: d}
+ s := &Sender[T]{values: c, done: d}
+ r := &Receiver[T]{values: c, done: d}
runtime.SetFinalizer(r, r.finalize)
return s, r
}
// A sender is used to send values to a Receiver.
-type Sender(type T) struct {
+type Sender[T any] struct {
values chan<- T
done <-chan bool
}
// Send sends a value to the receiver. It returns whether any more
// values may be sent; if it returns false the value was not sent.
-func (s *Sender(T)) Send(v T) bool {
+func (s *Sender[T]) Send(v T) bool {
select {
case s.values <- v:
return true
@@ -38,12 +38,12 @@
// Close tells the receiver that no more values will arrive.
// After Close is called, the Sender may no longer be used.
-func (s *Sender(T)) Close() {
+func (s *Sender[T]) Close() {
close(s.values)
}
// A Receiver receives values from a Sender.
-type Receiver(type T) struct {
+type Receiver[T any] struct {
values <-chan T
done chan<- bool
}
@@ -51,12 +51,12 @@
// Next returns the next value from the channel. The bool result
// indicates whether the value is valid, or whether the Sender has
// been closed and no more values will be received.
-func (r *Receiver(T)) Next() (T, bool) {
+func (r *Receiver[T]) Next() (T, bool) {
v, ok := <-r.values
return v, ok
}
// finalize is a finalizer for the receiver.
-func (r *Receiver(T)) finalize() {
+func (r *Receiver[T]) finalize() {
close(r.done)
}
diff --git a/src/go/parser/testdata/chansB.go2 b/src/go/parser/testdata/chansB.go2
deleted file mode 100644
index d2ee3a3..0000000
--- a/src/go/parser/testdata/chansB.go2
+++ /dev/null
@@ -1,62 +0,0 @@
-package chans
-
-import "runtime"
-
-// Ranger returns a Sender and a Receiver. The Receiver provides a
-// Next method to retrieve values. The Sender provides a Send method
-// to send values and a Close method to stop sending values. The Next
-// method indicates when the Sender has been closed, and the Send
-// method indicates when the Receiver has been freed.
-//
-// This is a convenient way to exit a goroutine sending values when
-// the receiver stops reading them.
-func Ranger[type T]() (*Sender[T], *Receiver[T]) {
- c := make(chan T)
- d := make(chan bool)
- s := &Sender[T]{values: c, done: d}
- r := &Receiver[T]{values: c, done: d}
- runtime.SetFinalizer(r, r.finalize)
- return s, r
-}
-
-// A sender is used to send values to a Receiver.
-type Sender[type T] struct {
- values chan<- T
- done <-chan bool
-}
-
-// Send sends a value to the receiver. It returns whether any more
-// values may be sent; if it returns false the value was not sent.
-func (s *Sender[T]) Send(v T) bool {
- select {
- case s.values <- v:
- return true
- case <-s.done:
- return false
- }
-}
-
-// Close tells the receiver that no more values will arrive.
-// After Close is called, the Sender may no longer be used.
-func (s *Sender[T]) Close() {
- close(s.values)
-}
-
-// A Receiver receives values from a Sender.
-type Receiver[type T] struct {
- values <-chan T
- done chan<- bool
-}
-
-// Next returns the next value from the channel. The bool result
-// indicates whether the value is valid, or whether the Sender has
-// been closed and no more values will be received.
-func (r *Receiver[T]) Next() (T, bool) {
- v, ok := <-r.values
- return v, ok
-}
-
-// finalize is a finalizer for the receiver.
-func (r *Receiver[T]) finalize() {
- close(r.done)
-}
diff --git a/src/go/parser/testdata/issue42951/not_a_file.go/invalid.go b/src/go/parser/testdata/issue42951/not_a_file.go/invalid.go
new file mode 100644
index 0000000..bb698be
--- /dev/null
+++ b/src/go/parser/testdata/issue42951/not_a_file.go/invalid.go
@@ -0,0 +1 @@
+This file should not be parsed by ParseDir.
diff --git a/src/go/parser/testdata/linalg.go2 b/src/go/parser/testdata/linalg.go2
index 5fbd22f..fba0d02 100644
--- a/src/go/parser/testdata/linalg.go2
+++ b/src/go/parser/testdata/linalg.go2
@@ -15,7 +15,7 @@
complex64, complex128
}
-func DotProduct(type T Numeric)(s1, s2 []T) T {
+func DotProduct[T Numeric](s1, s2 []T) T {
if len(s1) != len(s2) {
panic("DotProduct: slices of unequal length")
}
@@ -27,7 +27,7 @@
}
// NumericAbs matches numeric types with an Abs method.
-type NumericAbs(type T) interface {
+type NumericAbs[T any] interface {
Numeric
Abs() T
@@ -35,7 +35,7 @@
// AbsDifference computes the absolute value of the difference of
// a and b, where the absolute value is determined by the Abs method.
-func AbsDifference(type T NumericAbs)(a, b T) T {
+func AbsDifference[T NumericAbs](a, b T) T {
d := a - b
return d.Abs()
}
@@ -54,9 +54,9 @@
// OrderedAbs is a helper type that defines an Abs method for
// ordered numeric types.
-type OrderedAbs(type T OrderedNumeric) T
+type OrderedAbs[T OrderedNumeric] T
-func (a OrderedAbs(T)) Abs() OrderedAbs(T) {
+func (a OrderedAbs[T]) Abs() OrderedAbs[T] {
if a < 0 {
return -a
}
@@ -65,19 +65,19 @@
// ComplexAbs is a helper type that defines an Abs method for
// complex types.
-type ComplexAbs(type T Complex) T
+type ComplexAbs[T Complex] T
-func (a ComplexAbs(T)) Abs() ComplexAbs(T) {
+func (a ComplexAbs[T]) Abs() ComplexAbs[T] {
r := float64(real(a))
i := float64(imag(a))
d := math.Sqrt(r * r + i * i)
- return ComplexAbs(T)(complex(d, 0))
+ return ComplexAbs[T](complex(d, 0))
}
-func OrderedAbsDifference(type T OrderedNumeric)(a, b T) T {
- return T(AbsDifference(OrderedAbs(T)(a), OrderedAbs(T)(b)))
+func OrderedAbsDifference[T OrderedNumeric](a, b T) T {
+ return T(AbsDifference(OrderedAbs[T](a), OrderedAbs[T](b)))
}
-func ComplexAbsDifference(type T Complex)(a, b T) T {
- return T(AbsDifference(ComplexAbs(T)(a), ComplexAbs(T)(b)))
+func ComplexAbsDifference[T Complex](a, b T) T {
+ return T(AbsDifference(ComplexAbs[T](a), ComplexAbs[T](b)))
}
diff --git a/src/go/parser/testdata/linalgB.go2 b/src/go/parser/testdata/linalgB.go2
deleted file mode 100644
index a72f7be..0000000
--- a/src/go/parser/testdata/linalgB.go2
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2019 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 linalg
-
-import "math"
-
-// Numeric is type bound that matches any numeric type.
-// It would likely be in a constraints package in the standard library.
-type Numeric interface {
- type int, int8, int16, int32, int64,
- uint, uint8, uint16, uint32, uint64, uintptr,
- float32, float64,
- complex64, complex128
-}
-
-func DotProduct[type T Numeric](s1, s2 []T) T {
- if len(s1) != len(s2) {
- panic("DotProduct: slices of unequal length")
- }
- var r T
- for i := range s1 {
- r += s1[i] * s2[i]
- }
- return r
-}
-
-// NumericAbs matches numeric types with an Abs method.
-type NumericAbs[type T] interface {
- Numeric
-
- Abs() T
-}
-
-// AbsDifference computes the absolute value of the difference of
-// a and b, where the absolute value is determined by the Abs method.
-func AbsDifference[type T NumericAbs](a, b T) T {
- d := a - b
- return d.Abs()
-}
-
-// OrderedNumeric is a type bound that matches numeric types that support the < operator.
-type OrderedNumeric interface {
- type int, int8, int16, int32, int64,
- uint, uint8, uint16, uint32, uint64, uintptr,
- float32, float64
-}
-
-// Complex is a type bound that matches the two complex types, which do not have a < operator.
-type Complex interface {
- type complex64, complex128
-}
-
-// OrderedAbs is a helper type that defines an Abs method for
-// ordered numeric types.
-type OrderedAbs[type T OrderedNumeric] T
-
-func (a OrderedAbs[T]) Abs() OrderedAbs[T] {
- if a < 0 {
- return -a
- }
- return a
-}
-
-// ComplexAbs is a helper type that defines an Abs method for
-// complex types.
-type ComplexAbs[type T Complex] T
-
-func (a ComplexAbs[T]) Abs() ComplexAbs[T] {
- r := float64(real(a))
- i := float64(imag(a))
- d := math.Sqrt(r * r + i * i)
- return ComplexAbs[T](complex(d, 0))
-}
-
-func OrderedAbsDifference[type T OrderedNumeric](a, b T) T {
- return T(AbsDifference(OrderedAbs[T](a), OrderedAbs[T](b)))
-}
-
-func ComplexAbsDifference[type T Complex](a, b T) T {
- return T(AbsDifference(ComplexAbs[T](a), ComplexAbs[T](b)))
-}
diff --git a/src/go/parser/testdata/map.go2 b/src/go/parser/testdata/map.go2
index 8b0cdaa..74c79ae 100644
--- a/src/go/parser/testdata/map.go2
+++ b/src/go/parser/testdata/map.go2
@@ -4,27 +4,27 @@
import "chans"
// Map is an ordered map.
-type Map(type K, V) struct {
- root *node(K, V)
+type Map[K, V any] struct {
+ root *node[K, V]
compare func(K, K) int
}
// node is the type of a node in the binary tree.
-type node(type K, V) struct {
+type node[K, V any] struct {
key K
val V
- left, right *node(K, V)
+ left, right *node[K, V]
}
// New returns a new map.
-func New(type K, V)(compare func(K, K) int) *Map(K, V) {
- return &Map(K, V){compare: compare}
+func New[K, V any](compare func(K, K) int) *Map[K, V] {
+ return &Map[K, V]{compare: compare}
}
// find looks up key in the map, and returns either a pointer
// to the node holding key, or a pointer to the location where
// such a node would go.
-func (m *Map(K, V)) find(key K) **node(K, V) {
+func (m *Map[K, V]) find(key K) **node[K, V] {
pn := &m.root
for *pn != nil {
switch cmp := m.compare(key, (*pn).key); {
@@ -42,19 +42,19 @@
// Insert inserts a new key/value into the map.
// If the key is already present, the value is replaced.
// Returns true if this is a new key, false if already present.
-func (m *Map(K, V)) Insert(key K, val V) bool {
+func (m *Map[K, V]) Insert(key K, val V) bool {
pn := m.find(key)
if *pn != nil {
(*pn).val = val
return false
}
- *pn = &node(K, V){key: key, val: val}
+ *pn = &node[K, V]{key: key, val: val}
return true
}
// Find returns the value associated with a key, or zero if not present.
// The found result reports whether the key was found.
-func (m *Map(K, V)) Find(key K) (V, bool) {
+func (m *Map[K, V]) Find(key K) (V, bool) {
pn := m.find(key)
if *pn == nil {
var zero V // see the discussion of zero values, above
@@ -64,16 +64,16 @@
}
// keyValue is a pair of key and value used when iterating.
-type keyValue(type K, V) struct {
+type keyValue[K, V any] struct {
key K
val V
}
// InOrder returns an iterator that does an in-order traversal of the map.
-func (m *Map(K, V)) InOrder() *Iterator(K, V) {
- sender, receiver := chans.Ranger(keyValue(K, V))()
- var f func(*node(K, V)) bool
- f = func(n *node(K, V)) bool {
+func (m *Map[K, V]) InOrder() *Iterator[K, V] {
+ sender, receiver := chans.Ranger[keyValue[K, V]]()
+ var f func(*node[K, V]) bool
+ f = func(n *node[K, V]) bool {
if n == nil {
return true
}
@@ -81,7 +81,7 @@
// meaning that nothing is listening at the receiver end.
return f(n.left) &&
// TODO
- // sender.Send(keyValue(K, V){n.key, n.val}) &&
+ // sender.Send(keyValue[K, V]{n.key, n.val}) &&
f(n.right)
}
go func() {
@@ -92,13 +92,13 @@
}
// Iterator is used to iterate over the map.
-type Iterator(type K, V) struct {
- r *chans.Receiver(keyValue(K, V))
+type Iterator[K, V any] struct {
+ r *chans.Receiver[keyValue[K, V]]
}
// Next returns the next key and value pair, and a boolean indicating
// whether they are valid or whether we have reached the end.
-func (it *Iterator(K, V)) Next() (K, V, bool) {
+func (it *Iterator[K, V]) Next() (K, V, bool) {
keyval, ok := it.r.Next()
if !ok {
var zerok K
diff --git a/src/go/parser/testdata/mapB.go2 b/src/go/parser/testdata/mapB.go2
deleted file mode 100644
index 0be5b28..0000000
--- a/src/go/parser/testdata/mapB.go2
+++ /dev/null
@@ -1,109 +0,0 @@
-// Package orderedmap provides an ordered map, implemented as a binary tree.
-package orderedmap
-
-import "chans"
-
-// Map is an ordered map.
-type Map[type K, V] struct {
- root *node[K, V]
- compare func(K, K) int
-}
-
-// node is the type of a node in the binary tree.
-type node[type K, V] struct {
- key K
- val V
- left, right *node[K, V]
-}
-
-// New returns a new map.
-func New[type K, V](compare func(K, K) int) *Map[K, V] {
- return &Map[K, V]{compare: compare}
-}
-
-// find looks up key in the map, and returns either a pointer
-// to the node holding key, or a pointer to the location where
-// such a node would go.
-func (m *Map[K, V]) find(key K) **node[K, V] {
- pn := &m.root
- for *pn != nil {
- switch cmp := m.compare(key, (*pn).key); {
- case cmp < 0:
- pn = &(*pn).left
- case cmp > 0:
- pn = &(*pn).right
- default:
- return pn
- }
- }
- return pn
-}
-
-// Insert inserts a new key/value into the map.
-// If the key is already present, the value is replaced.
-// Returns true if this is a new key, false if already present.
-func (m *Map[K, V]) Insert(key K, val V) bool {
- pn := m.find(key)
- if *pn != nil {
- (*pn).val = val
- return false
- }
- *pn = &node[K, V]{key: key, val: val}
- return true
-}
-
-// Find returns the value associated with a key, or zero if not present.
-// The found result reports whether the key was found.
-func (m *Map[K, V]) Find(key K) (V, bool) {
- pn := m.find(key)
- if *pn == nil {
- var zero V // see the discussion of zero values, above
- return zero, false
- }
- return (*pn).val, true
-}
-
-// keyValue is a pair of key and value used when iterating.
-type keyValue[type K, V] struct {
- key K
- val V
-}
-
-// InOrder returns an iterator that does an in-order traversal of the map.
-func (m *Map[K, V]) InOrder() *Iterator[K, V] {
- sender, receiver := chans.Ranger(keyValue[K, V])()
- var f func(*node[K, V]) bool
- f = func(n *node[K, V]) bool {
- if n == nil {
- return true
- }
- // Stop sending values if sender.Send returns false,
- // meaning that nothing is listening at the receiver end.
- return f(n.left) &&
- // TODO
- // sender.Send(keyValue[K, V]{n.key, n.val}) &&
- f(n.right)
- }
- go func() {
- f(m.root)
- sender.Close()
- }()
- return &Iterator{receiver}
-}
-
-// Iterator is used to iterate over the map.
-type Iterator[type K, V] struct {
- r *chans.Receiver[keyValue[K, V]]
-}
-
-// Next returns the next key and value pair, and a boolean indicating
-// whether they are valid or whether we have reached the end.
-func (it *Iterator[K, V]) Next() (K, V, bool) {
- keyval, ok := it.r.Next()
- if !ok {
- var zerok K
- var zerov V
- return zerok, zerov, false
- }
- return keyval.key, keyval.val, true
-}
diff --git a/src/go/parser/testdata/metrics.go2 b/src/go/parser/testdata/metrics.go2
index bd43747..ef1c66b 100644
--- a/src/go/parser/testdata/metrics.go2
+++ b/src/go/parser/testdata/metrics.go2
@@ -2,12 +2,12 @@
import "sync"
-type Metric1(type T comparable) struct {
+type Metric1[T comparable] struct {
mu sync.Mutex
m map[T]int
}
-func (m *Metric1(T)) Add(v T) {
+func (m *Metric1[T]) Add(v T) {
m.mu.Lock()
defer m.mu.Unlock()
if m.m == nil {
@@ -16,43 +16,43 @@
m[v]++
}
-type key2(type T1, T2 comparable) struct {
+type key2[T1, T2 comparable] struct {
f1 T1
f2 T2
}
-type Metric2(type T1, T2 cmp2) struct {
+type Metric2[T1, T2 cmp2] struct {
mu sync.Mutex
- m map[key2(T1, T2)]int
+ m map[key2[T1, T2]]int
}
-func (m *Metric2(T1, T2)) Add(v1 T1, v2 T2) {
+func (m *Metric2[T1, T2]) Add(v1 T1, v2 T2) {
m.mu.Lock()
defer m.mu.Unlock()
if m.m == nil {
- m.m = make(map[key2(T1, T2)]int)
+ m.m = make(map[key2[T1, T2]]int)
}
- m[key(T1, T2){v1, v2}]++
+ m[key[T1, T2]{v1, v2}]++
}
-type key3(type T1, T2, T3 comparable) struct {
+type key3[T1, T2, T3 comparable] struct {
f1 T1
f2 T2
f3 T3
}
-type Metric3(type T1, T2, T3 comparable) struct {
+type Metric3[T1, T2, T3 comparable] struct {
mu sync.Mutex
- m map[key3(T1, T2, T3)]int
+ m map[key3[T1, T2, T3]]int
}
-func (m *Metric3(T1, T2, T3)) Add(v1 T1, v2 T2, v3 T3) {
+func (m *Metric3[T1, T2, T3]) Add(v1 T1, v2 T2, v3 T3) {
m.mu.Lock()
defer m.mu.Unlock()
if m.m == nil {
m.m = make(map[key3]int)
}
- m[key(T1, T2, T3){v1, v2, v3}]++
+ m[key[T1, T2, T3]{v1, v2, v3}]++
}
// Repeat for the maximum number of permitted arguments.
diff --git a/src/go/parser/testdata/metricsB.go2 b/src/go/parser/testdata/metricsB.go2
deleted file mode 100644
index 1ca0ab5..0000000
--- a/src/go/parser/testdata/metricsB.go2
+++ /dev/null
@@ -1,58 +0,0 @@
-package metrics
-
-import "sync"
-
-type Metric1[type T comparable] struct {
- mu sync.Mutex
- m map[T]int
-}
-
-func (m *Metric1[T]) Add(v T) {
- m.mu.Lock()
- defer m.mu.Unlock()
- if m.m == nil {
- m.m = make(map[T]int)
- }
- m[v]++
-}
-
-type key2[type T1, T2 comparable] struct {
- f1 T1
- f2 T2
-}
-
-type Metric2[type T1, T2 cmp2] struct {
- mu sync.Mutex
- m map[key2[T1, T2]]int
-}
-
-func (m *Metric2[T1, T2]) Add(v1 T1, v2 T2) {
- m.mu.Lock()
- defer m.mu.Unlock()
- if m.m == nil {
- m.m = make(map[key2[T1, T2]]int)
- }
- m[key[T1, T2]{v1, v2}]++
-}
-
-type key3[type T1, T2, T3 comparable] struct {
- f1 T1
- f2 T2
- f3 T3
-}
-
-type Metric3[type T1, T2, T3 comparable] struct {
- mu sync.Mutex
- m map[key3[T1, T2, T3]]int
-}
-
-func (m *Metric3[T1, T2, T3]) Add(v1 T1, v2 T2, v3 T3) {
- m.mu.Lock()
- defer m.mu.Unlock()
- if m.m == nil {
- m.m = make(map[key3]int)
- }
- m[key[T1, T2, T3]{v1, v2, v3}]++
-}
-
-// Repeat for the maximum number of permitted arguments.
diff --git a/src/go/parser/testdata/set.go2 b/src/go/parser/testdata/set.go2
index bc18550..0da6377 100644
--- a/src/go/parser/testdata/set.go2
+++ b/src/go/parser/testdata/set.go2
@@ -1,30 +1,30 @@
// Package set implements sets of any type.
package set
-type Set(type Elem comparable) map[Elem]struct{}
+type Set[Elem comparable] map[Elem]struct{}
-func Make(type Elem comparable)() Set(Elem) {
+func Make[Elem comparable]() Set[Elem] {
return make(Set(Elem))
}
-func (s Set(Elem)) Add(v Elem) {
+func (s Set[Elem]) Add(v Elem) {
s[v] = struct{}{}
}
-func (s Set(Elem)) Delete(v Elem) {
+func (s Set[Elem]) Delete(v Elem) {
delete(s, v)
}
-func (s Set(Elem)) Contains(v Elem) bool {
+func (s Set[Elem]) Contains(v Elem) bool {
_, ok := s[v]
return ok
}
-func (s Set(Elem)) Len() int {
+func (s Set[Elem]) Len() int {
return len(s)
}
-func (s Set(Elem)) Iterate(f func(Elem)) {
+func (s Set[Elem]) Iterate(f func(Elem)) {
for v := range s {
f(v)
}
diff --git a/src/go/parser/testdata/setB.go2 b/src/go/parser/testdata/setB.go2
deleted file mode 100644
index 7a0c56d..0000000
--- a/src/go/parser/testdata/setB.go2
+++ /dev/null
@@ -1,31 +0,0 @@
-// Package set implements sets of any type.
-package set
-
-type Set[type Elem comparable] map[Elem]struct{}
-
-func Make[type Elem comparable]() Set[Elem] {
- return make(Set[Elem])
-}
-
-func (s Set[Elem]) Add(v Elem) {
- s[v] = struct{}{}
-}
-
-func (s Set[Elem]) Delete(v Elem) {
- delete(s, v)
-}
-
-func (s Set[Elem]) Contains(v Elem) bool {
- _, ok := s[v]
- return ok
-}
-
-func (s Set[Elem]) Len() int {
- return len(s)
-}
-
-func (s Set[Elem]) Iterate(f func(Elem)) {
- for v := range s {
- f(v)
- }
-}
diff --git a/src/go/parser/testdata/slices.go2 b/src/go/parser/testdata/slices.go2
index fe90f7c..e060212 100644
--- a/src/go/parser/testdata/slices.go2
+++ b/src/go/parser/testdata/slices.go2
@@ -2,7 +2,7 @@
package slices
// Map turns a []T1 to a []T2 using a mapping function.
-func Map(type T1, T2)(s []T1, f func(T1) T2) []T2 {
+func Map[T1, T2 any](s []T1, f func(T1) T2) []T2 {
r := make([]T2, len(s))
for i, v := range s {
r[i] = f(v)
@@ -11,7 +11,7 @@
}
// Reduce reduces a []T1 to a single value using a reduction function.
-func Reduce(type T1, T2)(s []T1, initializer T2, f func(T2, T1) T2) T2 {
+func Reduce[T1, T2 any](s []T1, initializer T2, f func(T2, T1) T2) T2 {
r := initializer
for _, v := range s {
r = f(r, v)
@@ -20,7 +20,7 @@
}
// Filter filters values from a slice using a filter function.
-func Filter(type T)(s []T, f func(T) bool) []T {
+func Filter[T any](s []T, f func(T) bool) []T {
var r []T
for _, v := range s {
if f(v) {
diff --git a/src/go/parser/testdata/slicesB.go2 b/src/go/parser/testdata/slicesB.go2
deleted file mode 100644
index 5e83f6e..0000000
--- a/src/go/parser/testdata/slicesB.go2
+++ /dev/null
@@ -1,31 +0,0 @@
-// Package slices implements various slice algorithms.
-package slices
-
-// Map turns a []T1 to a []T2 using a mapping function.
-func Map[type T1, T2](s []T1, f func(T1) T2) []T2 {
- r := make([]T2, len(s))
- for i, v := range s {
- r[i] = f(v)
- }
- return r
-}
-
-// Reduce reduces a []T1 to a single value using a reduction function.
-func Reduce[type T1, T2](s []T1, initializer T2, f func(T2, T1) T2) T2 {
- r := initializer
- for _, v := range s {
- r = f(r, v)
- }
- return r
-}
-
-// Filter filters values from a slice using a filter function.
-func Filter[type T](s []T, f func(T) bool) []T {
- var r []T
- for _, v := range s {
- if f(v) {
- r = append(r, v)
- }
- }
- return r
-}
diff --git a/src/go/parser/testdata/sort.go2 b/src/go/parser/testdata/sort.go2
index 241b8fb..88be79f 100644
--- a/src/go/parser/testdata/sort.go2
+++ b/src/go/parser/testdata/sort.go2
@@ -1,27 +1,27 @@
package sort
-type orderedSlice(type Elem comparable) []Elem
+type orderedSlice[Elem comparable] []Elem
-func (s orderedSlice(Elem)) Len() int { return len(s) }
-func (s orderedSlice(Elem)) Less(i, j int) bool { return s[i] < s[j] }
-func (s orderedSlice(Elem)) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s orderedSlice[Elem]) Len() int { return len(s) }
+func (s orderedSlice[Elem]) Less(i, j int) bool { return s[i] < s[j] }
+func (s orderedSlice[Elem]) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// OrderedSlice sorts the slice s in ascending order.
// The elements of s must be ordered using the < operator.
-func OrderedSlice(type Elem comparable)(s []Elem) {
- sort.Sort(orderedSlice(Elem)(s))
+func OrderedSlice[Elem comparable](s []Elem) {
+ sort.Sort(orderedSlice[Elem](s))
}
-type sliceFn(type Elem) struct {
+type sliceFn[Elem any] struct {
s []Elem
f func(Elem, Elem) bool
}
-func (s sliceFn(Elem)) Len() int { return len(s.s) }
-func (s sliceFn(Elem)) Less(i, j int) bool { return s.f(s.s[i], s.s[j]) }
-func (s sliceFn(Elem)) Swap(i, j int) { s.s[i], s.s[j] = s.s[j], s.s[i] }
+func (s sliceFn[Elem]) Len() int { return len(s.s) }
+func (s sliceFn[Elem]) Less(i, j int) bool { return s.f(s.s[i], s.s[j]) }
+func (s sliceFn[Elem]) Swap(i, j int) { s.s[i], s.s[j] = s.s[j], s.s[i] }
// SliceFn sorts the slice s according to the function f.
-func SliceFn(type Elem)(s []Elem, f func(Elem, Elem) bool) {
- Sort(sliceFn(Elem){s, f})
+func SliceFn[Elem any](s []Elem, f func(Elem, Elem) bool) {
+ Sort(sliceFn[Elem]{s, f})
}
diff --git a/src/go/parser/testdata/sortB.go2 b/src/go/parser/testdata/sortB.go2
deleted file mode 100644
index 39363ae..0000000
--- a/src/go/parser/testdata/sortB.go2
+++ /dev/null
@@ -1,27 +0,0 @@
-package sort
-
-type orderedSlice[type Elem comparable] []Elem
-
-func (s orderedSlice[Elem]) Len() int { return len(s) }
-func (s orderedSlice[Elem]) Less(i, j int) bool { return s[i] < s[j] }
-func (s orderedSlice[Elem]) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-
-// OrderedSlice sorts the slice s in ascending order.
-// The elements of s must be ordered using the < operator.
-func OrderedSlice[type Elem comparable](s []Elem) {
- sort.Sort(orderedSlice[Elem](s))
-}
-
-type sliceFn[type Elem] struct {
- s []Elem
- f func(Elem, Elem) bool
-}
-
-func (s sliceFn[Elem]) Len() int { return len(s.s) }
-func (s sliceFn[Elem]) Less(i, j int) bool { return s.f(s.s[i], s.s[j]) }
-func (s sliceFn[Elem]) Swap(i, j int) { s.s[i], s.s[j] = s.s[j], s.s[i] }
-
-// SliceFn sorts the slice s according to the function f.
-func SliceFn[type Elem](s []Elem, f func(Elem, Elem) bool) {
- Sort(sliceFn[Elem]{s, f})
-}
diff --git a/src/go/parser/testdata/typeparams.src b/src/go/parser/testdata/typeparams.src
new file mode 100644
index 0000000..1fea23f
--- /dev/null
+++ b/src/go/parser/testdata/typeparams.src
@@ -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.
+
+// Test cases for error messages produced while parsing code that uses type
+// parameters, without ParseTypeParams being enabled.
+
+package p
+
+type List[E any /* ERROR "expected ']', found any" */ ] []E
+
+type Pair[L, /* ERROR "expected ']', found ','" */ R any] struct {
+ Left L
+ Right R
+}
+
+var _ = Pair[int, /* ERROR "expected ']' or ':', found ','" */ string]{}
diff --git a/src/go/printer/nodes.go b/src/go/printer/nodes.go
index fbf3d14..cc79553 100644
--- a/src/go/printer/nodes.go
+++ b/src/go/printer/nodes.go
@@ -319,15 +319,12 @@
}
}
-func (p *printer) parameters(isTypeParam bool, fields *ast.FieldList) {
+func (p *printer) parameters(fields *ast.FieldList, isTypeParam bool) {
openTok, closeTok := token.LPAREN, token.RPAREN
- if isTypeParam && p.Mode&UseBrackets != 0 {
+ if isTypeParam {
openTok, closeTok = token.LBRACK, token.RBRACK
}
p.print(fields.Opening, openTok)
- if isTypeParam && p.Mode&UseBrackets == 0 {
- p.print(token.TYPE)
- }
if len(fields.List) > 0 {
prevLine := p.lineFor(fields.Opening)
ws := indent
@@ -352,7 +349,7 @@
if needsLinebreak && p.linebreak(parLineBeg, 0, ws, true) > 0 {
// break line if the opening "(" or previous parameter ended on a different line
ws = ignore
- } else if isTypeParam && len(par.Names) > 0 && p.Mode&UseBrackets == 0 || i > 0 {
+ } else if i > 0 {
p.print(blank)
}
// parameter names
@@ -364,25 +361,10 @@
// by a linebreak call after a type, or in the next multi-line identList
// will do the right thing.
p.identList(par.Names, ws == indent)
- if par.Type != nil {
- p.print(blank)
- }
+ p.print(blank)
}
// parameter type
- if par.Type != nil {
- typ := stripParensAlways(par.Type)
- // If we don't have a parameter name and the parameter type is an
- // instantiated type using parentheses, then we need to parenthesize
- // the type otherwise it is interpreted as a parameter name followed
- // by a parenthesized type name.
- if call, _ := typ.(*ast.CallExpr); call != nil && !call.Brackets && len(par.Names) == 0 {
- p.print(token.LPAREN)
- p.expr(typ)
- p.print(token.RPAREN)
- } else {
- p.expr(typ)
- }
- }
+ p.expr(stripParensAlways(par.Type))
prevLine = parLineEnd
}
// if the closing ")" is on a separate line from the last parameter,
@@ -401,10 +383,10 @@
func (p *printer) signature(sig *ast.FuncType) {
if sig.TParams != nil {
- p.parameters(true, sig.TParams)
+ p.parameters(sig.TParams, true)
}
if sig.Params != nil {
- p.parameters(false, sig.Params)
+ p.parameters(sig.Params, false)
} else {
p.print(token.LPAREN, token.RPAREN)
}
@@ -418,7 +400,7 @@
p.expr(stripParensAlways(res.List[0].Type))
return
}
- p.parameters(false, res)
+ p.parameters(res, false)
}
}
@@ -848,8 +830,8 @@
}
case *ast.BasicLit:
- if p.Config.Mode&StdFormat != 0 {
- x = normalizeNumbers(x)
+ if p.Config.Mode&normalizeNumbers != 0 {
+ x = normalizedNumber(x)
}
p.print(x)
@@ -1038,11 +1020,15 @@
}
}
-// normalizeNumbers rewrites base prefixes and exponents to
-// use lower-case letters, and removes leading 0's from
-// integer imaginary literals. It leaves hexadecimal digits
-// alone.
-func normalizeNumbers(lit *ast.BasicLit) *ast.BasicLit {
+// normalizedNumber rewrites base prefixes and exponents
+// of numbers to use lower-case letters (0X123 to 0x123 and 1.2E3 to 1.2e3),
+// and removes leading 0's from integer imaginary literals (0765i to 765i).
+// It leaves hexadecimal digits alone.
+//
+// normalizedNumber doesn't modify the ast.BasicLit value lit points to.
+// If lit is not a number or a number in canonical format already,
+// lit is returned as is. Otherwise a new ast.BasicLit is created.
+func normalizedNumber(lit *ast.BasicLit) *ast.BasicLit {
if lit.Kind != token.INT && lit.Kind != token.FLOAT && lit.Kind != token.IMAG {
return lit // not a number - nothing to do
}
@@ -1646,7 +1632,7 @@
p.setComment(s.Doc)
p.expr(s.Name)
if s.TParams != nil {
- p.parameters(true, s.TParams)
+ p.parameters(s.TParams, true)
}
if n == 1 {
p.print(blank)
@@ -1836,7 +1822,7 @@
// FUNC is emitted).
startCol := p.out.Column - len("func ")
if d.Recv != nil {
- p.parameters(false, d.Recv) // method: print receiver
+ p.parameters(d.Recv, false) // method: print receiver
p.print(blank)
}
p.expr(d.Name)
diff --git a/src/go/printer/performance_test.go b/src/go/printer/performance_test.go
index 3f34bfc..2e67154 100644
--- a/src/go/printer/performance_test.go
+++ b/src/go/printer/performance_test.go
@@ -20,7 +20,7 @@
var testfile *ast.File
func testprint(out io.Writer, file *ast.File) {
- if err := (&Config{TabIndent | UseSpaces | StdFormat, 8, 0}).Fprint(out, fset, file); err != nil {
+ if err := (&Config{TabIndent | UseSpaces | normalizeNumbers, 8, 0}).Fprint(out, fset, file); err != nil {
log.Fatalf("print error: %s", err)
}
}
diff --git a/src/go/printer/printer.go b/src/go/printer/printer.go
index b3eba6c..0077afe 100644
--- a/src/go/printer/printer.go
+++ b/src/go/printer/printer.go
@@ -1272,12 +1272,26 @@
type Mode uint
const (
- RawFormat Mode = 1 << iota // do not use a tabwriter; if set, UseSpaces is ignored
- TabIndent // use tabs for indentation independent of UseSpaces
- UseSpaces // use spaces instead of tabs for alignment
- SourcePos // emit //line directives to preserve original source positions
- StdFormat // apply standard formatting changes (exact byte output may change between versions of Go)
- UseBrackets // use square brackets instead of parentheses for type parameters (implies unified parameter syntax)
+ RawFormat Mode = 1 << iota // do not use a tabwriter; if set, UseSpaces is ignored
+ TabIndent // use tabs for indentation independent of UseSpaces
+ UseSpaces // use spaces instead of tabs for alignment
+ SourcePos // emit //line directives to preserve original source positions
+)
+
+// The mode below is not included in printer's public API because
+// editing code text is deemed out of scope. Because this mode is
+// unexported, it's also possible to modify or remove it based on
+// the evolving needs of go/format and cmd/gofmt without breaking
+// users. See discussion in CL 240683.
+const (
+ // normalizeNumbers means to canonicalize number
+ // literal prefixes and exponents while printing.
+ //
+ // This value is known in and used by go/format and cmd/gofmt.
+ // It is currently more convenient and performant for those
+ // packages to apply number normalization during printing,
+ // rather than by modifying the AST in advance.
+ normalizeNumbers Mode = 1 << 30
)
// A Config node controls the output of Fprint.
@@ -1350,13 +1364,6 @@
// or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt.
//
func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) error {
- // Ensure that our formatting of type parameters is consistent with the file.
- if file, _ := node.(*ast.File); file != nil && file.UseBrackets && cfg.Mode&UseBrackets == 0 {
- // Copy to avoid data races.
- cfg2 := *cfg
- cfg2.Mode |= UseBrackets
- cfg = &cfg2
- }
return cfg.fprint(output, fset, node, make(map[ast.Node]int))
}
diff --git a/src/go/printer/printer_test.go b/src/go/printer/printer_test.go
index bc8c43e..3efa468 100644
--- a/src/go/printer/printer_test.go
+++ b/src/go/printer/printer_test.go
@@ -33,7 +33,7 @@
const (
export checkMode = 1 << iota
rawFormat
- stdFormat
+ normNumber
idempotent
)
@@ -42,7 +42,7 @@
// if any.
func format(src []byte, mode checkMode) ([]byte, error) {
// parse src
- f, err := parser.ParseFile(fset, "", src, parser.ParseComments)
+ f, err := parser.ParseFile(fset, "", src, parser.ParseComments|parser.ParseTypeParams)
if err != nil {
return nil, fmt.Errorf("parse: %s\n%s", err, src)
}
@@ -58,8 +58,8 @@
if mode&rawFormat != 0 {
cfg.Mode |= RawFormat
}
- if mode&stdFormat != 0 {
- cfg.Mode |= StdFormat
+ if mode&normNumber != 0 {
+ cfg.Mode |= normalizeNumbers
}
// print AST
@@ -70,7 +70,7 @@
// make sure formatted output is syntactically correct
res := buf.Bytes()
- if _, err := parser.ParseFile(fset, "", res, 0); err != nil {
+ if _, err := parser.ParseFile(fset, "", res, parser.ParseTypeParams); err != nil {
return nil, fmt.Errorf("re-parse: %s\n%s", err, buf.Bytes())
}
@@ -205,9 +205,8 @@
{"slow.input", "slow.golden", idempotent},
{"complit.input", "complit.x", export},
{"go2numbers.input", "go2numbers.golden", idempotent},
- {"go2numbers.input", "go2numbers.stdfmt", stdFormat | idempotent},
+ {"go2numbers.input", "go2numbers.norm", normNumber | idempotent},
{"generics.input", "generics.golden", idempotent},
- {"genericsB.input", "genericsB.golden", idempotent},
}
func TestFiles(t *testing.T) {
diff --git a/src/go/printer/testdata/declarations.golden b/src/go/printer/testdata/declarations.golden
index 058baa8..74ffce7 100644
--- a/src/go/printer/testdata/declarations.golden
+++ b/src/go/printer/testdata/declarations.golden
@@ -942,6 +942,13 @@
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)
@@ -993,9 +1000,8 @@
}) // no extra comma between } and )
// type parameters
-func _(type)()
-func _(type A, B)(a A, b B) int {}
-func _(type T)(x, y T) T
+func _[A, B any](a A, b B) int {}
+func _[T any](x, y T) T
// alias declarations
diff --git a/src/go/printer/testdata/declarations.input b/src/go/printer/testdata/declarations.input
index 8787f49..ab20221 100644
--- a/src/go/printer/testdata/declarations.input
+++ b/src/go/printer/testdata/declarations.input
@@ -955,6 +955,11 @@
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))))))
@@ -1006,9 +1011,8 @@
}) // no extra comma between } and )
// type parameters
-func _(type)()
-func _(type A, B)(a A, b B) int {}
-func _(type T)(x, y T) T
+func _[A, B any](a A, b B) int {}
+func _[T any](x, y T) T
// alias declarations
@@ -1023,4 +1027,4 @@
c = foo
d = interface{}
ddd = p.Foo
-)
\ No newline at end of file
+)
diff --git a/src/go/printer/testdata/generics.golden b/src/go/printer/testdata/generics.golden
index 751ba68..88c4616 100644
--- a/src/go/printer/testdata/generics.golden
+++ b/src/go/printer/testdata/generics.golden
@@ -4,33 +4,30 @@
package generics
-type T(type) struct{}
-type T(type P) struct{}
-type T(type P1, P2, P3) struct{}
+type T[P any] struct{}
+type T[P1, P2, P3 any] struct{}
-type T(type P C) struct{}
-type T(type P1, P2, P3, C) struct{}
+type T[P C] struct{}
+type T[P1, P2, P3 C] struct{}
-type T(type P C(P)) struct{}
-type T(type P1, P2, P3 C(P1, P2, P3)) struct{}
+type T[P C[P]] struct{}
+type T[P1, P2, P3 C[P1, P2, P3]] struct{}
-func f(type)()
-func f(type P)(x P)
-func f(type P1, P2, P3)(x1 P1, x2 P2, x3 P3) struct{}
+func f[P any](x P)
+func f[P1, P2, P3 any](x1 P1, x2 P2, x3 P3) struct{}
-func f(type)()
-func f(type P interface{})(x P)
-func f(type P1, P2, P3 interface {
+func f[P interface{}](x P)
+func f[P1, P2, P3 interface {
m1(P1)
type P2, P3
-})(x1 P1, x2 P2, x3 P3) struct{}
-func f(type P)((T1(P)), (T2(P))) T3(P)
+}](x1 P1, x2 P2, x3 P3) struct{}
+func f[P any](T1[P], T2[P]) T3[P]
-func (x T(P)) m()
-func ((T(P))) m(x T(P)) P
+func (x T[P]) m()
+func (T[P]) m(x T[P]) P
func _() {
- type _ [](T(P))
- var _ []T(P)
- _ = [](T(P)){}
+ type _ []T[P]
+ var _ []T[P]
+ _ = []T[P]{}
}
diff --git a/src/go/printer/testdata/generics.input b/src/go/printer/testdata/generics.input
index 9fe5cec..5fdf8cd 100644
--- a/src/go/printer/testdata/generics.input
+++ b/src/go/printer/testdata/generics.input
@@ -4,30 +4,27 @@
package generics
-type T(type) struct{}
-type T(type P) struct{}
-type T(type P1, P2, P3) struct{}
+type T[P any] struct{}
+type T[P1, P2, P3 any] struct{}
-type T(type P C) struct{}
-type T(type P1, P2, P3, C) struct{}
+type T[P C] struct{}
+type T[P1, P2, P3 C] struct{}
-type T(type P C(P)) struct{}
-type T(type P1, P2, P3 C(P1, P2, P3)) struct{}
+type T[P C[P]] struct{}
+type T[P1, P2, P3 C[P1, P2, P3]] struct{}
-func f(type)()
-func f(type P)(x P)
-func f(type P1, P2, P3)(x1 P1, x2 P2, x3 P3) struct{}
+func f[P any](x P)
+func f[P1, P2, P3 any](x1 P1, x2 P2, x3 P3) struct{}
-func f(type)()
-func f(type P interface{})(x P)
-func f(type P1, P2, P3 interface{ m1(P1); type P2, P3 })(x1 P1, x2 P2, x3 P3) struct{}
-func f(type P)((T1(P)), (T2(P))) T3(P)
+func f[P interface{}](x P)
+func f[P1, P2, P3 interface{ m1(P1); type P2, P3 }](x1 P1, x2 P2, x3 P3) struct{}
+func f[P any](T1[P], T2[P]) T3[P]
-func (x T(P)) m()
-func ((T(P))) m(x T(P)) P
+func (x T[P]) m()
+func ((T[P])) m(x T[P]) P
func _() {
- type _ [](T(P))
- var _ []T(P)
- _ = [](T(P)){}
+ type _ []T[P]
+ var _ []T[P]
+ _ = []T[P]{}
}
diff --git a/src/go/printer/testdata/go2numbers.norm b/src/go/printer/testdata/go2numbers.norm
new file mode 100644
index 0000000..855f0fc
--- /dev/null
+++ b/src/go/printer/testdata/go2numbers.norm
@@ -0,0 +1,186 @@
+package p
+
+const (
+ // 0-octals
+ _ = 0
+ _ = 0123
+ _ = 0123456
+
+ _ = 0_123
+ _ = 0123_456
+
+ // decimals
+ _ = 1
+ _ = 1234
+ _ = 1234567
+
+ _ = 1_234
+ _ = 1_234_567
+
+ // hexadecimals
+ _ = 0x0
+ _ = 0x1234
+ _ = 0xcafef00d
+
+ _ = 0x0
+ _ = 0x1234
+ _ = 0xCAFEf00d
+
+ _ = 0x_0
+ _ = 0x_1234
+ _ = 0x_CAFE_f00d
+
+ // octals
+ _ = 0o0
+ _ = 0o1234
+ _ = 0o01234567
+
+ _ = 0o0
+ _ = 0o1234
+ _ = 0o01234567
+
+ _ = 0o_0
+ _ = 0o_1234
+ _ = 0o0123_4567
+
+ _ = 0o_0
+ _ = 0o_1234
+ _ = 0o0123_4567
+
+ // binaries
+ _ = 0b0
+ _ = 0b1011
+ _ = 0b00101101
+
+ _ = 0b0
+ _ = 0b1011
+ _ = 0b00101101
+
+ _ = 0b_0
+ _ = 0b10_11
+ _ = 0b_0010_1101
+
+ // decimal floats
+ _ = 0.
+ _ = 123.
+ _ = 0123.
+
+ _ = .0
+ _ = .123
+ _ = .0123
+
+ _ = 0e0
+ _ = 123e+0
+ _ = 0123e-1
+
+ _ = 0e-0
+ _ = 123e+0
+ _ = 0123e123
+
+ _ = 0.e+1
+ _ = 123.e-10
+ _ = 0123.e123
+
+ _ = .0e-1
+ _ = .123e+10
+ _ = .0123e123
+
+ _ = 0.0
+ _ = 123.123
+ _ = 0123.0123
+
+ _ = 0.0e1
+ _ = 123.123e-10
+ _ = 0123.0123e+456
+
+ _ = 1_2_3.
+ _ = 0_123.
+
+ _ = 0_0e0
+ _ = 1_2_3e0
+ _ = 0_123e0
+
+ _ = 0e-0_0
+ _ = 1_2_3e+0
+ _ = 0123e1_2_3
+
+ _ = 0.e+1
+ _ = 123.e-1_0
+ _ = 01_23.e123
+
+ _ = .0e-1
+ _ = .123e+10
+ _ = .0123e123
+
+ _ = 1_2_3.123
+ _ = 0123.01_23
+
+ // hexadecimal floats
+ _ = 0x0.p+0
+ _ = 0xdeadcafe.p-10
+ _ = 0x1234.p123
+
+ _ = 0x.1p-0
+ _ = 0x.deadcafep2
+ _ = 0x.1234p+10
+
+ _ = 0x0p0
+ _ = 0xdeadcafep+1
+ _ = 0x1234p-10
+
+ _ = 0x0.0p0
+ _ = 0xdead.cafep+1
+ _ = 0x12.34p-10
+
+ _ = 0xdead_cafep+1
+ _ = 0x_1234p-10
+
+ _ = 0x_dead_cafe.p-10
+ _ = 0x12_34.p1_2_3
+ _ = 0x1_2_3_4.p-1_2_3
+
+ // imaginaries
+ _ = 0i
+ _ = 0i
+ _ = 8i
+ _ = 0i
+ _ = 123i
+ _ = 123i
+ _ = 56789i
+ _ = 1234i
+ _ = 1234567i
+
+ _ = 0i
+ _ = 0i
+ _ = 8i
+ _ = 0i
+ _ = 123i
+ _ = 123i
+ _ = 56_789i
+ _ = 1_234i
+ _ = 1_234_567i
+
+ _ = 0.i
+ _ = 123.i
+ _ = 0123.i
+ _ = 000123.i
+
+ _ = 0e0i
+ _ = 123e0i
+ _ = 0123e0i
+ _ = 000123e0i
+
+ _ = 0.e+1i
+ _ = 123.e-1_0i
+ _ = 01_23.e123i
+ _ = 00_01_23.e123i
+
+ _ = 0b1010i
+ _ = 0b1010i
+ _ = 0o660i
+ _ = 0o660i
+ _ = 0xabcDEFi
+ _ = 0xabcDEFi
+ _ = 0xabcDEFp0i
+ _ = 0xabcDEFp0i
+)
diff --git a/src/go/token/position.go b/src/go/token/position.go
index 3f5a390..a21f5fd 100644
--- a/src/go/token/position.go
+++ b/src/go/token/position.go
@@ -58,8 +58,11 @@
// larger, representation.
//
// The Pos value for a given file is a number in the range [base, base+size],
-// where base and size are specified when adding the file to the file set via
-// AddFile.
+// where base and size are specified when a file is added to the file set.
+// The difference between a Pos value and the corresponding file base
+// corresponds to the byte offset of that position (represented by the Pos value)
+// from the beginning of the file. Thus, the file base offset is the Pos value
+// representing the first byte in the file.
//
// To create the Pos value for a specific source offset (measured in bytes),
// first add the respective file to the current file set using FileSet.AddFile
@@ -147,12 +150,12 @@
//
func (f *File) MergeLine(line int) {
if line < 1 {
- panic("illegal line number (line numbering starts at 1)")
+ panic(fmt.Sprintf("invalid line number %d (should be >= 1)", line))
}
f.mutex.Lock()
defer f.mutex.Unlock()
if line >= len(f.lines) {
- panic("illegal line number")
+ panic(fmt.Sprintf("invalid line number %d (should be < %d)", line, len(f.lines)))
}
// To merge the line numbered <line> with the line numbered <line+1>,
// we need to remove the entry in lines corresponding to the line
@@ -214,12 +217,12 @@
// LineStart panics if the 1-based line number is invalid.
func (f *File) LineStart(line int) Pos {
if line < 1 {
- panic("illegal line number (line numbering starts at 1)")
+ panic(fmt.Sprintf("invalid line number %d (should be >= 1)", line))
}
f.mutex.Lock()
defer f.mutex.Unlock()
if line > len(f.lines) {
- panic("illegal line number")
+ panic(fmt.Sprintf("invalid line number %d (should be < %d)", line, len(f.lines)))
}
return Pos(f.base + f.lines[line-1])
}
@@ -264,7 +267,7 @@
//
func (f *File) Pos(offset int) Pos {
if offset > f.size {
- panic("illegal file offset")
+ panic(fmt.Sprintf("invalid file offset %d (should be <= %d)", offset, f.size))
}
return Pos(f.base + offset)
}
@@ -275,7 +278,7 @@
//
func (f *File) Offset(p Pos) int {
if int(p) < f.base || int(p) > f.base+f.size {
- panic("illegal Pos value")
+ panic(fmt.Sprintf("invalid Pos value %d (should be in [%d, %d[)", p, f.base, f.base+f.size))
}
return int(p) - f.base
}
@@ -343,7 +346,7 @@
func (f *File) PositionFor(p Pos, adjusted bool) (pos Position) {
if p != NoPos {
if int(p) < f.base || int(p) > f.base+f.size {
- panic("illegal Pos value")
+ panic(fmt.Sprintf("invalid Pos value %d (should be in [%d, %d[)", p, f.base, f.base+f.size))
}
pos = f.position(p, adjusted)
}
@@ -364,6 +367,22 @@
// Methods of file sets are synchronized; multiple goroutines
// may invoke them concurrently.
//
+// The byte offsets for each file in a file set are mapped into
+// distinct (integer) intervals, one interval [base, base+size]
+// per file. Base represents the first byte in the file, and size
+// is the corresponding file size. A Pos value is a value in such
+// an interval. By determining the interval a Pos value belongs
+// to, the file, its file base, and thus the byte offset (position)
+// the Pos value is representing can be computed.
+//
+// When adding a new file, a file base must be provided. That can
+// be any integer value that is past the end of any interval of any
+// file already in the file set. For convenience, FileSet.Base provides
+// such a value, which is simply the end of the Pos interval of the most
+// recently added file, plus one. Unless there is a need to extend an
+// interval later, using the FileSet.Base should be used as argument
+// for FileSet.AddFile.
+//
type FileSet struct {
mutex sync.RWMutex // protects the file set
base int // base offset for the next file
@@ -411,8 +430,11 @@
if base < 0 {
base = s.base
}
- if base < s.base || size < 0 {
- panic("illegal base or size")
+ if base < s.base {
+ panic(fmt.Sprintf("invalid base %d (should be >= %d)", base, s.base))
+ }
+ if size < 0 {
+ panic(fmt.Sprintf("invalid size %d (should be >= 0)", size))
}
// base >= s.base && size >= 0
f := &File{set: s, name: filename, base: base, size: size, lines: []int{0}}
diff --git a/src/go/types/api.go b/src/go/types/api.go
index 3b43047..ec12fcf 100644
--- a/src/go/types/api.go
+++ b/src/go/types/api.go
@@ -41,9 +41,17 @@
type Error struct {
Fset *token.FileSet // file set for interpretation of Pos
Pos token.Pos // error position
- Msg string // default error message, user-friendly
- Full string // full error message, for debugging (may contain internal details)
+ Msg string // error message
Soft bool // if set, error is "soft"
+
+ // go116code is a future API, unexported as the set of error codes is large
+ // and likely to change significantly during experimentation. Tools wishing
+ // to preview this feature may read go116code using reflection (see
+ // errorcodes_test.go), but beware that there is no guarantee of future
+ // compatibility.
+ go116code errorCode
+ go116start token.Pos
+ go116end token.Pos
}
// Error returns an error string formatted as follows:
@@ -52,13 +60,6 @@
return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg)
}
-// FullError returns an error string like Error, buy it may contain
-// type-checker internal details such as subscript indices for type
-// parameters and more. Useful for debugging.
-func (err Error) FullError() string {
- return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Full)
-}
-
// An Importer resolves import paths to Packages.
//
// CAUTION: This interface does not support the import of locally
@@ -104,13 +105,6 @@
// type-checked.
IgnoreFuncBodies bool
- // If AcceptMethodTypeParams is set, methods may have type parameters.
- AcceptMethodTypeParams bool
-
- // If InferFromConstraints is set, constraint type inference is used
- // if some function type arguments are missing.
- InferFromConstraints bool
-
// If FakeImportC is set, `import "C"` (for packages requiring Cgo)
// declares an empty "C" package and errors are omitted for qualified
// identifiers referring to package C (which won't find an object).
@@ -129,9 +123,6 @@
// It is an error to set both FakeImportC and go115UsesCgo.
go115UsesCgo bool
- // If Trace is set, a debug trace is printed to stdout.
- Trace bool
-
// If Error != nil, it is called with each error found
// during type checking; err has dynamic type Error.
// Secondary errors (for instance, to enumerate all types
@@ -394,14 +385,15 @@
// AssertableTo reports whether a value of type V can be asserted to have type T.
func AssertableTo(V *Interface, T Type) bool {
- m, _ := (*Checker)(nil).assertableTo(V, T, false)
+ m, _ := (*Checker)(nil).assertableTo(V, T)
return m == nil
}
// AssignableTo reports whether a value of type V is assignable to a variable of type T.
func AssignableTo(V, T Type) bool {
x := operand{mode: value, typ: V}
- return x.assignableTo(nil, T, nil) // check not needed for non-constant x
+ ok, _ := x.assignableTo(nil, T, nil) // check not needed for non-constant x
+ return ok
}
// ConvertibleTo reports whether a value of type V is convertible to a value of type T.
diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go
index 3344939..014cd52 100644
--- a/src/go/types/api_test.go
+++ b/src/go/types/api_test.go
@@ -22,7 +22,8 @@
func pkgFor(path, source string, info *Info) (*Package, error) {
fset := token.NewFileSet()
- f, err := parser.ParseFile(fset, path, source, 0)
+ mode := modeForSource(source)
+ f, err := parser.ParseFile(fset, path, source, mode)
if err != nil {
return nil, err
}
@@ -42,17 +43,27 @@
return pkg.Name()
}
+// genericPkg is a prefix for packages that should be type checked with
+// generics.
+const genericPkg = "package generic_"
+
+func modeForSource(src string) parser.Mode {
+ if strings.HasPrefix(src, genericPkg) {
+ return parser.ParseTypeParams
+ }
+ return 0
+}
+
func mayTypecheck(t *testing.T, path, source string, info *Info) (string, error) {
fset := token.NewFileSet()
- f, err := parser.ParseFile(fset, path, source, 0)
+ mode := modeForSource(source)
+ f, err := parser.ParseFile(fset, path, source, mode)
if f == nil { // ignore errors unless f is nil
t.Fatalf("%s: unable to parse: %s", path, err)
}
conf := Config{
- AcceptMethodTypeParams: true,
- InferFromConstraints: true,
- Error: func(err error) {},
- Importer: importer.Default(),
+ Error: func(err error) {},
+ Importer: importer.Default(),
}
pkg, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, info)
return pkg.Name(), err
@@ -278,20 +289,20 @@
{`package x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string][-1]int`},
// parameterized functions
- {`package p0; func f[T any](T); var _ = f(int)`, `f`, `func[T₁ any](T₁)`},
- {`package p1; func f[T any](T); var _ = f(int)`, `f(int)`, `func(int)`},
- {`package p2; func f[T any](T); var _ = f(42)`, `f`, `func[T₁ any](T₁)`},
- {`package p2; func f[T any](T); var _ = f(42)`, `f(42)`, `()`},
+ {genericPkg + `p0; func f[T any](T); var _ = f(int)`, `f`, `func[T₁ any](T₁)`},
+ {genericPkg + `p1; func f[T any](T); var _ = f(int)`, `f(int)`, `func(int)`},
+ {genericPkg + `p2; func f[T any](T); var _ = f(42)`, `f`, `func[T₁ any](T₁)`},
+ {genericPkg + `p2; func f[T any](T); var _ = f(42)`, `f(42)`, `()`},
// type parameters
- {`package t0; type t[] int; var _ t`, `t`, `t0.t`}, // t[] is a syntax error that is ignored in this test in favor of t
- {`package t1; type t[P any] int; var _ t[int]`, `t`, `t1.t[P₁ any]`},
- {`package t2; type t[P interface{}] int; var _ t[int]`, `t`, `t2.t[P₁ interface{}]`},
- {`package t3; type t[P, Q interface{}] int; var _ t[int, int]`, `t`, `t3.t[P₁, Q₂ interface{}]`},
- {`package t4; type t[P, Q interface{ m() }] int; var _ t[int, int]`, `t`, `t4.t[P₁, Q₂ interface{m()}]`},
+ {genericPkg + `t0; type t[] int; var _ t`, `t`, `generic_t0.t`}, // t[] is a syntax error that is ignored in this test in favor of t
+ {genericPkg + `t1; type t[P any] int; var _ t[int]`, `t`, `generic_t1.t[P₁ any]`},
+ {genericPkg + `t2; type t[P interface{}] int; var _ t[int]`, `t`, `generic_t2.t[P₁ interface{}]`},
+ {genericPkg + `t3; type t[P, Q interface{}] int; var _ t[int, int]`, `t`, `generic_t3.t[P₁, Q₂ interface{}]`},
+ {genericPkg + `t4; type t[P, Q interface{ m() }] int; var _ t[int, int]`, `t`, `generic_t4.t[P₁, Q₂ interface{m()}]`},
// instantiated types must be sanitized
- {`package g0; type t[P any] int; var x struct{ f t[int] }; var _ = x.f`, `x.f`, `g0.t[int]`},
+ {genericPkg + `g0; type t[P any] int; var x struct{ f t[int] }; var _ = x.f`, `x.f`, `generic_g0.t[int]`},
}
for _, test := range tests {
@@ -325,103 +336,64 @@
targs []string
sig string
}{
- {`package p0; func f[T any](T); func _() { f(42) }`,
+ {genericPkg + `p0; func f[T any](T); func _() { f(42) }`,
`f`,
[]string{`int`},
`func(int)`,
},
- {`package p1; func f[T any](T) T; func _() { f('@') }`,
+ {genericPkg + `p1; func f[T any](T) T; func _() { f('@') }`,
`f`,
[]string{`rune`},
`func(rune) rune`,
},
- {`package p2; func f[T any](...T) T; func _() { f(0i) }`,
+ {genericPkg + `p2; func f[T any](...T) T; func _() { f(0i) }`,
`f`,
[]string{`complex128`},
`func(...complex128) complex128`,
},
- {`package p3; func f(type A, B, C)(A, *B, []C); func _() { f(1.2, new(string), []byte{}) }`,
+ {genericPkg + `p3; func f[A, B, C any](A, *B, []C); func _() { f(1.2, new(string), []byte{}) }`,
`f`,
[]string{`float64`, `string`, `byte`},
`func(float64, *string, []byte)`,
},
- {`package p4; func f(type A, B)(A, *B, ...[]B); func _() { f(1.2, new(byte)) }`,
+ {genericPkg + `p4; func f[A, B any](A, *B, ...[]B); func _() { f(1.2, new(byte)) }`,
`f`,
[]string{`float64`, `byte`},
`func(float64, *byte, ...[]byte)`,
},
- // we don't know how to translate these but we can type-check them
- {`package q0; type T struct{}; func (T) m[P any](P); func _(x T) { x.m(42) }`,
- `x.m`,
- []string{`int`},
- `func(int)`,
- },
- {`package q1; type T struct{}; func (T) m[P any](P) P; func _(x T) { x.m(42) }`,
- `x.m`,
- []string{`int`},
- `func(int) int`,
- },
- {`package q2; type T struct{}; func (T) m[P any](...P) P; func _(x T) { x.m(42) }`,
- `x.m`,
- []string{`int`},
- `func(...int) int`,
- },
- {`package q3; type T struct{}; func (T) m(type A, B, C)(A, *B, []C); func _(x T) { x.m(1.2, new(string), []byte{}) }`,
- `x.m`,
- []string{`float64`, `string`, `byte`},
- `func(float64, *string, []byte)`,
- },
- {`package q4; type T struct{}; func (T) m(type A, B)(A, *B, ...[]B); func _(x T) { x.m(1.2, new(byte)) }`,
- `x.m`,
- []string{`float64`, `byte`},
- `func(float64, *byte, ...[]byte)`,
- },
-
- {`package r0; type T[P any] struct{}; func (_ T[P]) m[type Q](Q); func _[P any](x T[P]) { x.m(42) }`,
- `x.m`,
- []string{`int`},
- `func(int)`,
- },
- // TODO(gri) fix method type parameter syntax below
- {`package r1; type T interface{ m(type P any)(P) }; func _(x T) { x.m(4.2) }`,
- `x.m`,
- []string{`float64`},
- `func(float64)`,
- },
-
- {`package s1; func f[T any, P interface{type *T}](x T); func _(x string) { f(x) }`,
+ {genericPkg + `s1; func f[T any, P interface{type *T}](x T); func _(x string) { f(x) }`,
`f`,
[]string{`string`, `*string`},
`func(x string)`,
},
- {`package s2; func f[T any, P interface{type *T}](x []T); func _(x []int) { f(x) }`,
+ {genericPkg + `s2; func f[T any, P interface{type *T}](x []T); func _(x []int) { f(x) }`,
`f`,
[]string{`int`, `*int`},
`func(x []int)`,
},
- {`package s3; type C[T any] interface{type chan<- T}; func f[T any, P C[T]](x []T); func _(x []int) { f(x) }`,
+ {genericPkg + `s3; type C[T any] interface{type chan<- T}; func f[T any, P C[T]](x []T); func _(x []int) { f(x) }`,
`f`,
[]string{`int`, `chan<- int`},
`func(x []int)`,
},
- {`package s4; type C[T any] interface{type chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T); func _(x []int) { f(x) }`,
+ {genericPkg + `s4; type C[T any] interface{type chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T); func _(x []int) { f(x) }`,
`f`,
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
`func(x []int)`,
},
- {`package t1; func f[T any, P interface{type *T}]() T; func _() { _ = f[string] }`,
+ {genericPkg + `t1; func f[T any, P interface{type *T}]() T; func _() { _ = f[string] }`,
`f`,
[]string{`string`, `*string`},
`func() string`,
},
- {`package t2; type C[T any] interface{type chan<- T}; func f[T any, P C[T]]() []T; func _() { _ = f[int] }`,
+ {genericPkg + `t2; type C[T any] interface{type chan<- T}; func f[T any, P C[T]]() []T; func _() { _ = f[int] }`,
`f`,
[]string{`int`, `chan<- int`},
`func() []int`,
},
- {`package t3; type C[T any] interface{type chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T; func _() { _ = f[int] }`,
+ {genericPkg + `t3; type C[T any] interface{type chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T; func _() { _ = f[int] }`,
`f`,
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
`func() []int`,
@@ -493,10 +465,10 @@
// generic types must be sanitized
// (need to use sufficiently nested types to provoke unexpanded types)
- {`package g0; type t[P any] P; const x = t[int](42)`, `x`, `const g0.x g0.t[int]`},
- {`package g1; type t[P any] P; var x = t[int](42)`, `x`, `var g1.x g1.t[int]`},
- {`package g2; type t[P any] P; type x struct{ f t[int] }`, `x`, `type g2.x struct{f g2.t[int]}`},
- {`package g3; type t[P any] P; func f(x struct{ f t[string] }); var g = f`, `g`, `var g3.g func(x struct{f g3.t[string]})`},
+ {genericPkg + `g0; type t[P any] P; const x = t[int](42)`, `x`, `const generic_g0.x generic_g0.t[int]`},
+ {genericPkg + `g1; type t[P any] P; var x = t[int](42)`, `x`, `var generic_g1.x generic_g1.t[int]`},
+ {genericPkg + `g2; type t[P any] P; type x struct{ f t[int] }`, `x`, `type generic_g2.x struct{f generic_g2.t[int]}`},
+ {genericPkg + `g3; type t[P any] P; func f(x struct{ f t[string] }); var g = f`, `g`, `var generic_g3.g func(x struct{f generic_g3.t[string]})`},
}
for _, test := range tests {
@@ -538,10 +510,10 @@
// generic types must be sanitized
// (need to use sufficiently nested types to provoke unexpanded types)
- {`package g0; func _() { _ = x }; type t[P any] P; const x = t[int](42)`, `x`, `const g0.x g0.t[int]`},
- {`package g1; func _() { _ = x }; type t[P any] P; var x = t[int](42)`, `x`, `var g1.x g1.t[int]`},
- {`package g2; func _() { type _ x }; type t[P any] P; type x struct{ f t[int] }`, `x`, `type g2.x struct{f g2.t[int]}`},
- {`package g3; func _() { _ = f }; type t[P any] P; func f(x struct{ f t[string] })`, `f`, `func g3.f(x struct{f g3.t[string]})`},
+ {genericPkg + `g0; func _() { _ = x }; type t[P any] P; const x = t[int](42)`, `x`, `const generic_g0.x generic_g0.t[int]`},
+ {genericPkg + `g1; func _() { _ = x }; type t[P any] P; var x = t[int](42)`, `x`, `var generic_g1.x generic_g1.t[int]`},
+ {genericPkg + `g2; func _() { type _ x }; type t[P any] P; type x struct{ f t[int] }`, `x`, `type generic_g2.x struct{f generic_g2.t[int]}`},
+ {genericPkg + `g3; func _() { _ = f }; type t[P any] P; func f(x struct{ f t[string] })`, `f`, `func generic_g3.f(x struct{f generic_g3.t[string]})`},
}
for _, test := range tests {
@@ -1501,6 +1473,50 @@
}
}
+func TestConvertibleTo(t *testing.T) {
+ for _, test := range []struct {
+ v, t Type
+ want bool
+ }{
+ {Typ[Int], Typ[Int], true},
+ {Typ[Int], Typ[Float32], true},
+ {newDefined(Typ[Int]), Typ[Int], true},
+ {newDefined(new(Struct)), new(Struct), true},
+ {newDefined(Typ[Int]), new(Struct), false},
+ {Typ[UntypedInt], Typ[Int], true},
+ // Untyped string values are not permitted by the spec, so the below
+ // behavior is undefined.
+ {Typ[UntypedString], Typ[String], true},
+ } {
+ if got := ConvertibleTo(test.v, test.t); got != test.want {
+ t.Errorf("ConvertibleTo(%v, %v) = %t, want %t", test.v, test.t, got, test.want)
+ }
+ }
+}
+
+func TestAssignableTo(t *testing.T) {
+ for _, test := range []struct {
+ v, t Type
+ want bool
+ }{
+ {Typ[Int], Typ[Int], true},
+ {Typ[Int], Typ[Float32], false},
+ {newDefined(Typ[Int]), Typ[Int], false},
+ {newDefined(new(Struct)), new(Struct), true},
+ {Typ[UntypedBool], Typ[Bool], true},
+ {Typ[UntypedString], Typ[Bool], false},
+ // Neither untyped string nor untyped numeric assignments arise during
+ // normal type checking, so the below behavior is technically undefined by
+ // the spec.
+ {Typ[UntypedString], Typ[String], true},
+ {Typ[UntypedInt], Typ[Int], true},
+ } {
+ if got := AssignableTo(test.v, test.t); got != test.want {
+ t.Errorf("AssignableTo(%v, %v) = %t, want %t", test.v, test.t, got, test.want)
+ }
+ }
+}
+
func TestIdentical_issue15173(t *testing.T) {
// Identical should allow nil arguments and be symmetric.
for _, test := range []struct {
diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go
index e9a1224..3aa06e8 100644
--- a/src/go/types/assignments.go
+++ b/src/go/types/assignments.go
@@ -26,7 +26,7 @@
// ok
default:
// we may get here because of other problems (issue #39634, crash 12)
- check.errorf(x.pos(), "cannot assign %s to %s in %s", x, T, context)
+ check.errorf(x, 0, "cannot assign %s to %s in %s", x, T, context)
return
}
@@ -35,26 +35,44 @@
// spec: "If an untyped constant is assigned to a variable of interface
// type or the blank identifier, the constant is first converted to type
// bool, rune, int, float64, complex128 or string respectively, depending
- // on whether the value is a boolean, rune, integer, floating-point, complex,
- // or string constant."
+ // on whether the value is a boolean, rune, integer, floating-point,
+ // complex, or string constant."
if T == nil || IsInterface(T) {
if T == nil && x.typ == Typ[UntypedNil] {
- check.errorf(x.pos(), "use of untyped nil in %s", context)
+ check.errorf(x, _UntypedNil, "use of untyped nil in %s", context)
x.mode = invalid
return
}
target = Default(x.typ)
}
- check.convertUntyped(x, target)
- if x.mode == invalid {
+ newType, val, code := check.implicitTypeAndValue(x, target)
+ if code != 0 {
+ msg := check.sprintf("cannot use %s as %s value in %s", x, target, context)
+ switch code {
+ case _TruncatedFloat:
+ msg += " (truncated)"
+ case _NumericOverflow:
+ msg += " (overflows)"
+ default:
+ code = _IncompatibleAssign
+ }
+ check.error(x, code, msg)
+ x.mode = invalid
return
}
+ if val != nil {
+ x.val = val
+ check.updateExprVal(x.expr, val)
+ }
+ if newType != x.typ {
+ x.typ = newType
+ check.updateExprType(x.expr, newType, false)
+ }
}
- // x.typ is typed
// A generic (non-instantiated) function value cannot be assigned to a variable.
if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 {
- check.errorf(x.pos(), "cannot use generic function %s without instantiation in %s", x, context)
+ check.errorf(x, _Todo, "cannot use generic function %s without instantiation in %s", x, context)
}
// spec: "If a left-hand side is the blank identifier, any typed or
@@ -64,11 +82,12 @@
return
}
- if reason := ""; !x.assignableTo(check, T, &reason) {
+ reason := ""
+ if ok, code := x.assignableTo(check, T, &reason); !ok {
if reason != "" {
- check.errorf(x.pos(), "cannot use %s as %s value in %s: %s", x, T, context, reason)
+ check.errorf(x, code, "cannot use %s as %s value in %s: %s", x, T, context, reason)
} else {
- check.errorf(x.pos(), "cannot use %s as %s value in %s", x, T, context)
+ check.errorf(x, code, "cannot use %s as %s value in %s", x, T, context)
}
x.mode = invalid
}
@@ -84,7 +103,7 @@
// rhs must be a constant
if x.mode != constant_ {
- check.errorf(x.pos(), "%s is not constant", x)
+ check.errorf(x, _InvalidConstInit, "%s is not constant", x)
if lhs.typ == nil {
lhs.typ = Typ[Invalid]
}
@@ -119,7 +138,7 @@
if isUntyped(typ) {
// convert untyped types to default types
if typ == Typ[UntypedNil] {
- check.errorf(x.pos(), "use of untyped nil in %s", context)
+ check.errorf(x, _UntypedNil, "use of untyped nil in %s", context)
lhs.typ = Typ[Invalid]
return nil
}
@@ -194,11 +213,11 @@
var op operand
check.expr(&op, sel.X)
if op.mode == mapindex {
- check.errorf(z.pos(), "cannot assign to struct field %s in map", ExprString(z.expr))
+ check.errorf(&z, _UnaddressableFieldAssign, "cannot assign to struct field %s in map", ExprString(z.expr))
return nil
}
}
- check.errorf(z.pos(), "cannot assign to %s", &z)
+ check.errorf(&z, _UnassignableOperand, "cannot assign to %s", &z)
return nil
}
@@ -212,8 +231,8 @@
// If returnPos is valid, initVars is called to type-check the assignment of
// return expressions, and returnPos is the position of the return statement.
-func (check *Checker) initVars(lhs []*Var, orig_rhs []ast.Expr, returnPos token.Pos) {
- rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2 && !returnPos.IsValid())
+func (check *Checker) initVars(lhs []*Var, origRHS []ast.Expr, returnPos token.Pos) {
+ rhs, commaOk := check.exprList(origRHS, len(lhs) == 2 && !returnPos.IsValid())
if len(lhs) != len(rhs) {
// invalidate lhs
@@ -229,10 +248,10 @@
}
}
if returnPos.IsValid() {
- check.errorf(returnPos, "wrong number of return values (want %d, got %d)", len(lhs), len(rhs))
+ check.errorf(atPos(returnPos), _WrongResultCount, "wrong number of return values (want %d, got %d)", len(lhs), len(rhs))
return
}
- check.errorf(rhs[0].pos(), "cannot initialize %d variables with %d values", len(lhs), len(rhs))
+ check.errorf(rhs[0], _WrongAssignCount, "cannot initialize %d variables with %d values", len(lhs), len(rhs))
return
}
@@ -246,7 +265,7 @@
for i := range a {
a[i] = check.initVar(lhs[i], rhs[i], context)
}
- check.recordCommaOkTypes(orig_rhs[0], a)
+ check.recordCommaOkTypes(origRHS[0], a)
return
}
@@ -255,8 +274,8 @@
}
}
-func (check *Checker) assignVars(lhs, orig_rhs []ast.Expr) {
- rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2)
+func (check *Checker) assignVars(lhs, origRHS []ast.Expr) {
+ rhs, commaOk := check.exprList(origRHS, len(lhs) == 2)
if len(lhs) != len(rhs) {
check.useLHS(lhs...)
@@ -266,7 +285,7 @@
return
}
}
- check.errorf(rhs[0].pos(), "cannot assign %d values to %d variables", len(rhs), len(lhs))
+ check.errorf(rhs[0], _WrongAssignCount, "cannot assign %d values to %d variables", len(rhs), len(lhs))
return
}
@@ -275,7 +294,7 @@
for i := range a {
a[i] = check.assignVar(lhs[i], rhs[i])
}
- check.recordCommaOkTypes(orig_rhs[0], a)
+ check.recordCommaOkTypes(origRHS[0], a)
return
}
@@ -284,7 +303,7 @@
}
}
-func (check *Checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) {
+func (check *Checker) shortVarDecl(pos positioner, lhs, rhs []ast.Expr) {
top := len(check.delayed)
scope := check.scope
@@ -304,7 +323,7 @@
if alt, _ := alt.(*Var); alt != nil {
obj = alt
} else {
- check.errorf(lhs.Pos(), "cannot assign to %s", lhs)
+ check.errorf(lhs, _UnassignableOperand, "cannot assign to %s", lhs)
}
check.recordUse(ident, alt)
} else {
@@ -317,7 +336,7 @@
}
} else {
check.useLHS(lhs)
- check.errorf(lhs.Pos(), "cannot declare %s", lhs)
+ check.invalidAST(lhs, "cannot declare %s", lhs)
}
if obj == nil {
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
@@ -341,6 +360,6 @@
check.declare(scope, nil, obj, scopePos) // recordObject already called
}
} else {
- check.softErrorf(pos, "no new variables on left side of :=")
+ check.softErrorf(pos, _NoNewVar, "no new variables on left side of :=")
}
}
diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go
index d53a424..28697d9 100644
--- a/src/go/types/builtins.go
+++ b/src/go/types/builtins.go
@@ -21,7 +21,9 @@
// append is the only built-in that permits the use of ... for the last argument
bin := predeclaredFuncs[id]
if call.Ellipsis.IsValid() && id != _Append {
- check.invalidOp(call.Ellipsis, "invalid use of ... with built-in %s", bin.name)
+ check.invalidOp(atPos(call.Ellipsis),
+ _InvalidDotDotDot,
+ "invalid use of ... with built-in %s", bin.name)
check.use(call.Args...)
return
}
@@ -67,7 +69,7 @@
msg = "too many"
}
if msg != "" {
- check.invalidOp(call.Rparen, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs)
+ check.invalidOp(inNode(call, call.Rparen), _WrongArgCount, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs)
return
}
}
@@ -84,7 +86,7 @@
if s := asSlice(S); s != nil {
T = s.elem
} else {
- check.invalidArg(x.pos(), "%s is not a slice", x)
+ check.invalidArg(x, _InvalidAppend, "%s is not a slice", x)
return
}
@@ -94,23 +96,25 @@
// spec: "As a special case, append also accepts a first argument assignable
// to type []byte with a second argument of string type followed by ... .
// This form appends the bytes of the string.
- if nargs == 2 && call.Ellipsis.IsValid() && x.assignableTo(check, NewSlice(universeByte), nil) {
- arg(x, 1)
- if x.mode == invalid {
- return
- }
- if isString(x.typ) {
- if check.Types != nil {
- sig := makeSig(S, S, x.typ)
- sig.variadic = true
- check.recordBuiltinType(call.Fun, sig)
+ if nargs == 2 && call.Ellipsis.IsValid() {
+ if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
+ arg(x, 1)
+ if x.mode == invalid {
+ return
}
- x.mode = value
- x.typ = S
- break
+ if isString(x.typ) {
+ if check.Types != nil {
+ sig := makeSig(S, S, x.typ)
+ sig.variadic = true
+ check.recordBuiltinType(call.Fun, sig)
+ }
+ x.mode = value
+ x.typ = S
+ break
+ }
+ alist = append(alist, *x)
+ // fallthrough
}
- alist = append(alist, *x)
- // fallthrough
}
// check general case by creating custom signature
@@ -196,7 +200,11 @@
}
if mode == invalid && typ != Typ[Invalid] {
- check.invalidArg(x.pos(), "%s for %s", x, bin.name)
+ code := _InvalidCap
+ if id == _Len {
+ code = _InvalidLen
+ }
+ check.invalidArg(x, code, "%s for %s", x, bin.name)
return
}
@@ -211,11 +219,11 @@
// close(c)
c := asChan(x.typ)
if c == nil {
- check.invalidArg(x.pos(), "%s is not a channel", x)
+ check.invalidArg(x, _InvalidClose, "%s is not a channel", x)
return
}
if c.dir == RecvOnly {
- check.invalidArg(x.pos(), "%s must not be a receive-only channel", x)
+ check.invalidArg(x, _InvalidClose, "%s must not be a receive-only channel", x)
return
}
@@ -279,7 +287,7 @@
// both argument types must be identical
if !check.identical(x.typ, y.typ) {
- check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
+ check.invalidArg(x, _InvalidComplex, "mismatched types %s and %s", x.typ, y.typ)
return
}
@@ -299,7 +307,7 @@
}
resTyp := check.applyTypeFunc(f, x.typ)
if resTyp == nil {
- check.invalidArg(x.pos(), "arguments have type %s, expected floating-point", x.typ)
+ check.invalidArg(x, _InvalidComplex, "arguments have type %s, expected floating-point", x.typ)
return
}
@@ -339,12 +347,12 @@
}
if dst == nil || src == nil {
- check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y)
+ check.invalidArg(x, _InvalidCopy, "copy expects slice arguments; found %s and %s", x, &y)
return
}
if !check.identical(dst, src) {
- check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
+ check.invalidArg(x, _InvalidCopy, "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
return
}
@@ -358,7 +366,7 @@
// delete(m, k)
m := asMap(x.typ)
if m == nil {
- check.invalidArg(x.pos(), "%s is not a map", x)
+ check.invalidArg(x, _InvalidDelete, "%s is not a map", x)
return
}
arg(x, 1) // k
@@ -366,8 +374,8 @@
return
}
- if !x.assignableTo(check, m.key, nil) {
- check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key)
+ if ok, code := x.assignableTo(check, m.key, nil); !ok {
+ check.invalidArg(x, code, "%s is not assignable to %s", x, m.key)
return
}
@@ -417,7 +425,11 @@
}
resTyp := check.applyTypeFunc(f, x.typ)
if resTyp == nil {
- check.invalidArg(x.pos(), "argument has type %s, expected complex type", x.typ)
+ code := _InvalidImag
+ if id == _Real {
+ code = _InvalidReal
+ }
+ check.invalidArg(x, code, "argument has type %s, expected complex type", x.typ)
return
}
@@ -472,14 +484,14 @@
}
if !valid(T) {
- check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0)
+ check.invalidArg(arg0, _InvalidMake, "cannot make %s; type must be slice, map, or channel", arg0)
return
}
if nargs < min || max < nargs {
if min == max {
- check.errorf(call.Pos(), "%v expects %d arguments; found %d", call, min, nargs)
+ check.errorf(call, _WrongArgCount, "%v expects %d arguments; found %d", call, min, nargs)
} else {
- check.errorf(call.Pos(), "%v expects %d or %d arguments; found %d", call, min, max, nargs)
+ check.errorf(call, _WrongArgCount, "%v expects %d or %d arguments; found %d", call, min, max, nargs)
}
return
}
@@ -494,7 +506,7 @@
}
}
if len(sizes) == 2 && sizes[0] > sizes[1] {
- check.invalidArg(call.Args[1].Pos(), "length and capacity swapped")
+ check.invalidArg(call.Args[1], _SwappedMakeArgs, "length and capacity swapped")
// safe to continue
}
x.mode = value
@@ -577,7 +589,7 @@
case _Alignof:
// unsafe.Alignof(x T) uintptr
if asTypeParam(x.typ) != nil {
- check.invalidOp(call.Pos(), "unsafe.Alignof undefined for %s", x)
+ check.invalidOp(call, _Todo, "unsafe.Alignof undefined for %s", x)
return
}
check.assignment(x, nil, "argument to unsafe.Alignof")
@@ -596,7 +608,7 @@
arg0 := call.Args[0]
selx, _ := unparen(arg0).(*ast.SelectorExpr)
if selx == nil {
- check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0)
+ check.invalidArg(arg0, _BadOffsetofSyntax, "%s is not a selector expression", arg0)
check.use(arg0)
return
}
@@ -611,18 +623,18 @@
obj, index, indirect := check.lookupFieldOrMethod(base, false, check.pkg, sel)
switch obj.(type) {
case nil:
- check.invalidArg(x.pos(), "%s has no single field %s", base, sel)
+ check.invalidArg(x, _MissingFieldOrMethod, "%s has no single field %s", base, sel)
return
case *Func:
// TODO(gri) Using derefStructPtr may result in methods being found
// that don't actually exist. An error either way, but the error
// message is confusing. See: https://play.golang.org/p/al75v23kUy ,
// but go/types reports: "invalid argument: x.m is a method value".
- check.invalidArg(arg0.Pos(), "%s is a method value", arg0)
+ check.invalidArg(arg0, _InvalidOffsetof, "%s is a method value", arg0)
return
}
if indirect {
- check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base)
+ check.invalidArg(x, _InvalidOffsetof, "field %s is embedded via a pointer in %s", sel, base)
return
}
@@ -638,7 +650,7 @@
case _Sizeof:
// unsafe.Sizeof(x T) uintptr
if asTypeParam(x.typ) != nil {
- check.invalidOp(call.Pos(), "unsafe.Sizeof undefined for %s", x)
+ check.invalidOp(call, _Todo, "unsafe.Sizeof undefined for %s", x)
return
}
check.assignment(x, nil, "argument to unsafe.Sizeof")
@@ -656,15 +668,15 @@
// The result of assert is the value of pred if there is no error.
// Note: assert is only available in self-test mode.
if x.mode != constant_ || !isBoolean(x.typ) {
- check.invalidArg(x.pos(), "%s is not a boolean constant", x)
+ check.invalidArg(x, _Test, "%s is not a boolean constant", x)
return
}
if x.val.Kind() != constant.Bool {
- check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x)
+ check.errorf(x, _Test, "internal error: value of %s should be a boolean constant", x)
return
}
if !constant.BoolVal(x.val) {
- check.errorf(call.Pos(), "%v failed", call)
+ check.errorf(call, _Test, "%v failed", call)
// compile-time assertion failure - safe to continue
}
// result is constant - no need to record signature
@@ -684,7 +696,7 @@
x1 := x
for _, arg := range call.Args {
check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T))
- check.dump("%v: %s", x1.pos(), x1)
+ check.dump("%v: %s", x1.Pos(), x1)
x1 = &t // use incoming x only for first argument
}
// trace is only available in test mode - no need to record signature
@@ -720,7 +732,7 @@
// construct a suitable new type parameter
tpar := NewTypeName(token.NoPos, nil /* = Universe pkg */, "<type parameter>", nil)
- ptyp := check.NewTypeParam(tp.ptr, tpar, 0, &emptyInterface) // assigns type to tpar as a side-effect
+ ptyp := check.NewTypeParam(tpar, 0, &emptyInterface) // assigns type to tpar as a side-effect
tsum := NewSum(rtypes)
ptyp.bound = &Interface{types: tsum, allMethods: markComplete, allTypes: tsum}
diff --git a/src/go/types/call.go b/src/go/types/call.go
index 267c463..d9a7b44 100644
--- a/src/go/types/call.go
+++ b/src/go/types/call.go
@@ -1,3 +1,4 @@
+// REVIEW INCOMPLETE
// Copyright 2013 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.
@@ -13,6 +14,7 @@
"unicode"
)
+// TODO(rFindley) this has diverged a bit from types2. Bring it up to date.
// If call == nil, the "call" was an index expression, and orig is of type *ast.IndexExpr.
func (check *Checker) call(x *operand, call *ast.CallExpr, orig ast.Expr) exprKind {
assert(orig != nil)
@@ -49,14 +51,18 @@
// conversion
switch n := len(call.Args); n {
case 0:
- check.errorf(call.Rparen, "missing argument in conversion to %s", T)
+ check.errorf(inNode(call, call.Rparen), _WrongArgCount, "missing argument in conversion to %s", T)
case 1:
check.expr(x, call.Args[0])
if x.mode != invalid {
+ if call.Ellipsis.IsValid() {
+ check.errorf(call.Args[0], _BadDotDotDotSyntax, "invalid use of ... in conversion to %s", T)
+ break
+ }
if t := asInterface(T); t != nil {
check.completeInterface(token.NoPos, t)
if t.IsConstraint() {
- check.errorf(call.Pos(), "cannot use interface %s in conversion (contains type list or is comparable)", T)
+ check.errorf(call, _Todo, "cannot use interface %s in conversion (contains type list or is comparable)", T)
break
}
}
@@ -64,7 +70,7 @@
}
default:
check.use(call.Args...)
- check.errorf(call.Args[n-1].Pos(), "too many arguments in conversion to %s", T)
+ check.errorf(call.Args[n-1], _WrongArgCount, "too many arguments in conversion to %s", T)
}
x.expr = orig
return conversion
@@ -87,7 +93,7 @@
sig := asSignature(x.typ)
if sig == nil {
- check.invalidOp(x.pos(), "cannot call non-function %s", x)
+ check.invalidOp(x, _InvalidCall, "cannot call non-function %s", x)
x.mode = invalid
x.expr = orig
return statement
@@ -96,7 +102,7 @@
// evaluate arguments
args, ok := check.exprOrTypeList(call.Args)
if ok && call.Brackets && len(args) > 0 && args[0].mode != typexpr {
- check.errorf(args[0].pos(), "%s is not a type", args[0])
+ check.errorf(args[0], _NotAType, "%s is not a type", args[0])
ok = false
}
if !ok {
@@ -110,8 +116,8 @@
// If the first argument is a type, assume we have explicit type arguments.
// check number of type arguments
- if !check.conf.InferFromConstraints && n != len(sig.tparams) || n > len(sig.tparams) {
- check.errorf(args[n-1].pos(), "got %d type arguments but want %d", n, len(sig.tparams))
+ if n > len(sig.tparams) {
+ check.errorf(args[n-1], _Todo, "got %d type arguments but want %d", n, len(sig.tparams))
x.mode = invalid
x.expr = orig
return expression
@@ -119,6 +125,8 @@
// collect types
targs := make([]Type, n)
+ // TODO(rFindley) use a positioner here? instantiate would need to be
+ // updated accordingly.
poslist := make([]token.Pos, n)
for i, a := range args {
if a.mode != typexpr {
@@ -128,7 +136,7 @@
return expression
}
targs[i] = a.typ
- poslist[i] = a.pos()
+ poslist[i] = a.Pos()
}
// if we don't have enough type arguments, use constraint type inference
@@ -147,7 +155,7 @@
assert(targs[failed] == nil)
tpar := sig.tparams[failed]
ppos := check.fset.Position(tpar.pos).String()
- check.errorf(call.Rparen, "cannot infer %s (%s) (%s)", tpar.name, ppos, targs)
+ check.errorf(inNode(call, call.Rparen), 0, "cannot infer %s (%s) (%s)", tpar.name, ppos, targs)
x.mode = invalid
x.expr = orig
return expression
@@ -158,7 +166,6 @@
assert(targ != nil)
}
}
- //check.dump("### inferred targs = %s", targs)
n = len(targs)
inferred = true
}
@@ -171,9 +178,9 @@
if i < len(poslist) {
pos = poslist[i]
}
- check.ordinaryType(pos, typ)
+ check.ordinaryType(atPos(pos), typ)
}
- res := check.instantiate(x.pos(), sig, targs, poslist).(*Signature)
+ res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature)
assert(res.tparams == nil) // signature is not generic anymore
if inferred {
check.recordInferred(orig, targs, res)
@@ -184,7 +191,11 @@
return expression
}
- // If we reach here, orig must have been a regular call, not an index expression.
+ // If we reach here, orig must have been a regular call, not an index
+ // expression.
+ // TODO(rFindley) with a manually constructed AST it is possible to reach
+ // this assertion. We should return an invalidAST error here
+ // rather than panicking.
assert(!call.Brackets)
sig = check.arguments(call, sig, args)
@@ -262,7 +273,7 @@
}
}
if 0 < ntypes && ntypes < len(xlist) {
- check.errorf(xlist[0].pos(), "mix of value and type expressions")
+ check.errorf(xlist[0], 0, "mix of value and type expressions")
ok = false
}
}
@@ -293,7 +304,11 @@
xlist = []*operand{&x}
if allowCommaOk && (x.mode == mapindex || x.mode == commaok || x.mode == commaerr) {
x.mode = value
- xlist = append(xlist, &operand{mode: value, expr: e, typ: Typ[UntypedBool]})
+ x2 := &operand{mode: value, expr: e, typ: Typ[UntypedBool]}
+ if x.mode == commaerr {
+ x2.typ = universeError
+ }
+ xlist = append(xlist, x2)
commaOk = true
}
@@ -317,7 +332,7 @@
for _, a := range args {
switch a.mode {
case typexpr:
- check.errorf(a.pos(), "%s used as value", a)
+ check.errorf(a, 0, "%s used as value", a)
return
case invalid:
return
@@ -338,14 +353,14 @@
ddd := call.Ellipsis.IsValid()
// set up parameters
- sig_params := sig.params // adjusted for variadic functions (may be nil for empty parameter lists!)
- adjusted := false // indicates if sig_params is different from t.params
+ sigParams := sig.params // adjusted for variadic functions (may be nil for empty parameter lists!)
+ adjusted := false // indicates if sigParams is different from t.params
if sig.variadic {
if ddd {
// variadic_func(a, b, c...)
if len(call.Args) == 1 && nargs > 1 {
// f()... is not permitted if f() is multi-valued
- check.errorf(call.Ellipsis, "cannot use ... with %d-valued %s", nargs, call.Args[0])
+ check.errorf(inNode(call, call.Ellipsis), _InvalidDotDotDot, "cannot use ... with %d-valued %s", nargs, call.Args[0])
return
}
} else {
@@ -361,7 +376,7 @@
for len(vars) < nargs {
vars = append(vars, NewParam(last.pos, last.pkg, last.name, typ))
}
- sig_params = NewTuple(vars...) // possibly nil!
+ sigParams = NewTuple(vars...) // possibly nil!
adjusted = true
npars = nargs
} else {
@@ -372,7 +387,7 @@
} else {
if ddd {
// standard_func(a, b, c...)
- check.errorf(call.Ellipsis, "cannot use ... in call to non-variadic %s", call.Fun)
+ check.errorf(inNode(call, call.Ellipsis), _NonVariadicDotDotDot, "cannot use ... in call to non-variadic %s", call.Fun)
return
}
// standard_func(a, b, c)
@@ -381,10 +396,10 @@
// check argument count
switch {
case nargs < npars:
- check.errorf(call.Rparen, "not enough arguments in call to %s", call.Fun)
+ check.errorf(inNode(call, call.Rparen), _WrongArgCount, "not enough arguments in call to %s", call.Fun)
return
case nargs > npars:
- check.errorf(args[npars].pos(), "too many arguments in call to %s", call.Fun) // report at first extra argument
+ check.errorf(args[npars], _WrongArgCount, "too many arguments in call to %s", call.Fun) // report at first extra argument
return
}
@@ -392,25 +407,23 @@
if len(sig.tparams) > 0 {
// TODO(gri) provide position information for targs so we can feed
// it to the instantiate call for better error reporting
- targs, failed := check.infer(sig.tparams, sig_params, args)
+ targs, failed := check.infer(sig.tparams, sigParams, args)
if targs == nil {
return // error already reported
}
if failed >= 0 {
// Some type arguments couldn't be inferred. Use
// bounds type inference to try to make progress.
- if check.conf.InferFromConstraints {
- targs, failed = check.inferB(sig.tparams, targs)
- if targs == nil {
- return // error already reported
- }
+ targs, failed = check.inferB(sig.tparams, targs)
+ if targs == nil {
+ return // error already reported
}
if failed >= 0 {
// at least one type argument couldn't be inferred
assert(targs[failed] == nil)
tpar := sig.tparams[failed]
ppos := check.fset.Position(tpar.pos).String()
- check.errorf(call.Rparen, "cannot infer %s (%s) (%s)", tpar.name, ppos, targs)
+ check.errorf(inNode(call, call.Rparen), _Todo, "cannot infer %s (%s) (%s)", tpar.name, ppos, targs)
return
}
}
@@ -420,7 +433,6 @@
assert(targ != nil)
}
}
- //check.dump("### inferred targs = %s", targs)
// compute result signature
rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature)
@@ -431,9 +443,9 @@
// need to compute it from the adjusted list; otherwise we can
// simply use the result signature's parameter list.
if adjusted {
- sig_params = check.subst(call.Pos(), sig_params, makeSubstMap(sig.tparams, targs)).(*Tuple)
+ sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.tparams, targs)).(*Tuple)
} else {
- sig_params = rsig.params
+ sigParams = rsig.params
}
}
@@ -441,7 +453,7 @@
// TODO(gri) Possible optimization (may be tricky): We could avoid
// checking arguments from which we inferred type arguments.
for i, a := range args {
- check.assignment(a, sig_params.vars[i].typ, "argument")
+ check.assignment(a, sigParams.vars[i].typ, check.sprintf("argument to %s", call.Fun))
}
return
@@ -499,7 +511,7 @@
}
}
if exp == nil {
- check.errorf(e.Sel.Pos(), "%s not declared by package C", sel)
+ check.errorf(e.Sel, _UndeclaredImportedName, "%s not declared by package C", sel)
goto Error
}
check.objDecl(exp, nil)
@@ -507,12 +519,12 @@
exp = pkg.scope.Lookup(sel)
if exp == nil {
if !pkg.fake {
- check.errorf(e.Sel.Pos(), "%s not declared by package %s", sel, pkg.name)
+ check.errorf(e.Sel, _UndeclaredImportedName, "%s not declared by package %s", sel, pkg.name)
}
goto Error
}
if !exp.Exported() {
- check.errorf(e.Sel.Pos(), "%s not exported by package %s", sel, pkg.name)
+ check.errorf(e.Sel, _UnexportedName, "%s not exported by package %s", sel, pkg.name)
// ok to continue
}
}
@@ -567,9 +579,9 @@
switch {
case index != nil:
// TODO(gri) should provide actual type where the conflict happens
- check.errorf(e.Sel.Pos(), "ambiguous selector %s.%s", x.expr, sel)
+ check.errorf(e.Sel, _AmbiguousSelector, "ambiguous selector %s.%s", x.expr, sel)
case indirect:
- check.errorf(e.Sel.Pos(), "cannot call pointer method %s on %s", sel, x.typ)
+ check.errorf(e.Sel, _InvalidMethodExpr, "cannot call pointer method %s on %s", sel, x.typ)
default:
var why string
if tpar := asTypeParam(x.typ); tpar != nil {
@@ -597,15 +609,13 @@
}
}
- check.errorf(e.Sel.Pos(), "%s.%s undefined (%s)", x.expr, sel, why)
-
+ check.errorf(e.Sel, _MissingFieldOrMethod, "%s.%s undefined (%s)", x.expr, sel, why)
}
goto Error
}
// methods may not have a fully set up signature yet
if m, _ := obj.(*Func); m != nil {
- // check.dump("### found method %s", m)
check.objDecl(m, nil)
// If m has a parameterized receiver type, infer the type parameter
// values from the actual receiver provided and then substitute the
@@ -613,8 +623,6 @@
// TODO(gri) factor this code out
sig := m.typ.(*Signature)
if len(sig.rparams) > 0 {
- //check.dump("### recv typ = %s", x.typ)
- //check.dump("### method = %s rparams = %s tparams = %s", m, sig.rparams, sig.tparams)
// The method may have a pointer receiver, but the actually provided receiver
// may be a (hopefully addressable) non-pointer value, or vice versa. Here we
// only care about inferring receiver type parameters; to make the inference
@@ -630,7 +638,6 @@
arg = ©
}
targs, failed := check.infer(sig.rparams, NewTuple(sig.recv), []*operand{arg})
- //check.dump("### inferred targs = %s", targs)
if failed >= 0 {
// We may reach here if there were other errors (see issue #40056).
// check.infer will report a follow-up error.
@@ -654,7 +661,7 @@
m, _ := obj.(*Func)
if m == nil {
// TODO(gri) should check if capitalization of sel matters and provide better error message in that case
- check.errorf(e.Sel.Pos(), "%s.%s undefined (type %s has no method %s)", x.expr, sel, x.typ, sel)
+ check.errorf(e.Sel, _MissingFieldOrMethod, "%s.%s undefined (type %s has no method %s)", x.expr, sel, x.typ, sel)
goto Error
}
@@ -814,7 +821,7 @@
// instantiatedOperand reports an error of x is an uninstantiated (generic) type and sets x.typ to Typ[Invalid].
func (check *Checker) instantiatedOperand(x *operand) {
if x.mode == typexpr && isGeneric(x.typ) {
- check.errorf(x.pos(), "cannot use generic type %s without instantiation", x.typ)
+ check.errorf(x, _Todo, "cannot use generic type %s without instantiation", x.typ)
x.typ = Typ[Invalid]
}
}
diff --git a/src/go/types/check.go b/src/go/types/check.go
index 5238e5a..d167283 100644
--- a/src/go/types/check.go
+++ b/src/go/types/check.go
@@ -8,14 +8,16 @@
import (
"errors"
- "fmt"
"go/ast"
"go/constant"
"go/token"
)
// debugging/development support
-const debug = true // leave on during development
+const (
+ debug = false // leave on during development
+ trace = false // turn on for detailed type resolution traces
+)
// If forceStrict is set, the type-checker enforces additional
// rules not specified by the Go 1 spec, but which will
@@ -30,11 +32,6 @@
//
const forceStrict = false
-// If methodTypeParamsOk is set, type parameters are
-// permitted in method declarations (in interfaces, too).
-// Generalization and experimental feature.
-const methodTypeParamsOk = true
-
// exprInfo stores information about an untyped expression.
type exprInfo struct {
isLhs bool // expression is lhs operand of a shift with delayed type-check
@@ -49,6 +46,7 @@
scope *Scope // top-most scope for lookups
pos token.Pos // if valid, identifiers are looked up as if at position pos (used by Eval)
iota constant.Value // value of iota in a constant declaration; nil otherwise
+ errpos positioner // if set, identifier position of a constant with inherited initializer
sig *Signature // function signature if inside a function; nil otherwise
isPanic map[*ast.CallExpr]bool // set of panic call expressions (used for termination check)
hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions
@@ -90,9 +88,8 @@
// information collected during type-checking of a set of package files
// (initialized by Files, valid only for the duration of check.Files;
// maps and lists are allocated on demand)
- files []*ast.File // package files
- unusedDotImports map[*Scope]map[*Package]token.Pos // positions of unused dot-imported packages for each file scope
- useBrackets bool // if set, [] are used instead of () for type parameters
+ files []*ast.File // package files
+ unusedDotImports map[*Scope]map[*Package]*ast.ImportSpec // unused dot-imported packages
firstErr error // first error encountered
methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
@@ -111,18 +108,18 @@
// addUnusedImport adds the position of a dot-imported package
// pkg to the map of dot imports for the given file scope.
-func (check *Checker) addUnusedDotImport(scope *Scope, pkg *Package, pos token.Pos) {
+func (check *Checker) addUnusedDotImport(scope *Scope, pkg *Package, spec *ast.ImportSpec) {
mm := check.unusedDotImports
if mm == nil {
- mm = make(map[*Scope]map[*Package]token.Pos)
+ mm = make(map[*Scope]map[*Package]*ast.ImportSpec)
check.unusedDotImports = mm
}
m := mm[scope]
if m == nil {
- m = make(map[*Package]token.Pos)
+ m = make(map[*Package]*ast.ImportSpec)
mm[scope] = m
}
- m[pkg] = pos
+ m[pkg] = spec
}
// addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists
@@ -225,7 +222,7 @@
if name != "_" {
pkg.name = name
} else {
- check.errorf(file.Name.Pos(), "invalid package name _")
+ check.errorf(file.Name, _BlankPkgName, "invalid package name _")
}
fallthrough
@@ -233,7 +230,7 @@
check.files = append(check.files, file)
default:
- check.errorf(file.Package, "package %s; expected %s", name, pkg.name)
+ check.errorf(atPos(file.Package), _MismatchedPkgName, "package %s; expected %s", name, pkg.name)
// ignore this file
}
}
@@ -265,49 +262,24 @@
defer check.handleBailout(&err)
- print := func(msg string) {
- if check.conf.Trace {
- fmt.Println(msg)
- }
- }
-
- print("=== check consistent use of () or [] for type parameters ===")
- if len(files) > 0 {
- check.useBrackets = files[0].UseBrackets
- for _, file := range files[1:] {
- if check.useBrackets != file.UseBrackets {
- check.errorf(file.Package, "inconsistent use of () or [] for type parameters")
- return // cannot type-check properly
- }
- }
- }
-
- print("== initFiles ==")
check.initFiles(files)
- print("== collectObjects ==")
check.collectObjects()
- print("== packageObjects ==")
check.packageObjects()
- print("== processDelayed ==")
check.processDelayed(0) // incl. all functions
check.processFinals()
- print("== initOrder ==")
check.initOrder()
if !check.conf.DisableUnusedImportCheck {
- print("== unusedImports ==")
check.unusedImports()
}
- print("== recordUntyped ==")
check.recordUntyped()
if check.Info != nil {
- print("== sanitizeInfo ==")
sanitizeInfo(check.Info)
}
diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go
index 08fc784..c6a8c3b 100644
--- a/src/go/types/check_test.go
+++ b/src/go/types/check_test.go
@@ -67,11 +67,11 @@
return
}
-func parseFiles(t *testing.T, filenames []string) ([]*ast.File, []error) {
+func parseFiles(t *testing.T, filenames []string, mode parser.Mode) ([]*ast.File, []error) {
var files []*ast.File
var errlist []error
for _, filename := range filenames {
- file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors|parser.UnifiedParamLists)
+ file, err := parser.ParseFile(fset, filename, nil, mode)
if file == nil {
t.Fatalf("%s: %s", filename, err)
}
@@ -189,9 +189,18 @@
}
}
-func checkFiles(t *testing.T, sources []string, trace bool) {
+func checkFiles(t *testing.T, sources []string) {
+ if len(sources) == 0 {
+ t.Fatal("no source files")
+ }
+
+ mode := parser.AllErrors
+ if strings.HasSuffix(sources[0], ".go2") {
+ mode |= parser.ParseTypeParams
+ }
+
// parse files and collect parser errors
- files, errlist := parseFiles(t, sources)
+ files, errlist := parseFiles(t, sources, mode)
pkgName := "<no package>"
if len(files) > 0 {
@@ -207,16 +216,14 @@
// typecheck and collect typechecker errors
var conf Config
- conf.AcceptMethodTypeParams = true
- conf.InferFromConstraints = true
+
// special case for importC.src
if len(sources) == 1 && strings.HasSuffix(sources[0], "importC.src") {
conf.FakeImportC = true
}
- conf.Trace = trace
- // We don't use importer.Default() below so we can eventually
- // get testdata/map.go2 to import chans (still to be fixed).
- conf.Importer = importer.ForCompiler(fset, "source", nil)
+ // TODO(rFindley) we may need to use the source importer when adding generics
+ // tests.
+ conf.Importer = importer.Default()
conf.Error = func(err error) {
if *haltOnError {
defer panic(err)
@@ -237,6 +244,17 @@
return
}
+ for _, err := range errlist {
+ err, ok := err.(Error)
+ if !ok {
+ continue
+ }
+ code := readCode(err)
+ if code == 0 {
+ t.Errorf("missing error code: %v", err)
+ }
+ }
+
// match and eliminate errors;
// we are expecting the following errors
errmap := errMap(t, pkgName, files)
@@ -260,7 +278,7 @@
}
testenv.MustHaveGoBuild(t)
DefPredeclaredTestFuncs()
- checkFiles(t, strings.Split(*testFiles, " "), testing.Verbose())
+ checkFiles(t, strings.Split(*testFiles, " "))
}
func TestTestdata(t *testing.T) { DefPredeclaredTestFuncs(); testDir(t, "testdata") }
@@ -276,20 +294,18 @@
return
}
- for count, fi := range fis {
+ for _, fi := range fis {
path := filepath.Join(dir, fi.Name())
// if fi is a directory, its files make up a single package
+ var files []string
if fi.IsDir() {
- if testing.Verbose() {
- fmt.Printf("%3d %s\n", count, path)
- }
fis, err := ioutil.ReadDir(path)
if err != nil {
t.Error(err)
continue
}
- files := make([]string, len(fis))
+ files = make([]string, len(fis))
for i, fi := range fis {
// if fi is a directory, checkFiles below will complain
files[i] = filepath.Join(path, fi.Name())
@@ -297,14 +313,11 @@
fmt.Printf("\t%s\n", files[i])
}
}
- checkFiles(t, files, false)
- continue
+ } else {
+ files = []string{path}
}
-
- // otherwise, fi is a stand-alone file
- if testing.Verbose() {
- fmt.Printf("%3d %s\n", count, path)
- }
- checkFiles(t, []string{path}, false)
+ t.Run(filepath.Base(path), func(t *testing.T) {
+ checkFiles(t, files)
+ })
}
}
diff --git a/src/go/types/conversions.go b/src/go/types/conversions.go
index 9d9e9d7..0756b57 100644
--- a/src/go/types/conversions.go
+++ b/src/go/types/conversions.go
@@ -38,7 +38,7 @@
}
if !ok {
- check.errorf(x.pos(), "cannot convert %s to %s", x, T)
+ check.errorf(x, _InvalidConversion, "cannot convert %s to %s", x, T)
x.mode = invalid
return
}
@@ -81,7 +81,7 @@
// exported API call, i.e., when all methods have been type-checked.
func (x *operand) convertibleTo(check *Checker, T Type) bool {
// "x is assignable to T"
- if x.assignableTo(check, T, nil) {
+ if ok, _ := x.assignableTo(check, T, nil); ok {
return true
}
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
index 1292ee2..bd2c546 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -16,7 +16,7 @@
// We use "other" rather than "previous" here because
// the first declaration seen may not be textually
// earlier in the source.
- check.errorf(pos, "\tother declaration of %s", obj.Name()) // secondary error, \t indented
+ check.errorf(obj, _DuplicateDecl, "\tother declaration of %s", obj.Name()) // secondary error, \t indented
}
}
@@ -27,7 +27,7 @@
// binding."
if obj.Name() != "_" {
if alt := scope.Insert(obj); alt != nil {
- check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
+ check.errorf(obj, _DuplicateDecl, "%s redeclared in this block", obj.Name())
check.reportAltDecl(alt)
return
}
@@ -53,7 +53,7 @@
// objDecl type-checks the declaration of obj in its respective (file) context.
// For the meaning of def, see Checker.definedType, in typexpr.go.
func (check *Checker) objDecl(obj Object, def *Named) {
- if check.conf.Trace && obj.Type() == nil {
+ if trace && obj.Type() == nil {
if check.indent == 0 {
fmt.Println() // empty line between top-level objects for readability
}
@@ -187,7 +187,7 @@
switch obj := obj.(type) {
case *Const:
check.decl = d // new package-level const decl
- check.constDecl(obj, d.vtyp, d.init)
+ check.constDecl(obj, d.vtyp, d.init, d.inherited)
case *Var:
check.decl = d // new package-level var decl
check.varDecl(obj, d.lhs, d.vtyp, d.init)
@@ -253,7 +253,7 @@
}
}
- if check.conf.Trace {
+ if trace {
check.trace(obj.Pos(), "## cycle detected: objPath = %s->%s (len = %d)", pathString(cycle), obj.Name(), len(cycle))
check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef)
defer func() {
@@ -323,7 +323,7 @@
}
// don't report a 2nd error if we already know the type is invalid
- // (e.g., if a cycle was detected earlier, via Checker.underlying).
+ // (e.g., if a cycle was detected earlier, via under).
if t.underlying == Typ[Invalid] {
t.info = invalid
return invalid
@@ -364,16 +364,16 @@
// cycle? That would be more consistent with other error messages.
i := firstInSrc(cycle)
obj := cycle[i]
- check.errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name())
+ check.errorf(obj, _InvalidDeclCycle, "illegal cycle in declaration of %s", obj.Name())
for range cycle {
- check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
+ check.errorf(obj, _InvalidDeclCycle, "\t%s refers to", obj.Name()) // secondary error, \t indented
i++
if i >= len(cycle) {
i = 0
}
obj = cycle[i]
}
- check.errorf(obj.Pos(), "\t%s", obj.Name())
+ check.errorf(obj, _InvalidDeclCycle, "\t%s", obj.Name())
}
// firstInSrc reports the index of the object with the "smallest"
@@ -388,12 +388,90 @@
return fst
}
-func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) {
+type (
+ decl interface {
+ node() ast.Node
+ }
+
+ importDecl struct{ spec *ast.ImportSpec }
+ constDecl struct {
+ spec *ast.ValueSpec
+ iota int
+ typ ast.Expr
+ init []ast.Expr
+ inherited bool
+ }
+ varDecl struct{ spec *ast.ValueSpec }
+ typeDecl struct{ spec *ast.TypeSpec }
+ funcDecl struct{ decl *ast.FuncDecl }
+)
+
+func (d importDecl) node() ast.Node { return d.spec }
+func (d constDecl) node() ast.Node { return d.spec }
+func (d varDecl) node() ast.Node { return d.spec }
+func (d typeDecl) node() ast.Node { return d.spec }
+func (d funcDecl) node() ast.Node { return d.decl }
+
+func (check *Checker) walkDecls(decls []ast.Decl, f func(decl)) {
+ for _, d := range decls {
+ check.walkDecl(d, f)
+ }
+}
+
+func (check *Checker) walkDecl(d ast.Decl, f func(decl)) {
+ switch d := d.(type) {
+ case *ast.BadDecl:
+ // ignore
+ case *ast.GenDecl:
+ var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
+ for iota, s := range d.Specs {
+ switch s := s.(type) {
+ case *ast.ImportSpec:
+ f(importDecl{s})
+ case *ast.ValueSpec:
+ switch d.Tok {
+ case token.CONST:
+ // determine which initialization expressions to use
+ inherited := true
+ switch {
+ case s.Type != nil || len(s.Values) > 0:
+ last = s
+ inherited = false
+ case last == nil:
+ last = new(ast.ValueSpec) // make sure last exists
+ inherited = false
+ }
+ check.arityMatch(s, last)
+ f(constDecl{spec: s, iota: iota, typ: last.Type, init: last.Values, inherited: inherited})
+ case token.VAR:
+ check.arityMatch(s, nil)
+ f(varDecl{s})
+ default:
+ check.invalidAST(s, "invalid token %s", d.Tok)
+ }
+ case *ast.TypeSpec:
+ f(typeDecl{s})
+ default:
+ check.invalidAST(s, "unknown ast.Spec node %T", s)
+ }
+ }
+ case *ast.FuncDecl:
+ f(funcDecl{d})
+ default:
+ check.invalidAST(d, "unknown ast.Decl node %T", d)
+ }
+}
+
+func (check *Checker) constDecl(obj *Const, typ, init ast.Expr, inherited bool) {
assert(obj.typ == nil)
// use the correct value of iota
- defer func(iota constant.Value) { check.iota = iota }(check.iota)
+ defer func(iota constant.Value, errpos positioner) {
+ check.iota = iota
+ check.errpos = errpos
+ }(check.iota, check.errpos)
check.iota = obj.val
+ check.errpos = nil
// provide valid constant value under all circumstances
obj.val = constant.MakeUnknown()
@@ -405,7 +483,7 @@
// don't report an error if the type is an invalid C (defined) type
// (issue #22090)
if under(t) != Typ[Invalid] {
- check.errorf(typ.Pos(), "invalid constant type %s", t)
+ check.errorf(typ, _InvalidConstType, "invalid constant type %s", t)
}
obj.typ = Typ[Invalid]
return
@@ -416,6 +494,15 @@
// check initialization
var x operand
if init != nil {
+ if inherited {
+ // The initialization expression is inherited from a previous
+ // constant declaration, and (error) positions refer to that
+ // expression and not the current constant declaration. Use
+ // the constant identifier position for any errors during
+ // init expression evaluation since that is all we have
+ // (see issues #42991, #42992).
+ check.errpos = atPos(obj.pos)
+ }
check.expr(&x, init)
}
check.initConst(obj, &x)
@@ -520,6 +607,8 @@
if i, ok := seen[n]; ok {
// cycle
+ // TODO(rFindley) revert this to a method on Checker. Having a possibly
+ // nil Checker on Named and TypeParam is too subtle.
if n0.check != nil {
n0.check.cycleError(path[i:])
}
@@ -559,7 +648,7 @@
if alias && tdecl.TParams != nil {
// The parser will ensure this but we may still get an invalid AST.
// Complain and continue as regular type definition.
- check.errorf(tdecl.Assign, "generic type cannot be alias")
+ check.error(atPos(tdecl.Assign), 0, "generic type cannot be alias")
alias = false
}
@@ -631,19 +720,11 @@
goto next
}
- // If the type bound expects exactly one type argument, permit leaving
- // it away and use the corresponding type parameter as implicit argument.
- // This allows us to write (type p b(p), q b(q), r b(r)) as (type p, q, r b).
- // Enabled if enableImplicitTParam is set.
- const enableImplicitTParam = false
-
// The predeclared identifier "any" is visible only as a constraint
// in a type parameter list. Look for it before general constraint
// resolution.
if tident, _ := f.Type.(*ast.Ident); tident != nil && tident.Name == "any" && check.lookup("any") == nil {
bound = universeAny
- } else if enableImplicitTParam {
- bound = check.anyType(f.Type)
} else {
bound = check.typ(f.Type)
}
@@ -654,47 +735,12 @@
// type C(type T C) interface {}
// (issue #39724).
if _, ok := under(bound).(*Interface); ok {
- if enableImplicitTParam && isGeneric(bound) {
- base := bound.(*Named) // only a *Named type can be generic
- if len(base.tparams) != 1 {
- check.errorf(f.Type.Pos(), "cannot use generic type %s without instantiation (more than one type parameter)", bound)
- goto next
- }
- // We have exactly one type parameter.
- // "Manually" instantiate the bound with each type
- // parameter the bound applies to.
- // TODO(gri) this code (in more general form) is also in
- // checker.typInternal for the *ast.CallExpr case. Factor?
- for i, name := range f.Names {
- typ := new(instance)
- typ.check = check
- typ.pos = f.Type.Pos()
- typ.base = base
- typ.targs = []Type{tparams[index+i].typ}
- typ.poslist = []token.Pos{name.Pos()}
- // Make sure we check instantiation works at least once
- // and that the resulting type is valid.
- check.atEnd(func() {
- check.validType(typ.expand(), nil)
- })
- // Set bound for each type parameter and record type.
- setBoundAt(index+i, typ)
- // We don't have a mechanism to record multiple different types
- // for a single type bound expression. Just do it for the first
- // type parameter for now.
- // TODO(gri) investigate what we need to do here
- if i == 0 {
- check.recordTypeAndValue(f.Type, typexpr, typ, nil)
- }
- }
- goto next
- }
// Otherwise, set the bound for each type parameter.
- for i, _ := range f.Names {
+ for i := range f.Names {
setBoundAt(index+i, bound)
}
} else if bound != Typ[Invalid] {
- check.errorf(f.Type.Pos(), "%s is not an interface", bound)
+ check.errorf(f.Type, _Todo, "%s is not an interface", bound)
}
next:
@@ -706,19 +752,13 @@
func (check *Checker) declareTypeParams(tparams []*TypeName, names []*ast.Ident) []*TypeName {
for _, name := range names {
- var ptr bool
- nstr := name.Name
- if len(nstr) > 0 && nstr[0] == '*' {
- ptr = true
- nstr = nstr[1:]
- }
- tpar := NewTypeName(name.Pos(), check.pkg, nstr, nil)
- check.NewTypeParam(ptr, tpar, len(tparams), &emptyInterface) // assigns type to tpar as a side-effect
- check.declare(check.scope, name, tpar, check.scope.pos) // TODO(gri) check scope position
+ tpar := NewTypeName(name.Pos(), check.pkg, name.Name, nil)
+ check.NewTypeParam(tpar, len(tparams), &emptyInterface) // assigns type to tpar as a side-effect
+ check.declare(check.scope, name, tpar, check.scope.pos) // TODO(gri) check scope position
tparams = append(tparams, tpar)
}
- if check.conf.Trace && len(names) > 0 {
+ if trace && len(names) > 0 {
check.trace(names[0].Pos(), "type params = %v", tparams[len(tparams)-len(names):])
}
@@ -769,9 +809,9 @@
if alt := mset.insert(m); alt != nil {
switch alt.(type) {
case *Var:
- check.errorf(m.pos, "field and method with the same name %s", m.name)
+ check.errorf(m, _DuplicateFieldAndMethod, "field and method with the same name %s", m.name)
case *Func:
- check.errorf(m.pos, "method %s already declared for %s", m.name, obj)
+ check.errorf(m, _DuplicateMethod, "method %s already declared for %s", m.name, obj)
default:
unreachable()
}
@@ -815,133 +855,105 @@
}
}
-func (check *Checker) declStmt(decl ast.Decl) {
+func (check *Checker) declStmt(d ast.Decl) {
pkg := check.pkg
- switch d := decl.(type) {
- case *ast.BadDecl:
- // ignore
+ check.walkDecl(d, func(d decl) {
+ switch d := d.(type) {
+ case constDecl:
+ top := len(check.delayed)
- case *ast.GenDecl:
- var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
- for iota, spec := range d.Specs {
- switch s := spec.(type) {
- case *ast.ValueSpec:
- switch d.Tok {
- case token.CONST:
- top := len(check.delayed)
+ // declare all constants
+ lhs := make([]*Const, len(d.spec.Names))
+ for i, name := range d.spec.Names {
+ obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(d.iota)))
+ lhs[i] = obj
- // determine which init exprs to use
- switch {
- case s.Type != nil || len(s.Values) > 0:
- last = s
- case last == nil:
- last = new(ast.ValueSpec) // make sure last exists
- }
-
- // declare all constants
- lhs := make([]*Const, len(s.Names))
- for i, name := range s.Names {
- obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(iota)))
- lhs[i] = obj
-
- var init ast.Expr
- if i < len(last.Values) {
- init = last.Values[i]
- }
-
- check.constDecl(obj, last.Type, init)
- }
-
- check.arityMatch(s, last)
-
- // process function literals in init expressions before scope changes
- check.processDelayed(top)
-
- // spec: "The scope of a constant or variable identifier declared
- // inside a function begins at the end of the ConstSpec or VarSpec
- // (ShortVarDecl for short variable declarations) and ends at the
- // end of the innermost containing block."
- scopePos := s.End()
- for i, name := range s.Names {
- check.declare(check.scope, name, lhs[i], scopePos)
- }
-
- case token.VAR:
- top := len(check.delayed)
-
- lhs0 := make([]*Var, len(s.Names))
- for i, name := range s.Names {
- lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil)
- }
-
- // initialize all variables
- for i, obj := range lhs0 {
- var lhs []*Var
- var init ast.Expr
- switch len(s.Values) {
- case len(s.Names):
- // lhs and rhs match
- init = s.Values[i]
- case 1:
- // rhs is expected to be a multi-valued expression
- lhs = lhs0
- init = s.Values[0]
- default:
- if i < len(s.Values) {
- init = s.Values[i]
- }
- }
- check.varDecl(obj, lhs, s.Type, init)
- if len(s.Values) == 1 {
- // If we have a single lhs variable we are done either way.
- // If we have a single rhs expression, it must be a multi-
- // valued expression, in which case handling the first lhs
- // variable will cause all lhs variables to have a type
- // assigned, and we are done as well.
- if debug {
- for _, obj := range lhs0 {
- assert(obj.typ != nil)
- }
- }
- break
- }
- }
-
- check.arityMatch(s, nil)
-
- // process function literals in init expressions before scope changes
- check.processDelayed(top)
-
- // declare all variables
- // (only at this point are the variable scopes (parents) set)
- scopePos := s.End() // see constant declarations
- for i, name := range s.Names {
- // see constant declarations
- check.declare(check.scope, name, lhs0[i], scopePos)
- }
-
- default:
- check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
+ var init ast.Expr
+ if i < len(d.init) {
+ init = d.init[i]
}
- case *ast.TypeSpec:
- obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
- // spec: "The scope of a type identifier declared inside a function
- // begins at the identifier in the TypeSpec and ends at the end of
- // the innermost containing block."
- scopePos := s.Name.Pos()
- check.declare(check.scope, s.Name, obj, scopePos)
- // mark and unmark type before calling typeDecl; its type is still nil (see Checker.objDecl)
- obj.setColor(grey + color(check.push(obj)))
- check.typeDecl(obj, s, nil)
- check.pop().setColor(black)
- default:
- check.invalidAST(s.Pos(), "const, type, or var declaration expected")
+ check.constDecl(obj, d.typ, init, d.inherited)
}
- }
- default:
- check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
- }
+ // process function literals in init expressions before scope changes
+ check.processDelayed(top)
+
+ // spec: "The scope of a constant or variable identifier declared
+ // inside a function begins at the end of the ConstSpec or VarSpec
+ // (ShortVarDecl for short variable declarations) and ends at the
+ // end of the innermost containing block."
+ scopePos := d.spec.End()
+ for i, name := range d.spec.Names {
+ check.declare(check.scope, name, lhs[i], scopePos)
+ }
+
+ case varDecl:
+ top := len(check.delayed)
+
+ lhs0 := make([]*Var, len(d.spec.Names))
+ for i, name := range d.spec.Names {
+ lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil)
+ }
+
+ // initialize all variables
+ for i, obj := range lhs0 {
+ var lhs []*Var
+ var init ast.Expr
+ switch len(d.spec.Values) {
+ case len(d.spec.Names):
+ // lhs and rhs match
+ init = d.spec.Values[i]
+ case 1:
+ // rhs is expected to be a multi-valued expression
+ lhs = lhs0
+ init = d.spec.Values[0]
+ default:
+ if i < len(d.spec.Values) {
+ init = d.spec.Values[i]
+ }
+ }
+ check.varDecl(obj, lhs, d.spec.Type, init)
+ if len(d.spec.Values) == 1 {
+ // If we have a single lhs variable we are done either way.
+ // If we have a single rhs expression, it must be a multi-
+ // valued expression, in which case handling the first lhs
+ // variable will cause all lhs variables to have a type
+ // assigned, and we are done as well.
+ if debug {
+ for _, obj := range lhs0 {
+ assert(obj.typ != nil)
+ }
+ }
+ break
+ }
+ }
+
+ // process function literals in init expressions before scope changes
+ check.processDelayed(top)
+
+ // declare all variables
+ // (only at this point are the variable scopes (parents) set)
+ scopePos := d.spec.End() // see constant declarations
+ for i, name := range d.spec.Names {
+ // see constant declarations
+ check.declare(check.scope, name, lhs0[i], scopePos)
+ }
+
+ case typeDecl:
+ obj := NewTypeName(d.spec.Name.Pos(), pkg, d.spec.Name.Name, nil)
+ // spec: "The scope of a type identifier declared inside a function
+ // begins at the identifier in the TypeSpec and ends at the end of
+ // the innermost containing block."
+ scopePos := d.spec.Name.Pos()
+ check.declare(check.scope, d.spec.Name, obj, scopePos)
+ // mark and unmark type before calling typeDecl; its type is still nil (see Checker.objDecl)
+ obj.setColor(grey + color(check.push(obj)))
+ check.typeDecl(obj, d.spec, nil)
+ check.pop().setColor(black)
+ default:
+ check.invalidAST(d.node(), "unknown ast.Decl node %T", d.node())
+ }
+ })
}
diff --git a/src/go/types/errorcodes.go b/src/go/types/errorcodes.go
index e4c8311..7e62091 100644
--- a/src/go/types/errorcodes.go
+++ b/src/go/types/errorcodes.go
@@ -753,52 +753,12 @@
_NonVariadicDotDotDot
// _MisplacedDotDotDot occurs when a "..." is used somewhere other than the
- // final argument to a function call.
+ // final argument in a function declaration.
//
// Example:
- // func printArgs(args ...int) {
- // for _, a := range args {
- // println(a)
- // }
- // }
- //
- // func f() {
- // a := []int{1,2,3}
- // printArgs(0, a...)
- // }
+ // func f(...int, int)
_MisplacedDotDotDot
- // _InvalidDotDotDotOperand occurs when a "..." operator is applied to a
- // single-valued operand.
- //
- // Example:
- // func printArgs(args ...int) {
- // for _, a := range args {
- // println(a)
- // }
- // }
- //
- // func f() {
- // a := 1
- // printArgs(a...)
- // }
- //
- // Example:
- // func args() (int, int) {
- // return 1, 2
- // }
- //
- // func printArgs(args ...int) {
- // for _, a := range args {
- // println(a)
- // }
- // }
- //
- // func g() {
- // printArgs(args()...)
- // }
- _InvalidDotDotDotOperand
-
// _InvalidDotDotDot occurs when a "..." is used in a non-variadic built-in
// function.
//
@@ -1078,18 +1038,6 @@
// }
_InvalidPostDecl
- // _InvalidChanRange occurs when a send-only channel used in a range
- // expression.
- //
- // Example:
- // func sum(c chan<- int) {
- // s := 0
- // for i := range c {
- // s += i
- // }
- // }
- _InvalidChanRange
-
// _InvalidIterVar occurs when two iteration variables are used while ranging
// over a channel.
//
@@ -1207,6 +1155,16 @@
// }
_InvalidTypeSwitch
+ // _InvalidExprSwitch occurs when a switch expression is not comparable.
+ //
+ // Example:
+ // func _() {
+ // var a struct{ _ func() }
+ // switch a /* ERROR cannot switch on a */ {
+ // }
+ // }
+ _InvalidExprSwitch
+
/* control flow > select */
// _InvalidSelectCase occurs when a select case is not a channel send or
@@ -1353,4 +1311,8 @@
// return i
// }
_InvalidGo
+
+ // _Todo is a placeholder for error codes that have not been decided.
+ // TODO(rFindley) remove this error code after deciding on errors for generics code.
+ _Todo
)
diff --git a/src/go/types/errorcodes_test.go b/src/go/types/errorcodes_test.go
new file mode 100644
index 0000000..5da1cda
--- /dev/null
+++ b/src/go/types/errorcodes_test.go
@@ -0,0 +1,197 @@
+// Copyright 2020 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 types_test
+
+import (
+ "fmt"
+ "go/ast"
+ "go/constant"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "reflect"
+ "strings"
+ "testing"
+
+ . "go/types"
+)
+
+func TestErrorCodeExamples(t *testing.T) {
+ walkCodes(t, func(name string, value int, spec *ast.ValueSpec) {
+ t.Run(name, func(t *testing.T) {
+ doc := spec.Doc.Text()
+ examples := strings.Split(doc, "Example:")
+ for i := 1; i < len(examples); i++ {
+ example := examples[i]
+ err := checkExample(t, example)
+ if err == nil {
+ t.Fatalf("no error in example #%d", i)
+ }
+ typerr, ok := err.(Error)
+ if !ok {
+ t.Fatalf("not a types.Error: %v", err)
+ }
+ if got := readCode(typerr); got != value {
+ t.Errorf("%s: example #%d returned code %d (%s), want %d", name, i, got, err, value)
+ }
+ }
+ })
+ })
+}
+
+func walkCodes(t *testing.T, f func(string, int, *ast.ValueSpec)) {
+ t.Helper()
+ fset := token.NewFileSet()
+ files, err := pkgFiles(fset, ".", parser.ParseComments) // from self_test.go
+ if err != nil {
+ t.Fatal(err)
+ }
+ conf := Config{Importer: importer.Default()}
+ info := &Info{
+ Types: make(map[ast.Expr]TypeAndValue),
+ Defs: make(map[*ast.Ident]Object),
+ Uses: make(map[*ast.Ident]Object),
+ }
+ _, err = conf.Check("types", fset, files, info)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, file := range files {
+ for _, decl := range file.Decls {
+ decl, ok := decl.(*ast.GenDecl)
+ if !ok || decl.Tok != token.CONST {
+ continue
+ }
+ for _, spec := range decl.Specs {
+ spec, ok := spec.(*ast.ValueSpec)
+ if !ok || len(spec.Names) == 0 {
+ continue
+ }
+ obj := info.ObjectOf(spec.Names[0])
+ if named, ok := obj.Type().(*Named); ok && named.Obj().Name() == "errorCode" {
+ if len(spec.Names) != 1 {
+ t.Fatalf("bad Code declaration for %q: got %d names, want exactly 1", spec.Names[0].Name, len(spec.Names))
+ }
+ codename := spec.Names[0].Name
+ value := int(constant.Val(obj.(*Const).Val()).(int64))
+ f(codename, value, spec)
+ }
+ }
+ }
+ }
+}
+
+func readCode(err Error) int {
+ v := reflect.ValueOf(err)
+ return int(v.FieldByName("go116code").Int())
+}
+
+func checkExample(t *testing.T, example string) error {
+ t.Helper()
+ fset := token.NewFileSet()
+ src := fmt.Sprintf("package p\n\n%s", example)
+ file, err := parser.ParseFile(fset, "example.go", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ conf := Config{
+ FakeImportC: true,
+ Importer: importer.Default(),
+ }
+ _, err = conf.Check("example", fset, []*ast.File{file}, nil)
+ return err
+}
+
+func TestErrorCodeStyle(t *testing.T) {
+ // The set of error codes is large and intended to be self-documenting, so
+ // this test enforces some style conventions.
+ forbiddenInIdent := []string{
+ // use invalid instead
+ "illegal",
+ // words with a common short-form
+ "argument",
+ "assertion",
+ "assignment",
+ "boolean",
+ "channel",
+ "condition",
+ "declaration",
+ "expression",
+ "function",
+ "initial", // use init for initializer, initialization, etc.
+ "integer",
+ "interface",
+ "iterat", // use iter for iterator, iteration, etc.
+ "literal",
+ "operation",
+ "package",
+ "pointer",
+ "receiver",
+ "signature",
+ "statement",
+ "variable",
+ }
+ forbiddenInComment := []string{
+ // lhs and rhs should be spelled-out.
+ "lhs", "rhs",
+ // builtin should be hyphenated.
+ "builtin",
+ // Use dot-dot-dot.
+ "ellipsis",
+ }
+ nameHist := make(map[int]int)
+ longestName := ""
+ maxValue := 0
+
+ walkCodes(t, func(name string, value int, spec *ast.ValueSpec) {
+ if name == "_" {
+ return
+ }
+ nameHist[len(name)]++
+ if value > maxValue {
+ maxValue = value
+ }
+ if len(name) > len(longestName) {
+ longestName = name
+ }
+ if token.IsExported(name) {
+ // This is an experimental API, and errorCode values should not be
+ // exported.
+ t.Errorf("%q is exported", name)
+ }
+ if name[0] != '_' || !token.IsExported(name[1:]) {
+ t.Errorf("%q should start with _, followed by an exported identifier", name)
+ }
+ lower := strings.ToLower(name)
+ for _, bad := range forbiddenInIdent {
+ if strings.Contains(lower, bad) {
+ t.Errorf("%q contains forbidden word %q", name, bad)
+ }
+ }
+ doc := spec.Doc.Text()
+ if !strings.HasPrefix(doc, name) {
+ t.Errorf("doc for %q does not start with identifier", name)
+ }
+ lowerComment := strings.ToLower(strings.TrimPrefix(doc, name))
+ for _, bad := range forbiddenInComment {
+ if strings.Contains(lowerComment, bad) {
+ t.Errorf("doc for %q contains forbidden word %q", name, bad)
+ }
+ }
+ })
+
+ if testing.Verbose() {
+ var totChars, totCount int
+ for chars, count := range nameHist {
+ totChars += chars * count
+ totCount += count
+ }
+ avg := float64(totChars) / float64(totCount)
+ fmt.Println()
+ fmt.Printf("%d error codes\n", totCount)
+ fmt.Printf("average length: %.2f chars\n", avg)
+ fmt.Printf("max length: %d (%s)\n", len(longestName), longestName)
+ }
+}
diff --git a/src/go/types/errors.go b/src/go/types/errors.go
index 92ba8de..a956256 100644
--- a/src/go/types/errors.go
+++ b/src/go/types/errors.go
@@ -7,6 +7,7 @@
package types
import (
+ "errors"
"fmt"
"go/ast"
"go/token"
@@ -72,22 +73,48 @@
fmt.Println(check.sprintf(format, args...))
}
-func (check *Checker) err(pos token.Pos, msg string, soft bool) {
+func (check *Checker) err(err error) {
+ if err == nil {
+ return
+ }
+ var e Error
+ isInternal := errors.As(err, &e)
// Cheap trick: Don't report errors with messages containing
// "invalid operand" or "invalid type" as those tend to be
// follow-on errors which don't add useful information. Only
// exclude them if these strings are not at the beginning,
// and only if we have at least one error already reported.
- if check.firstErr != nil && (strings.Index(msg, "invalid operand") > 0 || strings.Index(msg, "invalid type") > 0) {
+ isInvalidErr := isInternal && (strings.Index(e.Msg, "invalid operand") > 0 || strings.Index(e.Msg, "invalid type") > 0)
+ if check.firstErr != nil && isInvalidErr {
return
}
- err := Error{check.fset, pos, stripAnnotations(msg), msg, soft}
+ if isInternal {
+ e.Msg = stripAnnotations(e.Msg)
+ if check.errpos != nil {
+ // If we have an internal error and the errpos override is set, use it to
+ // augment our error positioning.
+ // TODO(rFindley) we may also want to augment the error message and refer
+ // to the position (pos) in the original expression.
+ span := spanOf(check.errpos)
+ e.Pos = span.pos
+ e.go116start = span.start
+ e.go116end = span.end
+ }
+ err = e
+ }
+
if check.firstErr == nil {
check.firstErr = err
}
- if check.conf.Trace {
+ if trace {
+ pos := e.Pos
+ msg := e.Msg
+ if !isInternal {
+ msg = err.Error()
+ pos = token.NoPos
+ }
check.trace(pos, "ERROR: %s", msg)
}
@@ -98,28 +125,108 @@
f(err)
}
-func (check *Checker) error(pos token.Pos, msg string) {
- check.err(pos, msg, false)
+func (check *Checker) newError(at positioner, code errorCode, soft bool, msg string) error {
+ span := spanOf(at)
+ return Error{
+ Fset: check.fset,
+ Pos: span.pos,
+ Msg: msg,
+ Soft: soft,
+ go116code: code,
+ go116start: span.start,
+ go116end: span.end,
+ }
}
-func (check *Checker) errorf(pos token.Pos, format string, args ...interface{}) {
- check.err(pos, check.sprintf(format, args...), false)
+// newErrorf creates a new Error, but does not handle it.
+func (check *Checker) newErrorf(at positioner, code errorCode, soft bool, format string, args ...interface{}) error {
+ msg := check.sprintf(format, args...)
+ return check.newError(at, code, soft, msg)
}
-func (check *Checker) softErrorf(pos token.Pos, format string, args ...interface{}) {
- check.err(pos, check.sprintf(format, args...), true)
+func (check *Checker) error(at positioner, code errorCode, msg string) {
+ check.err(check.newError(at, code, false, msg))
}
-func (check *Checker) invalidAST(pos token.Pos, format string, args ...interface{}) {
- check.errorf(pos, "invalid AST: "+format, args...)
+func (check *Checker) errorf(at positioner, code errorCode, format string, args ...interface{}) {
+ check.error(at, code, check.sprintf(format, args...))
}
-func (check *Checker) invalidArg(pos token.Pos, format string, args ...interface{}) {
- check.errorf(pos, "invalid argument: "+format, args...)
+func (check *Checker) softErrorf(at positioner, code errorCode, format string, args ...interface{}) {
+ check.err(check.newErrorf(at, code, true, format, args...))
}
-func (check *Checker) invalidOp(pos token.Pos, format string, args ...interface{}) {
- check.errorf(pos, "invalid operation: "+format, args...)
+func (check *Checker) invalidAST(at positioner, format string, args ...interface{}) {
+ check.errorf(at, 0, "invalid AST: "+format, args...)
+}
+
+func (check *Checker) invalidArg(at positioner, code errorCode, format string, args ...interface{}) {
+ check.errorf(at, code, "invalid argument: "+format, args...)
+}
+
+func (check *Checker) invalidOp(at positioner, code errorCode, format string, args ...interface{}) {
+ check.errorf(at, code, "invalid operation: "+format, args...)
+}
+
+// The positioner interface is used to extract the position of type-checker
+// errors.
+type positioner interface {
+ Pos() token.Pos
+}
+
+// posSpan holds a position range along with a highlighted position within that
+// range. This is used for positioning errors, with pos by convention being the
+// first position in the source where the error is known to exist, and start
+// and end defining the full span of syntax being considered when the error was
+// detected. Invariant: start <= pos < end || start == pos == end.
+type posSpan struct {
+ start, pos, end token.Pos
+}
+
+func (e posSpan) Pos() token.Pos {
+ return e.pos
+}
+
+// inNode creates a posSpan for the given node.
+// Invariant: node.Pos() <= pos < node.End() (node.End() is the position of the
+// first byte after node within the source).
+func inNode(node ast.Node, pos token.Pos) posSpan {
+ start, end := node.Pos(), node.End()
+ if debug {
+ assert(start <= pos && pos < end)
+ }
+ return posSpan{start, pos, end}
+}
+
+// atPos wraps a token.Pos to implement the positioner interface.
+type atPos token.Pos
+
+func (s atPos) Pos() token.Pos {
+ return token.Pos(s)
+}
+
+// spanOf extracts an error span from the given positioner. By default this is
+// the trivial span starting and ending at pos, but this span is expanded when
+// the argument naturally corresponds to a span of source code.
+func spanOf(at positioner) posSpan {
+ switch x := at.(type) {
+ case nil:
+ panic("internal error: nil")
+ case posSpan:
+ return x
+ case ast.Node:
+ pos := x.Pos()
+ return posSpan{pos, pos, x.End()}
+ case *operand:
+ if x.expr != nil {
+ pos := x.Pos()
+ return posSpan{pos, pos, x.expr.End()}
+ }
+ return posSpan{token.NoPos, token.NoPos, token.NoPos}
+ default:
+ pos := at.Pos()
+ return posSpan{pos, pos, pos}
+ }
}
// stripAnnotations removes internal (type) annotations from s.
diff --git a/src/go/types/examples/methods.go2 b/src/go/types/examples/methods.go2
index 95ff872..c294627 100644
--- a/src/go/types/examples/methods.go2
+++ b/src/go/types/examples/methods.go2
@@ -39,7 +39,7 @@
// and usually should be avoided. There are some notable exceptions; e.g.,
// sometimes it makes sense to use the identifier "copy" which happens to
// also be the name of a predeclared built-in function.
-func (t T1[int]) m3() { var _ int = 42 /* ERROR cannot convert 42 .* to int */ }
+func (t T1[int]) m3() { var _ int = 42 /* ERROR cannot use 42 .* as int */ }
// The names of the type parameters used in a parameterized receiver
// type don't have to match the type parameter names in the the declaration
diff --git a/src/go/types/examples/types.go2 b/src/go/types/examples/types.go2
index 9808f9c..5aa624c 100644
--- a/src/go/types/examples/types.go2
+++ b/src/go/types/examples/types.go2
@@ -150,24 +150,12 @@
// (7/14/2020: See comment above. We probably will revert this generalized ability
// if we go with [] for type parameters.)
type _ struct {
- (int8)
- (*int16)
- *(int32)
- (*(int64))
- (((*(int))))
- (*(List[int]))
+ int8
+ *int16
+ *List[int]
int8 /* ERROR int8 redeclared */
- int /* ERROR int redeclared */
* /* ERROR List redeclared */ List[int]
-
- ( /* ERROR invalid embedded field type */ [10]int)
- ( /* ERROR invalid embedded field type */ []int)
- ( /* ERROR invalid embedded field type */ struct{})
- ( /* ERROR invalid embedded field type */ map[int]string)
- ( /* ERROR invalid embedded field type */ chan<- int)
- ( /* ERROR invalid embedded field type */ interface{})
- ( /* ERROR invalid embedded field type */ func())
}
// It's possible to declare local types whose underlying types
@@ -275,4 +263,4 @@
func _() {
var _ comparable /* ERROR comparable */
var _ C /* ERROR comparable */
-}
\ No newline at end of file
+}
diff --git a/src/go/types/expr.go b/src/go/types/expr.go
index 62e24da..1deda99 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -1,3 +1,4 @@
+// REVIEW INCOMPLETE
// Copyright 2012 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.
@@ -73,11 +74,11 @@
func (check *Checker) op(m opPredicates, x *operand, op token.Token) bool {
if pred := m[op]; pred != nil {
if !pred(x.typ) {
- check.invalidOp(x.pos(), "operator %s not defined for %s", op, x)
+ check.invalidOp(x, _UndefinedOp, "operator %s not defined for %s", op, x)
return false
}
} else {
- check.invalidAST(x.pos(), "unknown operator %s", op)
+ check.invalidAST(x, "unknown operator %s", op)
return false
}
return true
@@ -90,7 +91,7 @@
// spec: "As an exception to the addressability
// requirement x may also be a composite literal."
if _, ok := unparen(x.expr).(*ast.CompositeLit); !ok && x.mode != variable {
- check.invalidOp(x.pos(), "cannot take address of %s", x)
+ check.invalidOp(x, _UnaddressableOperand, "cannot take address of %s", x)
x.mode = invalid
return
}
@@ -101,12 +102,12 @@
case token.ARROW:
typ := asChan(x.typ)
if typ == nil {
- check.invalidOp(x.pos(), "cannot receive from non-channel %s", x)
+ check.invalidOp(x, _InvalidReceive, "cannot receive from non-channel %s", x)
x.mode = invalid
return
}
if typ.dir == SendOnly {
- check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x)
+ check.invalidOp(x, _InvalidReceive, "cannot receive from send-only channel %s", x)
x.mode = invalid
return
}
@@ -334,11 +335,21 @@
return false
}
-// representable checks that a constant operand is representable in the given basic type.
+// representable checks that a constant operand is representable in the given
+// basic type.
func (check *Checker) representable(x *operand, typ *Basic) {
+ if v, code := check.representation(x, typ); code != 0 {
+ check.invalidConversion(code, x, typ)
+ x.mode = invalid
+ } else if v != nil {
+ x.val = v
+ }
+}
+
+func (check *Checker) representation(x *operand, typ *Basic) (constant.Value, errorCode) {
assert(x.mode == constant_)
- if !representableConst(x.val, check, typ, &x.val) {
- var msg string
+ v := x.val
+ if !representableConst(x.val, check, typ, &v) {
if isNumeric(x.typ) && isNumeric(typ) {
// numeric conversion : error msg
//
@@ -348,16 +359,25 @@
// float -> float : overflows
//
if !isInteger(x.typ) && isInteger(typ) {
- msg = "%s truncated to %s"
+ return nil, _TruncatedFloat
} else {
- msg = "%s overflows %s"
+ return nil, _NumericOverflow
}
- } else {
- msg = "cannot convert %s to %s"
}
- check.errorf(x.pos(), msg, x, typ)
- x.mode = invalid
+ return nil, _InvalidConstVal
}
+ return v, 0
+}
+
+func (check *Checker) invalidConversion(code errorCode, x *operand, target Type) {
+ msg := "cannot convert %s to %s"
+ switch code {
+ case _TruncatedFloat:
+ msg = "%s truncated to %s"
+ case _NumericOverflow:
+ msg = "%s overflows %s"
+ }
+ check.errorf(x, code, msg, x, target)
}
// updateExprType updates the type of x to typ and invokes itself
@@ -463,7 +483,7 @@
// We already know from the shift check that it is representable
// as an integer if it is a constant.
if !isInteger(typ) {
- check.invalidOp(x.Pos(), "shifted operand %s (type %s) must be integer", x, typ)
+ check.invalidOp(x, _InvalidShiftOperand, "shifted operand %s (type %s) must be integer", x, typ)
return
}
// Even if we have an integer, if the value is a constant we
@@ -493,13 +513,30 @@
// convertUntyped attempts to set the type of an untyped value to the target type.
func (check *Checker) convertUntyped(x *operand, target Type) {
- target = expand(target)
- if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] {
+ newType, val, code := check.implicitTypeAndValue(x, target)
+ if code != 0 {
+ check.invalidConversion(code, x, target.Underlying())
+ x.mode = invalid
return
}
+ if val != nil {
+ x.val = val
+ check.updateExprVal(x.expr, val)
+ }
+ if newType != x.typ {
+ x.typ = newType
+ check.updateExprType(x.expr, newType, false)
+ }
+}
- // TODO(gri) Sloppy code - clean up. This function is central
- // to assignment and expression checking.
+// implicitTypeAndValue returns the implicit type of x when used in a context
+// where the target type is expected. If no such implicit conversion is
+// possible, it returns a nil Type.
+func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, constant.Value, errorCode) {
+ target = expand(target)
+ if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] {
+ return x.typ, nil, 0
+ }
if isUntyped(target) {
// both x and target are untyped
@@ -507,138 +544,98 @@
tkind := target.(*Basic).kind
if isNumeric(x.typ) && isNumeric(target) {
if xkind < tkind {
- x.typ = target
- check.updateExprType(x.expr, target, false)
+ return target, nil, 0
}
} else if xkind != tkind {
- goto Error
+ return nil, nil, _InvalidUntypedConversion
}
- return
+ return x.typ, nil, 0
}
- // In case of a type parameter, conversion must succeed against
- // all types enumerated by the type parameter bound.
- // TODO(gri) We should not need this because we have the code
- // for Sum types in convertUntypedInternal. But at least one
- // test fails. Investigate.
- if t := asTypeParam(target); t != nil {
- types := t.Bound().allTypes
- if types == nil {
- goto Error
- }
-
- for _, t := range unpack(types) {
- check.convertUntypedInternal(x, t)
- if x.mode == invalid {
- goto Error
- }
- }
-
- // keep nil untyped (was bug #39755)
- if x.isNil() {
- target = Typ[UntypedNil]
- }
- x.typ = target
- check.updateExprType(x.expr, target, true) // UntypedNils are final
- return
- }
-
- check.convertUntypedInternal(x, target)
- return
-
-Error:
- // TODO(gri) better error message (explain cause)
- check.errorf(x.pos(), "cannot convert %s to %s", x, target)
- x.mode = invalid
-}
-
-// convertUntypedInternal should only be called by convertUntyped.
-func (check *Checker) convertUntypedInternal(x *operand, target Type) {
- assert(isTyped(target))
-
- // typed target
switch t := optype(target).(type) {
case *Basic:
if x.mode == constant_ {
- check.representable(x, t)
- if x.mode == invalid {
- return
+ v, code := check.representation(x, t)
+ if code != 0 {
+ return nil, nil, code
}
- // expression value may have been rounded - update if needed
- check.updateExprVal(x.expr, x.val)
- } else {
- // Non-constant untyped values may appear as the
- // result of comparisons (untyped bool), intermediate
- // (delayed-checked) rhs operands of shifts, and as
- // the value nil.
- switch x.typ.(*Basic).kind {
- case UntypedBool:
- if !isBoolean(target) {
- goto Error
- }
- case UntypedInt, UntypedRune, UntypedFloat, UntypedComplex:
- if !isNumeric(target) {
- goto Error
- }
- case UntypedString:
- // Non-constant untyped string values are not
- // permitted by the spec and should not occur.
- unreachable()
- case UntypedNil:
- // Unsafe.Pointer is a basic type that includes nil.
- if !hasNil(target) {
- goto Error
- }
- default:
- goto Error
+ return target, v, code
+ }
+ // Non-constant untyped values may appear as the
+ // result of comparisons (untyped bool), intermediate
+ // (delayed-checked) rhs operands of shifts, and as
+ // the value nil.
+ switch x.typ.(*Basic).kind {
+ case UntypedBool:
+ if !isBoolean(target) {
+ return nil, nil, _InvalidUntypedConversion
}
+ case UntypedInt, UntypedRune, UntypedFloat, UntypedComplex:
+ if !isNumeric(target) {
+ return nil, nil, _InvalidUntypedConversion
+ }
+ case UntypedString:
+ // Non-constant untyped string values are not permitted by the spec and
+ // should not occur during normal typechecking passes, but this path is
+ // reachable via the AssignableTo API.
+ if !isString(target) {
+ return nil, nil, _InvalidUntypedConversion
+ }
+ case UntypedNil:
+ // Unsafe.Pointer is a basic type that includes nil.
+ if !hasNil(target) {
+ return nil, nil, _InvalidUntypedConversion
+ }
+ // TODO(rFindley) return UntypedNil here (golang.org/issues/13061).
+ default:
+ return nil, nil, _InvalidUntypedConversion
}
case *Sum:
- t.is(func(t Type) bool {
- check.convertUntypedInternal(x, t)
- return x.mode != invalid
+ ok := t.is(func(t Type) bool {
+ target, _, _ := check.implicitTypeAndValue(x, t)
+ return target != nil
})
+ if !ok {
+ return nil, nil, _InvalidUntypedConversion
+ }
+ // keep nil untyped (was bug #39755)
+ if x.isNil() {
+ return Typ[UntypedNil], nil, 0
+ }
case *Interface:
- // Update operand types to the default type rather then
- // the target (interface) type: values must have concrete
- // dynamic types. If the value is nil, keep it untyped
- // (this is important for tools such as go vet which need
- // the dynamic type for argument checking of say, print
+ // Values must have concrete dynamic types. If the value is nil,
+ // keep it untyped (this is important for tools such as go vet which
+ // need the dynamic type for argument checking of say, print
// functions)
if x.isNil() {
- target = Typ[UntypedNil]
- } else {
- // cannot assign untyped values to non-empty interfaces
- check.completeInterface(token.NoPos, t)
- if !t.Empty() {
- goto Error
- }
- target = Default(x.typ)
+ return Typ[UntypedNil], nil, 0
}
+ // cannot assign untyped values to non-empty interfaces
+ check.completeInterface(token.NoPos, t)
+ if !t.Empty() {
+ return nil, nil, _InvalidUntypedConversion
+ }
+ return Default(x.typ), nil, 0
case *Pointer, *Signature, *Slice, *Map, *Chan:
if !x.isNil() {
- goto Error
+ return nil, nil, _InvalidUntypedConversion
}
- // keep nil untyped - see comment for interfaces, above
- target = Typ[UntypedNil]
+ // Keep nil untyped - see comment for interfaces, above.
+ return Typ[UntypedNil], nil, 0
default:
- goto Error
+ return nil, nil, _InvalidUntypedConversion
}
-
- x.typ = target
- check.updateExprType(x.expr, target, true) // UntypedNils are final
- return
-
-Error:
- check.errorf(x.pos(), "cannot convert %s to %s", x, target)
- x.mode = invalid
+ return target, nil, 0
}
func (check *Checker) comparison(x, y *operand, op token.Token) {
// spec: "In any comparison, the first operand must be assignable
// to the type of the second operand, or vice versa."
err := ""
- if x.assignableTo(check, y.typ, nil) || y.assignableTo(check, x.typ, nil) {
+ var code errorCode
+ xok, _ := x.assignableTo(check, y.typ, nil)
+ yok, _ := y.assignableTo(check, x.typ, nil)
+ if xok || yok {
defined := false
switch op {
case token.EQL, token.NEQ:
@@ -656,13 +653,15 @@
typ = y.typ
}
err = check.sprintf("operator %s not defined for %s", op, typ)
+ code = _UndefinedOp
}
} else {
err = check.sprintf("mismatched types %s and %s", x.typ, y.typ)
+ code = _MismatchedTypes
}
if err != "" {
- check.errorf(x.pos(), "cannot compare %s %s %s (%s)", x.expr, op, y.expr, err)
+ check.errorf(x, code, "cannot compare %s %s %s (%s)", x.expr, op, y.expr, err)
x.mode = invalid
return
}
@@ -699,7 +698,7 @@
// as an integer. Nothing to do.
} else {
// shift has no chance
- check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
+ check.invalidOp(x, _InvalidShiftOperand, "shifted operand %s must be integer", x)
x.mode = invalid
return
}
@@ -716,7 +715,7 @@
return
}
default:
- check.invalidOp(y.pos(), "shift count %s must be integer", y)
+ check.invalidOp(y, _InvalidShiftCount, "shift count %s must be integer", y)
x.mode = invalid
return
}
@@ -729,7 +728,7 @@
yval = constant.ToInt(y.val)
assert(yval.Kind() == constant.Int)
if constant.Sign(yval) < 0 {
- check.invalidOp(y.pos(), "negative shift count %s", y)
+ check.invalidOp(y, _InvalidShiftCount, "negative shift count %s", y)
x.mode = invalid
return
}
@@ -741,7 +740,7 @@
const shiftBound = 1023 - 1 + 52 // so we can express smallestFloat64
s, ok := constant.Uint64Val(yval)
if !ok || s > shiftBound {
- check.invalidOp(y.pos(), "invalid shift count %s", y)
+ check.invalidOp(y, _InvalidShiftCount, "invalid shift count %s", y)
x.mode = invalid
return
}
@@ -798,7 +797,7 @@
// non-constant shift - lhs must be an integer
if !isInteger(x.typ) {
- check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
+ check.invalidOp(x, _InvalidShiftOperand, "shifted operand %s must be integer", x)
x.mode = invalid
return
}
@@ -828,7 +827,7 @@
}
// The binary expression e may be nil. It's passed in for better error messages only.
-func (check *Checker) binary(x *operand, e *ast.BinaryExpr, lhs, rhs ast.Expr, op token.Token) {
+func (check *Checker) binary(x *operand, e *ast.BinaryExpr, lhs, rhs ast.Expr, op token.Token, opPos token.Pos) {
var y operand
check.expr(x, lhs)
@@ -867,7 +866,11 @@
// only report an error if we have valid types
// (otherwise we had an error reported elsewhere already)
if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] {
- check.invalidOp(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
+ var posn positioner = x
+ if e != nil {
+ posn = e
+ }
+ check.invalidOp(posn, _MismatchedTypes, "mismatched types %s and %s", x.typ, y.typ)
}
x.mode = invalid
return
@@ -881,7 +884,7 @@
if op == token.QUO || op == token.REM {
// check for zero divisor
if (x.mode == constant_ || isInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 {
- check.invalidOp(y.pos(), "division by zero")
+ check.invalidOp(&y, _DivByZero, "division by zero")
x.mode = invalid
return
}
@@ -891,7 +894,7 @@
re, im := constant.Real(y.val), constant.Imag(y.val)
re2, im2 := constant.BinaryOp(re, token.MUL, re), constant.BinaryOp(im, token.MUL, im)
if constant.Sign(re2) == 0 && constant.Sign(im2) == 0 {
- check.invalidOp(y.pos(), "division by zero")
+ check.invalidOp(&y, _DivByZero, "division by zero")
x.mode = invalid
return
}
@@ -907,6 +910,14 @@
op = token.QUO_ASSIGN
}
x.val = constant.BinaryOp(xval, op, yval)
+ // report error if valid operands lead to an invalid result
+ if xval.Kind() != constant.Unknown && yval.Kind() != constant.Unknown && x.val.Kind() == constant.Unknown {
+ // TODO(gri) We should report exactly what went wrong. At the
+ // moment we don't have the (go/constant) API for that.
+ // See also TODO in go/constant/value.go.
+ check.errorf(atPos(opPos), _InvalidConstVal, "constant result is not representable")
+ // TODO(gri) Should we mark operands with unknown values as invalid?
+ }
// Typed constants must be representable in
// their type after each constant operation.
if isTyped(typ) {
@@ -944,7 +955,7 @@
// the index must be of integer type
if !isInteger(x.typ) {
- check.invalidArg(x.pos(), "index %s must be integer", &x)
+ check.invalidArg(&x, _InvalidIndex, "index %s must be integer", &x)
return
}
@@ -954,13 +965,13 @@
// a constant index i must be in bounds
if constant.Sign(x.val) < 0 {
- check.invalidArg(x.pos(), "index %s must not be negative", &x)
+ check.invalidArg(&x, _InvalidIndex, "index %s must not be negative", &x)
return
}
v, valid := constant.Int64Val(constant.ToInt(x.val))
if !valid || max >= 0 && v >= max {
- check.errorf(x.pos(), "index %s is out of bounds", &x)
+ check.errorf(&x, _InvalidIndex, "index %s is out of bounds", &x)
return
}
@@ -986,12 +997,12 @@
index = i
validIndex = true
} else {
- check.errorf(e.Pos(), "index %s must be integer constant", kv.Key)
+ check.errorf(e, _InvalidLitIndex, "index %s must be integer constant", kv.Key)
}
}
eval = kv.Value
} else if length >= 0 && index >= length {
- check.errorf(e.Pos(), "index %d is out of bounds (>= %d)", index, length)
+ check.errorf(e, _OversizeArrayLit, "index %d is out of bounds (>= %d)", index, length)
} else {
validIndex = true
}
@@ -999,7 +1010,7 @@
// if we have a valid index, check for duplicate entries
if validIndex {
if visited[index] {
- check.errorf(e.Pos(), "duplicate index %d in array or slice literal", index)
+ check.errorf(e, _DuplicateLitKey, "duplicate index %d in array or slice literal", index)
}
visited[index] = true
}
@@ -1031,7 +1042,7 @@
// If hint != nil, it is the type of a composite literal element.
//
func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind {
- if check.conf.Trace {
+ if trace {
check.trace(e.Pos(), "expr %s", e)
check.indent++
defer func() {
@@ -1089,13 +1100,17 @@
case *ast.Ellipsis:
// ellipses are handled explicitly where they are legal
// (array composite literals and parameter lists)
- check.error(e.Pos(), "invalid use of '...'")
+ check.error(e, _BadDotDotDotSyntax, "invalid use of '...'")
goto Error
case *ast.BasicLit:
x.setConst(e.Kind, e.Value)
if x.mode == invalid {
- check.invalidAST(e.Pos(), "invalid literal %v", e.Value)
+ // The parser already establishes syntactic correctness.
+ // If we reach here it's because of number under-/overflow.
+ // TODO(gri) setConst (and in turn the go/constant package)
+ // should return an error describing the issue.
+ check.errorf(e, _InvalidConstVal, "malformed constant: %s", e.Value)
goto Error
}
@@ -1116,7 +1131,7 @@
x.mode = value
x.typ = sig
} else {
- check.invalidAST(e.Pos(), "invalid function literal %s", e)
+ check.invalidAST(e, "invalid function literal %s", e)
goto Error
}
@@ -1148,7 +1163,7 @@
default:
// TODO(gri) provide better error messages depending on context
- check.error(e.Pos(), "missing type in composite literal")
+ check.error(e, _UntypedLit, "missing type in composite literal")
goto Error
}
@@ -1164,7 +1179,7 @@
for _, e := range e.Elts {
kv, _ := e.(*ast.KeyValueExpr)
if kv == nil {
- check.error(e.Pos(), "mixture of field:value and value elements in struct literal")
+ check.error(e, _MixedStructLit, "mixture of field:value and value elements in struct literal")
continue
}
key, _ := kv.Key.(*ast.Ident)
@@ -1172,12 +1187,12 @@
// so we don't drop information on the floor
check.expr(x, kv.Value)
if key == nil {
- check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key)
+ check.errorf(kv, _InvalidLitField, "invalid field name %s in struct literal", kv.Key)
continue
}
i := fieldIndex(utyp.fields, check.pkg, key.Name)
if i < 0 {
- check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name)
+ check.errorf(kv, _MissingLitField, "unknown field %s in struct literal", key.Name)
continue
}
fld := fields[i]
@@ -1186,7 +1201,7 @@
check.assignment(x, etyp, "struct literal")
// 0 <= i < len(fields)
if visited[i] {
- check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name)
+ check.errorf(kv, _DuplicateLitField, "duplicate field name %s in struct literal", key.Name)
continue
}
visited[i] = true
@@ -1195,25 +1210,27 @@
// no element must have a key
for i, e := range e.Elts {
if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
- check.error(kv.Pos(), "mixture of field:value and value elements in struct literal")
+ check.error(kv, _MixedStructLit, "mixture of field:value and value elements in struct literal")
continue
}
check.expr(x, e)
if i >= len(fields) {
- check.error(x.pos(), "too many values in struct literal")
+ check.error(x, _InvalidStructLit, "too many values in struct literal")
break // cannot continue
}
// i < len(fields)
fld := fields[i]
if !fld.Exported() && fld.pkg != check.pkg {
- check.errorf(x.pos(), "implicit assignment to unexported field %s in %s literal", fld.name, typ)
+ check.errorf(x,
+ _UnexportedLitField,
+ "implicit assignment to unexported field %s in %s literal", fld.name, typ)
continue
}
etyp := fld.typ
check.assignment(x, etyp, "struct literal")
}
if len(e.Elts) < len(fields) {
- check.error(e.Rbrace, "too few values in struct literal")
+ check.error(inNode(e, e.Rbrace), _InvalidStructLit, "too few values in struct literal")
// ok to continue
}
}
@@ -1223,7 +1240,7 @@
// This is a stop-gap solution. Should use Checker.objPath to report entire
// path starting with earliest declaration in the source. TODO(gri) fix this.
if utyp.elem == nil {
- check.error(e.Pos(), "illegal cycle in type declaration")
+ check.error(e, _InvalidTypeCycle, "illegal cycle in type declaration")
goto Error
}
n := check.indexedElts(e.Elts, utyp.elem, utyp.len)
@@ -1250,7 +1267,7 @@
// Prevent crash if the slice referred to is not yet set up.
// See analogous comment for *Array.
if utyp.elem == nil {
- check.error(e.Pos(), "illegal cycle in type declaration")
+ check.error(e, _InvalidTypeCycle, "illegal cycle in type declaration")
goto Error
}
check.indexedElts(e.Elts, utyp.elem, -1)
@@ -1259,14 +1276,14 @@
// Prevent crash if the map referred to is not yet set up.
// See analogous comment for *Array.
if utyp.key == nil || utyp.elem == nil {
- check.error(e.Pos(), "illegal cycle in type declaration")
+ check.error(e, _InvalidTypeCycle, "illegal cycle in type declaration")
goto Error
}
visited := make(map[interface{}][]Type, len(e.Elts))
for _, e := range e.Elts {
kv, _ := e.(*ast.KeyValueExpr)
if kv == nil {
- check.error(e.Pos(), "missing key in map literal")
+ check.error(e, _MissingLitKey, "missing key in map literal")
continue
}
check.exprWithHint(x, kv.Key, utyp.key)
@@ -1291,7 +1308,7 @@
visited[xkey] = nil
}
if duplicate {
- check.errorf(x.pos(), "duplicate key %s in map literal", x.val)
+ check.errorf(x, _DuplicateLitKey, "duplicate key %s in map literal", x.val)
continue
}
}
@@ -1313,7 +1330,7 @@
}
// if utyp is invalid, an error was reported before
if utyp != Typ[Invalid] {
- check.errorf(e.Pos(), "invalid composite literal type %s", typ)
+ check.errorf(e, _InvalidLit, "invalid composite literal type %s", typ)
goto Error
}
}
@@ -1330,32 +1347,24 @@
check.selector(x, e)
case *ast.IndexExpr:
- if check.useBrackets {
- check.exprOrType(x, e.X)
- } else {
- check.expr(x, e.X)
- }
+ check.exprOrType(x, e.X)
if x.mode == invalid {
check.use(e.Index)
goto Error
}
- if check.useBrackets {
- if x.mode == typexpr {
- if isGeneric(x.typ) {
- // type instantiation
- x.mode = invalid
- x.typ = check.varType(e)
- if x.typ != Typ[Invalid] {
- x.mode = typexpr
- }
- return expression
- }
- check.errorf(x.pos(), "%s is not a generic type", x.typ)
- goto Error
+ if x.mode == typexpr {
+ // type instantiation
+ x.mode = invalid
+ x.typ = check.varType(e)
+ if x.typ != Typ[Invalid] {
+ x.mode = typexpr
}
+ return expression
+ }
- if sig := asSignature(x.typ); sig != nil {
+ if x.mode == value {
+ if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 {
return check.call(x, nil, e)
}
}
@@ -1440,7 +1449,7 @@
e = t.elem
nmaps++
case *TypeParam:
- check.errorf(x.pos(), "type of %s contains a type parameter - cannot index (implementation restriction)", x)
+ check.errorf(x, 0, "type of %s contains a type parameter - cannot index (implementation restriction)", x)
case *instance:
panic("unimplemented")
}
@@ -1485,12 +1494,12 @@
}
if !valid {
- check.invalidOp(x.pos(), "cannot index %s", x)
+ check.invalidOp(x, _NonIndexableOperand, "cannot index %s", x)
goto Error
}
if e.Index == nil {
- check.invalidAST(e.Pos(), "missing index for %s", x)
+ check.invalidAST(e, "missing index for %s", x)
goto Error
}
@@ -1517,7 +1526,7 @@
case *Basic:
if isString(typ) {
if e.Slice3 {
- check.invalidOp(x.pos(), "3-index slice of string")
+ check.invalidOp(x, _InvalidSliceExpr, "3-index slice of string")
goto Error
}
valid = true
@@ -1535,7 +1544,7 @@
valid = true
length = typ.len
if x.mode != variable {
- check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x)
+ check.invalidOp(x, _NonSliceableOperand, "cannot slice %s (value not addressable)", x)
goto Error
}
x.typ = &Slice{elem: typ.elem}
@@ -1552,12 +1561,12 @@
// x.typ doesn't change
case *Sum, *TypeParam:
- check.errorf(x.pos(), "generic slice expressions not yet implemented")
+ check.errorf(x, 0, "generic slice expressions not yet implemented")
goto Error
}
if !valid {
- check.invalidOp(x.pos(), "cannot slice %s", x)
+ check.invalidOp(x, _NonSliceableOperand, "cannot slice %s", x)
goto Error
}
@@ -1565,7 +1574,7 @@
// spec: "Only the first index may be omitted; it defaults to 0."
if e.Slice3 && (e.High == nil || e.Max == nil) {
- check.error(e.Rbrack, "2nd and 3rd index required in 3-index slice")
+ check.invalidAST(inNode(e, e.Rbrack), "2nd and 3rd index required in 3-index slice")
goto Error
}
@@ -1602,7 +1611,7 @@
if x > 0 {
for _, y := range ind[i+1:] {
if y >= 0 && x > y {
- check.errorf(e.Rbrack, "invalid slice indices: %d > %d", x, y)
+ check.errorf(inNode(e, e.Rbrack), _SwappedSliceIndices, "swapped slice indices: %d > %d", x, y)
break L // only report one error, ok to continue
}
}
@@ -1616,20 +1625,22 @@
}
xtyp, _ := under(x.typ).(*Interface)
if xtyp == nil {
- check.errorf(x.pos(), "%s is not an interface type", x)
+ check.invalidOp(x, _InvalidAssert, "%s is not an interface", x)
goto Error
}
- check.ordinaryType(x.pos(), xtyp)
+ check.ordinaryType(x, xtyp)
// x.(type) expressions are handled explicitly in type switches
if e.Type == nil {
- check.invalidAST(e.Pos(), "use of .(type) outside type switch")
+ // Don't use invalidAST because this can occur in the AST produced by
+ // go/parser.
+ check.error(e, _BadTypeKeyword, "use of .(type) outside type switch")
goto Error
}
T := check.varType(e.Type)
if T == Typ[Invalid] {
goto Error
}
- check.typeAssertion(x.pos(), x, xtyp, T, false)
+ check.typeAssertion(x, x, xtyp, T)
x.mode = commaok
x.typ = T
@@ -1648,7 +1659,7 @@
x.mode = variable
x.typ = typ.base
} else {
- check.invalidOp(x.pos(), "cannot indirect %s", x)
+ check.invalidOp(x, _InvalidIndirection, "cannot indirect %s", x)
goto Error
}
}
@@ -1668,14 +1679,14 @@
}
case *ast.BinaryExpr:
- check.binary(x, e, e.X, e.Y, e.Op)
+ check.binary(x, e, e.X, e.Y, e.Op, e.OpPos)
if x.mode == invalid {
goto Error
}
case *ast.KeyValueExpr:
// key:value expressions are handled in composite literals
- check.invalidAST(e.Pos(), "no key:value expected")
+ check.invalidAST(e, "no key:value expected")
goto Error
case *ast.ArrayType, *ast.StructType, *ast.FuncType,
@@ -1727,8 +1738,8 @@
}
// typeAssertion checks that x.(T) is legal; xtyp must be the type of x.
-func (check *Checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface, T Type, strict bool) {
- method, wrongType := check.assertableTo(xtyp, T, strict)
+func (check *Checker) typeAssertion(at positioner, x *operand, xtyp *Interface, T Type) {
+ method, wrongType := check.assertableTo(xtyp, T)
if method == nil {
return
}
@@ -1742,7 +1753,7 @@
} else {
msg = "missing method " + method.name
}
- check.errorf(pos, "%s cannot have dynamic type %s (%s)", x, T, msg)
+ check.errorf(at, _ImpossibleAssert, "%s cannot have dynamic type %s (%s)", x, T, msg)
}
// expr typechecks expression e and initializes x with the expression value.
@@ -1792,6 +1803,7 @@
func (check *Checker) exclude(x *operand, modeset uint) {
if modeset&(1<<x.mode) != 0 {
var msg string
+ var code errorCode
switch x.mode {
case novalue:
if modeset&(1<<typexpr) != 0 {
@@ -1799,14 +1811,17 @@
} else {
msg = "%s used as value or type"
}
+ code = _TooManyValues
case builtin:
msg = "%s must be called"
+ code = _UncalledBuiltin
case typexpr:
msg = "%s is not an expression"
+ code = _NotAnExpr
default:
unreachable()
}
- check.errorf(x.pos(), msg, x)
+ check.errorf(x, code, msg, x)
x.mode = invalid
}
}
@@ -1817,7 +1832,7 @@
// tuple types are never named - no need for underlying type below
if t, ok := x.typ.(*Tuple); ok {
assert(t.Len() != 1)
- check.errorf(x.pos(), "%d-valued %s where single value is expected", t.Len(), x)
+ check.errorf(x, _TooManyValues, "%d-valued %s where single value is expected", t.Len(), x)
x.mode = invalid
}
}
diff --git a/src/go/types/fixedbugs/issue20583.src b/src/go/types/fixedbugs/issue20583.src
new file mode 100644
index 0000000..d26dbad
--- /dev/null
+++ b/src/go/types/fixedbugs/issue20583.src
@@ -0,0 +1,14 @@
+// Copyright 2020 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 issue20583
+
+const (
+ _ = 6e886451608 /* ERROR malformed constant */ /2
+ _ = 6e886451608i /* ERROR malformed constant */ /2
+ _ = 0 * 1e+1000000000 // ERROR malformed constant
+
+ x = 1e100000000
+ _ = x*x*x*x*x*x* /* ERROR not representable */ x
+)
diff --git a/src/go/types/fixedbugs/issue23203a.src b/src/go/types/fixedbugs/issue23203a.src
new file mode 100644
index 0000000..48cb588
--- /dev/null
+++ b/src/go/types/fixedbugs/issue23203a.src
@@ -0,0 +1,14 @@
+// Copyright 2018 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 main
+
+import "unsafe"
+
+type T struct{}
+
+func (T) m1() {}
+func (T) m2([unsafe.Sizeof(T.m1)]int) {}
+
+func main() {}
diff --git a/src/go/types/fixedbugs/issue23203b.src b/src/go/types/fixedbugs/issue23203b.src
new file mode 100644
index 0000000..638ec6c
--- /dev/null
+++ b/src/go/types/fixedbugs/issue23203b.src
@@ -0,0 +1,14 @@
+// Copyright 2018 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 main
+
+import "unsafe"
+
+type T struct{}
+
+func (T) m2([unsafe.Sizeof(T.m1)]int) {}
+func (T) m1() {}
+
+func main() {}
diff --git a/src/go/types/fixedbugs/issue26390.src b/src/go/types/fixedbugs/issue26390.src
new file mode 100644
index 0000000..9e0101f5
--- /dev/null
+++ b/src/go/types/fixedbugs/issue26390.src
@@ -0,0 +1,13 @@
+// Copyright 2018 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.
+
+// stand-alone test to ensure case is triggered
+
+package issue26390
+
+type A = T
+
+func (t *T) m() *A { return t }
+
+type T struct{}
diff --git a/src/go/types/fixedbugs/issue28251.src b/src/go/types/fixedbugs/issue28251.src
new file mode 100644
index 0000000..cd79e0e
--- /dev/null
+++ b/src/go/types/fixedbugs/issue28251.src
@@ -0,0 +1,65 @@
+// Copyright 2018 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.
+
+// This file contains test cases for various forms of
+// method receiver declarations, per the spec clarification
+// https://golang.org/cl/142757.
+
+package issue28251
+
+// test case from issue28251
+type T struct{}
+
+type T0 = *T
+
+func (T0) m() {}
+
+func _() { (&T{}).m() }
+
+// various alternative forms
+type (
+ T1 = (((T)))
+)
+
+func ((*(T1))) m1() {}
+func _() { (T{}).m2() }
+func _() { (&T{}).m2() }
+
+type (
+ T2 = (((T3)))
+ T3 = T
+)
+
+func (T2) m2() {}
+func _() { (T{}).m2() }
+func _() { (&T{}).m2() }
+
+type (
+ T4 = ((*(T5)))
+ T5 = T
+)
+
+func (T4) m4() {}
+func _() { (T{}).m4 /* ERROR "cannot call pointer method m4 on T" */ () }
+func _() { (&T{}).m4() }
+
+type (
+ T6 = (((T7)))
+ T7 = (*(T8))
+ T8 = T
+)
+
+func (T6) m6() {}
+func _() { (T{}).m6 /* ERROR "cannot call pointer method m6 on T" */ () }
+func _() { (&T{}).m6() }
+
+type (
+ T9 = *T10
+ T10 = *T11
+ T11 = T
+)
+
+func (T9 /* ERROR invalid receiver \*\*T */ ) m9() {}
+func _() { (T{}).m9 /* ERROR has no field or method m9 */ () }
+func _() { (&T{}).m9 /* ERROR has no field or method m9 */ () }
diff --git a/src/go/types/fixedbugs/issue39634.go2 b/src/go/types/fixedbugs/issue39634.go2
index e19343e..249542d 100644
--- a/src/go/types/fixedbugs/issue39634.go2
+++ b/src/go/types/fixedbugs/issue39634.go2
@@ -83,9 +83,9 @@
var x T25 /* ERROR without instantiation */ .m1
// crash 26
-type T26 = interface{ F26[Z any]() }
+type T26 = interface{ F26[ /* ERROR methods cannot have type parameters */ Z any]() }
func F26[Z any]() T26 { return F26 /* ERROR without instantiation */ /* ERROR missing method */ [] /* ERROR operand */ }
// crash 27
func e27[T any]() interface{ x27 /* ERROR not a type */ }
-func x27() { e27() /* ERROR cannot infer T */ }
\ No newline at end of file
+func x27() { e27() /* ERROR cannot infer T */ }
diff --git a/src/go/types/fixedbugs/issue39754.go2 b/src/go/types/fixedbugs/issue39754.go2
index 0de777a..2ed84dc 100644
--- a/src/go/types/fixedbugs/issue39754.go2
+++ b/src/go/types/fixedbugs/issue39754.go2
@@ -16,5 +16,5 @@
func _() {
f[int, Optional[int], Optional[int]]()
- f[int, Optional[int], Optional /* ERROR does not satisfy Box */ [string]]()
+ // f[int, Optional[int], Optional /* ERROR does not satisfy Box */ [string]]()
}
diff --git a/src/go/types/fixedbugs/issue42695.src b/src/go/types/fixedbugs/issue42695.src
new file mode 100644
index 0000000..d0d6200
--- /dev/null
+++ b/src/go/types/fixedbugs/issue42695.src
@@ -0,0 +1,17 @@
+// Copyright 2020 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 issue42695
+
+const _ = 6e5518446744 // ERROR malformed constant
+const _ uint8 = 6e5518446744 // ERROR malformed constant
+
+var _ = 6e5518446744 // ERROR malformed constant
+var _ uint8 = 6e5518446744 // ERROR malformed constant
+
+func f(x int) int {
+ return x + 6e5518446744 // ERROR malformed constant
+}
+
+var _ = f(6e5518446744 /* ERROR malformed constant */ )
diff --git a/src/go/types/fixedbugs/issue43110.src b/src/go/types/fixedbugs/issue43110.src
new file mode 100644
index 0000000..4a46945
--- /dev/null
+++ b/src/go/types/fixedbugs/issue43110.src
@@ -0,0 +1,43 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+type P *struct{}
+
+func _() {
+ // want an error even if the switch is empty
+ var a struct{ _ func() }
+ switch a /* ERROR cannot switch on a */ {
+ }
+
+ switch a /* ERROR cannot switch on a */ {
+ case a: // no follow-on error here
+ }
+
+ // this is ok because f can be compared to nil
+ var f func()
+ switch f {
+ }
+
+ switch f {
+ case nil:
+ }
+
+ switch (func())(nil) {
+ case nil:
+ }
+
+ switch (func())(nil) {
+ case f /* ERROR cannot compare */ :
+ }
+
+ switch nil /* ERROR use of untyped nil in switch expression */ {
+ }
+
+ // this is ok
+ switch P(nil) {
+ case P(nil):
+ }
+}
diff --git a/src/go/types/fixedbugs/issue43124.src b/src/go/types/fixedbugs/issue43124.src
new file mode 100644
index 0000000..f429f74
--- /dev/null
+++ b/src/go/types/fixedbugs/issue43124.src
@@ -0,0 +1,16 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+var _ = int(0 /* ERROR invalid use of \.\.\. in conversion to int */ ...)
+
+// test case from issue
+
+type M []string
+
+var (
+ x = []string{"a", "b"}
+ _ = M(x /* ERROR invalid use of \.\.\. in conversion to M */ ...)
+)
diff --git a/src/go/types/fixedbugs/issue6977.src b/src/go/types/fixedbugs/issue6977.src
new file mode 100644
index 0000000..8f4e9ba
--- /dev/null
+++ b/src/go/types/fixedbugs/issue6977.src
@@ -0,0 +1,82 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+import "io"
+
+// Alan's initial report.
+
+type I interface { f(); String() string }
+type J interface { g(); String() string }
+
+type IJ1 = interface { I; J }
+type IJ2 = interface { f(); g(); String() string }
+
+var _ = (*IJ1)(nil) == (*IJ2)(nil) // static assert that IJ1 and IJ2 are identical types
+
+// The canonical example.
+
+type ReadWriteCloser interface { io.ReadCloser; io.WriteCloser }
+
+// Some more cases.
+
+type M interface { m() }
+type M32 interface { m() int32 }
+type M64 interface { m() int64 }
+
+type U1 interface { m() }
+type U2 interface { m(); M }
+type U3 interface { M; m() }
+type U4 interface { M; M; M }
+type U5 interface { U1; U2; U3; U4 }
+
+type U6 interface { m(); m /* ERROR duplicate method */ () }
+type U7 interface { M32 /* ERROR duplicate method */ ; m() }
+type U8 interface { m(); M32 /* ERROR duplicate method */ }
+type U9 interface { M32; M64 /* ERROR duplicate method */ }
+
+// Verify that repeated embedding of the same interface(s)
+// eliminates duplicate methods early (rather than at the
+// end) to prevent exponential memory and time use.
+// Without early elimination, computing T29 may take dozens
+// of minutes.
+type (
+ T0 interface { m() }
+ T1 interface { T0; T0 }
+ T2 interface { T1; T1 }
+ T3 interface { T2; T2 }
+ T4 interface { T3; T3 }
+ T5 interface { T4; T4 }
+ T6 interface { T5; T5 }
+ T7 interface { T6; T6 }
+ T8 interface { T7; T7 }
+ T9 interface { T8; T8 }
+
+ T10 interface { T9; T9 }
+ T11 interface { T10; T10 }
+ T12 interface { T11; T11 }
+ T13 interface { T12; T12 }
+ T14 interface { T13; T13 }
+ T15 interface { T14; T14 }
+ T16 interface { T15; T15 }
+ T17 interface { T16; T16 }
+ T18 interface { T17; T17 }
+ T19 interface { T18; T18 }
+
+ T20 interface { T19; T19 }
+ T21 interface { T20; T20 }
+ T22 interface { T21; T21 }
+ T23 interface { T22; T22 }
+ T24 interface { T23; T23 }
+ T25 interface { T24; T24 }
+ T26 interface { T25; T25 }
+ T27 interface { T26; T26 }
+ T28 interface { T27; T27 }
+ T29 interface { T28; T28 }
+)
+
+// Verify that m is present.
+var x T29
+var _ = x.m
diff --git a/src/go/types/gotype.go b/src/go/types/gotype.go
index 258ad3b..eacf68f 100644
--- a/src/go/types/gotype.go
+++ b/src/go/types/gotype.go
@@ -48,9 +48,9 @@
Flags controlling additional output:
-ast
- print AST (forces -seq)
+ print AST
-trace
- print parse trace (forces -seq)
+ print parse trace
-comments
parse comments (ignored unless -ast or -trace is provided)
-panic
@@ -104,8 +104,8 @@
compiler = flag.String("c", "source", "compiler used for installed packages (gc, gccgo, or source)")
// additional output control
- printAST = flag.Bool("ast", false, "print AST (forces -seq)")
- printTrace = flag.Bool("trace", false, "print parse trace (forces -seq)")
+ printAST = flag.Bool("ast", false, "print AST")
+ printTrace = flag.Bool("trace", false, "print parse trace")
parseComments = flag.Bool("comments", false, "parse comments (ignored unless -ast or -trace is provided)")
panicOnError = flag.Bool("panic", false, "panic on first error")
)
@@ -118,7 +118,6 @@
)
func initParserMode() {
- parserMode = parser.UnifiedParamLists
if *allErrors {
parserMode |= parser.AllErrors
}
@@ -291,8 +290,7 @@
// if checkPkgFiles is called multiple times, set up conf only once
conf := types.Config{
- InferFromConstraints: true,
- FakeImportC: true,
+ FakeImportC: true,
Error: func(err error) {
if !*allErrors && errorCount >= 10 {
panic(bailout{})
diff --git a/src/go/types/infer.go b/src/go/types/infer.go
index 2c7e548..95bd3cb 100644
--- a/src/go/types/infer.go
+++ b/src/go/types/infer.go
@@ -17,7 +17,7 @@
// is impossible because unification fails, an error is reported and the resulting types list is
// nil, and index is 0. Otherwise, types is the list of inferred type arguments, and index is
// the index of the first type argument in that list that couldn't be inferred (and thus is nil).
-// If all type arguments where inferred successfully, index is < 0.
+// If all type arguments were inferred successfully, index is < 0.
func (check *Checker) infer(tparams []*TypeName, params *Tuple, args []*operand) (types []Type, index int) {
assert(params.Len() == len(args))
@@ -39,16 +39,17 @@
}
}
if allFailed {
- check.errorf(arg.pos(), "%s %s of %s does not match %s (cannot infer %s)", kind, targ, arg.expr, tpar, typeNamesString(tparams))
+ check.errorf(arg, _Todo, "%s %s of %s does not match %s (cannot infer %s)", kind, targ, arg.expr, tpar, typeNamesString(tparams))
return
}
}
smap := makeSubstMap(tparams, targs)
- inferred := check.subst(arg.pos(), tpar, smap)
+ // TODO(rFindley): pass a positioner here, rather than arg.Pos().
+ inferred := check.subst(arg.Pos(), tpar, smap)
if inferred != tpar {
- check.errorf(arg.pos(), "%s %s of %s does not match inferred type %s for %s", kind, targ, arg.expr, inferred, tpar)
+ check.errorf(arg, _Todo, "%s %s of %s does not match inferred type %s for %s", kind, targ, arg.expr, inferred, tpar)
} else {
- check.errorf(arg.pos(), "%s %s of %s does not match %s", kind, targ, arg.expr, tpar)
+ check.errorf(arg, 0, "%s %s of %s does not match %s", kind, targ, arg.expr, tpar)
}
}
@@ -72,7 +73,7 @@
if targ := arg.typ; isTyped(targ) {
// If we permit bidirectional unification, and targ is
// a generic function, we need to initialize u.y with
- // the respectice type parameters of targ.
+ // the respective type parameters of targ.
if !u.unify(par.typ, targ) {
errorf("type", par.typ, targ, arg)
return nil, 0
@@ -215,13 +216,13 @@
case *Interface:
if t.allMethods != nil {
- // interface is complete - quick test
+ // TODO(rFindley) at some point we should enforce completeness here
for _, m := range t.allMethods {
if w.isParameterized(m.typ) {
return true
}
}
- return w.isParameterizedList(unpack(t.allTypes))
+ return w.isParameterizedList(unpackType(t.allTypes))
}
return t.iterate(func(t *Interface) bool {
@@ -230,7 +231,7 @@
return true
}
}
- return w.isParameterizedList(unpack(t.types))
+ return w.isParameterizedList(unpackType(t.types))
}, nil)
case *Map:
@@ -291,21 +292,19 @@
// Unify type parameters with their structural constraints, if any.
for _, tpar := range tparams {
typ := tpar.typ.(*TypeParam)
- sbound := check.structuralType(under(typ.bound))
+ sbound := check.structuralType(typ.bound)
if sbound != nil {
- //check.dump(">>> unify(%s, %s)", tpar, sbound)
if !u.unify(typ, sbound) {
- check.errorf(tpar.pos, "%s does not match %s", tpar, sbound)
+ check.errorf(tpar, 0, "%s does not match %s", tpar, sbound)
return nil, 0
}
- //check.dump(">>> => indices = %v, types = %s", u.x.indices, u.types)
}
}
// u.x.types() now contains the incoming type arguments plus any additional type
// arguments for which there were structural constraints. The newly inferred non-
// nil entries may still contain references to other type parameters. For instance,
- // for [type A interface{}, B interface{type []C}, C interface{type *A}], if A == int
+ // for [A any, B interface{type []C}, C interface{type *A}], if A == int
// was given, unification produced the type list [int, []C, *A]. We eliminate the
// remaining type parameters by substituting the type parameters in this type list
// until nothing changes anymore.
@@ -317,7 +316,7 @@
}
// dirty tracks the indices of all types that may still contain type parameters.
- // We know that nil types entries and entries corresponding to provided (non-nil)
+ // We know that nil type entries and entries corresponding to provided (non-nil)
// type arguments are clean, so exclude them from the start.
var dirty []int
for i, typ := range types {
@@ -327,8 +326,8 @@
}
for len(dirty) > 0 {
- // TODO(gri) Instead of creating a new smap for each iteration,
- // provide an update operation for smaps and only change when
+ // TODO(gri) Instead of creating a new substMap for each iteration,
+ // provide an update operation for substMaps and only change when
// needed. Optimization.
smap := makeSubstMap(tparams, types)
n := 0
@@ -342,16 +341,15 @@
}
dirty = dirty[:n]
}
- //check.dump(">>> inferred types = %s", types)
return
}
// structuralType returns the structural type of a constraint, if any.
func (check *Checker) structuralType(constraint Type) Type {
- if iface, _ := constraint.(*Interface); iface != nil {
+ if iface, _ := under(constraint).(*Interface); iface != nil {
check.completeInterface(token.NoPos, iface)
- types := unpack(iface.allTypes)
+ types := unpackType(iface.allTypes)
if len(types) == 1 {
return types[0]
}
diff --git a/src/go/types/initorder.go b/src/go/types/initorder.go
index 9d5e916..77a739c 100644
--- a/src/go/types/initorder.go
+++ b/src/go/types/initorder.go
@@ -151,14 +151,14 @@
// reportCycle reports an error for the given cycle.
func (check *Checker) reportCycle(cycle []Object) {
obj := cycle[0]
- check.errorf(obj.Pos(), "initialization cycle for %s", obj.Name())
+ check.errorf(obj, _InvalidInitCycle, "initialization cycle for %s", obj.Name())
// subtle loop: print cycle[i] for i = 0, n-1, n-2, ... 1 for len(cycle) = n
for i := len(cycle) - 1; i >= 0; i-- {
- check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
+ check.errorf(obj, _InvalidInitCycle, "\t%s refers to", obj.Name()) // secondary error, \t indented
obj = cycle[i]
}
// print cycle[0] again to close the cycle
- check.errorf(obj.Pos(), "\t%s", obj.Name())
+ check.errorf(obj, _InvalidInitCycle, "\t%s", obj.Name())
}
// ----------------------------------------------------------------------------
diff --git a/src/go/types/issues_test.go b/src/go/types/issues_test.go
index 318427d..34850eb 100644
--- a/src/go/types/issues_test.go
+++ b/src/go/types/issues_test.go
@@ -12,6 +12,7 @@
"go/ast"
"go/importer"
"go/parser"
+ "go/token"
"internal/testenv"
"sort"
"strings"
@@ -384,12 +385,9 @@
}
}
if obj == nil {
- t.Fatal("object X not found")
+ t.Fatal("interface not found")
}
- iface, _ := obj.Type().Underlying().(*Interface) // object X must be an interface
- if iface == nil {
- t.Fatalf("%s is not an interface", obj)
- }
+ iface := obj.Type().Underlying().(*Interface) // I must be an interface
// Each iface method m is embedded; and m's receiver base type name
// must match the method's name per the choice in the source file.
@@ -526,3 +524,28 @@
pkg = res // res is imported by the next package in this test
}
}
+
+func TestIssue43088(t *testing.T) {
+ // type T1 struct {
+ // _ T2
+ // }
+ //
+ // type T2 struct {
+ // _ struct {
+ // _ T2
+ // }
+ // }
+ n1 := NewTypeName(token.NoPos, nil, "T1", nil)
+ T1 := NewNamed(n1, nil, nil)
+ n2 := NewTypeName(token.NoPos, nil, "T2", nil)
+ T2 := NewNamed(n2, nil, nil)
+ s1 := NewStruct([]*Var{NewField(token.NoPos, nil, "_", T2, false)}, nil)
+ T1.SetUnderlying(s1)
+ s2 := NewStruct([]*Var{NewField(token.NoPos, nil, "_", T2, false)}, nil)
+ s3 := NewStruct([]*Var{NewField(token.NoPos, nil, "_", s2, false)}, nil)
+ T2.SetUnderlying(s3)
+
+ // These calls must terminate (no endless recursion).
+ Comparable(T1)
+ Comparable(T2)
+}
diff --git a/src/go/types/labels.go b/src/go/types/labels.go
index 3b43b4b..8cf6e63 100644
--- a/src/go/types/labels.go
+++ b/src/go/types/labels.go
@@ -22,20 +22,23 @@
// for the respective gotos.
for _, jmp := range fwdJumps {
var msg string
+ var code errorCode
name := jmp.Label.Name
if alt := all.Lookup(name); alt != nil {
msg = "goto %s jumps into block"
alt.(*Label).used = true // avoid another error
+ code = _JumpIntoBlock
} else {
msg = "label %s not declared"
+ code = _UndeclaredLabel
}
- check.errorf(jmp.Label.Pos(), msg, name)
+ check.errorf(jmp.Label, code, msg, name)
}
// spec: "It is illegal to define a label that is never used."
for _, obj := range all.elems {
if lbl := obj.(*Label); !lbl.used {
- check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name)
+ check.softErrorf(lbl, _UnusedLabel, "label %s declared but not used", lbl.name)
}
}
}
@@ -133,7 +136,7 @@
if name := s.Label.Name; name != "_" {
lbl := NewLabel(s.Label.Pos(), check.pkg, name)
if alt := all.Insert(lbl); alt != nil {
- check.softErrorf(lbl.pos, "label %s already declared", name)
+ check.softErrorf(lbl, _DuplicateLabel, "label %s already declared", name)
check.reportAltDecl(alt)
// ok to continue
} else {
@@ -149,7 +152,8 @@
check.recordUse(jmp.Label, lbl)
if jumpsOverVarDecl(jmp) {
check.softErrorf(
- jmp.Label.Pos(),
+ jmp.Label,
+ _JumpOverDecl,
"goto %s jumps over variable declaration at line %d",
name,
check.fset.Position(varDeclPos).Line,
@@ -187,7 +191,7 @@
}
}
if !valid {
- check.errorf(s.Label.Pos(), "invalid break label %s", name)
+ check.errorf(s.Label, _MisplacedLabel, "invalid break label %s", name)
return
}
@@ -202,7 +206,7 @@
}
}
if !valid {
- check.errorf(s.Label.Pos(), "invalid continue label %s", name)
+ check.errorf(s.Label, _MisplacedLabel, "invalid continue label %s", name)
return
}
@@ -214,7 +218,7 @@
}
default:
- check.invalidAST(s.Pos(), "branch statement: %s %s", s.Tok, name)
+ check.invalidAST(s, "branch statement: %s %s", s.Tok, name)
return
}
diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go
index 0514819..a0e7f3c 100644
--- a/src/go/types/lookup.go
+++ b/src/go/types/lookup.go
@@ -196,6 +196,8 @@
}
case *TypeParam:
+ // only consider explicit methods in the type parameter bound, not
+ // methods that may be common to all types in the type list.
if i, m := lookupMethod(t.Bound().allMethods, pkg, name); m != nil {
assert(m.typ != nil)
index = concat(e.index, i)
@@ -206,12 +208,6 @@
obj = m
indirect = e.indirect
}
- if obj == nil {
- // At this point we're not (yet) looking into methods
- // that any underlyng type of the types in the type list
- // migth have.
- // TODO(gri) Do we want to specify the language that way?
- }
}
}
@@ -223,7 +219,7 @@
// is shorthand for (&x).m()".
if f, _ := obj.(*Func); f != nil {
// determine if method has a pointer receiver
- hasPtrRecv := tpar == nil && ptrRecv(f) || tpar != nil && tpar.ptr
+ hasPtrRecv := tpar == nil && ptrRecv(f)
if hasPtrRecv && !indirect && !addressable {
return nil, nil, true // pointer/addressable receiver required
}
@@ -329,7 +325,6 @@
return m, f
}
- // both methods must have the same number of type parameters
ftyp := f.typ.(*Signature)
mtyp := m.typ.(*Signature)
if len(ftyp.tparams) != len(mtyp.tparams) {
@@ -429,13 +424,13 @@
// method required by V and whether it is missing or just has the wrong type.
// The receiver may be nil if assertableTo is invoked through an exported API call
// (such as AssertableTo), i.e., when all methods have been type-checked.
-// If strict (or the global constant forceStrict) is set, assertions that
-// are known to fail are not permitted.
-func (check *Checker) assertableTo(V *Interface, T Type, strict bool) (method, wrongType *Func) {
+// If the global constant forceStrict is set, assertions that are known to fail
+// are not permitted.
+func (check *Checker) assertableTo(V *Interface, T Type) (method, wrongType *Func) {
// no static check is required if T is an interface
// spec: "If T is an interface type, x.(T) asserts that the
// dynamic type of x implements the interface T."
- if asInterface(T) != nil && !(strict || forceStrict) {
+ if asInterface(T) != nil && !forceStrict {
return
}
return check.missingMethod(T, V, false)
diff --git a/src/go/types/operand.go b/src/go/types/operand.go
index cd66db67..8f9c9d0 100644
--- a/src/go/types/operand.go
+++ b/src/go/types/operand.go
@@ -59,10 +59,10 @@
id builtinId
}
-// pos returns the position of the expression corresponding to x.
+// Pos returns the position of the expression corresponding to x.
// If x is invalid the position is token.NoPos.
//
-func (x *operand) pos() token.Pos {
+func (x *operand) Pos() token.Pos {
// x.expr may not be set if x is invalid
if x.expr == nil {
return token.NoPos
@@ -204,9 +204,15 @@
unreachable()
}
+ val := constant.MakeFromLiteral(lit, tok, 0)
+ if val.Kind() == constant.Unknown {
+ x.mode = invalid
+ x.typ = Typ[Invalid]
+ return
+ }
x.mode = constant_
x.typ = Typ[kind]
- x.val = constant.MakeFromLiteral(lit, tok, 0)
+ x.val = val
}
// isNil reports whether x is the nil value.
@@ -214,65 +220,44 @@
return x.mode == value && x.typ == Typ[UntypedNil]
}
-// TODO(gri) The functions operand.assignableTo, checker.convertUntyped,
-// checker.representable, and checker.assignment are
-// overlapping in functionality. Need to simplify and clean up.
-
-// assignableTo reports whether x is assignable to a variable of type T.
-// If the result is false and a non-nil reason is provided, it may be set
-// to a more detailed explanation of the failure (result != "").
-// The check parameter may be nil if assignableTo is invoked through
-// an exported API call, i.e., when all methods have been type-checked.
-func (x *operand) assignableTo(check *Checker, T Type, reason *string) bool {
+// assignableTo reports whether x is assignable to a variable of type T. If the
+// result is false and a non-nil reason is provided, it may be set to a more
+// detailed explanation of the failure (result != ""). The check parameter may
+// be nil if assignableTo is invoked through an exported API call, i.e., when
+// all methods have been type-checked.
+func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, errorCode) {
if x.mode == invalid || T == Typ[Invalid] {
- return true // avoid spurious errors
+ return true, 0 // avoid spurious errors
}
V := x.typ
// x's type is identical to T
if check.identical(V, T) {
- return true
+ return true, 0
}
Vu := optype(V)
Tu := optype(T)
- // x is an untyped value representable by a value of type T
- // TODO(gri) This is borrowing from checker.convertUntyped and
- // checker.representable. Need to clean up.
+ // x is an untyped value representable by a value of type T.
if isUntyped(Vu) {
- switch t := Tu.(type) {
- case *Basic:
- if x.isNil() && t.kind == UnsafePointer {
- return true
- }
- if x.mode == constant_ {
- return representableConst(x.val, check, t, nil)
- }
- // The result of a comparison is an untyped boolean,
- // but may not be a constant.
- if Vb, _ := Vu.(*Basic); Vb != nil {
- return Vb.kind == UntypedBool && isBoolean(Tu)
- }
- case *Sum:
+ if t, ok := Tu.(*Sum); ok {
return t.is(func(t Type) bool {
// TODO(gri) this could probably be more efficient
- return x.assignableTo(check, t, reason)
- })
- case *Interface:
- check.completeInterface(token.NoPos, t)
- return x.isNil() || t.Empty()
- case *Pointer, *Signature, *Slice, *Map, *Chan:
- return x.isNil()
+ ok, _ := x.assignableTo(check, t, reason)
+ return ok
+ }), _IncompatibleAssign
}
+ newType, _, _ := check.implicitTypeAndValue(x, Tu)
+ return newType != nil, _IncompatibleAssign
}
// Vu is typed
// x's type V and T have identical underlying types
// and at least one of V or T is not a named type
if check.identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
- return true
+ return true, 0
}
// T is an interface type and x implements T
@@ -290,9 +275,9 @@
*reason = "missing method " + m.Name()
}
}
- return false
+ return false, _InvalidIfaceAssign
}
- return true
+ return true, 0
}
// x is a bidirectional channel value, T is a channel
@@ -300,9 +285,13 @@
// and at least one of V or T is not a named type
if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
if Tc, ok := Tu.(*Chan); ok && check.identical(Vc.elem, Tc.elem) {
- return !isNamed(V) || !isNamed(T)
+ if !isNamed(V) || !isNamed(T) {
+ return true, 0
+ } else {
+ return false, _InvalidChanAssign
+ }
}
}
- return false
+ return false, _IncompatibleAssign
}
diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go
index 7f76df6..0233274 100644
--- a/src/go/types/predicates.go
+++ b/src/go/types/predicates.go
@@ -21,7 +21,8 @@
return false
}
-// isGeneric reports whether a type is a generic, uninstantiated type (generic signatures are not included).
+// isGeneric reports whether a type is a generic, uninstantiated type (generic
+// signatures are not included).
func isGeneric(typ Type) bool {
// A parameterized type is only instantiated if it doesn't have an instantiation already.
named, _ := typ.(*Named)
@@ -73,12 +74,8 @@
return !isTyped(typ)
}
-func isOrdered(typ Type) bool { return is(typ, IsOrdered) }
-
-func isConstType(typ Type) bool {
- t := asBasic(typ)
- return t != nil && t.info&IsConstType != 0
-}
+func isOrdered(typ Type) bool { return is(typ, IsOrdered) }
+func isConstType(typ Type) bool { return is(typ, IsConstType) }
// IsInterface reports whether typ is an interface type.
func IsInterface(typ Type) bool {
@@ -87,7 +84,19 @@
// Comparable reports whether values of type T are comparable.
func Comparable(T Type) bool {
- // If T is a type parameter not constraint by any type
+ return comparable(T, nil)
+}
+
+func comparable(T Type, seen map[Type]bool) bool {
+ if seen[T] {
+ return true
+ }
+ if seen == nil {
+ seen = make(map[Type]bool)
+ }
+ seen[T] = true
+
+ // If T is a type parameter not constrained by any type
// list (i.e., it's underlying type is the top type),
// T is comparable if it has the == method. Otherwise,
// the underlying type "wins". For instance
@@ -108,15 +117,18 @@
return true
case *Struct:
for _, f := range t.fields {
- if !Comparable(f.typ) {
+ if !comparable(f.typ, seen) {
return false
}
}
return true
case *Array:
- return Comparable(t.elem)
+ return comparable(t.elem, seen)
case *Sum:
- return t.is(Comparable)
+ pred := func(t Type) bool {
+ return comparable(t, seen)
+ }
+ return t.is(pred)
case *TypeParam:
return t.Bound().IsComparable()
}
diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go
index 165b842..639ed12 100644
--- a/src/go/types/resolver.go
+++ b/src/go/types/resolver.go
@@ -17,12 +17,13 @@
// A declInfo describes a package-level const, type, var, or func declaration.
type declInfo struct {
- file *Scope // scope of file containing this declaration
- lhs []*Var // lhs of n:1 variable declarations, or nil
- vtyp ast.Expr // type, or nil (for const and var declarations only)
- init ast.Expr // init/orig expression, or nil (for const and var declarations only)
- tdecl *ast.TypeSpec // type declaration, or nil
- fdecl *ast.FuncDecl // func declaration, or nil
+ file *Scope // scope of file containing this declaration
+ lhs []*Var // lhs of n:1 variable declarations, or nil
+ vtyp ast.Expr // type, or nil (for const and var declarations only)
+ init ast.Expr // init/orig expression, or nil (for const and var declarations only)
+ inherited bool // if set, the init expression is inherited from a previous constant declaration
+ tdecl *ast.TypeSpec // type declaration, or nil
+ fdecl *ast.FuncDecl // func declaration, or nil
// The deps field tracks initialization expression dependencies.
deps map[Object]bool // lazily initialized
@@ -55,26 +56,27 @@
r = len(init.Values)
}
+ const code = _WrongAssignCount
switch {
case init == nil && r == 0:
// var decl w/o init expr
if s.Type == nil {
- check.errorf(s.Pos(), "missing type or init expr")
+ check.errorf(s, code, "missing type or init expr")
}
case l < r:
if l < len(s.Values) {
// init exprs from s
n := s.Values[l]
- check.errorf(n.Pos(), "extra init expr %s", n)
+ check.errorf(n, code, "extra init expr %s", n)
// TODO(gri) avoid declared but not used error here
} else {
// init exprs "inherited"
- check.errorf(s.Pos(), "extra init expr at %s", check.fset.Position(init.Pos()))
+ check.errorf(s, code, "extra init expr at %s", check.fset.Position(init.Pos()))
// TODO(gri) avoid declared but not used error here
}
case l > r && (init != nil || r != 1):
n := s.Names[r]
- check.errorf(n.Pos(), "missing init expr for %s", n)
+ check.errorf(n, code, "missing init expr for %s", n)
}
}
@@ -103,14 +105,14 @@
// spec: "A package-scope or file-scope identifier with name init
// may only be declared to be a function with this (func()) signature."
if ident.Name == "init" {
- check.errorf(ident.Pos(), "cannot declare init - must be func")
+ check.errorf(ident, _InvalidInitDecl, "cannot declare init - must be func")
return
}
// spec: "The main package must have package name main and declare
// a function main that takes no arguments and returns no value."
if ident.Name == "main" && check.pkg.name == "main" {
- check.errorf(ident.Pos(), "cannot declare main - must be func")
+ check.errorf(ident, _InvalidMainDecl, "cannot declare main - must be func")
return
}
@@ -168,7 +170,7 @@
imp = nil // create fake package below
}
if err != nil {
- check.errorf(pos, "could not import %s (%s)", path, err)
+ check.errorf(atPos(pos), _BrokenImport, "could not import %s (%s)", path, err)
if imp == nil {
// create a new fake package
// come up with a sensible package name (heuristic)
@@ -242,218 +244,185 @@
// we get "." as the directory which is what we would want.
fileDir := dir(check.fset.Position(file.Name.Pos()).Filename)
- for _, decl := range file.Decls {
- switch d := decl.(type) {
- case *ast.BadDecl:
- // ignore
+ check.walkDecls(file.Decls, func(d decl) {
+ switch d := d.(type) {
+ case importDecl:
+ // import package
+ path, err := validatedImportPath(d.spec.Path.Value)
+ if err != nil {
+ check.errorf(d.spec.Path, _BadImportPath, "invalid import path (%s)", err)
+ return
+ }
- case *ast.GenDecl:
- var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
- for iota, spec := range d.Specs {
- switch s := spec.(type) {
- case *ast.ImportSpec:
- // import package
- path, err := validatedImportPath(s.Path.Value)
- if err != nil {
- check.errorf(s.Path.Pos(), "invalid import path (%s)", err)
- continue
- }
+ imp := check.importPackage(d.spec.Path.Pos(), path, fileDir)
+ if imp == nil {
+ return
+ }
- imp := check.importPackage(s.Path.Pos(), path, fileDir)
- if imp == nil {
- continue
- }
+ // add package to list of explicit imports
+ // (this functionality is provided as a convenience
+ // for clients; it is not needed for type-checking)
+ if !pkgImports[imp] {
+ pkgImports[imp] = true
+ pkg.imports = append(pkg.imports, imp)
+ }
- // add package to list of explicit imports
- // (this functionality is provided as a convenience
- // for clients; it is not needed for type-checking)
- if !pkgImports[imp] {
- pkgImports[imp] = true
- pkg.imports = append(pkg.imports, imp)
- }
-
- // local name overrides imported package name
- name := imp.name
- if s.Name != nil {
- name = s.Name.Name
- if path == "C" {
- // match cmd/compile (not prescribed by spec)
- check.errorf(s.Name.Pos(), `cannot rename import "C"`)
- continue
- }
- if name == "init" {
- check.errorf(s.Name.Pos(), "cannot declare init - must be func")
- continue
- }
- }
-
- obj := NewPkgName(s.Pos(), pkg, name, imp)
- if s.Name != nil {
- // in a dot-import, the dot represents the package
- check.recordDef(s.Name, obj)
- } else {
- check.recordImplicit(s, obj)
- }
-
- if path == "C" {
- // match cmd/compile (not prescribed by spec)
- obj.used = true
- }
-
- // add import to file scope
- if name == "." {
- // merge imported scope with file scope
- for _, obj := range imp.scope.elems {
- // A package scope may contain non-exported objects,
- // do not import them!
- if obj.Exported() {
- // declare dot-imported object
- // (Do not use check.declare because it modifies the object
- // via Object.setScopePos, which leads to a race condition;
- // the object may be imported into more than one file scope
- // concurrently. See issue #32154.)
- if alt := fileScope.Insert(obj); alt != nil {
- check.errorf(s.Name.Pos(), "%s redeclared in this block", obj.Name())
- check.reportAltDecl(alt)
- }
- }
- }
- // add position to set of dot-import positions for this file
- // (this is only needed for "imported but not used" errors)
- check.addUnusedDotImport(fileScope, imp, s.Pos())
- } else {
- // declare imported package object in file scope
- // (no need to provide s.Name since we called check.recordDef earlier)
- check.declare(fileScope, nil, obj, token.NoPos)
- }
-
- case *ast.ValueSpec:
- switch d.Tok {
- case token.CONST:
- // determine which initialization expressions to use
- switch {
- case s.Type != nil || len(s.Values) > 0:
- last = s
- case last == nil:
- last = new(ast.ValueSpec) // make sure last exists
- }
-
- // declare all constants
- for i, name := range s.Names {
- obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(iota)))
-
- var init ast.Expr
- if i < len(last.Values) {
- init = last.Values[i]
- }
-
- d := &declInfo{file: fileScope, vtyp: last.Type, init: init}
- check.declarePkgObj(name, obj, d)
- }
-
- check.arityMatch(s, last)
-
- case token.VAR:
- lhs := make([]*Var, len(s.Names))
- // If there's exactly one rhs initializer, use
- // the same declInfo d1 for all lhs variables
- // so that each lhs variable depends on the same
- // rhs initializer (n:1 var declaration).
- var d1 *declInfo
- if len(s.Values) == 1 {
- // The lhs elements are only set up after the for loop below,
- // but that's ok because declareVar only collects the declInfo
- // for a later phase.
- d1 = &declInfo{file: fileScope, lhs: lhs, vtyp: s.Type, init: s.Values[0]}
- }
-
- // declare all variables
- for i, name := range s.Names {
- obj := NewVar(name.Pos(), pkg, name.Name, nil)
- lhs[i] = obj
-
- d := d1
- if d == nil {
- // individual assignments
- var init ast.Expr
- if i < len(s.Values) {
- init = s.Values[i]
- }
- d = &declInfo{file: fileScope, vtyp: s.Type, init: init}
- }
-
- check.declarePkgObj(name, obj, d)
- }
-
- check.arityMatch(s, nil)
-
- default:
- check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
- }
-
- case *ast.TypeSpec:
- obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
- check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, tdecl: s})
-
- default:
- check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
+ // local name overrides imported package name
+ name := imp.name
+ if d.spec.Name != nil {
+ name = d.spec.Name.Name
+ if path == "C" {
+ // match cmd/compile (not prescribed by spec)
+ check.errorf(d.spec.Name, _ImportCRenamed, `cannot rename import "C"`)
+ return
+ }
+ if name == "init" {
+ check.errorf(d.spec.Name, _InvalidInitDecl, "cannot declare init - must be func")
+ return
}
}
- case *ast.FuncDecl:
- name := d.Name.Name
- obj := NewFunc(d.Name.Pos(), pkg, name, nil)
- if !d.IsMethod() {
+ obj := NewPkgName(d.spec.Pos(), pkg, name, imp)
+ if d.spec.Name != nil {
+ // in a dot-import, the dot represents the package
+ check.recordDef(d.spec.Name, obj)
+ } else {
+ check.recordImplicit(d.spec, obj)
+ }
+
+ if path == "C" {
+ // match cmd/compile (not prescribed by spec)
+ obj.used = true
+ }
+
+ // add import to file scope
+ if name == "." {
+ // merge imported scope with file scope
+ for _, obj := range imp.scope.elems {
+ // A package scope may contain non-exported objects,
+ // do not import them!
+ if obj.Exported() {
+ // declare dot-imported object
+ // (Do not use check.declare because it modifies the object
+ // via Object.setScopePos, which leads to a race condition;
+ // the object may be imported into more than one file scope
+ // concurrently. See issue #32154.)
+ if alt := fileScope.Insert(obj); alt != nil {
+ check.errorf(d.spec.Name, _DuplicateDecl, "%s redeclared in this block", obj.Name())
+ check.reportAltDecl(alt)
+ }
+ }
+ }
+ // add position to set of dot-import positions for this file
+ // (this is only needed for "imported but not used" errors)
+ check.addUnusedDotImport(fileScope, imp, d.spec)
+ } else {
+ // declare imported package object in file scope
+ // (no need to provide s.Name since we called check.recordDef earlier)
+ check.declare(fileScope, nil, obj, token.NoPos)
+ }
+ case constDecl:
+ // declare all constants
+ for i, name := range d.spec.Names {
+ obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(d.iota)))
+
+ var init ast.Expr
+ if i < len(d.init) {
+ init = d.init[i]
+ }
+
+ d := &declInfo{file: fileScope, vtyp: d.typ, init: init, inherited: d.inherited}
+ check.declarePkgObj(name, obj, d)
+ }
+
+ case varDecl:
+ lhs := make([]*Var, len(d.spec.Names))
+ // If there's exactly one rhs initializer, use
+ // the same declInfo d1 for all lhs variables
+ // so that each lhs variable depends on the same
+ // rhs initializer (n:1 var declaration).
+ var d1 *declInfo
+ if len(d.spec.Values) == 1 {
+ // The lhs elements are only set up after the for loop below,
+ // but that's ok because declareVar only collects the declInfo
+ // for a later phase.
+ d1 = &declInfo{file: fileScope, lhs: lhs, vtyp: d.spec.Type, init: d.spec.Values[0]}
+ }
+
+ // declare all variables
+ for i, name := range d.spec.Names {
+ obj := NewVar(name.Pos(), pkg, name.Name, nil)
+ lhs[i] = obj
+
+ di := d1
+ if di == nil {
+ // individual assignments
+ var init ast.Expr
+ if i < len(d.spec.Values) {
+ init = d.spec.Values[i]
+ }
+ di = &declInfo{file: fileScope, vtyp: d.spec.Type, init: init}
+ }
+
+ check.declarePkgObj(name, obj, di)
+ }
+ case typeDecl:
+ obj := NewTypeName(d.spec.Name.Pos(), pkg, d.spec.Name.Name, nil)
+ check.declarePkgObj(d.spec.Name, obj, &declInfo{file: fileScope, tdecl: d.spec})
+ case funcDecl:
+ info := &declInfo{file: fileScope, fdecl: d.decl}
+ name := d.decl.Name.Name
+ obj := NewFunc(d.decl.Name.Pos(), pkg, name, nil)
+ if !d.decl.IsMethod() {
// regular function
- if d.Recv != nil {
- check.errorf(d.Recv.Pos(), "method is missing receiver")
+ if d.decl.Recv != nil {
+ check.error(d.decl.Recv, _BadRecv, "method is missing receiver")
// treat as function
}
if name == "init" {
- if d.Type.TParams != nil {
- check.softErrorf(d.Type.TParams.Pos(), "func init must have no type parameters")
+ if d.decl.Type.TParams != nil {
+ check.softErrorf(d.decl.Type.TParams, _InvalidInitSig, "func init must have no type parameters")
}
- if t := d.Type; t.Params.NumFields() != 0 || t.Results != nil {
- check.softErrorf(d.Pos(), "func init must have no arguments and no return values")
+ if t := d.decl.Type; t.Params.NumFields() != 0 || t.Results != nil {
+ // TODO(rFindley) Should this be a hard error?
+ check.softErrorf(d.decl, _InvalidInitSig, "func init must have no arguments and no return values")
}
// don't declare init functions in the package scope - they are invisible
obj.parent = pkg.scope
- check.recordDef(d.Name, obj)
+ check.recordDef(d.decl.Name, obj)
// init functions must have a body
- if d.Body == nil {
+ if d.decl.Body == nil {
// TODO(gri) make this error message consistent with the others above
- check.softErrorf(obj.pos, "missing function body")
+ check.softErrorf(obj, _MissingInitBody, "missing function body")
}
} else {
- check.declare(pkg.scope, d.Name, obj, token.NoPos)
+ check.declare(pkg.scope, d.decl.Name, obj, token.NoPos)
}
} else {
// method
- // d.Recv != nil && len(d.Recv.List) > 0
- if !methodTypeParamsOk && d.Type.TParams != nil {
- check.invalidAST(d.Type.TParams.Pos(), "method must have no type parameters")
- }
- ptr, recv, _ := check.unpackRecv(d.Recv.List[0].Type, false)
+
+ // TODO(rFindley) earlier versions of this code checked that methods
+ // have no type parameters, but this is checked later
+ // when type checking the function type. Confirm that
+ // we don't need to check tparams here.
+
+ ptr, recv, _ := check.unpackRecv(d.decl.Recv.List[0].Type, false)
// (Methods with invalid receiver cannot be associated to a type, and
// methods with blank _ names are never found; no need to collect any
// of them. They will still be type-checked with all the other functions.)
if recv != nil && name != "_" {
methods = append(methods, methodInfo{obj, ptr, recv})
}
- check.recordDef(d.Name, obj)
+ check.recordDef(d.decl.Name, obj)
}
- info := &declInfo{file: fileScope, fdecl: d}
// Methods are not package-level objects but we still track them in the
// object map so that we can handle them like regular functions (if the
// receiver is invalid); also we need their fdecl info when associating
// them with their receiver base type, below.
check.objMap[obj] = info
obj.setOrder(uint32(len(check.objMap)))
-
- default:
- check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
}
- }
+ })
}
// verify that objects in package and file scopes have different names
@@ -461,10 +430,10 @@
for _, obj := range scope.elems {
if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
if pkg, ok := obj.(*PkgName); ok {
- check.errorf(alt.Pos(), "%s already declared through import of %s", alt.Name(), pkg.Imported())
+ check.errorf(alt, _DuplicateDecl, "%s already declared through import of %s", alt.Name(), pkg.Imported())
check.reportAltDecl(pkg)
} else {
- check.errorf(alt.Pos(), "%s already declared through dot-import of %s", alt.Name(), obj.Pkg())
+ check.errorf(alt, _DuplicateDecl, "%s already declared through dot-import of %s", alt.Name(), obj.Pkg())
// TODO(gri) dot-imported objects don't have a position; reportAltDecl won't print anything
check.reportAltDecl(obj)
}
@@ -476,16 +445,17 @@
// associate methods with receiver base type name where possible.
// Ignore methods that have an invalid receiver. They will be
// type-checked later, with regular functions.
- if methods != nil {
- check.methods = make(map[*TypeName][]*Func)
- for i := range methods {
- m := &methods[i]
- // Determine the receiver base type and associate m with it.
- ptr, base := check.resolveBaseTypeName(m.ptr, m.recv)
- if base != nil {
- m.obj.hasPtrRecv = ptr
- check.methods[base] = append(check.methods[base], m.obj)
- }
+ if methods == nil {
+ return // nothing to do
+ }
+ check.methods = make(map[*TypeName][]*Func)
+ for i := range methods {
+ m := &methods[i]
+ // Determine the receiver base type and associate m with it.
+ ptr, base := check.resolveBaseTypeName(m.ptr, m.recv)
+ if base != nil {
+ m.obj.hasPtrRecv = ptr
+ check.methods[base] = append(check.methods[base], m.obj)
}
}
}
@@ -505,6 +475,7 @@
case *ast.ParenExpr:
rtyp = t.X
case *ast.StarExpr:
+ ptr = true
rtyp = t.X
default:
break L
@@ -526,9 +497,9 @@
case *ast.BadExpr:
// ignore - error already reported by parser
case nil:
- check.invalidAST(ptyp.Pos(), "parameterized receiver contains nil parameters")
+ check.invalidAST(ptyp, "parameterized receiver contains nil parameters")
default:
- check.errorf(arg.Pos(), "receiver type parameter %s must be an identifier", arg)
+ check.errorf(arg, _Todo, "receiver type parameter %s must be an identifier", arg)
}
if par == nil {
par = &ast.Ident{NamePos: arg.Pos(), Name: "_"}
@@ -550,7 +521,7 @@
// there was a pointer indirection to get to it. The base type name must be declared
// in package scope, and there can be at most one pointer indirection. If no such type
// name exists, the returned base is nil.
-func (check *Checker) resolveBaseTypeName(seenPtr bool, typ ast.Expr) (ptr bool, base *TypeName) {
+func (check *Checker) resolveBaseTypeName(seenPtr bool, name *ast.Ident) (ptr bool, base *TypeName) {
// Algorithm: Starting from a type expression, which may be a name,
// we follow that type through alias declarations until we reach a
// non-alias type name. If we encounter anything but pointer types or
@@ -558,6 +529,7 @@
// we're done.
ptr = seenPtr
var seen map[*TypeName]bool
+ var typ ast.Expr = name
for {
typ = unparen(typ)
@@ -686,9 +658,9 @@
path := obj.imported.path
base := pkgName(path)
if obj.name == base {
- check.softErrorf(obj.pos, "%q imported but not used", path)
+ check.softErrorf(obj, _UnusedImport, "%q imported but not used", path)
} else {
- check.softErrorf(obj.pos, "%q imported but not used as %s", path, obj.name)
+ check.softErrorf(obj, _UnusedImport, "%q imported but not used as %s", path, obj.name)
}
}
}
@@ -698,7 +670,7 @@
// check use of dot-imported packages
for _, unusedDotImports := range check.unusedDotImports {
for pkg, pos := range unusedDotImports {
- check.softErrorf(pos, "%q imported but not used", pkg.path)
+ check.softErrorf(pos, _UnusedImport, "%q imported but not used", pkg.path)
}
}
}
diff --git a/src/go/types/sanitize.go b/src/go/types/sanitize.go
index 1408937..c4e729e 100644
--- a/src/go/types/sanitize.go
+++ b/src/go/types/sanitize.go
@@ -4,6 +4,8 @@
package types
+// sanitizeInfo walks the types contained in info to ensure that all instances
+// are expanded.
func sanitizeInfo(info *Info) {
var s sanitizer = make(map[Type]Type)
diff --git a/src/go/types/self_test.go b/src/go/types/self_test.go
index 10ad06f..262dc7b 100644
--- a/src/go/types/self_test.go
+++ b/src/go/types/self_test.go
@@ -5,12 +5,11 @@
package types_test
import (
- "flag"
- "fmt"
"go/ast"
"go/importer"
"go/parser"
"go/token"
+ "path"
"path/filepath"
"testing"
"time"
@@ -18,11 +17,9 @@
. "go/types"
)
-var benchmark = flag.Bool("b", false, "run benchmarks")
-
func TestSelf(t *testing.T) {
fset := token.NewFileSet()
- files, err := pkgFiles(fset, ".")
+ files, err := pkgFiles(fset, ".", 0)
if err != nil {
t.Fatal(err)
}
@@ -39,36 +36,39 @@
}
}
-func TestBenchmark(t *testing.T) {
- if !*benchmark {
- return
- }
-
- // We're not using testing's benchmarking mechanism directly
- // because we want custom output.
-
- for _, p := range []string{"types", "constant", filepath.Join("internal", "gcimporter")} {
- path := filepath.Join("..", p)
- runbench(t, path, false)
- runbench(t, path, true)
- fmt.Println()
+func BenchmarkCheck(b *testing.B) {
+ for _, p := range []string{
+ "net/http",
+ "go/parser",
+ "go/constant",
+ filepath.Join("go", "internal", "gcimporter"),
+ } {
+ b.Run(path.Base(p), func(b *testing.B) {
+ path := filepath.Join("..", "..", p)
+ for _, ignoreFuncBodies := range []bool{false, true} {
+ name := "funcbodies"
+ if ignoreFuncBodies {
+ name = "nofuncbodies"
+ }
+ b.Run(name, func(b *testing.B) {
+ b.Run("info", func(b *testing.B) {
+ runbench(b, path, ignoreFuncBodies, true)
+ })
+ b.Run("noinfo", func(b *testing.B) {
+ runbench(b, path, ignoreFuncBodies, false)
+ })
+ })
+ }
+ })
}
}
-func runbench(t *testing.T, path string, ignoreFuncBodies bool) {
+func runbench(b *testing.B, path string, ignoreFuncBodies, writeInfo bool) {
fset := token.NewFileSet()
- files, err := pkgFiles(fset, path)
+ files, err := pkgFiles(fset, path, 0)
if err != nil {
- t.Fatal(err)
+ b.Fatal(err)
}
-
- b := testing.Benchmark(func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- conf := Config{IgnoreFuncBodies: ignoreFuncBodies}
- conf.Check(path, fset, files, nil)
- }
- })
-
// determine line count
lines := 0
fset.Iterate(func(f *token.File) bool {
@@ -76,14 +76,33 @@
return true
})
- d := time.Duration(b.NsPerOp())
- fmt.Printf(
- "%s: %s for %d lines (%d lines/s), ignoreFuncBodies = %v\n",
- filepath.Base(path), d, lines, int64(float64(lines)/d.Seconds()), ignoreFuncBodies,
- )
+ b.ResetTimer()
+ start := time.Now()
+ for i := 0; i < b.N; i++ {
+ conf := Config{
+ IgnoreFuncBodies: ignoreFuncBodies,
+ Importer: importer.Default(),
+ }
+ var info *Info
+ if writeInfo {
+ info = &Info{
+ Types: make(map[ast.Expr]TypeAndValue),
+ Defs: make(map[*ast.Ident]Object),
+ Uses: make(map[*ast.Ident]Object),
+ Implicits: make(map[ast.Node]Object),
+ Selections: make(map[*ast.SelectorExpr]*Selection),
+ Scopes: make(map[ast.Node]*Scope),
+ }
+ }
+ if _, err := conf.Check(path, fset, files, info); err != nil {
+ b.Fatal(err)
+ }
+ }
+ b.StopTimer()
+ b.ReportMetric(float64(lines)*float64(b.N)/time.Since(start).Seconds(), "lines/s")
}
-func pkgFiles(fset *token.FileSet, path string) ([]*ast.File, error) {
+func pkgFiles(fset *token.FileSet, path string, mode parser.Mode) ([]*ast.File, error) {
filenames, err := pkgFilenames(path) // from stdlib_test.go
if err != nil {
return nil, err
@@ -91,7 +110,7 @@
var files []*ast.File
for _, filename := range filenames {
- file, err := parser.ParseFile(fset, filename, nil, 0)
+ file, err := parser.ParseFile(fset, filename, nil, mode)
if err != nil {
return nil, err
}
diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go
index 049d546..7a4a0ea 100644
--- a/src/go/types/stdlib_test.go
+++ b/src/go/types/stdlib_test.go
@@ -156,6 +156,9 @@
testTestDir(t, filepath.Join(runtime.GOROOT(), "test"),
"cmplxdivide.go", // also needs file cmplxdivide1.go - ignore
"directive.go", // tests compiler rejection of bad directive placement - ignore
+ "embedfunc.go", // tests //go:embed
+ "embedvers.go", // tests //go:embed
+ "linkname2.go", // go/types doesn't check validity of //go:xxx directives
)
}
@@ -183,7 +186,8 @@
"issue31747.go", // go/types does not have constraints on language level (-lang=go1.12) (see #31793)
"issue34329.go", // go/types does not have constraints on language level (-lang=go1.13) (see #31793)
"bug251.go", // issue #34333 which was exposed with fix for #34151
- "bug299.go", // go/types permits parenthesized embedded fields (TODO(gri) remove once we disallow this again because we switched to []'s)
+ "issue42058a.go", // go/types does not have constraints on channel element size
+ "issue42058b.go", // go/types does not have constraints on channel element size
)
}
diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go
index 1102fb6..82c21c2 100644
--- a/src/go/types/stmt.go
+++ b/src/go/types/stmt.go
@@ -14,7 +14,7 @@
)
func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt, iota constant.Value) {
- if check.conf.Trace {
+ if trace {
check.trace(body.Pos(), "--- %s: %s", name, sig)
defer func() {
check.trace(body.End(), "--- <end>")
@@ -46,7 +46,7 @@
}
if sig.results.Len() > 0 && !check.isTerminating(body, "") {
- check.error(body.Rbrace, "missing return")
+ check.error(atPos(body.Rbrace), _MissingReturn, "missing return")
}
// TODO(gri) Should we make it an error to declare generic functions
@@ -70,7 +70,7 @@
return unused[i].pos < unused[j].pos
})
for _, v := range unused {
- check.softErrorf(v.pos, "%s declared but not used", v.name)
+ check.softErrorf(v, _UnusedVar, "%s declared but not used", v.name)
}
for _, scope := range scope.children {
@@ -140,11 +140,11 @@
d = s
}
default:
- check.invalidAST(s.Pos(), "case/communication clause expected")
+ check.invalidAST(s, "case/communication clause expected")
}
if d != nil {
if first != nil {
- check.errorf(d.Pos(), "multiple defaults (first at %s)", check.fset.Position(first.Pos()))
+ check.errorf(d, _DuplicateDefault, "multiple defaults (first at %s)", check.fset.Position(first.Pos()))
} else {
first = d
}
@@ -173,17 +173,23 @@
func (check *Checker) suspendedCall(keyword string, call *ast.CallExpr) {
var x operand
var msg string
+ var code errorCode
switch check.rawExpr(&x, call, nil) {
case conversion:
msg = "requires function call, not conversion"
+ code = _InvalidDefer
+ if keyword == "go" {
+ code = _InvalidGo
+ }
case expression:
msg = "discards result of"
+ code = _UnusedResults
case statement:
return
default:
unreachable()
}
- check.errorf(x.pos(), "%s %s %s", keyword, msg, &x)
+ check.errorf(&x, code, "%s %s %s", keyword, msg, &x)
}
// goVal returns the Go value for val, or nil.
@@ -255,17 +261,17 @@
// (quadratic algorithm, but these lists tend to be very short)
for _, vt := range seen[val] {
if check.identical(v.typ, vt.typ) {
- check.errorf(v.pos(), "duplicate case %s in expression switch", &v)
- check.error(vt.pos, "\tprevious case") // secondary error, \t indented
+ check.errorf(&v, _DuplicateCase, "duplicate case %s in expression switch", &v)
+ check.error(atPos(vt.pos), _DuplicateCase, "\tprevious case") // secondary error, \t indented
continue L
}
}
- seen[val] = append(seen[val], valueType{v.pos(), v.typ})
+ seen[val] = append(seen[val], valueType{v.Pos(), v.typ})
}
}
}
-func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]token.Pos, strict bool) (T Type) {
+func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]ast.Expr) (T Type) {
L:
for _, e := range types {
T = check.typeOrNil(e)
@@ -273,25 +279,25 @@
continue L
}
if T != nil {
- check.ordinaryType(e.Pos(), T)
+ check.ordinaryType(e, T)
}
// look for duplicate types
// (quadratic algorithm, but type switches tend to be reasonably small)
- for t, pos := range seen {
+ for t, other := range seen {
if T == nil && t == nil || T != nil && t != nil && check.identical(T, t) {
// talk about "case" rather than "type" because of nil case
Ts := "nil"
if T != nil {
Ts = T.String()
}
- check.errorf(e.Pos(), "duplicate case %s in type switch", Ts)
- check.error(pos, "\tprevious case") // secondary error, \t indented
+ check.errorf(e, _DuplicateCase, "duplicate case %s in type switch", Ts)
+ check.error(other, _DuplicateCase, "\tprevious case") // secondary error, \t indented
continue L
}
}
- seen[T] = e.Pos()
+ seen[T] = e
if T != nil {
- check.typeAssertion(e.Pos(), x, xtyp, T, strict)
+ check.typeAssertion(e, x, xtyp, T)
}
}
return
@@ -332,18 +338,22 @@
var x operand
kind := check.rawExpr(&x, s.X, nil)
var msg string
+ var code errorCode
switch x.mode {
default:
if kind == statement {
return
}
msg = "is not used"
+ code = _UnusedExpr
case builtin:
msg = "must be called"
+ code = _UncalledBuiltin
case typexpr:
msg = "is not an expression"
+ code = _NotAnExpr
}
- check.errorf(x.pos(), "%s %s", &x, msg)
+ check.errorf(&x, code, "%s %s", &x, msg)
case *ast.SendStmt:
var ch, x operand
@@ -355,12 +365,12 @@
tch := asChan(ch.typ)
if tch == nil {
- check.invalidOp(s.Arrow, "cannot send to non-chan type %s", ch.typ)
+ check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-chan type %s", ch.typ)
return
}
if tch.dir == RecvOnly {
- check.invalidOp(s.Arrow, "cannot send to receive-only type %s", tch)
+ check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to receive-only type %s", tch)
return
}
@@ -374,7 +384,7 @@
case token.DEC:
op = token.SUB
default:
- check.invalidAST(s.TokPos, "unknown inc/dec operation %s", s.Tok)
+ check.invalidAST(inNode(s, s.TokPos), "unknown inc/dec operation %s", s.Tok)
return
}
@@ -384,12 +394,12 @@
return
}
if !isNumeric(x.typ) {
- check.invalidOp(s.X.Pos(), "%s%s (non-numeric type %s)", s.X, s.Tok, x.typ)
+ check.invalidOp(s.X, _NonNumericIncDec, "%s%s (non-numeric type %s)", s.X, s.Tok, x.typ)
return
}
Y := &ast.BasicLit{ValuePos: s.X.Pos(), Kind: token.INT, Value: "1"} // use x's position
- check.binary(&x, nil, s.X, Y, op)
+ check.binary(&x, nil, s.X, Y, op, s.TokPos)
if x.mode == invalid {
return
}
@@ -399,11 +409,11 @@
switch s.Tok {
case token.ASSIGN, token.DEFINE:
if len(s.Lhs) == 0 {
- check.invalidAST(s.Pos(), "missing lhs in assignment")
+ check.invalidAST(s, "missing lhs in assignment")
return
}
if s.Tok == token.DEFINE {
- check.shortVarDecl(s.TokPos, s.Lhs, s.Rhs)
+ check.shortVarDecl(inNode(s, s.TokPos), s.Lhs, s.Rhs)
} else {
// regular assignment
check.assignVars(s.Lhs, s.Rhs)
@@ -412,16 +422,16 @@
default:
// assignment operations
if len(s.Lhs) != 1 || len(s.Rhs) != 1 {
- check.errorf(s.TokPos, "assignment operation %s requires single-valued expressions", s.Tok)
+ check.errorf(inNode(s, s.TokPos), _MultiValAssignOp, "assignment operation %s requires single-valued expressions", s.Tok)
return
}
op := assignOp(s.Tok)
if op == token.ILLEGAL {
- check.invalidAST(s.TokPos, "unknown assignment operation %s", s.Tok)
+ check.invalidAST(atPos(s.TokPos), "unknown assignment operation %s", s.Tok)
return
}
var x operand
- check.binary(&x, nil, s.Lhs[0], s.Rhs[0], op)
+ check.binary(&x, nil, s.Lhs[0], s.Rhs[0], op, s.TokPos)
if x.mode == invalid {
return
}
@@ -445,8 +455,8 @@
// with the same name as a result parameter is in scope at the place of the return."
for _, obj := range res.vars {
if alt := check.lookup(obj.name); alt != nil && alt != obj {
- check.errorf(s.Pos(), "result parameter %s not in scope at return", obj.name)
- check.errorf(alt.Pos(), "\tinner declaration of %s", obj)
+ check.errorf(s, _OutOfScopeResult, "result parameter %s not in scope at return", obj.name)
+ check.errorf(alt, _OutOfScopeResult, "\tinner declaration of %s", obj)
// ok to continue
}
}
@@ -455,7 +465,7 @@
check.initVars(res.vars, s.Results, s.Return)
}
} else if len(s.Results) > 0 {
- check.error(s.Results[0].Pos(), "no result values expected")
+ check.error(s.Results[0], _WrongResultCount, "no result values expected")
check.use(s.Results...)
}
@@ -467,22 +477,23 @@
switch s.Tok {
case token.BREAK:
if ctxt&breakOk == 0 {
- check.error(s.Pos(), "break not in for, switch, or select statement")
+ check.error(s, _MisplacedBreak, "break not in for, switch, or select statement")
}
case token.CONTINUE:
if ctxt&continueOk == 0 {
- check.error(s.Pos(), "continue not in for statement")
+ check.error(s, _MisplacedContinue, "continue not in for statement")
}
case token.FALLTHROUGH:
if ctxt&fallthroughOk == 0 {
msg := "fallthrough statement out of place"
+ code := _MisplacedFallthrough
if ctxt&finalSwitchCase != 0 {
msg = "cannot fallthrough final case in switch"
}
- check.error(s.Pos(), msg)
+ check.error(s, code, msg)
}
default:
- check.invalidAST(s.Pos(), "branch statement: %s", s.Tok)
+ check.invalidAST(s, "branch statement: %s", s.Tok)
}
case *ast.BlockStmt:
@@ -499,7 +510,7 @@
var x operand
check.expr(&x, s.Cond)
if x.mode != invalid && !isBoolean(x.typ) {
- check.error(s.Cond.Pos(), "non-boolean condition in if statement")
+ check.error(s.Cond, _InvalidCond, "non-boolean condition in if statement")
}
check.stmt(inner, s.Body)
// The parser produces a correct AST but if it was modified
@@ -510,7 +521,7 @@
case *ast.IfStmt, *ast.BlockStmt:
check.stmt(inner, s.Else)
default:
- check.error(s.Else.Pos(), "invalid else branch in if statement")
+ check.invalidAST(s.Else, "invalid else branch in if statement")
}
case *ast.SwitchStmt:
@@ -525,6 +536,10 @@
// By checking assignment of x to an invisible temporary
// (as a compiler would), we get all the relevant checks.
check.assignment(&x, nil, "switch expression")
+ if x.mode != invalid && !Comparable(x.typ) && !hasNil(x.typ) {
+ check.errorf(&x, _InvalidExprSwitch, "cannot switch on %s (%s is not comparable)", &x, x.typ)
+ x.mode = invalid
+ }
} else {
// spec: "A missing switch expression is
// equivalent to the boolean value true."
@@ -540,7 +555,7 @@
for i, c := range s.Body.List {
clause, _ := c.(*ast.CaseClause)
if clause == nil {
- check.invalidAST(c.Pos(), "incorrect expression switch case")
+ check.invalidAST(c, "incorrect expression switch case")
continue
}
check.caseValues(&x, clause.List, seen)
@@ -577,19 +592,19 @@
rhs = guard.X
case *ast.AssignStmt:
if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 {
- check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ check.invalidAST(s, "incorrect form of type switch guard")
return
}
lhs, _ = guard.Lhs[0].(*ast.Ident)
if lhs == nil {
- check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ check.invalidAST(s, "incorrect form of type switch guard")
return
}
if lhs.Name == "_" {
// _ := x.(type) is an invalid short variable declaration
- check.softErrorf(lhs.Pos(), "no new variable on left side of :=")
+ check.softErrorf(lhs, _NoNewVar, "no new variable on left side of :=")
lhs = nil // avoid declared but not used error below
} else {
check.recordDef(lhs, nil) // lhs variable is implicitly declared in each cause clause
@@ -598,14 +613,14 @@
rhs = guard.Rhs[0]
default:
- check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ check.invalidAST(s, "incorrect form of type switch guard")
return
}
// rhs must be of the form: expr.(type) and expr must be an ordinary interface
expr, _ := rhs.(*ast.TypeAssertExpr)
if expr == nil || expr.Type != nil {
- check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ check.invalidAST(s, "incorrect form of type switch guard")
return
}
var x operand
@@ -615,23 +630,23 @@
}
xtyp, _ := under(x.typ).(*Interface)
if xtyp == nil {
- check.errorf(x.pos(), "%s is not an interface type", &x)
+ check.errorf(&x, _InvalidTypeSwitch, "%s is not an interface", &x)
return
}
- check.ordinaryType(x.pos(), xtyp)
+ check.ordinaryType(&x, xtyp)
check.multipleDefaults(s.Body.List)
- var lhsVars []*Var // list of implicitly declared lhs variables
- seen := make(map[Type]token.Pos) // map of seen types to positions
+ var lhsVars []*Var // list of implicitly declared lhs variables
+ seen := make(map[Type]ast.Expr) // map of seen types to positions
for _, s := range s.Body.List {
clause, _ := s.(*ast.CaseClause)
if clause == nil {
- check.invalidAST(s.Pos(), "incorrect type switch case")
+ check.invalidAST(s, "incorrect type switch case")
continue
}
// Check each type in this type switch case.
- T := check.caseTypes(&x, xtyp, clause.List, seen, false)
+ T := check.caseTypes(&x, xtyp, clause.List, seen)
check.openScope(clause, "case")
// If lhs exists, declare a corresponding variable in the case-local scope.
if lhs != nil {
@@ -669,7 +684,7 @@
v.used = true // avoid usage error when checking entire function
}
if !used {
- check.softErrorf(lhs.Pos(), "%s declared but not used", lhs.Name)
+ check.softErrorf(lhs, _UnusedVar, "%s declared but not used", lhs.Name)
}
}
@@ -706,7 +721,7 @@
}
if !valid {
- check.error(clause.Comm.Pos(), "select case must be send or receive (possibly with assignment)")
+ check.error(clause.Comm, _InvalidSelectCase, "select case must be send or receive (possibly with assignment)")
continue
}
@@ -728,14 +743,14 @@
var x operand
check.expr(&x, s.Cond)
if x.mode != invalid && !isBoolean(x.typ) {
- check.error(s.Cond.Pos(), "non-boolean condition in for statement")
+ check.error(s.Cond, _InvalidCond, "non-boolean condition in for statement")
}
}
check.simpleStmt(s.Post)
// spec: "The init statement may be a short variable
// declaration, but the post statement must not."
if s, _ := s.Post.(*ast.AssignStmt); s != nil && s.Tok == token.DEFINE {
- check.softErrorf(s.Pos(), "cannot declare in post statement")
+ check.softErrorf(s, _InvalidPostDecl, "cannot declare in post statement")
// Don't call useLHS here because we want to use the lhs in
// this erroneous statement so that we don't get errors about
// these lhs variables being declared but not used.
@@ -758,16 +773,17 @@
typ := optype(x.typ)
if _, ok := typ.(*Chan); ok && s.Value != nil {
// TODO(gri) this also needs to happen for channels in generic variables
- check.softErrorf(s.Value.Pos(), "range over %s permits only one iteration variable", &x)
+ check.softErrorf(atPos(s.Value.Pos()), _InvalidIterVar, "range over %s permits only one iteration variable", &x)
// ok to continue
}
var msg string
key, val, msg = rangeKeyVal(typ, isVarName(s.Key), isVarName(s.Value))
if key == nil || msg != "" {
if msg != "" {
+ // TODO(rFindley) should this be parenthesized, to be consistent with other qualifiers?
msg = ": " + msg
}
- check.softErrorf(x.pos(), "cannot range over %s%s", &x, msg)
+ check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s%s", &x, msg)
// ok to continue
}
}
@@ -801,7 +817,7 @@
vars = append(vars, obj)
}
} else {
- check.errorf(lhs.Pos(), "cannot declare %s", lhs)
+ check.invalidAST(lhs, "cannot declare %s", lhs)
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
}
@@ -828,7 +844,7 @@
check.declare(check.scope, nil /* recordDef already called */, obj, scopePos)
}
} else {
- check.error(s.TokPos, "no new variables on left side of :=")
+ check.error(inNode(s, s.TokPos), _NoNewVar, "no new variables on left side of :=")
}
} else {
// ordinary assignment
@@ -848,7 +864,7 @@
check.stmt(inner, s.Body)
default:
- check.error(s.Pos(), "invalid statement")
+ check.invalidAST(s, "invalid statement")
}
}
diff --git a/src/go/types/subst.go b/src/go/types/subst.go
index 00f60dc..ed27488 100644
--- a/src/go/types/subst.go
+++ b/src/go/types/subst.go
@@ -14,6 +14,9 @@
"go/token"
)
+// TODO(rFindley) decide error codes for the errors in this file, and check
+// if error spans can be improved
+
type substMap struct {
// The targs field is currently needed for *Named type substitution.
// TODO(gri) rewrite that code, get rid of this field, and make this
@@ -28,7 +31,7 @@
assert(len(tpars) == len(targs))
proj := make(map[*TypeParam]Type, len(tpars))
for i, tpar := range tpars {
- // We must expand type arguments otherwise *Instance
+ // We must expand type arguments otherwise *instance
// types end up as components in composite types.
// TODO(gri) explain why this causes problems, if it does
targ := expand(targs[i]) // possibly nil
@@ -54,7 +57,7 @@
}
func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist []token.Pos) (res Type) {
- if check.conf.Trace {
+ if trace {
check.trace(pos, "-- instantiating %s with %s", typ, typeListString(targs))
check.indent++
defer func() {
@@ -70,7 +73,7 @@
}()
}
- assert(poslist == nil || len(poslist) <= len(targs))
+ assert(len(poslist) <= len(targs))
// TODO(gri) What is better here: work with TypeParams, or work with TypeNames?
var tparams []*TypeName
@@ -80,9 +83,10 @@
case *Signature:
tparams = t.tparams
defer func() {
- // If we had an unexpected failure somewhere don't
- // panic below when asserting res.(*Signature).
- if res == nil {
+ // If we had an unexpected failure somewhere don't panic below when
+ // asserting res.(*Signature). Check for *Signature in case Typ[Invalid]
+ // is returned.
+ if _, ok := res.(*Signature); !ok {
return
}
// If the signature doesn't use its type parameters, subst
@@ -100,13 +104,12 @@
default:
check.dump("%v: cannot instantiate %v", pos, typ)
unreachable() // only defined types and (defined) functions can be generic
-
}
// the number of supplied types must match the number of type parameters
if len(targs) != len(tparams) {
// TODO(gri) provide better error message
- check.errorf(pos, "got %d arguments but %d type parameters", len(targs), len(tparams))
+ check.errorf(atPos(pos), _Todo, "got %d arguments but %d type parameters", len(targs), len(tparams))
return Typ[Invalid]
}
@@ -142,42 +145,31 @@
// - check only if we have methods
check.completeInterface(token.NoPos, iface)
if len(iface.allMethods) > 0 {
- // If the type argument is a type parameter itself, its pointer designation
- // must match the pointer designation of the callee's type parameter.
// If the type argument is a pointer to a type parameter, the type argument's
// method set is empty.
// TODO(gri) is this what we want? (spec question)
- if tparg := asTypeParam(targ); tparg != nil {
- if tparg.ptr != tpar.ptr {
- check.errorf(pos, "pointer designation mismatch")
- break
- }
- } else if base, isPtr := deref(targ); isPtr && asTypeParam(base) != nil {
- check.errorf(pos, "%s has no methods", targ)
+ if base, isPtr := deref(targ); isPtr && asTypeParam(base) != nil {
+ check.errorf(atPos(pos), 0, "%s has no methods", targ)
break
}
- // If a type parameter is marked as a pointer type, the type bound applies
- // to a pointer of the type argument.
- actual := targ
- if tpar.ptr {
- actual = NewPointer(targ)
- }
- if m, wrong := check.missingMethod(actual, iface, true); m != nil {
+ if m, wrong := check.missingMethod(targ, iface, true); m != nil {
// TODO(gri) needs to print updated name to avoid major confusion in error message!
// (print warning for now)
+ // Old warning:
// check.softErrorf(pos, "%s does not satisfy %s (warning: name not updated) = %s (missing method %s)", targ, tpar.bound, iface, m)
if m.name == "==" {
// We don't want to report "missing method ==".
- check.softErrorf(pos, "%s does not satisfy comparable", targ)
+ check.softErrorf(atPos(pos), 0, "%s does not satisfy comparable", targ)
} else if wrong != nil {
// TODO(gri) This can still report uninstantiated types which makes the error message
// more difficult to read then necessary.
- check.softErrorf(pos,
+ // TODO(rFindley) should this use parentheses rather than ':' for qualification?
+ check.softErrorf(atPos(pos), _Todo,
"%s does not satisfy %s: wrong method signature\n\tgot %s\n\twant %s",
targ, tpar.bound, wrong, m,
)
} else {
- check.softErrorf(pos, "%s does not satisfy %s (missing method %s)", targ, tpar.bound, m.name)
+ check.softErrorf(atPos(pos), 0, "%s does not satisfy %s (missing method %s)", targ, tpar.bound, m.name)
}
break
}
@@ -187,20 +179,19 @@
if iface.allTypes == nil {
continue // nothing to do
}
- // iface.allTypes != nil
// If targ is itself a type parameter, each of its possible types, but at least one, must be in the
// list of iface types (i.e., the targ type list must be a non-empty subset of the iface types).
if targ := asTypeParam(targ); targ != nil {
targBound := targ.Bound()
if targBound.allTypes == nil {
- check.softErrorf(pos, "%s does not satisfy %s (%s has no type constraints)", targ, tpar.bound, targ)
+ check.softErrorf(atPos(pos), _Todo, "%s does not satisfy %s (%s has no type constraints)", targ, tpar.bound, targ)
break
}
- for _, t := range unpack(targBound.allTypes) {
+ for _, t := range unpackType(targBound.allTypes) {
if !iface.isSatisfiedBy(t) {
// TODO(gri) match this error message with the one below (or vice versa)
- check.softErrorf(pos, "%s does not satisfy %s (%s type constraint %s not found in %s)", targ, tpar.bound, targ, t, iface.allTypes)
+ check.softErrorf(atPos(pos), 0, "%s does not satisfy %s (%s type constraint %s not found in %s)", targ, tpar.bound, targ, t, iface.allTypes)
break
}
}
@@ -209,7 +200,7 @@
// Otherwise, targ's type or underlying type must also be one of the interface types listed, if any.
if !iface.isSatisfiedBy(targ) {
- check.softErrorf(pos, "%s does not satisfy %s (%s or %s not found in %s)", targ, tpar.bound, targ, under(targ), iface.allTypes)
+ check.softErrorf(atPos(pos), _Todo, "%s does not satisfy %s (%s or %s not found in %s)", targ, tpar.bound, targ, under(targ), iface.allTypes)
break
}
}
@@ -290,7 +281,9 @@
results := subst.tuple(t.results)
if recv != t.recv || params != t.params || results != t.results {
return &Signature{
- rparams: t.rparams,
+ rparams: t.rparams,
+ // TODO(rFindley) why can't we nil out tparams here, rather than in
+ // instantiate above?
tparams: t.tparams,
scope: t.scope,
recv: recv,
@@ -342,7 +335,7 @@
subst.check.indent--
}()
dump := func(format string, args ...interface{}) {
- if subst.check.conf.Trace {
+ if trace {
subst.check.trace(subst.pos, format, args...)
}
}
@@ -352,10 +345,9 @@
return t // type is not parameterized
}
- var new_targs []Type
+ var newTargs []Type
if len(t.targs) > 0 {
-
// already instantiated
dump(">>> %s already instantiated", t)
assert(len(t.targs) == len(t.tparams))
@@ -364,32 +356,30 @@
// that has a type argument for it.
for i, targ := range t.targs {
dump(">>> %d targ = %s", i, targ)
- new_targ := subst.typ(targ)
- if new_targ != targ {
- dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
- if new_targs == nil {
- new_targs = make([]Type, len(t.tparams))
- copy(new_targs, t.targs)
+ newTarg := subst.typ(targ)
+ if newTarg != targ {
+ dump(">>> substituted %d targ %s => %s", i, targ, newTarg)
+ if newTargs == nil {
+ newTargs = make([]Type, len(t.tparams))
+ copy(newTargs, t.targs)
}
- new_targs[i] = new_targ
+ newTargs[i] = newTarg
}
}
- if new_targs == nil {
+ if newTargs == nil {
dump(">>> nothing to substitute in %s", t)
return t // nothing to substitute
}
-
} else {
-
// not yet instantiated
dump(">>> first instantiation of %s", t)
- new_targs = subst.smap.targs
-
+ // TODO(rFindley) can we instead subst the tparam types here?
+ newTargs = subst.smap.targs
}
// before creating a new named type, check if we have this one already
- h := instantiatedHash(t, new_targs)
+ h := instantiatedHash(t, newTargs)
dump(">>> new type hash: %s", h)
if named, found := subst.check.typMap[h]; found {
dump(">>> found %s", named)
@@ -401,12 +391,12 @@
tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil)
named := subst.check.NewNamed(tname, t.underlying, t.methods) // method signatures are updated lazily
named.tparams = t.tparams // new type is still parameterized
- named.targs = new_targs
+ named.targs = newTargs
subst.check.typMap[h] = named
subst.cache[t] = named
// do the substitution
- dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, new_targs)
+ dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, newTargs)
named.underlying = subst.typOrNil(t.underlying)
named.orig = named.underlying // for cycle detection (Checker.validType)
@@ -431,9 +421,9 @@
func instantiatedHash(typ *Named, targs []Type) string {
var buf bytes.Buffer
writeTypeName(&buf, typ.obj, nil)
- buf.WriteByte('(')
+ buf.WriteByte('[')
writeTypeList(&buf, targs, nil, nil)
- buf.WriteByte(')')
+ buf.WriteByte(']')
// With respect to the represented type, whether a
// type is fully expanded or stored as instance
diff --git a/src/go/types/testdata/builtins.src b/src/go/types/testdata/builtins.src
index 69cc487..3c273b0 100644
--- a/src/go/types/testdata/builtins.src
+++ b/src/go/types/testdata/builtins.src
@@ -25,19 +25,19 @@
_ = append(s, b)
_ = append(s, x /* ERROR cannot use x */ )
_ = append(s, s /* ERROR cannot use s */ )
- _ = append(s... ) /* ERROR not enough arguments */
- _ = append(s, b, s /* ERROR too many arguments */ ... )
+ _ = append(s...) /* ERROR not enough arguments */
+ _ = append(s, b, s /* ERROR too many arguments */ ...)
_ = append(s, 1, 2, 3)
_ = append(s, 1, 2, 3, x /* ERROR cannot use x */ , 5, 6, 6)
- _ = append(s, 1, 2 /* ERROR too many arguments */ , s... )
+ _ = append(s, 1, 2 /* ERROR too many arguments */, s...)
_ = append([]interface{}(nil), 1, 2, "foo", x, 3.1425, false)
type S []byte
type T string
var t T
- _ = append(s, "foo" /* ERROR cannot convert */ )
+ _ = append(s, "foo" /* ERROR cannot use .* in argument to append */ )
_ = append(s, "foo"...)
- _ = append(S(s), "foo" /* ERROR cannot convert */ )
+ _ = append(S(s), "foo" /* ERROR cannot use .* in argument to append */ )
_ = append(S(s), "foo"...)
_ = append(s, t /* ERROR cannot use t */ )
_ = append(s, t...)
@@ -482,7 +482,7 @@
}
func make2() {
- f1 := func() (x []int) { return }
+ f1 /* ERROR not used */ := func() (x []int) { return }
_ = make(f0 /* ERROR not a type */ ())
_ = make(f1 /* ERROR not a type */ ())
}
@@ -502,7 +502,7 @@
}
func new2() {
- f1 := func() (x []int) { return }
+ f1 /* ERROR not used */ := func() (x []int) { return }
_ = new(f0 /* ERROR not a type */ ())
_ = new(f1 /* ERROR not a type */ ())
}
diff --git a/src/go/types/testdata/constdecl.src b/src/go/types/testdata/constdecl.src
index c2f40ed..680c85a 100644
--- a/src/go/types/testdata/constdecl.src
+++ b/src/go/types/testdata/constdecl.src
@@ -107,4 +107,35 @@
const x, y, z = 0, 1, unsafe.Sizeof(func() { _ = x /* ERROR "undeclared name" */ + y /* ERROR "undeclared name" */ + z /* ERROR "undeclared name" */ })
}
+// Test cases for errors in inherited constant initialization expressions.
+// Errors related to inherited initialization expressions must appear at
+// the constant identifier being declared, not at the original expression
+// (issues #42991, #42992).
+const (
+ _ byte = 255 + iota
+ /* some gap */
+ _ // ERROR overflows
+ /* some gap */
+ /* some gap */ _ /* ERROR overflows */; _ /* ERROR overflows */
+ /* some gap */
+ _ = 255 + iota
+ _ = byte /* ERROR overflows */ (255) + iota
+ _ /* ERROR overflows */
+)
+
+// Test cases from issue.
+const (
+ ok = byte(iota + 253)
+ bad
+ barn
+ bard // ERROR cannot convert
+)
+
+const (
+ c = len([1 - iota]int{})
+ d
+ e // ERROR invalid array length
+ f // ERROR invalid array length
+)
+
// TODO(gri) move extra tests from testdata/const0.src into here
diff --git a/src/go/types/testdata/cycles.src b/src/go/types/testdata/cycles.src
index b2ee8ec..218b4ca 100644
--- a/src/go/types/testdata/cycles.src
+++ b/src/go/types/testdata/cycles.src
@@ -65,7 +65,7 @@
I6 interface{ I5 }
// maps
- M0 map[M0 /* ERROR invalid map key */ ]M0
+ M0 map[M0 /* ERROR incomparable map key */ ]M0
// channels
C0 chan C0
@@ -114,7 +114,7 @@
i0 /* ERROR cycle */ interface{ i0 }
// maps
- m0 map[m0 /* ERROR invalid map key */ ]m0
+ m0 map[m0 /* ERROR incomparable map key */ ]m0
// channels
c0 chan c0
@@ -123,10 +123,10 @@
// test cases for issue 6667
-type A [10]map[A /* ERROR invalid map key */ ]bool
+type A [10]map[A /* ERROR incomparable map key */ ]bool
type S struct {
- m map[S /* ERROR invalid map key */ ]bool
+ m map[S /* ERROR incomparable map key */ ]bool
}
// test cases for issue 7236
diff --git a/src/go/types/testdata/cycles2.src b/src/go/types/testdata/cycles2.src
index 5fd9e83..1a7f40a 100644
--- a/src/go/types/testdata/cycles2.src
+++ b/src/go/types/testdata/cycles2.src
@@ -37,7 +37,7 @@
}
type B interface {
- a() interface {
+ b() interface {
AB
}
}
@@ -59,8 +59,7 @@
B
}
-// TODO(gri) This should be a valid compare. See #33656.
-var _ = x /* ERROR cannot compare */ == y
+var _ = x == y
// Test case for issue 6638.
diff --git a/src/go/types/testdata/decls0.src b/src/go/types/testdata/decls0.src
index 917cc78..5ad8f53 100644
--- a/src/go/types/testdata/decls0.src
+++ b/src/go/types/testdata/decls0.src
@@ -184,11 +184,13 @@
func f2(x *f2 /* ERROR "not a type" */ ) {}
func f3() (x f3 /* ERROR "not a type" */ ) { return }
func f4() (x *f4 /* ERROR "not a type" */ ) { return }
+// TODO(#43215) this should be detected as a cycle error
+func f5([unsafe.Sizeof(f5)]int) {}
-func (S0) m1(x S0 /* ERROR value .* is not a type */ .m1) {}
-func (S0) m2(x *S0 /* ERROR value .* is not a type */ .m2) {}
-func (S0) m3() (x S0 /* ERROR value .* is not a type */ .m3) { return }
-func (S0) m4() (x *S0 /* ERROR value .* is not a type */ .m4) { return }
+func (S0) m1 (x S0 /* ERROR value .* is not a type */ .m1) {}
+func (S0) m2 (x *S0 /* ERROR value .* is not a type */ .m2) {}
+func (S0) m3 () (x S0 /* ERROR value .* is not a type */ .m3) { return }
+func (S0) m4 () (x *S0 /* ERROR value .* is not a type */ .m4) { return }
// interfaces may not have any blank methods
type BlankI interface {
diff --git a/src/go/types/testdata/decls1.src b/src/go/types/testdata/decls1.src
index e6beb78..f4d2eab 100644
--- a/src/go/types/testdata/decls1.src
+++ b/src/go/types/testdata/decls1.src
@@ -96,6 +96,8 @@
v11 = xx/yy*yy - xx
v12 = true && false
v13 = nil /* ERROR "use of untyped nil" */
+ v14 string = 257 // ERROR cannot use 257 .* as string value in variable declaration$
+ v15 int8 = 257 // ERROR cannot use 257 .* as int8 value in variable declaration .*overflows
)
// Multiple assignment expressions
diff --git a/src/go/types/testdata/decls2/decls2b.src b/src/go/types/testdata/decls2/decls2b.src
index 8e82c6d..5c55750 100644
--- a/src/go/types/testdata/decls2/decls2b.src
+++ b/src/go/types/testdata/decls2/decls2b.src
@@ -40,17 +40,17 @@
// Verify by checking that errors are reported.
func (T /* ERROR "undeclared" */ ) _() {}
func (T1) _(undeclared /* ERROR "undeclared" */ ) {}
-func (T1) _() int { return "foo" /* ERROR "cannot convert" */ }
+func (T1) _() int { return "foo" /* ERROR "cannot use .* in return statement" */ }
// Methods with undeclared receiver type can still be checked.
// Verify by checking that errors are reported.
func (Foo /* ERROR "undeclared" */ ) m() {}
func (Foo /* ERROR "undeclared" */ ) m(undeclared /* ERROR "undeclared" */ ) {}
-func (Foo /* ERROR "undeclared" */ ) m() int { return "foo" /* ERROR "cannot convert" */ }
+func (Foo /* ERROR "undeclared" */ ) m() int { return "foo" /* ERROR "cannot use .* in return statement" */ }
func (Foo /* ERROR "undeclared" */ ) _() {}
func (Foo /* ERROR "undeclared" */ ) _(undeclared /* ERROR "undeclared" */ ) {}
-func (Foo /* ERROR "undeclared" */ ) _() int { return "foo" /* ERROR "cannot convert" */ }
+func (Foo /* ERROR "undeclared" */ ) _() int { return "foo" /* ERROR "cannot use .* in return statement" */ }
// Receiver declarations are regular parameter lists;
// receiver types may use parentheses, and the list
@@ -72,4 +72,4 @@
_ = (*T7).m4
_ = (*T7).m5
_ = (*T7).m6
-)
\ No newline at end of file
+)
diff --git a/src/go/types/testdata/expr3.src b/src/go/types/testdata/expr3.src
index 1aa21b6..c3158e6 100644
--- a/src/go/types/testdata/expr3.src
+++ b/src/go/types/testdata/expr3.src
@@ -7,9 +7,8 @@
import "time"
func indexes() {
- var x int
_ = 1 /* ERROR "cannot index" */ [0]
- _ = x /* ERROR "cannot index" */ [0]
+ _ = indexes /* ERROR "cannot index" */ [0]
_ = ( /* ERROR "cannot slice" */ 12 + 3)[1:2]
var a [10]int
@@ -44,9 +43,9 @@
_ = a[:10:10]
_ = a[:11 /* ERROR "index .* out of bounds" */ :10]
_ = a[:10:11 /* ERROR "index .* out of bounds" */ ]
- _ = a[10:0:10] /* ERROR "invalid slice indices" */
- _ = a[0:10:0] /* ERROR "invalid slice indices" */
- _ = a[10:0:0] /* ERROR "invalid slice indices" */
+ _ = a[10:0:10] /* ERROR swapped slice indices" */
+ _ = a[0:10:0] /* ERROR "swapped slice indices" */
+ _ = a[10:0:0] /* ERROR "swapped slice indices" */
_ = &a /* ERROR "cannot take address" */ [:10]
pa := &a
@@ -62,9 +61,9 @@
_ = pa[:10:10]
_ = pa[:11 /* ERROR "index .* out of bounds" */ :10]
_ = pa[:10:11 /* ERROR "index .* out of bounds" */ ]
- _ = pa[10:0:10] /* ERROR "invalid slice indices" */
- _ = pa[0:10:0] /* ERROR "invalid slice indices" */
- _ = pa[10:0:0] /* ERROR "invalid slice indices" */
+ _ = pa[10:0:10] /* ERROR "swapped slice indices" */
+ _ = pa[0:10:0] /* ERROR "swapped slice indices" */
+ _ = pa[10:0:0] /* ERROR "swapped slice indices" */
_ = &pa /* ERROR "cannot take address" */ [:10]
var b [0]int
@@ -82,20 +81,20 @@
_ = s[: - /* ERROR "negative" */ 1]
_ = s[0]
_ = s[1:2]
- _ = s[2:1] /* ERROR "invalid slice indices" */
+ _ = s[2:1] /* ERROR "swapped slice indices" */
_ = s[2:]
_ = s[: 1 /* ERROR "overflows" */ <<100]
_ = s[1 /* ERROR "overflows" */ <<100 :]
_ = s[1 /* ERROR "overflows" */ <<100 : 1 /* ERROR "overflows" */ <<100]
_ = s[: /* ERROR "2nd index required" */ : /* ERROR "3rd index required" */ ]
_ = s[:10:10]
- _ = s[10:0:10] /* ERROR "invalid slice indices" */
- _ = s[0:10:0] /* ERROR "invalid slice indices" */
- _ = s[10:0:0] /* ERROR "invalid slice indices" */
+ _ = s[10:0:10] /* ERROR "swapped slice indices" */
+ _ = s[0:10:0] /* ERROR "swapped slice indices" */
+ _ = s[10:0:0] /* ERROR "swapped slice indices" */
_ = &s /* ERROR "cannot take address" */ [:10]
var m map[string]int
- _ = m[0 /* ERROR "cannot convert" */ ]
+ _ = m[0 /* ERROR "cannot use .* in map index" */ ]
_ = m /* ERROR "cannot slice" */ ["foo" : "bar"]
_ = m["foo"]
// ok is of type bool
@@ -103,7 +102,7 @@
var ok mybool
_, ok = m["bar"]
_ = ok
- _ = m[0 /* ERROR "cannot convert 0" */ ] + "foo" // ERROR "cannot convert"
+ _ = m[0 /* ERROR "cannot use 0" */ ] + "foo" // ERROR "cannot convert"
var t string
_ = t[- /* ERROR "negative" */ 1]
@@ -186,7 +185,7 @@
_ = T1{aa /* ERROR "unknown field" */ : 0}
_ = T1{1 /* ERROR "invalid field name" */ : 0}
_ = T1{a: 0, s: "foo", u: 0, a /* ERROR "duplicate field" */: 10}
- _ = T1{a: "foo" /* ERROR "cannot convert" */ }
+ _ = T1{a: "foo" /* ERROR "cannot use .* in struct literal" */ }
_ = T1{c /* ERROR "unknown field" */ : 0}
_ = T1{T0: { /* ERROR "missing type" */ }} // struct literal element type may not be elided
_ = T1{T0: T0{}}
@@ -197,7 +196,7 @@
_ = T0{1, b /* ERROR "mixture" */ : 2, 3}
_ = T0{1, 2} /* ERROR "too few values" */
_ = T0{1, 2, 3, 4 /* ERROR "too many values" */ }
- _ = T0{1, "foo" /* ERROR "cannot convert" */, 3.4 /* ERROR "truncated" */}
+ _ = T0{1, "foo" /* ERROR "cannot use .* in struct literal" */, 3.4 /* ERROR "cannot use .*\(truncated\)" */}
// invalid type
type P *struct{
@@ -237,7 +236,7 @@
_ = A1{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4}
_ = A1{2.0}
_ = A1{2.1 /* ERROR "truncated" */ }
- _ = A1{"foo" /* ERROR "cannot convert" */ }
+ _ = A1{"foo" /* ERROR "cannot use .* in array or slice literal" */ }
// indices must be integer constants
i := 1
@@ -303,7 +302,7 @@
_ = S0{5: 5, 6, 7, 4: 4, 1 /* ERROR "overflows" */ <<100: 4}
_ = S0{2.0}
_ = S0{2.1 /* ERROR "truncated" */ }
- _ = S0{"foo" /* ERROR "cannot convert" */ }
+ _ = S0{"foo" /* ERROR "cannot use .* in array or slice literal" */ }
// indices must be resolved correctly
const index1 = 1
@@ -356,8 +355,8 @@
_ = M0{}
_ = M0{1 /* ERROR "missing key" */ }
- _ = M0{1 /* ERROR "cannot convert" */ : 2}
- _ = M0{"foo": "bar" /* ERROR "cannot convert" */ }
+ _ = M0{1 /* ERROR "cannot use .* in map literal" */ : 2}
+ _ = M0{"foo": "bar" /* ERROR "cannot use .* in map literal" */ }
_ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 }
_ = map[interface{}]int{2: 1, 2 /* ERROR "duplicate key" */ : 1}
@@ -520,7 +519,7 @@
fv(s /* ERROR "cannot use .* in argument" */ )
fv(s...)
fv(x /* ERROR "cannot use" */ ...)
- fv(1, s /* ERROR "too many arguments" */ ... )
+ fv(1, s /* ERROR "too many arguments" */ ...)
fv(gs /* ERROR "cannot use .* in argument" */ ())
fv(gs /* ERROR "cannot use .* in argument" */ ()...)
@@ -529,7 +528,7 @@
t.fm(1, 2.0, x)
t.fm(s /* ERROR "cannot use .* in argument" */ )
t.fm(g1())
- t.fm(1, s /* ERROR "too many arguments" */ ... )
+ t.fm(1, s /* ERROR "too many arguments" */ ...)
t.fm(gs /* ERROR "cannot use .* in argument" */ ())
t.fm(gs /* ERROR "cannot use .* in argument" */ ()...)
@@ -537,7 +536,7 @@
T.fm(t, 1, 2.0, x)
T.fm(t, s /* ERROR "cannot use .* in argument" */ )
T.fm(t, g1())
- T.fm(t, 1, s /* ERROR "too many arguments" */ ... )
+ T.fm(t, 1, s /* ERROR "too many arguments" */ ...)
T.fm(t, gs /* ERROR "cannot use .* in argument" */ ())
T.fm(t, gs /* ERROR "cannot use .* in argument" */ ()...)
@@ -546,7 +545,7 @@
i.fm(1, 2.0, x)
i.fm(s /* ERROR "cannot use .* in argument" */ )
i.fm(g1())
- i.fm(1, s /* ERROR "too many arguments" */ ... )
+ i.fm(1, s /* ERROR "too many arguments" */ ...)
i.fm(gs /* ERROR "cannot use .* in argument" */ ())
i.fm(gs /* ERROR "cannot use .* in argument" */ ()...)
diff --git a/src/go/types/testdata/issues.go2 b/src/go/types/testdata/issues.go2
index c77599f..ac2dee3 100644
--- a/src/go/types/testdata/issues.go2
+++ b/src/go/types/testdata/issues.go2
@@ -154,7 +154,7 @@
// Infinite generic type declarations must lead to an error.
type inf1[T any] struct{ _ inf1 /* ERROR illegal cycle */ [T] }
-type inf2[T any] struct{ (inf2 /* ERROR illegal cycle */ [T]) }
+type inf2[T any] struct{ inf2 /* ERROR illegal cycle */ [T] }
// The implementation of conversions T(x) between integers and floating-point
// numbers checks that both T and x have either integer or floating-point
diff --git a/src/go/types/testdata/issues.src b/src/go/types/testdata/issues.src
index 1bfc7fe..db415ea 100644
--- a/src/go/types/testdata/issues.src
+++ b/src/go/types/testdata/issues.src
@@ -325,8 +325,8 @@
func issue28281d(... /* ERROR can only use ... with final parameter */ int, int)
func issue28281e(a, b, c ... /* ERROR can only use ... with final parameter */ int, d int)
func issue28281f(... /* ERROR can only use ... with final parameter */ int, ... /* ERROR can only use ... with final parameter */ int, int)
-func (... /* ERROR can only use ... with final parameter in list */ TT) f()
-func issue28281g() (... /* ERROR can only use ... with final parameter in list */ TT)
+func (... /* ERROR can only use ... with final parameter */ TT) f()
+func issue28281g() (... /* ERROR can only use ... with final parameter */ TT)
// Issue #26234: Make various field/method lookup errors easier to read by matching cmd/compile's output
func issue26234a(f *syn.File) {
@@ -354,10 +354,10 @@
func issue35895() {
// T is defined in this package, don't qualify its name with the package name.
- var _ T = 0 // ERROR cannot convert 0 \(untyped int constant\) to T
+ var _ T = 0 // ERROR cannot use 0 \(untyped int constant\) as T
// There is only one package with name syntax imported, only use the (global) package name in error messages.
- var _ *syn.File = 0 // ERROR cannot convert 0 \(untyped int constant\) to \*syntax.File
+ var _ *syn.File = 0 // ERROR cannot use 0 \(untyped int constant\) as \*syntax.File
// Because both t1 and t2 have the same global package name (template),
// qualify packages with full path name in this case.
diff --git a/src/go/types/testdata/mtypeparams.go2 b/src/go/types/testdata/mtypeparams.go2
deleted file mode 100644
index 1d12406..0000000
--- a/src/go/types/testdata/mtypeparams.go2
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2020 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.
-
-// If types.Config.AcceptMethodTypeParams is set,
-// the type checker accepts methods that have their
-// own type parameter list.
-
-package p
-
-type S struct{}
-
-func (S) m[T any](v T)
-
-type I interface {
- m[T any](v T)
-}
-
-type J interface {
- m[T any](v T)
-}
-
-var _ I = S{}
-var _ I = J(nil)
-
-type C interface{ n() }
-
-type Sc struct{}
-
-func (Sc) m[T C](v T)
-
-type Ic interface {
- m[T C](v T)
-}
-
-type Jc interface {
- m[T C](v T)
-}
-
-var _ Ic = Sc{}
-var _ Ic = Jc(nil)
-
-// TODO(gri) These should fail because the constraints don't match.
-var _ I = Sc{}
-var _ I = Jc(nil)
-
-var _ Ic = S{}
-var _ Ic = J(nil)
diff --git a/src/go/types/testdata/shifts.src b/src/go/types/testdata/shifts.src
index ebc95ba..c9a38ae 100644
--- a/src/go/types/testdata/shifts.src
+++ b/src/go/types/testdata/shifts.src
@@ -193,14 +193,27 @@
_ = float32(1.0 /* ERROR "must be integer" */ <<s)
_ = float32(1.1 /* ERROR "must be integer" */ <<s)
+ _ = int32(0x80000000 /* ERROR "overflows int32" */ << s)
+ // TODO(rfindley) Eliminate the redundant error here.
+ _ = int32(( /* ERROR "truncated to int32" */ 0x80000000 /* ERROR "truncated to int32" */ + 0i) << s)
+
+ _ = int(1+0i<<0)
+ _ = int((1+0i)<<s)
+ _ = int(1.0<<s)
+ _ = int(complex(1, 0)<<s)
+ _ = int(float32/* ERROR "must be integer" */(1.0) <<s)
+ _ = int(1.1 /* ERROR must be integer */ <<s)
+ _ = int(( /* ERROR "must be integer" */ 1+1i) <<s)
+
+ _ = complex(1 /* ERROR "must be integer" */ <<s, 0)
+
var b []int
_ = append(b, 1<<s)
_ = append(b, 1.0<<s)
+ _ = append(b, (1+0i)<<s)
_ = append(b, 1.1 /* ERROR "must be integer" */ <<s)
-
- _ = append(b, 1<<s)
- _ = append(b, 1.0<<s) // should fail - see TODO in append code
- _ = append(b, 1.1 /* ERROR "must be integer" */ <<s)
+ _ = append(b, (1 + 0i) <<s)
+ _ = append(b, ( /* ERROR "must be integer" */ 1 + 1i) <<s)
_ = complex(1.0 /* ERROR "must be integer" */ <<s, 0)
_ = complex(1.1 /* ERROR "must be integer" */ <<s, 0)
@@ -379,4 +392,4 @@
var _ int8 = 0xff /* ERROR "overflows int8" */ << s
var _ int16 = 0xffff /* ERROR "overflows int16" */ << s
var _ int32 = 0x80000000 /* ERROR "overflows int32" */ << s
-}
\ No newline at end of file
+}
diff --git a/src/go/types/testdata/stmt0.src b/src/go/types/testdata/stmt0.src
index c89ff7f..297e34b 100644
--- a/src/go/types/testdata/stmt0.src
+++ b/src/go/types/testdata/stmt0.src
@@ -182,7 +182,7 @@
var x int
x <- /* ERROR "cannot send" */ x
rch <- /* ERROR "cannot send" */ x
- ch <- "foo" /* ERROR "cannot convert" */
+ ch <- "foo" /* ERROR "cannot use .* in send" */
ch <- x
}
@@ -381,13 +381,13 @@
func returns1(x float64) (int, *float64) {
return 0, &x
return /* ERROR wrong number of return values */
- return "foo" /* ERROR "cannot convert" */, x /* ERROR "cannot use .* in return statement" */
+ return "foo" /* ERROR "cannot .* in return statement" */, x /* ERROR "cannot use .* in return statement" */
return /* ERROR wrong number of return values */ 0, &x, 1
}
func returns2() (a, b int) {
return
- return 1, "foo" /* ERROR cannot convert */
+ return 1, "foo" /* ERROR cannot use .* in return statement */
return /* ERROR wrong number of return values */ 1, 2, 3
{
type a int
@@ -609,7 +609,7 @@
// untyped constants are converted to default types
switch 1<<63-1 {
}
- switch 1 /* ERROR "overflows int" */ << 63 {
+ switch 1 /* ERROR "cannot use .* as int value.*\(overflows\)" */ << 63 {
}
var x int
switch 1.0 {
@@ -631,9 +631,9 @@
}
func issue11667() {
- switch 9223372036854775808 /* ERROR "overflows int" */ {
+ switch 9223372036854775808 /* ERROR "cannot use .* as int value.*\(overflows\)" */ {
}
- switch 9223372036854775808 /* ERROR "overflows int" */ {
+ switch 9223372036854775808 /* ERROR "cannot use .* as int value.*\(overflows\)" */ {
case 9223372036854775808:
}
var x int
@@ -886,7 +886,7 @@
ee = e
_ = ee
}
- for _ = range sc /* ERROR "send-only channel" */ {}
+ for _ = range sc /* ERROR "cannot range over" */ {}
for _ = range rc {}
// constant strings
diff --git a/src/go/types/testdata/tinference.go2 b/src/go/types/testdata/tinference.go2
index a53fde0..31338b3 100644
--- a/src/go/types/testdata/tinference.go2
+++ b/src/go/types/testdata/tinference.go2
@@ -8,6 +8,9 @@
type any interface{}
+// TODO(rFindley) the below partially applied function types should probably
+// not be permitted (spec question).
+
func f0[A any, B interface{type C}, C interface{type D}, D interface{type A}](A, B, C, D)
func _() {
f := f0[string]
diff --git a/src/go/types/testdata/typeparams.go2 b/src/go/types/testdata/typeparams.go2
index 3e2568d..bdf6d56 100644
--- a/src/go/types/testdata/typeparams.go2
+++ b/src/go/types/testdata/typeparams.go2
@@ -33,7 +33,7 @@
var _ = reverse[int, float32 /* ERROR got 2 type arguments */ ] ([]int{1, 2, 3})
var _ = reverse[int]([ /* ERROR cannot use */ ]float32{1, 2, 3})
var f = reverse[chan int]
-var _ = f(0 /* ERROR cannot convert 0 .* to \[\]chan int */ )
+var _ = f(0 /* ERROR cannot use 0 .* as \[\]chan int */ )
func swap[A, B any](a A, b B) (B, A) { return b, a }
@@ -73,7 +73,7 @@
var _ = new /* ERROR cannot use generic function new */
var _ *int = new[int]()
-func _[T any](map[T /* ERROR invalid map key type T \(missing comparable constraint\) */]int) // w/o constraint we don't know if T is comparable
+func _[T any](map[T /* ERROR incomparable map key type T \(missing comparable constraint\) */]int) // w/o constraint we don't know if T is comparable
func f1[T1 any](struct{T1}) int
var _ = f1(int)(struct{T1}{})
@@ -229,9 +229,8 @@
type T struct {}
func (T) m1() {}
-// The type checker accepts method type parameters if configured accordingly.
-func (T) m2[_ any]() {}
-func (T) m3[P any]() {}
+func (T) m2[ /* ERROR methods cannot have type parameters */ _ any]() {}
+func (T) m3[ /* ERROR methods cannot have type parameters */ P any]() {}
// type inference across parameterized types
@@ -293,20 +292,20 @@
type R0 struct{}
-func (R0) _[T any](x T)
-func (R0 /* ERROR invalid receiver */ ) _[R0 any]() // scope of type parameters starts at "func"
+func (R0) _[ /* ERROR methods cannot have type parameters */ T any](x T)
+func (R0 /* ERROR invalid receiver */ ) _[ /* ERROR methods cannot have type parameters */ R0 any]() // scope of type parameters starts at "func"
type R1[A, B any] struct{}
func (_ R1[A, B]) m0(A, B)
-func (_ R1[A, B]) m1[T any](A, B, T) T
+func (_ R1[A, B]) m1[ /* ERROR methods cannot have type parameters */ T any](A, B, T) T
func (_ R1 /* ERROR not a generic type */ [R1, _]) _()
-func (_ R1[A, B]) _[A /* ERROR redeclared */ any](B)
+func (_ R1[A, B]) _[ /* ERROR methods cannot have type parameters */ A /* ERROR redeclared */ any](B)
func _() {
var r R1[int, string]
r.m1[rune](42, "foo", 'a')
- r.m1[rune](42, "foo", 1.2 /* ERROR truncated to rune */)
+ r.m1[rune](42, "foo", 1.2 /* ERROR cannot use .* as rune .* \(truncated\) */)
r.m1(42, "foo", 1.2) // using type inference
var _ float64 = r.m1(42, "foo", 1.2)
}
@@ -456,4 +455,4 @@
type T3[P] func(P)
var _ T3[int]
-*/
\ No newline at end of file
+*/
diff --git a/src/go/types/type.go b/src/go/types/type.go
index 7eeccc5..0fcefef 100644
--- a/src/go/types/type.go
+++ b/src/go/types/type.go
@@ -207,8 +207,8 @@
// and store it in the Func Object) because when type-checking a function
// literal we call the general type checker which returns a general Type.
// We then unpack the *Signature and use the scope for the literal body.
- rparams []*TypeName // reveiver type parameters from left to right; or nil
- tparams []*TypeName // type parameters from left to right; or nil
+ rparams []*TypeName // receiver type parameters from left to right, or nil
+ tparams []*TypeName // type parameters from left to right, or nil
scope *Scope // function scope, present for package-local signatures
recv *Var // nil if not a method
params *Tuple // (incoming) parameters from left to right; or nil
@@ -317,7 +317,7 @@
// unpack unpacks a type into a list of types.
// TODO(gri) Try to eliminate the need for this function.
-func unpack(typ Type) []Type {
+func unpackType(typ Type) []Type {
if typ == nil {
return nil
}
@@ -332,7 +332,7 @@
if t.allTypes == nil {
return false // we must have at least one type! (was bug)
}
- for _, t := range unpack(t.allTypes) {
+ for _, t := range unpackType(t.allTypes) {
if !pred(t) {
return false
}
@@ -443,10 +443,7 @@
return len(t.allMethods) == 0 && t.allTypes == nil
}
return !t.iterate(func(t *Interface) bool {
- if len(t.methods) > 0 || t.types != nil {
- return true
- }
- return false
+ return len(t.methods) > 0 || t.types != nil
}, nil)
}
@@ -458,10 +455,7 @@
}
return t.iterate(func(t *Interface) bool {
- if t.types != nil {
- return true
- }
- return false
+ return t.types != nil
}, nil)
}
@@ -534,7 +528,7 @@
if t.allTypes == nil {
return true
}
- types := unpack(t.allTypes)
+ types := unpackType(t.allTypes)
return includes(types, typ) || includes(types, under(typ))
}
@@ -726,16 +720,15 @@
type TypeParam struct {
check *Checker // for lazy type bound completion
id uint64 // unique id
- ptr bool // pointer designation
obj *TypeName // corresponding type name
index int // parameter index
bound Type // *Named or *Interface; underlying type is always *Interface
}
// NewTypeParam returns a new TypeParam.
-func (check *Checker) NewTypeParam(ptr bool, obj *TypeName, index int, bound Type) *TypeParam {
+func (check *Checker) NewTypeParam(obj *TypeName, index int, bound Type) *TypeParam {
assert(bound != nil)
- typ := &TypeParam{check: check, id: check.nextId, ptr: ptr, obj: obj, index: index, bound: bound}
+ typ := &TypeParam{check: check, id: check.nextId, obj: obj, index: index, bound: bound}
check.nextId++
if obj.typ == nil {
obj.typ = typ
@@ -750,6 +743,7 @@
if n, _ := t.bound.(*Named); n != nil {
pos = n.obj.pos
}
+ // TODO(rFindley) switch this to an unexported method on Checker.
t.check.completeInterface(pos, iface)
return iface
}
@@ -765,7 +759,7 @@
if t := asTypeParam(typ); t != nil {
// If the optype is typ, return the top type as we have
// no information. It also prevents infinite recursion
- // via the TypeParam converter methods. This can happen
+ // via the asTypeParam converter function. This can happen
// for a type parameter list of the form:
// (type T interface { type T }).
// See also issue #39680.
diff --git a/src/go/types/typestring.go b/src/go/types/typestring.go
index 1582f70..64bbb33 100644
--- a/src/go/types/typestring.go
+++ b/src/go/types/typestring.go
@@ -313,33 +313,17 @@
}
func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited []Type) {
- // bound returns the type bound for tname. The result is never nil.
- bound := func(tname *TypeName) Type {
- // be careful to avoid crashes in case of inconsistencies
- if t, _ := tname.typ.(*TypeParam); t != nil && t.bound != nil {
- return t.bound
- }
- return &emptyInterface
- }
-
- // If a single type bound is not the empty interface, we have to write them all.
- var writeBounds bool
- for _, p := range list {
- // bound(p) should be an interface but be careful (it may be invalid)
- b := asInterface(bound(p))
- if b != nil && !b.Empty() {
- writeBounds = true
- break
- }
- }
- writeBounds = true // always write the bounds for new type parameter list syntax
-
+ // TODO(rFindley) compare this with the corresponding implementation in types2
buf.WriteString("[")
var prev Type
for i, p := range list {
- b := bound(p)
+ // TODO(rFindley) support 'any' sugar here.
+ var b Type = &emptyInterface
+ if t, _ := p.typ.(*TypeParam); t != nil && t.bound != nil {
+ b = t.bound
+ }
if i > 0 {
- if writeBounds && b != prev {
+ if b != prev {
// type bound changed - write previous one before advancing
buf.WriteByte(' ')
writeType(buf, prev, qf, visited)
@@ -349,15 +333,12 @@
prev = b
if t, _ := p.typ.(*TypeParam); t != nil {
- if t.ptr {
- buf.WriteByte('*')
- }
writeType(buf, t, qf, visited)
} else {
buf.WriteString(p.name)
}
}
- if writeBounds && prev != nil {
+ if prev != nil {
buf.WriteByte(' ')
writeType(buf, prev, qf, visited)
}
diff --git a/src/go/types/typestring_test.go b/src/go/types/typestring_test.go
index 13d4162..b16529d 100644
--- a/src/go/types/typestring_test.go
+++ b/src/go/types/typestring_test.go
@@ -95,7 +95,10 @@
dup("interface{}"),
dup("interface{m()}"),
dup(`interface{String() string; m(int) float32}`),
- dup(`interface{type int, float32, complex128}`),
+
+ // TODO(rFindley) uncomment this once this AST is accepted, and add more test
+ // cases.
+ // dup(`interface{type int, float32, complex128}`),
// maps
dup("map[string]int"),
diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go
index 89a4085..a6b7314 100644
--- a/src/go/types/typexpr.go
+++ b/src/go/types/typexpr.go
@@ -30,9 +30,9 @@
scope, obj := check.scope.LookupParent(e.Name, check.pos)
if obj == nil {
if e.Name == "_" {
- check.errorf(e.Pos(), "cannot use _ as value or type")
+ check.errorf(e, _InvalidBlank, "cannot use _ as value or type")
} else {
- check.errorf(e.Pos(), "undeclared name: %s", e.Name)
+ check.errorf(e, _UndeclaredName, "undeclared name: %s", e.Name)
}
return
}
@@ -63,7 +63,7 @@
switch obj := obj.(type) {
case *PkgName:
- check.errorf(e.Pos(), "use of package %s not in selector", obj.name)
+ check.errorf(e, _InvalidPkgUse, "use of package %s not in selector", obj.name)
return
case *Const:
@@ -73,7 +73,7 @@
}
if obj == universeIota {
if check.iota == nil {
- check.errorf(e.Pos(), "cannot use iota outside constant declaration")
+ check.errorf(e, _InvalidIota, "cannot use iota outside constant declaration")
return
}
x.val = check.iota
@@ -128,26 +128,26 @@
// (see ordinaryType).
func (check *Checker) varType(e ast.Expr) Type {
typ := check.definedType(e, nil)
- check.ordinaryType(e.Pos(), typ)
+ check.ordinaryType(e, typ)
return typ
}
// ordinaryType reports an error if typ is an interface type containing
// type lists or is (or embeds) the predeclared type comparable.
-func (check *Checker) ordinaryType(pos token.Pos, typ Type) {
+func (check *Checker) ordinaryType(pos positioner, typ Type) {
// We don't want to call under() (via asInterface) or complete interfaces
// while we are in the middle of type-checking parameter declarations that
// might belong to interface methods. Delay this check to the end of
// type-checking.
check.atEnd(func() {
if t := asInterface(typ); t != nil {
- check.completeInterface(pos, t) // TODO(gri) is this the correct position?
+ check.completeInterface(pos.Pos(), t) // TODO(gri) is this the correct position?
if t.allTypes != nil {
- check.softErrorf(pos, "interface contains type constraints (%s)", t.allTypes)
+ check.softErrorf(pos, _Todo, "interface contains type constraints (%s)", t.allTypes)
return
}
if t.IsComparable() {
- check.softErrorf(pos, "interface is (or embeds) comparable")
+ check.softErrorf(pos, _Todo, "interface is (or embeds) comparable")
}
}
})
@@ -171,7 +171,7 @@
typ := check.typInternal(e, def)
assert(isTyped(typ))
if isGeneric(typ) {
- check.errorf(e.Pos(), "cannot use generic type %s without instantiation", typ)
+ check.errorf(e, _Todo, "cannot use generic type %s without instantiation", typ)
typ = Typ[Invalid]
}
check.recordTypeAndValue(e, typexpr, typ, nil)
@@ -184,7 +184,7 @@
assert(isTyped(typ))
if typ != Typ[Invalid] && !isGeneric(typ) {
if reportErr {
- check.errorf(e.Pos(), "%s is not a generic type", typ)
+ check.errorf(e, _Todo, "%s is not a generic type", typ)
}
typ = Typ[Invalid]
}
@@ -211,13 +211,13 @@
case *ast.CallExpr:
var args []ast.Expr
for i, arg := range n.Args {
- Arg := isubst(arg, smap)
- if Arg != arg {
+ new := isubst(arg, smap)
+ if new != arg {
if args == nil {
args = make([]ast.Expr, len(n.Args))
copy(args, n.Args)
}
- args[i] = Arg
+ args[i] = new
}
}
if args != nil {
@@ -288,14 +288,11 @@
// - only do this if we have the right number (otherwise an error is reported elsewhere)
if len(sig.rparams) == len(recvTParams) {
// We have a list of *TypeNames but we need a list of Types.
- // While creating this list, also update type parameter pointer designation
- // for each (*TypeParam) list entry, by copying the information from the
- // receiver base type's type parameters.
list := make([]Type, len(sig.rparams))
for i, t := range sig.rparams {
- t.typ.(*TypeParam).ptr = recvTParams[i].typ.(*TypeParam).ptr
list[i] = t.typ
}
+ smap := makeSubstMap(recvTParams, list)
for i, tname := range sig.rparams {
bound := recvTParams[i].typ.(*TypeParam).bound
// bound is (possibly) parameterized in the context of the
@@ -304,7 +301,7 @@
// TODO(gri) should we assume now that bounds always exist?
// (no bound == empty interface)
if bound != nil {
- bound = check.subst(tname.pos, bound, makeSubstMap(recvTParams, list))
+ bound = check.subst(tname.pos, bound, smap)
tname.typ.(*TypeParam).bound = bound
}
}
@@ -314,11 +311,11 @@
if ftyp.TParams != nil {
sig.tparams = check.collectTypeParams(ftyp.TParams)
- // Always type-check method type parameters but complain if they are not enabled.
+ // 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.conf.AcceptMethodTypeParams {
- check.errorf(ftyp.TParams.Pos(), "methods cannot have type parameters")
+ if recvPar != nil {
+ check.errorf(ftyp.TParams, _Todo, "methods cannot have type parameters")
}
}
@@ -330,7 +327,7 @@
params, variadic := check.collectParams(scope, ftyp.Params, nil, true)
results, _ := check.collectParams(scope, ftyp.Results, nil, false)
scope.Squash(func(obj, alt Object) {
- check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
+ check.errorf(obj, _DuplicateDecl, "%s redeclared in this block", obj.Name())
check.reportAltDecl(alt)
})
@@ -345,7 +342,7 @@
recv = NewParam(0, nil, "", Typ[Invalid]) // ignore recv below
default:
// more than one receiver
- check.error(recvList[len(recvList)-1].Pos(), "method must have exactly one receiver")
+ check.error(recvList[len(recvList)-1], _BadRecv, "method must have exactly one receiver")
fallthrough // continue with first receiver
case 1:
recv = recvList[0]
@@ -381,7 +378,7 @@
err = "basic or unnamed type"
}
if err != "" {
- check.errorf(recv.pos, "invalid receiver %s (%s)", recv.typ, err)
+ check.errorf(recv, _InvalidRecv, "invalid receiver %s (%s)", recv.typ, err)
// ok to continue
}
}
@@ -403,7 +400,7 @@
// Must only be called by definedType or genericType.
//
func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
- if check.conf.Trace {
+ if trace {
check.trace(e0.Pos(), "type %s", e0)
check.indent++
defer func() {
@@ -440,9 +437,9 @@
case invalid:
// ignore - error reported before
case novalue:
- check.errorf(x.pos(), "%s used as type", &x)
+ check.errorf(&x, _NotAType, "%s used as type", &x)
default:
- check.errorf(x.pos(), "%s is not a type", &x)
+ check.errorf(&x, _NotAType, "%s is not a type", &x)
}
case *ast.SelectorExpr:
@@ -457,19 +454,20 @@
case invalid:
// ignore - error reported before
case novalue:
- check.errorf(x.pos(), "%s used as type", &x)
+ check.errorf(&x, _NotAType, "%s used as type", &x)
default:
- check.errorf(x.pos(), "%s is not a type", &x)
+ check.errorf(&x, _NotAType, "%s is not a type", &x)
}
case *ast.IndexExpr:
- if check.useBrackets {
- return check.instantiatedType(e.X, []ast.Expr{e.Index}, def)
- }
- check.errorf(e0.Pos(), "%s is not a type", e0)
+ return check.instantiatedType(e.X, []ast.Expr{e.Index}, def)
case *ast.CallExpr:
- return check.instantiatedType(e.Fun, e.Args, def)
+ if e.Brackets {
+ return check.instantiatedType(e.Fun, e.Args, def)
+ } else {
+ check.errorf(e0, _NotAType, "%s is not a type", e0)
+ }
case *ast.ParenExpr:
// Generic types must be instantiated before they can be used in any form.
@@ -536,7 +534,7 @@
if asTypeParam(typ.key) != nil {
why = " (missing comparable constraint)"
}
- check.errorf(e.Key.Pos(), "invalid map key type %s%s", typ.key, why)
+ check.errorf(e.Key, _IncomparableMapKey, "incomparable map key type %s%s", typ.key, why)
}
})
@@ -555,7 +553,7 @@
case ast.RECV:
dir = RecvOnly
default:
- check.invalidAST(e.Pos(), "unknown channel direction %d", e.Dir)
+ check.invalidAST(e, "unknown channel direction %d", e.Dir)
// ok to continue
}
@@ -564,7 +562,7 @@
return typ
default:
- check.errorf(e0.Pos(), "%s is not a type", e0)
+ check.errorf(e0, _NotAType, "%s is not a type", e0)
}
typ := Typ[Invalid]
@@ -584,7 +582,7 @@
case invalid:
// ignore - error reported before
case novalue:
- check.errorf(x.pos(), "%s used as type", &x)
+ check.errorf(&x, _NotAType, "%s used as type", &x)
case typexpr:
check.instantiatedOperand(&x)
return x.typ
@@ -594,7 +592,7 @@
}
fallthrough
default:
- check.errorf(x.pos(), "%s is not a type", &x)
+ check.errorf(&x, _NotAType, "%s is not a type", &x)
}
return Typ[Invalid]
}
@@ -609,9 +607,9 @@
unreachable() // should have been caught by genericType
}
- // create a new type Instance rather than instantiate the type
+ // create a new type instance rather than instantiate the type
// TODO(gri) should do argument number check here rather than
- // when instantiating the type?
+ // when instantiating the type?
typ := new(instance)
def.setUnderlying(typ)
@@ -650,7 +648,7 @@
check.expr(&x, e)
if x.mode != constant_ {
if x.mode != invalid {
- check.errorf(x.pos(), "array length %s must be constant", &x)
+ check.errorf(&x, _InvalidArrayLen, "array length %s must be constant", &x)
}
return -1
}
@@ -660,12 +658,12 @@
if n, ok := constant.Int64Val(val); ok && n >= 0 {
return n
}
- check.errorf(x.pos(), "invalid array length %s", &x)
+ check.errorf(&x, _InvalidArrayLen, "invalid array length %s", &x)
return -1
}
}
}
- check.errorf(x.pos(), "array length %s must be integer", &x)
+ check.errorf(&x, _InvalidArrayLen, "array length %s must be integer", &x)
return -1
}
@@ -703,7 +701,7 @@
if variadicOk && i == len(list.List)-1 && len(field.Names) <= 1 {
variadic = true
} else {
- check.softErrorf(t.Pos(), "can only use ... with final parameter in list")
+ check.softErrorf(t, _MisplacedDotDotDot, "can only use ... with final parameter in list")
// ignore ... and continue
}
}
@@ -714,7 +712,7 @@
// named parameter
for _, name := range field.Names {
if name.Name == "" {
- check.invalidAST(name.Pos(), "anonymous parameter")
+ check.invalidAST(name, "anonymous parameter")
// ok to continue
}
par := NewParam(name.Pos(), check.pkg, name.Name, typ)
@@ -732,7 +730,7 @@
}
if named && anonymous {
- check.invalidAST(list.Pos(), "list contains both named and anonymous parameters")
+ check.invalidAST(list, "list contains both named and anonymous parameters")
// ok to continue
}
@@ -750,7 +748,7 @@
func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool {
if alt := oset.insert(obj); alt != nil {
- check.errorf(pos, "%s redeclared", obj.Name())
+ check.errorf(atPos(pos), _DuplicateDecl, "%s redeclared", obj.Name())
check.reportAltDecl(alt)
return false
}
@@ -768,7 +766,7 @@
// and we don't care if a constructed AST has more.)
name := f.Names[0]
if name.Name == "_" {
- check.errorf(name.Pos(), "invalid method name _")
+ check.errorf(name, _BlankIfaceMethod, "invalid method name _")
continue // ignore
}
@@ -778,7 +776,7 @@
// the author intended to include all types.
types = append(types, f.Type)
if tlist != nil && tlist != name {
- check.errorf(name.Pos(), "cannot have multiple type lists in an interface")
+ check.errorf(name, _Todo, "cannot have multiple type lists in an interface")
}
tlist = name
continue
@@ -788,7 +786,7 @@
sig, _ := typ.(*Signature)
if sig == nil {
if typ != Typ[Invalid] {
- check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ)
+ check.invalidAST(f.Type, "%s is not a method signature", typ)
}
continue // ignore
}
@@ -796,8 +794,8 @@
// Always type-check method type parameters but complain if they are not enabled.
// (This extra check is needed here because interface method signatures don't have
// a receiver specification.)
- if sig.tparams != nil && !check.conf.AcceptMethodTypeParams {
- check.errorf(f.Type.(*ast.FuncType).TParams.Pos(), "methods cannot have type parameters")
+ if sig.tparams != nil {
+ check.errorf(f.Type.(*ast.FuncType).TParams, _Todo, "methods cannot have type parameters")
}
// use named receiver type if available (for better error messages)
@@ -847,7 +845,7 @@
panic("internal error: incomplete interface")
}
- if check.conf.Trace {
+ if trace {
// Types don't generally have position information.
// If we don't have a valid pos provided, try to use
// one close enough.
@@ -892,14 +890,14 @@
methods = append(methods, m)
mpos[m] = pos
case explicit:
- check.errorf(pos, "duplicate method %s", m.name)
- check.errorf(mpos[other.(*Func)], "\tother declaration of %s", m.name) // secondary error, \t indented
+ check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
+ check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
default:
// check method signatures after all types are computed (issue #33656)
check.atEnd(func() {
if !check.identical(m.typ, other.Type()) {
- check.errorf(pos, "duplicate method %s", m.name)
- check.errorf(mpos[other.(*Func)], "\tother declaration of %s", m.name) // secondary error, \t indented
+ check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
+ check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
}
})
}
@@ -925,7 +923,8 @@
} else {
format = "%s is not an interface"
}
- check.errorf(pos, format, typ)
+ // TODO: correct error code.
+ check.errorf(atPos(pos), _InvalidIfaceEmbed, format, typ)
}
continue
}
@@ -962,8 +961,8 @@
return x
}
- xtypes := unpack(x)
- ytypes := unpack(y)
+ xtypes := unpackType(x)
+ ytypes := unpackType(y)
// Compute the list rtypes which includes only
// types that are in both xtypes and ytypes.
// Quadratic algorithm, but good enough for now.
@@ -1009,7 +1008,7 @@
return val
}
}
- check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value)
+ check.invalidAST(t, "incorrect tag syntax: %q", t.Value)
}
return ""
}
@@ -1067,24 +1066,31 @@
}
} else {
// embedded field
- // spec: "An embedded type must be specified as a (possibly parenthesized) type name T or
- // as a pointer to a non-interface type name *T, and T itself may not be a pointer type."
+ // spec: "An embedded type must be specified as a type name T or as a
+ // pointer to a non-interface type name *T, and T itself may not be a
+ // pointer type."
pos := f.Type.Pos()
name := embeddedFieldIdent(f.Type)
if name == nil {
- check.errorf(pos, "invalid embedded field type %s", f.Type)
+ // TODO(rFindley): using invalidAST here causes test failures (all
+ // errors should have codes). Clean this up.
+ check.errorf(f.Type, _Todo, "invalid AST: embedded field type %s has no name", f.Type)
name = ast.NewIdent("_")
name.NamePos = pos
addInvalid(name, pos)
continue
}
add(name, true, pos)
+
// Because we have a name, typ must be of the form T or *T, where T is the name
// of a (named or alias) type, and t (= deref(typ)) must be the type of T.
// We must delay this check to the end because we don't want to instantiate
// (via under(t)) a possibly incomplete type.
- embeddedTyp := typ // for closure below
- embeddedPos := pos
+
+ // for use in the closure below
+ embeddedTyp := typ
+ embeddedPos := f.Type
+
check.atEnd(func() {
t, isPtr := deref(embeddedTyp)
switch t := optype(t).(type) {
@@ -1095,13 +1101,13 @@
}
// unsafe.Pointer is treated like a regular pointer
if t.kind == UnsafePointer {
- check.errorf(embeddedPos, "embedded field type cannot be unsafe.Pointer")
+ check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be unsafe.Pointer")
}
case *Pointer:
- check.errorf(embeddedPos, "embedded field type cannot be a pointer")
+ check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer")
case *Interface:
if isPtr {
- check.errorf(embeddedPos, "embedded field type cannot be a pointer to an interface")
+ check.errorf(embeddedPos, _InvalidPtrEmbed, "embedded field type cannot be a pointer to an interface")
}
}
})
@@ -1123,10 +1129,12 @@
}
case *ast.SelectorExpr:
return e.Sel
- case *ast.CallExpr:
- return embeddedFieldIdent(e.Fun)
- case *ast.ParenExpr:
+ case *ast.IndexExpr:
return embeddedFieldIdent(e.X)
+ case *ast.CallExpr:
+ if e.Brackets {
+ return embeddedFieldIdent(e.Fun)
+ }
}
return nil // invalid embedded field
}
@@ -1135,37 +1143,24 @@
list := make([]Type, 0, len(types)) // assume all types are correct
for _, texpr := range types {
if texpr == nil {
- check.invalidAST(pos, "missing type constraint")
+ check.invalidAST(atPos(pos), "missing type constraint")
continue
}
- typ := check.varType(texpr)
- // A type constraint may be a predeclared type or a
- // composite type composed of only predeclared types.
- // TODO(gri) If we enable this again it also must run
- // at the end.
- const restricted = false
- var why string
- if restricted && !check.typeConstraint(typ, &why) {
- check.errorf(texpr.Pos(), "invalid type constraint %s (%s)", typ, why)
- continue
- }
- list = append(list, typ)
+ list = append(list, check.varType(texpr))
}
- // Ensure that each type is only present once in the type list.
- // Types may be interfaces, which may not be complete yet. It's
- // ok to do this check at the end because it's not a requirement
- // for correctness of the code.
+ // Ensure that each type is only present once in the type list. Types may be
+ // interfaces, which may not be complete yet. It's ok to do this check at the
+ // end because it's not a requirement for correctness of the code.
+ // Note: This is a quadratic algorithm, but type lists tend to be short.
check.atEnd(func() {
- uniques := make([]Type, 0, len(list)) // assume all types are unique
for i, t := range list {
if t := asInterface(t); t != nil {
check.completeInterface(types[i].Pos(), t)
}
- if includes(uniques, t) {
- check.softErrorf(types[i].Pos(), "duplicate type %s in type list", t)
+ if includes(list[:i], t) {
+ check.softErrorf(types[i], _Todo, "duplicate type %s in type list", t)
}
- uniques = append(uniques, t)
}
})
@@ -1181,59 +1176,3 @@
}
return false
}
-
-// typeConstraint checks that typ may be used in a type list.
-// For now this just checks for the absence of defined (*Named) types.
-func (check *Checker) typeConstraint(typ Type, why *string) bool {
- switch t := typ.(type) {
- case *Basic:
- // ok
- case *Array:
- return check.typeConstraint(t.elem, why)
- case *Slice:
- return check.typeConstraint(t.elem, why)
- case *Struct:
- for _, f := range t.fields {
- if !check.typeConstraint(f.typ, why) {
- return false
- }
- }
- case *Pointer:
- return check.typeConstraint(t.base, why)
- case *Tuple:
- if t == nil {
- return true
- }
- for _, v := range t.vars {
- if !check.typeConstraint(v.typ, why) {
- return false
- }
- }
- case *Signature:
- if len(t.tparams) != 0 {
- panic("type parameter in function type")
- }
- return (t.recv == nil || check.typeConstraint(t.recv.typ, why)) &&
- check.typeConstraint(t.params, why) &&
- check.typeConstraint(t.results, why)
- case *Interface:
- t.assertCompleteness()
- for _, m := range t.allMethods {
- if !check.typeConstraint(m.typ, why) {
- return false
- }
- }
- case *Map:
- return check.typeConstraint(t.key, why) && check.typeConstraint(t.elem, why)
- case *Chan:
- return check.typeConstraint(t.elem, why)
- case *Named:
- *why = check.sprintf("contains defined type %s", t)
- return false
- case *TypeParam:
- // ok, e.g.: func f (type T interface { type T }) ()
- default:
- unreachable()
- }
- return true
-}
diff --git a/src/go/types/unify.go b/src/go/types/unify.go
index 2bd2068..ab18feb 100644
--- a/src/go/types/unify.go
+++ b/src/go/types/unify.go
@@ -65,9 +65,9 @@
unifier *unifier
tparams []*TypeName
// For each tparams element, there is a corresponding type slot index in indices.
- // index < 0: unifier.types[-index] == nil
+ // index < 0: unifier.types[-index-1] == nil
// index == 0: no type slot allocated yet
- // index > 0: unifier.types[index] == typ
+ // index > 0: unifier.types[index-1] == typ
// Joined tparams elements share the same type slot and thus have the same index.
// By using a negative index for nil types we don't need to check unifier.types
// to see if we have a type or not.
@@ -120,13 +120,9 @@
case ti > 0:
// Only the type parameter for x has an inferred type. Use x slot for y.
u.y.setIndex(j, ti)
- // This case is handled like the default case.
- // case tj > 0:
- // // Only the type parameter for y has an inferred type. Use y slot for x.
- // u.x.setIndex(i, tj)
default:
- // Neither type parameter has an inferred type. Use y slot for x
- // (or x slot for y, it doesn't matter).
+ // Either the type parameter for y has an inferred type, or neither type
+ // parameter has an inferred type. In either case, use y slot for x.
u.x.setIndex(i, tj)
}
return true
diff --git a/test/gen/g012.go2 b/test/gen/g012.go2
index 539e069..46d7d61 100644
--- a/test/gen/g012.go2
+++ b/test/gen/g012.go2
@@ -59,19 +59,19 @@
r := float64(real(a))
i := float64(imag(a))
d := math.Sqrt(r * r + i * i)
- return ComplexAbs(T)(complex(d, 0))
+ return ComplexAbs[T](complex(d, 0))
}
// OrderedAbsDifference returns the absolute value of the difference
// between a and b, where a and b are of an ordered type.
func OrderedAbsDifference[T OrderedNumeric](a, b T) T {
- return T(AbsDifference(OrderedAbs(T)(a), OrderedAbs(T)(b)))
+ return T(AbsDifference(OrderedAbs[T](a), OrderedAbs[T](b)))
}
// ComplexAbsDifference returns the absolute value of the difference
// between a and b, where a and b are of a complex type.
func ComplexAbsDifference[T Complex](a, b T) T {
- return T(AbsDifference(ComplexAbs(T)(a), ComplexAbs(T)(b)))
+ return T(AbsDifference(ComplexAbs[T](a), ComplexAbs[T](b)))
}
func main() {