var mode Mode
if strings.HasSuffix(filename, ".go2") {
- mode = AllowGenerics | AllowTypeLists
+ mode = AllowGenerics | AllowTypeSets | AllowTypeLists
}
ParseFile(filename, func(err error) {
e, ok := err.(Error)
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 {
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()
}
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()
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)
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 {
}
default:
- if p.mode&AllowGenerics != 0 {
+ if p.allowGenerics() {
pos := p.pos()
if t := p.typeOrNil(); t != nil {
f := new(Field)
}
}
- 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)
// 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, ...]
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).
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()
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
}
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 {
x = s
}
- if p.mode&AllowGenerics != 0 && p.tok == _Lbrack {
+ if p.allowGenerics() && p.tok == _Lbrack {
x = p.typeInstance(x)
}
)
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
}
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)
}
}
}
"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)",
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
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
)
--- /dev/null
+// 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
+)