[dev.typeparams] import go2go changes to parse type parameters

This CL imports changes on the go2go branch to support parsing type
params, as well as the unsubmitted changes from CL 269300 to remove
support for parenthesize type parameter syntax.

Change-Id: I27ab942ce69eab62c2a1800f8f9661c4dcb233fe
Reviewed-on: https://go-review.googlesource.com/c/go/+/270857
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Robert Griesemer <gri@golang.org>
Trust: Robert Findley <rfindley@google.com>
This commit is contained in:
Rob Findley 2020-11-17 11:06:53 -05:00 committed by Robert Findley
parent a324aebb7d
commit b56762129e
12 changed files with 1068 additions and 248 deletions

View file

@ -114,6 +114,7 @@ func expectedErrors(fset *token.FileSet, filename string, src []byte) map[token.
// of found errors and reports discrepancies. // of found errors and reports discrepancies.
// //
func compareErrors(t *testing.T, fset *token.FileSet, expected map[token.Pos]string, found scanner.ErrorList) { func compareErrors(t *testing.T, fset *token.FileSet, expected map[token.Pos]string, found scanner.ErrorList) {
t.Helper()
for _, error := range found { for _, error := range found {
// error.Pos is a token.Position, but we want // error.Pos is a token.Position, but we want
// a token.Pos so we can do a map lookup // a token.Pos so we can do a map lookup
@ -149,7 +150,8 @@ func compareErrors(t *testing.T, fset *token.FileSet, expected map[token.Pos]str
} }
} }
func checkErrors(t *testing.T, filename string, input interface{}) { func checkErrors(t *testing.T, filename string, input interface{}, mode Mode) {
t.Helper()
src, err := readSource(filename, input) src, err := readSource(filename, input)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -157,7 +159,7 @@ func checkErrors(t *testing.T, filename string, input interface{}) {
} }
fset := token.NewFileSet() fset := token.NewFileSet()
_, err = ParseFile(fset, filename, src, DeclarationErrors|AllErrors) _, err = ParseFile(fset, filename, src, mode)
found, ok := err.(scanner.ErrorList) found, ok := err.(scanner.ErrorList)
if err != nil && !ok { if err != nil && !ok {
t.Error(err) t.Error(err)
@ -180,8 +182,12 @@ func TestErrors(t *testing.T) {
} }
for _, fi := range list { for _, fi := range list {
name := fi.Name() name := fi.Name()
if !fi.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".src") { if !fi.IsDir() && !strings.HasPrefix(name, ".") && (strings.HasSuffix(name, ".src") || strings.HasSuffix(name, ".go2")) {
checkErrors(t, filepath.Join(testdata, name), nil) mode := DeclarationErrors | AllErrors
if strings.HasSuffix(name, ".go2") {
mode |= ParseTypeParams
}
checkErrors(t, filepath.Join(testdata, name), nil, mode)
} }
} }
} }

View file

@ -55,6 +55,7 @@ const (
Trace // print a trace of parsed productions Trace // print a trace of parsed productions
DeclarationErrors // report declaration errors DeclarationErrors // report declaration errors
SpuriousErrors // same as AllErrors, for backward-compatibility SpuriousErrors // same as AllErrors, for backward-compatibility
ParseTypeParams // Placeholder. Will control the parsing of type parameters.
AllErrors = SpuriousErrors // report all errors (not just the first 10 on different lines) AllErrors = SpuriousErrors // report all errors (not just the first 10 on different lines)
) )

File diff suppressed because it is too large Load diff

View file

