mirror of
https://github.com/golang/go
synced 2024-11-17 04:04:46 -07:00
cmd/compile/internal/syntax: allow eliding interface in constraint literals
This CL permits an arbitrary type as well as the type sets ~T and A|B in constraint position, without the need of a surrrounding interface. For instance, the type parameter list [P interface{ ~map[K]V }, K comparable, V interface{ ~string }] may be written as [P ~map[K]V, K comparable, V ~string] The feature must be enabled explicitly with the AllowTypeSets mode and is only available if AllowGenerics is set as well. For #48424. Change-Id: Ic70bb97a49ff75e67e040853eac10e6aed0fef1a Reviewed-on: https://go-review.googlesource.com/c/go/+/353133 Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Griesemer <gri@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
f19b2d50c6
commit
5279e534b5
@ -130,7 +130,7 @@ func testSyntaxErrors(t *testing.T, filename string) {
|
||||
|
||||
var mode Mode
|
||||
if strings.HasSuffix(filename, ".go2") {
|
||||
mode = AllowGenerics | AllowTypeLists
|
||||
mode = AllowGenerics | AllowTypeSets | AllowTypeLists
|
||||
}
|
||||
ParseFile(filename, func(err error) {
|
||||
e, ok := err.(Error)
|
||||
|
@ -87,6 +87,8 @@ func (p *parser) init(file *PosBase, r io.Reader, errh ErrorHandler, pragh Pragm
|
||||
p.indent = nil
|
||||
}
|
||||
|
||||
func (p *parser) allowGenerics() bool { return p.mode&AllowGenerics != 0 }
|
||||
|
||||
// takePragma returns the current parsed pragmas
|
||||
// and clears them from the parser state.
|
||||
func (p *parser) takePragma() Pragma {
|
||||
@ -597,7 +599,7 @@ func (p *parser) typeDecl(group *Group) Decl {
|
||||
p.xnest++
|
||||
x := p.expr()
|
||||
p.xnest--
|
||||
if name0, ok := x.(*Name); p.mode&AllowGenerics != 0 && ok && p.tok != _Rbrack {
|
||||
if name0, ok := x.(*Name); p.allowGenerics() && ok && p.tok != _Rbrack {
|
||||
// generic type
|
||||
d.TParamList = p.paramList(name0, _Rbrack, true)
|
||||
pos := p.pos()
|
||||
@ -687,7 +689,7 @@ func (p *parser) funcDeclOrNil() *FuncDecl {
|
||||
}
|
||||
|
||||
f.Name = p.name()
|
||||
if p.mode&AllowGenerics != 0 && p.got(_Lbrack) {
|
||||
if p.allowGenerics() && p.got(_Lbrack) {
|
||||
if p.tok == _Rbrack {
|
||||
p.syntaxError("empty type parameter list")
|
||||
p.next()
|
||||
@ -1425,7 +1427,7 @@ func (p *parser) interfaceType() *InterfaceType {
|
||||
switch p.tok {
|
||||
case _Name:
|
||||
f := p.methodDecl()
|
||||
if f.Name == nil && p.mode&AllowGenerics != 0 {
|
||||
if f.Name == nil && p.allowGenerics() {
|
||||
f = p.embeddedElem(f)
|
||||
}
|
||||
typ.MethodList = append(typ.MethodList, f)
|
||||
@ -1443,14 +1445,14 @@ func (p *parser) interfaceType() *InterfaceType {
|
||||
return false
|
||||
|
||||
case _Operator:
|
||||
if p.op == Tilde && p.mode&AllowGenerics != 0 {
|
||||
if p.op == Tilde && p.allowGenerics() {
|
||||
typ.MethodList = append(typ.MethodList, p.embeddedElem(nil))
|
||||
return false
|
||||
}
|
||||
|
||||
case _Type:
|
||||
// TODO(gri) remove TypeList syntax if we accept #45346
|
||||
if p.mode&AllowGenerics != 0 && p.mode&AllowTypeLists != 0 {
|
||||
if p.allowGenerics() && p.mode&AllowTypeLists != 0 {
|
||||
type_ := NewName(p.pos(), "type") // cannot have a method named "type"
|
||||
p.next()
|
||||
if p.tok != _Semi && p.tok != _Rbrace {
|
||||
@ -1473,7 +1475,7 @@ func (p *parser) interfaceType() *InterfaceType {
|
||||
}
|
||||
|
||||
default:
|
||||
if p.mode&AllowGenerics != 0 {
|
||||
if p.allowGenerics() {
|
||||
pos := p.pos()
|
||||
if t := p.typeOrNil(); t != nil {
|
||||
f := new(Field)
|
||||
@ -1485,7 +1487,7 @@ func (p *parser) interfaceType() *InterfaceType {
|
||||
}
|
||||
}
|
||||
|
||||
if p.mode&AllowGenerics != 0 {
|
||||
if p.allowGenerics() {
|
||||
if p.mode&AllowTypeLists != 0 {
|
||||
p.syntaxError("expecting method, type list, or embedded element")
|
||||
p.advance(_Semi, _Rbrace, _Type)
|
||||
@ -1570,7 +1572,7 @@ func (p *parser) fieldDecl(styp *StructType) {
|
||||
|
||||
// Careful dance: We don't know if we have an embedded instantiated
|
||||
// type T[P1, P2, ...] or a field T of array/slice type [P]E or []E.
|
||||
if p.mode&AllowGenerics != 0 && len(names) == 1 && p.tok == _Lbrack {
|
||||
if p.allowGenerics() && len(names) == 1 && p.tok == _Lbrack {
|
||||
typ = p.arrayOrTArgs()
|
||||
if typ, ok := typ.(*IndexExpr); ok {
|
||||
// embedded type T[P1, P2, ...]
|
||||
@ -1708,7 +1710,7 @@ func (p *parser) methodDecl() *Field {
|
||||
f.Type = p.funcType()
|
||||
|
||||
case _Lbrack:
|
||||
if p.mode&AllowGenerics != 0 {
|
||||
if p.allowGenerics() {
|
||||
// Careful dance: We don't know if we have a generic method m[T C](x T)
|
||||
// or an embedded instantiated type T[P1, P2] (we accept generic methods
|
||||
// for generality and robustness of parsing).
|
||||
@ -1846,38 +1848,60 @@ func (p *parser) paramDeclOrNil(name *Name, follow token) *Field {
|
||||
defer p.trace("paramDecl")()
|
||||
}
|
||||
|
||||
f := new(Field)
|
||||
// type set notation is ok in type parameter lists
|
||||
typeSetsOk := p.mode&AllowTypeSets != 0 && follow == _Rbrack
|
||||
|
||||
pos := p.pos()
|
||||
if name != nil {
|
||||
f.pos = name.pos
|
||||
} else {
|
||||
f.pos = p.pos()
|
||||
pos = name.pos
|
||||
} else if typeSetsOk && p.tok == _Operator && p.op == Tilde {
|
||||
// "~" ...
|
||||
return p.embeddedElem(nil)
|
||||
}
|
||||
|
||||
f := new(Field)
|
||||
f.pos = pos
|
||||
|
||||
if p.tok == _Name || name != nil {
|
||||
// name
|
||||
if name == nil {
|
||||
name = p.name()
|
||||
}
|
||||
|
||||
if p.mode&AllowGenerics != 0 && p.tok == _Lbrack {
|
||||
if p.allowGenerics() && p.tok == _Lbrack {
|
||||
// name "[" ...
|
||||
f.Type = p.arrayOrTArgs()
|
||||
if typ, ok := f.Type.(*IndexExpr); ok {
|
||||
// name "[" ... "]"
|
||||
typ.X = name
|
||||
} else {
|
||||
// name "[" n "]" E
|
||||
f.Name = name
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
if p.tok == _Dot {
|
||||
// name_or_type
|
||||
// name "." ...
|
||||
f.Type = p.qualifiedName(name)
|
||||
if typeSetsOk && p.tok == _Operator && p.op == Or {
|
||||
// name "." name "|" ...
|
||||
f = p.embeddedElem(f)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
if typeSetsOk && p.tok == _Operator && p.op == Or {
|
||||
// name "|" ...
|
||||
f.Type = name
|
||||
return p.embeddedElem(f)
|
||||
}
|
||||
|
||||
f.Name = name
|
||||
}
|
||||
|
||||
if p.tok == _DotDotDot {
|
||||
// [name] "..." ...
|
||||
t := new(DotsType)
|
||||
t.pos = p.pos()
|
||||
p.next()
|
||||
@ -1890,7 +1914,17 @@ func (p *parser) paramDeclOrNil(name *Name, follow token) *Field {
|
||||
return f
|
||||
}
|
||||
|
||||
if typeSetsOk && p.tok == _Operator && p.op == Tilde {
|
||||
// [name] "~" ...
|
||||
f.Type = p.embeddedElem(nil).Type
|
||||
return f
|
||||
}
|
||||
|
||||
f.Type = p.typeOrNil()
|
||||
if typeSetsOk && p.tok == _Operator && p.op == Or && f.Type != nil {
|
||||
// [name] type "|"
|
||||
f = p.embeddedElem(f)
|
||||
}
|
||||
if f.Name != nil || f.Type != nil {
|
||||
return f
|
||||
}
|
||||
@ -1952,7 +1986,7 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []*
|
||||
if par.Type != nil {
|
||||
typ = par.Type
|
||||
if par.Name == nil {
|
||||
pos = typ.Pos()
|
||||
pos = StartPos(typ)
|
||||
par.Name = NewName(pos, "_")
|
||||
}
|
||||
} else if typ != nil {
|
||||
@ -2654,7 +2688,7 @@ func (p *parser) qualifiedName(name *Name) Expr {
|
||||
x = s
|
||||
}
|
||||
|
||||
if p.mode&AllowGenerics != 0 && p.tok == _Lbrack {
|
||||
if p.allowGenerics() && p.tok == _Lbrack {
|
||||
x = p.typeInstance(x)
|
||||
}
|
||||
|
||||
|
@ -26,11 +26,11 @@ var (
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics)
|
||||
ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics|AllowTypeSets)
|
||||
}
|
||||
|
||||
func TestVerify(t *testing.T) {
|
||||
ast, err := ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics)
|
||||
ast, err := ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics|AllowTypeSets)
|
||||
if err != nil {
|
||||
return // error already reported
|
||||
}
|
||||
@ -46,7 +46,7 @@ func TestParseGo2(t *testing.T) {
|
||||
for _, fi := range list {
|
||||
name := fi.Name()
|
||||
if !fi.IsDir() && !strings.HasPrefix(name, ".") {
|
||||
ParseFile(filepath.Join(dir, name), func(err error) { t.Error(err) }, nil, AllowGenerics|AllowTypeLists)
|
||||
ParseFile(filepath.Join(dir, name), func(err error) { t.Error(err) }, nil, AllowGenerics|AllowTypeSets|AllowTypeLists)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,6 +72,10 @@ var stringTests = []string{
|
||||
"package p; func (*R[A, B, C]) _()",
|
||||
"package p; func (_ *R[A, B, C]) _()",
|
||||
|
||||
// type constraint literals with elided interfaces (only if AllowTypeSets is set)
|
||||
"package p; func _[P ~int, Q int | string]() {}",
|
||||
"package p; func _[P struct{f int}, Q *P]() {}",
|
||||
|
||||
// channels
|
||||
"package p; type _ chan chan int",
|
||||
"package p; type _ chan (<-chan int)",
|
||||
@ -90,7 +94,7 @@ var stringTests = []string{
|
||||
|
||||
func TestPrintString(t *testing.T) {
|
||||
for _, want := range stringTests {
|
||||
ast, err := Parse(nil, strings.NewReader(want), nil, nil, AllowGenerics|AllowTypeLists)
|
||||
ast, err := Parse(nil, strings.NewReader(want), nil, nil, AllowGenerics|AllowTypeSets|AllowTypeLists)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
|
@ -17,6 +17,7 @@ type Mode uint
|
||||
const (
|
||||
CheckBranches Mode = 1 << iota // check correct use of labels, break, continue, and goto statements
|
||||
AllowGenerics
|
||||
AllowTypeSets // requires AllowGenerics; remove once #48424 is decided
|
||||
AllowTypeLists // requires AllowGenerics; remove once 1.18 is out
|
||||
)
|
||||
|
||||
|
72
src/cmd/compile/internal/syntax/testdata/typeset.go2
vendored
Normal file
72
src/cmd/compile/internal/syntax/testdata/typeset.go2
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
// 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.
|
||||
|
||||
// This file contains test cases for typeset-only constraint elements.
|
||||
// TODO(gri) gofmt once/if gofmt supports this notation.
|
||||
|
||||
package p
|
||||
|
||||
type (
|
||||
_[_ t] t
|
||||
_[_ ~t] t
|
||||
_[_ t|t] t
|
||||
_[_ ~t|t] t
|
||||
_[_ t|~t] t
|
||||
_[_ ~t|~t] t
|
||||
|
||||
_[_ t, _, _ t|t] t
|
||||
_[_ t, _, _ ~t|t] t
|
||||
_[_ t, _, _ t|~t] t
|
||||
_[_ t, _, _ ~t|~t] t
|
||||
|
||||
_[_ t.t] t
|
||||
_[_ ~t.t] t
|
||||
_[_ t.t|t.t] t
|
||||
_[_ ~t.t|t.t] t
|
||||
_[_ t.t|~t.t] t
|
||||
_[_ ~t.t|~t.t] t
|
||||
|
||||
_[_ t, _, _ t.t|t.t] t
|
||||
_[_ t, _, _ ~t.t|t.t] t
|
||||
_[_ t, _, _ t.t|~t.t] t
|
||||
_[_ t, _, _ ~t.t|~t.t] t
|
||||
|
||||
_[_ struct{}] t
|
||||
_[_ ~struct{}] t
|
||||
|
||||
_[_ struct{}|t] t
|
||||
_[_ ~struct{}|t] t
|
||||
_[_ struct{}|~t] t
|
||||
_[_ ~struct{}|~t] t
|
||||
|
||||
_[_ t|struct{}] t
|
||||
_[_ ~t|struct{}] t
|
||||
_[_ t|~struct{}] t
|
||||
_[_ ~t|~struct{}] t
|
||||
)
|
||||
|
||||
// Single-expression type parameter lists and those that don't start
|
||||
// with a (type parameter) name are considered array sizes.
|
||||
// The term must be a valid expression (it could be a type - and then
|
||||
// a type-checker will complain - but we don't allow ~ in the expr).
|
||||
type (
|
||||
_[t] t
|
||||
_[/* ERROR unexpected ~ */ ~t] t
|
||||
_[t|t] t
|
||||
_[/* ERROR unexpected ~ */ ~t|t] t
|
||||
_[t| /* ERROR unexpected ~ */ ~t] t
|
||||
_[/* ERROR unexpected ~ */ ~t|~t] t
|
||||
)
|
||||
|
||||
type (
|
||||
_[_ t, t /* ERROR missing type constraint */ ] t
|
||||
_[_ ~t, t /* ERROR missing type constraint */ ] t
|
||||
_[_ t, /* ERROR type parameters must be named */ ~t] t
|
||||
_[_ ~t, /* ERROR type parameters must be named */ ~t] t
|
||||
|
||||
_[_ t|t, /* ERROR type parameters must be named */ t|t] t
|
||||
_[_ ~t|t, /* ERROR type parameters must be named */ t|t] t
|
||||
_[_ t|t, /* ERROR type parameters must be named */ ~t|t] t
|
||||
_[_ ~t|t, /* ERROR type parameters must be named */ ~t|t] t
|
||||
)
|
Loading…
Reference in New Issue
Block a user