Skip to content

Commit 5279e53

Browse files
committed
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 <[email protected]> Run-TryBot: Robert Griesemer <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent f19b2d5 commit 5279e53

File tree

6 files changed

+133
-22
lines changed

6 files changed

+133
-22
lines changed

src/cmd/compile/internal/syntax/error_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ func testSyntaxErrors(t *testing.T, filename string) {
130130

131131
var mode Mode
132132
if strings.HasSuffix(filename, ".go2") {
133-
mode = AllowGenerics | AllowTypeLists
133+
mode = AllowGenerics | AllowTypeSets | AllowTypeLists
134134
}
135135
ParseFile(filename, func(err error) {
136136
e, ok := err.(Error)

src/cmd/compile/internal/syntax/parser.go

+51-17
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ func (p *parser) init(file *PosBase, r io.Reader, errh ErrorHandler, pragh Pragm
8787
p.indent = nil
8888
}
8989

90+
func (p *parser) allowGenerics() bool { return p.mode&AllowGenerics != 0 }
91+
9092
// takePragma returns the current parsed pragmas
9193
// and clears them from the parser state.
9294
func (p *parser) takePragma() Pragma {
@@ -597,7 +599,7 @@ func (p *parser) typeDecl(group *Group) Decl {
597599
p.xnest++
598600
x := p.expr()
599601
p.xnest--
600-
if name0, ok := x.(*Name); p.mode&AllowGenerics != 0 && ok && p.tok != _Rbrack {
602+
if name0, ok := x.(*Name); p.allowGenerics() && ok && p.tok != _Rbrack {
601603
// generic type
602604
d.TParamList = p.paramList(name0, _Rbrack, true)
603605
pos := p.pos()
@@ -687,7 +689,7 @@ func (p *parser) funcDeclOrNil() *FuncDecl {
687689
}
688690

689691
f.Name = p.name()
690-
if p.mode&AllowGenerics != 0 && p.got(_Lbrack) {
692+
if p.allowGenerics() && p.got(_Lbrack) {
691693
if p.tok == _Rbrack {
692694
p.syntaxError("empty type parameter list")
693695
p.next()
@@ -1425,7 +1427,7 @@ func (p *parser) interfaceType() *InterfaceType {
14251427
switch p.tok {
14261428
case _Name:
14271429
f := p.methodDecl()
1428-
if f.Name == nil && p.mode&AllowGenerics != 0 {
1430+
if f.Name == nil && p.allowGenerics() {
14291431
f = p.embeddedElem(f)
14301432
}
14311433
typ.MethodList = append(typ.MethodList, f)
@@ -1443,14 +1445,14 @@ func (p *parser) interfaceType() *InterfaceType {
14431445
return false
14441446

14451447
case _Operator:
1446-
if p.op == Tilde && p.mode&AllowGenerics != 0 {
1448+
if p.op == Tilde && p.allowGenerics() {
14471449
typ.MethodList = append(typ.MethodList, p.embeddedElem(nil))
14481450
return false
14491451
}
14501452

14511453
case _Type:
14521454
// TODO(gri) remove TypeList syntax if we accept #45346
1453-
if p.mode&AllowGenerics != 0 && p.mode&AllowTypeLists != 0 {
1455+
if p.allowGenerics() && p.mode&AllowTypeLists != 0 {
14541456
type_ := NewName(p.pos(), "type") // cannot have a method named "type"
14551457
p.next()
14561458
if p.tok != _Semi && p.tok != _Rbrace {
@@ -1473,7 +1475,7 @@ func (p *parser) interfaceType() *InterfaceType {
14731475
}
14741476

14751477
default:
1476-
if p.mode&AllowGenerics != 0 {
1478+
if p.allowGenerics() {
14771479
pos := p.pos()
14781480
if t := p.typeOrNil(); t != nil {
14791481
f := new(Field)
@@ -1485,7 +1487,7 @@ func (p *parser) interfaceType() *InterfaceType {
14851487
}
14861488
}
14871489

1488-
if p.mode&AllowGenerics != 0 {
1490+
if p.allowGenerics() {
14891491
if p.mode&AllowTypeLists != 0 {
14901492
p.syntaxError("expecting method, type list, or embedded element")
14911493
p.advance(_Semi, _Rbrace, _Type)
@@ -1570,7 +1572,7 @@ func (p *parser) fieldDecl(styp *StructType) {
15701572

15711573
// Careful dance: We don't know if we have an embedded instantiated
15721574
// type T[P1, P2, ...] or a field T of array/slice type [P]E or []E.
1573-
if p.mode&AllowGenerics != 0 && len(names) == 1 && p.tok == _Lbrack {
1575+
if p.allowGenerics() && len(names) == 1 && p.tok == _Lbrack {
15741576
typ = p.arrayOrTArgs()
15751577
if typ, ok := typ.(*IndexExpr); ok {
15761578
// embedded type T[P1, P2, ...]
@@ -1708,7 +1710,7 @@ func (p *parser) methodDecl() *Field {
17081710
f.Type = p.funcType()
17091711

17101712
case _Lbrack:
1711-
if p.mode&AllowGenerics != 0 {
1713+
if p.allowGenerics() {
17121714
// Careful dance: We don't know if we have a generic method m[T C](x T)
17131715
// or an embedded instantiated type T[P1, P2] (we accept generic methods
17141716
// for generality and robustness of parsing).
@@ -1846,38 +1848,60 @@ func (p *parser) paramDeclOrNil(name *Name, follow token) *Field {
18461848
defer p.trace("paramDecl")()
18471849
}
18481850

1849-
f := new(Field)
1851+
// type set notation is ok in type parameter lists
1852+
typeSetsOk := p.mode&AllowTypeSets != 0 && follow == _Rbrack
1853+
1854+
pos := p.pos()
18501855
if name != nil {
1851-
f.pos = name.pos
1852-
} else {
1853-
f.pos = p.pos()
1856+
pos = name.pos
1857+
} else if typeSetsOk && p.tok == _Operator && p.op == Tilde {
1858+
// "~" ...
1859+
return p.embeddedElem(nil)
18541860
}
18551861

1862+
f := new(Field)
1863+
f.pos = pos
1864+
18561865
if p.tok == _Name || name != nil {
1866+
// name
18571867
if name == nil {
18581868
name = p.name()
18591869
}
18601870

1861-
if p.mode&AllowGenerics != 0 && p.tok == _Lbrack {
1871+
if p.allowGenerics() && p.tok == _Lbrack {
1872+
// name "[" ...
18621873
f.Type = p.arrayOrTArgs()
18631874
if typ, ok := f.Type.(*IndexExpr); ok {
1875+
// name "[" ... "]"
18641876
typ.X = name
18651877
} else {
1878+
// name "[" n "]" E
18661879
f.Name = name
18671880
}
18681881
return f
18691882
}
18701883

18711884
if p.tok == _Dot {
1872-
// name_or_type
1885+
// name "." ...
18731886
f.Type = p.qualifiedName(name)
1887+
if typeSetsOk && p.tok == _Operator && p.op == Or {
1888+
// name "." name "|" ...
1889+
f = p.embeddedElem(f)
1890+
}
18741891
return f
18751892
}
18761893

1894+
if typeSetsOk && p.tok == _Operator && p.op == Or {
1895+
// name "|" ...
1896+
f.Type = name
1897+
return p.embeddedElem(f)
1898+
}
1899+
18771900
f.Name = name
18781901
}
18791902

18801903
if p.tok == _DotDotDot {
1904+
// [name] "..." ...
18811905
t := new(DotsType)
18821906
t.pos = p.pos()
18831907
p.next()
@@ -1890,7 +1914,17 @@ func (p *parser) paramDeclOrNil(name *Name, follow token) *Field {
18901914
return f
18911915
}
18921916

1917+
if typeSetsOk && p.tok == _Operator && p.op == Tilde {
1918+
// [name] "~" ...
1919+
f.Type = p.embeddedElem(nil).Type
1920+
return f
1921+
}
1922+
18931923
f.Type = p.typeOrNil()
1924+
if typeSetsOk && p.tok == _Operator && p.op == Or && f.Type != nil {
1925+
// [name] type "|"
1926+
f = p.embeddedElem(f)
1927+
}
18941928
if f.Name != nil || f.Type != nil {
18951929
return f
18961930
}
@@ -1952,7 +1986,7 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []*
19521986
if par.Type != nil {
19531987
typ = par.Type
19541988
if par.Name == nil {
1955-
pos = typ.Pos()
1989+
pos = StartPos(typ)
19561990
par.Name = NewName(pos, "_")
19571991
}
19581992
} else if typ != nil {
@@ -2654,7 +2688,7 @@ func (p *parser) qualifiedName(name *Name) Expr {
26542688
x = s
26552689
}
26562690

2657-
if p.mode&AllowGenerics != 0 && p.tok == _Lbrack {
2691+
if p.allowGenerics() && p.tok == _Lbrack {
26582692
x = p.typeInstance(x)
26592693
}
26602694

src/cmd/compile/internal/syntax/parser_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ var (
2626
)
2727

2828
func TestParse(t *testing.T) {
29-
ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics)
29+
ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics|AllowTypeSets)
3030
}
3131

3232
func TestVerify(t *testing.T) {
33-
ast, err := ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics)
33+
ast, err := ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics|AllowTypeSets)
3434
if err != nil {
3535
return // error already reported
3636
}
@@ -46,7 +46,7 @@ func TestParseGo2(t *testing.T) {
4646
for _, fi := range list {
4747
name := fi.Name()
4848
if !fi.IsDir() && !strings.HasPrefix(name, ".") {
49-
ParseFile(filepath.Join(dir, name), func(err error) { t.Error(err) }, nil, AllowGenerics|AllowTypeLists)
49+
ParseFile(filepath.Join(dir, name), func(err error) { t.Error(err) }, nil, AllowGenerics|AllowTypeSets|AllowTypeLists)
5050
}
5151
}
5252
}

src/cmd/compile/internal/syntax/printer_test.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ var stringTests = []string{
7272
"package p; func (*R[A, B, C]) _()",
7373
"package p; func (_ *R[A, B, C]) _()",
7474

75+
// type constraint literals with elided interfaces (only if AllowTypeSets is set)
76+
"package p; func _[P ~int, Q int | string]() {}",
77+
"package p; func _[P struct{f int}, Q *P]() {}",
78+
7579
// channels
7680
"package p; type _ chan chan int",
7781
"package p; type _ chan (<-chan int)",
@@ -90,7 +94,7 @@ var stringTests = []string{
9094

9195
func TestPrintString(t *testing.T) {
9296
for _, want := range stringTests {
93-
ast, err := Parse(nil, strings.NewReader(want), nil, nil, AllowGenerics|AllowTypeLists)
97+
ast, err := Parse(nil, strings.NewReader(want), nil, nil, AllowGenerics|AllowTypeSets|AllowTypeLists)
9498
if err != nil {
9599
t.Error(err)
96100
continue

src/cmd/compile/internal/syntax/syntax.go

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type Mode uint
1717
const (
1818
CheckBranches Mode = 1 << iota // check correct use of labels, break, continue, and goto statements
1919
AllowGenerics
20+
AllowTypeSets // requires AllowGenerics; remove once #48424 is decided
2021
AllowTypeLists // requires AllowGenerics; remove once 1.18 is out
2122
)
2223

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// This file contains test cases for typeset-only constraint elements.
6+
// TODO(gri) gofmt once/if gofmt supports this notation.
7+
8+
package p
9+
10+
type (
11+
_[_ t] t
12+
_[_ ~t] t
13+
_[_ t|t] t
14+
_[_ ~t|t] t
15+
_[_ t|~t] t
16+
_[_ ~t|~t] t
17+
18+
_[_ t, _, _ t|t] t
19+
_[_ t, _, _ ~t|t] t
20+
_[_ t, _, _ t|~t] t
21+
_[_ t, _, _ ~t|~t] t
22+
23+
_[_ t.t] t
24+
_[_ ~t.t] t
25+
_[_ t.t|t.t] t
26+
_[_ ~t.t|t.t] t
27+
_[_ t.t|~t.t] t
28+
_[_ ~t.t|~t.t] t
29+
30+
_[_ t, _, _ t.t|t.t] t
31+
_[_ t, _, _ ~t.t|t.t] t
32+
_[_ t, _, _ t.t|~t.t] t
33+
_[_ t, _, _ ~t.t|~t.t] t
34+
35+
_[_ struct{}] t
36+
_[_ ~struct{}] t
37+
38+
_[_ struct{}|t] t
39+
_[_ ~struct{}|t] t
40+
_[_ struct{}|~t] t
41+
_[_ ~struct{}|~t] t
42+
43+
_[_ t|struct{}] t
44+
_[_ ~t|struct{}] t
45+
_[_ t|~struct{}] t
46+
_[_ ~t|~struct{}] t
47+
)
48+
49+
// Single-expression type parameter lists and those that don't start
50+
// with a (type parameter) name are considered array sizes.
51+
// The term must be a valid expression (it could be a type - and then
52+
// a type-checker will complain - but we don't allow ~ in the expr).
53+
type (
54+
_[t] t
55+
_[/* ERROR unexpected ~ */ ~t] t
56+
_[t|t] t
57+
_[/* ERROR unexpected ~ */ ~t|t] t
58+
_[t| /* ERROR unexpected ~ */ ~t] t
59+
_[/* ERROR unexpected ~ */ ~t|~t] t
60+
)
61+
62+
type (
63+
_[_ t, t /* ERROR missing type constraint */ ] t
64+
_[_ ~t, t /* ERROR missing type constraint */ ] t
65+
_[_ t, /* ERROR type parameters must be named */ ~t] t
66+
_[_ ~t, /* ERROR type parameters must be named */ ~t] t
67+
68+
_[_ t|t, /* ERROR type parameters must be named */ t|t] t
69+
_[_ ~t|t, /* ERROR type parameters must be named */ t|t] t
70+
_[_ t|t, /* ERROR type parameters must be named */ ~t|t] t
71+
_[_ ~t|t, /* ERROR type parameters must be named */ ~t|t] t
72+
)

0 commit comments

Comments
 (0)