@ -48,14 +48,106 @@ var valids = []string{
`package p; var _ = map[*P]int{&P{}:0, {}:1}`, `package p; var _ = map[*P]int{&P{}:0, {}:1}`,
`package p; type T = int`, `package p; type T = int`,
`package p; type (T = p.T; _ = struct{}; x = *T)`, `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[P any] struct { P }`,
`package p; type T[P comparable] struct { P }`,
`package p; type T[P comparable[P]] struct { P }`,
`package p; type T[P1, P2 any] struct { P1; f []P2 }`,
`package p; type _ []T[int]`,
`package p; var _ = func()T(nil)`,
`package p; func _[T any]()`,
`package p; func _[T any]()()`,
`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 _ T[chan int]`,
`package p; func f[A, B any](); func _() { _ = f[int, int] }`,
`package p; type _[A interface{},] struct{}`,
`package p; type _[A interface{}] struct{}`,
`package p; type _[A, B any,] struct{}`,
`package p; type _[A, B any] struct{}`,
`package p; type _[A any,] struct{}`,
`package p; type _ [A+B]struct{}`, // this is an array!
`package p; type _[A any]struct{}`,
`package p; type _[A any] struct{ A }`, // this is not an array!
`package p; func _[T any]()`,
`package p; func _[T any](x T)`,
`package p; func _[T1, T2 any](x T)`,
`package p; func (R) _()`,
`package p; func (R[P]) _[T any]()`,
`package p; func (_ R[P]) _[T any](x T)`,
`package p; func (_ R[P, Q]) _[T1, T2 any](x T)`,
`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 _(T[P]) T[P]`,
`package p; func _(_ T[P], T P) T[P]`,
`package p; func _[A, B any](a A) B`,
`package p; func _[A, B C](a A) B`,
`package p; func _[A, B C[A, B]](a A) B`,
// method type parameters (if methodTypeParamsOk)
`package p; func (T) _[A, B any](a A) B`,
`package p; func (T) _[A, B C](a A) B`,
`package p; func (T) _[A, B C[A, B]](a A) B`,
// method type parameters are not permitted in interfaces.
`package p; type _[A, B any] interface { _(a A) B }`,
`package p; type _[A, B C[A, B]] interface { _(a A) B }`,
// type bounds
`package p; func _[T1, T2 interface{}](x T1) T2`,
`package p; func _[T1 interface{ m() }, T2, T3 interface{}](x T1, y T3) T2`,
// struct embedding
`package p; type _ struct{ T[P] }`,
`package p; type _ struct{ T[struct{a, b, c int}] }`,
`package p; type _ struct{ f [n]E }`,
`package p; type _ struct{ f [a+b+c+d]E }`,
// interfaces with type lists
`package p; type _ interface{type int}`,
`package p; type _ interface{type int, float32; type bool; m(); type string;}`,
// interface embedding
`package p; type I1 interface{}; type I2 interface{ I1 }`,
`package p; type I1[T any] interface{}; type I2 interface{ I1[int] }`,
`package p; type I1[T any] interface{}; type I2[T any] interface{ I1[T] }`,
} }
func TestValid(t *testing.T) { func TestValid(t *testing.T) {
for _, src := range valids { for _, src := range valids {
checkErrors(t, src, src) checkErrors(t, src, src, DeclarationErrors|AllErrors)
} }
} }
// TestSingle is useful to track down a problem with a single short test program.
func TestSingle(t *testing.T) {
const src = `package p; var _ = T[P]{}`
checkErrors(t, src, src, DeclarationErrors|AllErrors)
}
var invalids = []string{ var invalids = []string{
`foo /* ERROR "expected 'package'" */ !`, `foo /* ERROR "expected 'package'" */ !`,
`package p; func f() { if { /* ERROR "missing condition" */ } };`, `package p; func f() { if { /* ERROR "missing condition" */ } };`,
@ -79,7 +171,6 @@ var invalids = []string{
`package p; var a = chan /* ERROR "expected expression" */ int;`, `package p; var a = chan /* ERROR "expected expression" */ int;`,
`package p; var a = []int{[ /* 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 = ( /* 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; var a = <- /* ERROR "expected expression" */ chan int;`,
`package p; func f() { select { case _ <- chan /* ERROR "expected expression" */ int: } };`, `package p; func f() { select { case _ <- chan /* ERROR "expected expression" */ int: } };`,
`package p; func f() { _ = (<-<- /* ERROR "expected 'chan'" */ chan int)(nil) };`, `package p; func f() { _ = (<-<- /* ERROR "expected 'chan'" */ chan int)(nil) };`,
@ -102,7 +193,22 @@ var invalids = []string{
`package p; func f() { go f /* ERROR HERE "function must be invoked" */ }`, `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() { defer func() {} /* ERROR HERE "function must be invoked" */ }`,
`package p; func f() { go func() { func() { f(x func /* ERROR "missing ','" */ (){}) } } }`, `package p; func f() { go func() { func() { f(x func /* ERROR "missing ','" */ (){}) } } }`,
`package p; func f(x func(), u v 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" */ T any](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 _[_ any] int; var _ = T[] /* ERROR "expected operand" */ {}`,
`package p; type T[P any] = /* ERROR "cannot be alias" */ T0`,
`package p; func _[]/* ERROR "empty type parameter list" */()`,
// errors that could be improved
`package p; var a = a[[]int:[ /* ERROR "expected expression" */ ]int];`, // TODO: should this be on the ':'?
`package p; type _[A/* ERROR "all type parameters must be named" */,] struct{ A }`, // TODO: a better location would be after the ']'
`package p; func _[type /* ERROR "all type parameters must be named" */P, *Q interface{}]()`, // TODO: this is confusing.
`package p; type I1 interface{}; type I2 interface{ (/* ERROR "expected 'IDENT'" */I1) }`, // TODO: compiler error is 'syntax error: cannot parenthesize embedded type'
// issue 8656 // issue 8656
`package p; func f() (a b string /* ERROR "missing ','" */ , ok bool)`, `package p; func f() (a b string /* ERROR "missing ','" */ , ok bool)`,
@ -118,11 +224,11 @@ var invalids = []string{
`package p; var _ = struct { x int, /* ERROR "expected ';', found ','" */ y float }{};`, `package p; var _ = struct { x int, /* ERROR "expected ';', found ','" */ y float }{};`,
// issue 11611 // issue 11611
`package p; type _ struct { int, } /* ERROR "expected type, found '}'" */ ;`, `package p; type _ struct { int, } /* ERROR "expected 'IDENT', found '}'" */ ;`,
`package p; type _ struct { int, float } /* ERROR "expected type, found '}'" */ ;`, `package p; type _ struct { int, float } /* ERROR "expected type, found '}'" */ ;`,
`package p; type _ struct { ( /* ERROR "expected anonymous field" */ int) };`, //`package p; type _ struct { ( /* ERROR "cannot parenthesize embedded type" */ int) };`,
`package p; func _()(x, y, z ... /* ERROR "expected '\)', found '...'" */ int){}`, //`package p; func _()(x, y, z ... /* ERROR "expected '\)', found '...'" */ int){}`,
`package p; func _()(... /* ERROR "expected type, found '...'" */ int){}`, //`package p; func _()(... /* ERROR "expected type, found '...'" */ int){}`,
// issue 13475 // issue 13475
`package p; func f() { if true {} else ; /* ERROR "expected if statement or block" */ }`, `package p; func f() { if true {} else ; /* ERROR "expected if statement or block" */ }`,
@ -131,6 +237,6 @@ var invalids = []string{
func TestInvalid(t *testing.T) { func TestInvalid(t *testing.T) {
for _, src := range invalids { for _, src := range invalids {
checkErrors(t, src, src) checkErrors(t, src, src, DeclarationErrors|AllErrors)
} }
} }

62
src/go/parser/testdata/chans.go2 vendored Normal file
View file

@ -0,0 +1,62 @@
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[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}
runtime.SetFinalizer(r, r.finalize)
return s, r
}
// A sender is used to send values to a Receiver.
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 {
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[T any] 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)
}

83
src/go/parser/testdata/linalg.go2 vendored Normal file
View file

@ -0,0 +1,83 @@
// 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[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[T any] 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[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[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[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[T OrderedNumeric](a, b T) T {
return T(AbsDifference(OrderedAbs[T](a), OrderedAbs[T](b)))
}
func ComplexAbsDifference[T Complex](a, b T) T {
return T(AbsDifference(ComplexAbs[T](a), ComplexAbs[T](b)))
}

109
src/go/parser/testdata/map.go2 vendored Normal file
View file

@ -0,0 +1,109 @@
// Package orderedmap provides an ordered map, implemented as a binary tree.
package orderedmap
import "chans"
// Map is an ordered map.
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[K, V any] struct {
key K
val V
left, right *node[K, V]
}
// New returns a new map.
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] {
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[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 {
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[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) {
keyval, ok := it.r.Next()
if !ok {
var zerok K
var zerov V
return zerok, zerov, false
}
return keyval.key, keyval.val, true
}

58
src/go/parser/testdata/metrics.go2 vendored Normal file
View file

@ -0,0 +1,58 @@
package metrics
import "sync"
type Metric1[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[T1, T2 comparable] struct {
f1 T1
f2 T2
}
type Metric2[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[T1, T2, T3 comparable] struct {
f1 T1
f2 T2
f3 T3
}
type Metric3[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.

31
src/go/parser/testdata/set.go2 vendored Normal file
View file

@ -0,0 +1,31 @@
// Package set implements sets of any type.
package set
type Set[Elem comparable] map[Elem]struct{}
func Make[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)
}
}

31
src/go/parser/testdata/slices.go2 vendored Normal file
View file

@ -0,0 +1,31 @@
// Package slices implements various slice algorithms.
package slices
// Map turns a []T1 to a []T2 using a mapping function.
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)
}
return r
}
// Reduce reduces a []T1 to a single value using a reduction function.
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)
}
return r
}
// Filter filters values from a slice using a filter function.
func Filter[T any](s []T, f func(T) bool) []T {
var r []T
for _, v := range s {
if f(v) {
r = append(r, v)
}
}
return r
}

27
src/go/parser/testdata/sort.go2 vendored Normal file
View file

@ -0,0 +1,27 @@
package sort
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] }
// OrderedSlice sorts the slice s in ascending order.
// The elements of s must be ordered using the < operator.
func OrderedSlice[Elem comparable](s []Elem) {
sort.Sort(orderedSlice[Elem](s))
}
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] }
// SliceFn sorts the slice s according to the function f.
func SliceFn[Elem any](s []Elem, f func(Elem, Elem) bool) {
Sort(sliceFn[Elem]{s, f})
}

View file

@ -325,8 +325,8 @@ func issue28281c(a, b, c ... /* ERROR can only use ... with final parameter */ i
func issue28281d(... /* ERROR can only use ... with final parameter */ int, int) 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 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 issue28281f(... /* ERROR can only use ... with final parameter */ int, ... /* ERROR can only use ... with final parameter */ int, int)
func (... /* ERROR expected type */ TT) f() func (... /* ERROR can only use ... with final parameter */ TT) f()
func issue28281g() (... /* ERROR expected type */ TT) 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 // Issue #26234: Make various field/method lookup errors easier to read by matching cmd/compile's output
func issue26234a(f *syn.File) { func issue26234a(f *syn.File) {