p.next()
}
-func (p *parser) parseTypeParams() bool {
- return p.mode&typeparams.DisallowParsing == 0
-}
+func (p *parser) allowGenerics() bool { return p.mode&typeparams.DisallowParsing == 0 }
+func (p *parser) allowTypeSets() bool { return p.mode&typeparams.DisallowTypeSets == 0 }
// ----------------------------------------------------------------------------
// Parsing support
}
typ := p.parseTypeName(ident)
- if p.tok == token.LBRACK && p.parseTypeParams() {
+ if p.tok == token.LBRACK && p.allowGenerics() {
typ = p.parseTypeInstance(typ)
}
// TODO(rfindley): consider changing parseRhsOrType so that this function variable
// is not needed.
argparser := p.parseRhsOrType
- if !p.parseTypeParams() {
+ if !p.allowGenerics() {
argparser = p.parseRhs
}
if p.tok != token.RBRACK {
// x [P]E
return x, &ast.ArrayType{Lbrack: lbrack, Len: args[0], Elt: elt}
}
- if !p.parseTypeParams() {
+ if !p.allowGenerics() {
p.error(rbrack, "missing element type in array type expression")
return nil, &ast.BadExpr{From: args[0].Pos(), To: args[0].End()}
}
}
- if !p.parseTypeParams() {
+ if !p.allowGenerics() {
p.error(firstComma, "expected ']', found ','")
return x, &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()}
}
typ ast.Expr
}
-func (p *parser) parseParamDecl(name *ast.Ident) (f field) {
- // TODO(rFindley) compare with parser.paramDeclOrNil in the syntax package
+func (p *parser) parseParamDecl(name *ast.Ident, typeSetsOK bool) (f field) {
+ // TODO(rFindley) refactor to be more similar to paramDeclOrNil in the syntax
+ // package
if p.trace {
defer un(trace(p, "ParamDeclOrNil"))
}
ptok := p.tok
if name != nil {
p.tok = token.IDENT // force token.IDENT case in switch below
+ } else if typeSetsOK && p.tok == token.TILDE {
+ // "~" ...
+ return field{nil, p.embeddedElem(nil)}
}
switch p.tok {
case token.IDENT:
+ // name
if name != nil {
f.name = name
p.tok = ptok
f.typ = p.parseType()
case token.LBRACK:
- // name[type1, type2, ...] or name []type or name [len]type
+ // name "[" type1, ..., typeN "]" or name "[" n "]" type
f.name, f.typ = p.parseArrayFieldOrTypeInstance(f.name)
case token.ELLIPSIS:
- // name ...type
+ // name "..." type
f.typ = p.parseDotsType()
+ return // don't allow ...type "|" ...
case token.PERIOD:
- // qualified.typename
+ // name "." ...
f.typ = p.parseQualifiedIdent(f.name)
f.name = nil
+
+ case token.TILDE:
+ if typeSetsOK {
+ f.typ = p.embeddedElem(nil)
+ return
+ }
+
+ case token.OR:
+ if typeSetsOK {
+ // name "|" typeset
+ f.typ = p.embeddedElem(f.name)
+ f.name = nil
+ return
+ }
}
case token.MUL, token.ARROW, token.FUNC, token.LBRACK, token.CHAN, token.MAP, token.STRUCT, token.INTERFACE, token.LPAREN:
f.typ = p.parseType()
case token.ELLIPSIS:
- // ...type
+ // "..." type
// (always accepted)
f.typ = p.parseDotsType()
+ return // don't allow ...type "|" ...
default:
+ // TODO(rfindley): this looks incorrect in the case of type parameter
+ // lists.
p.errorExpected(p.pos, ")")
p.advance(exprEnd)
}
+ // [name] type "|"
+ if typeSetsOK && p.tok == token.OR && f.typ != nil {
+ f.typ = p.embeddedElem(f.typ)
+ }
+
return
}
-func (p *parser) parseParameterList(name0 *ast.Ident, closing token.Token, parseParamDecl func(*ast.Ident) field, tparams bool) (params []*ast.Field) {
+func (p *parser) parseParameterList(name0 *ast.Ident, closing token.Token) (params []*ast.Field) {
if p.trace {
defer un(trace(p, "ParameterList"))
}
+ // Type parameters are the only parameter list closed by ']'.
+ tparams := closing == token.RBRACK
+ // Type set notation is ok in type parameter lists.
+ typeSetsOK := tparams && p.allowTypeSets()
+
pos := p.pos
if name0 != nil {
pos = name0.Pos()
var named int // number of parameters that have an explicit name and type
for name0 != nil || p.tok != closing && p.tok != token.EOF {
- par := parseParamDecl(name0)
+ par := p.parseParamDecl(name0, typeSetsOK)
name0 = nil // 1st name was consumed if present
if par.name != nil || par.typ != nil {
list = append(list, par)
// some named => all must be named
ok := true
var typ ast.Expr
+ missingName := pos
for i := len(list) - 1; i >= 0; i-- {
if par := &list[i]; par.typ != nil {
typ = par.typ
if par.name == nil {
ok = false
+ missingName = par.typ.Pos()
n := ast.NewIdent("_")
n.NamePos = typ.Pos() // correct position
par.name = n
} else {
// par.typ == nil && typ == nil => we only have a par.name
ok = false
+ missingName = par.name.Pos()
par.typ = &ast.BadExpr{From: par.name.Pos(), To: p.pos}
}
}
if !ok {
if tparams {
- p.error(pos, "all type parameters must be named")
+ p.error(missingName, "all type parameters must be named")
} else {
p.error(pos, "mixed named and unnamed parameters")
}
defer un(trace(p, "Parameters"))
}
- if p.parseTypeParams() && acceptTParams && p.tok == token.LBRACK {
+ if p.allowGenerics() && acceptTParams && p.tok == token.LBRACK {
opening := p.pos
p.next()
// [T any](params) syntax
- list := p.parseParameterList(nil, token.RBRACK, p.parseParamDecl, true)
+ list := p.parseParameterList(nil, token.RBRACK)
rbrack := p.expect(token.RBRACK)
tparams = &ast.FieldList{Opening: opening, List: list, Closing: rbrack}
// Type parameter lists must not be empty.
var fields []*ast.Field
if p.tok != token.RPAREN {
- fields = p.parseParameterList(nil, token.RPAREN, p.parseParamDecl, false)
+ fields = p.parseParameterList(nil, token.RPAREN)
}
rparen := p.expect(token.RPAREN)
x := p.parseTypeName(nil)
if ident, _ := x.(*ast.Ident); ident != nil {
switch {
- case p.tok == token.LBRACK && p.parseTypeParams():
+ case p.tok == token.LBRACK && p.allowGenerics():
// generic method or embedded instantiated type
lbrack := p.pos
p.next()
p.exprLev--
if name0, _ := x.(*ast.Ident); name0 != nil && p.tok != token.COMMA && p.tok != token.RBRACK {
// generic method m[T any]
- list := p.parseParameterList(name0, token.RBRACK, p.parseParamDecl, true)
+ list := p.parseParameterList(name0, token.RBRACK)
rbrack := p.expect(token.RBRACK)
tparams := &ast.FieldList{Opening: lbrack, List: list, Closing: rbrack}
// TODO(rfindley) refactor to share code with parseFuncType.
} else {
// embedded, possibly instantiated type
typ = x
- if p.tok == token.LBRACK && p.parseTypeParams() {
+ if p.tok == token.LBRACK && p.allowGenerics() {
// embedded instantiated interface
typ = p.parseTypeInstance(typ)
}
return &ast.Field{Doc: doc, Names: idents, Type: typ}
}
-func (p *parser) embeddedElem(f *ast.Field) *ast.Field {
+func (p *parser) embeddedElem(x ast.Expr) ast.Expr {
if p.trace {
defer un(trace(p, "EmbeddedElem"))
}
- if f == nil {
- f = new(ast.Field)
- f.Type = p.embeddedTerm()
+ if x == nil {
+ x = p.embeddedTerm()
}
for p.tok == token.OR {
t := new(ast.BinaryExpr)
t.OpPos = p.pos
t.Op = token.OR
p.next()
- t.X = f.Type
+ t.X = x
t.Y = p.embeddedTerm()
- f.Type = t
+ x = t
}
- return f
+ return x
}
func (p *parser) embeddedTerm() ast.Expr {
switch {
case p.tok == token.IDENT:
f := p.parseMethodSpec()
- if f.Names == nil && p.parseTypeParams() {
- f = p.embeddedElem(f)
+ if f.Names == nil && p.allowGenerics() {
+ f.Type = p.embeddedElem(f.Type)
}
p.expectSemi()
f.Comment = p.lineComment
list = append(list, f)
- case p.tok == token.TILDE && p.parseTypeParams():
- f := p.embeddedElem(nil)
+ case p.tok == token.TILDE && p.allowGenerics():
+ typ := p.embeddedElem(nil)
p.expectSemi()
- f.Comment = p.lineComment
- list = append(list, f)
- case p.tok == token.TYPE && p.parseTypeParams():
+ comment := p.lineComment
+ list = append(list, &ast.Field{Type: typ, Comment: comment})
+ case p.tok == token.TYPE && p.allowGenerics():
// TODO(rfindley): remove TypeList syntax and refactor the clauses above.
// all types in a type list share the same field name "type"
list = append(list, &ast.Field{Names: name, Type: typ})
}
p.expectSemi()
- case p.parseTypeParams():
+ case p.allowGenerics():
if t := p.tryIdentOrType(); t != nil {
- f := new(ast.Field)
- f.Type = t
- f = p.embeddedElem(f)
+ typ := p.embeddedElem(t)
p.expectSemi()
- f.Comment = p.lineComment
- list = append(list, f)
+ comment := p.lineComment
+ list = append(list, &ast.Field{Type: typ, Comment: comment})
} else {
break parseElements
}
}
func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr {
- assert(p.parseTypeParams(), "parseTypeInstance while not parsing type params")
+ assert(p.allowGenerics(), "parseTypeInstance while not parsing type params")
if p.trace {
defer un(trace(p, "TypeInstance"))
}
switch p.tok {
case token.IDENT:
typ := p.parseTypeName(nil)
- if p.tok == token.LBRACK && p.parseTypeParams() {
+ if p.tok == token.LBRACK && p.allowGenerics() {
typ = p.parseTypeInstance(typ)
}
return typ
return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}
}
- if !p.parseTypeParams() {
+ if !p.allowGenerics() {
p.error(firstComma, "expected ']' or ':', found ','")
return &ast.BadExpr{From: args[0].Pos(), To: args[len(args)-1].End()}
}
}
func (p *parser) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 *ast.Ident) {
- list := p.parseParameterList(name0, token.RBRACK, p.parseParamDecl, true)
+ list := p.parseParameterList(name0, token.RBRACK)
closePos := p.expect(token.RBRACK)
spec.TypeParams = &ast.FieldList{Opening: openPos, List: list, Closing: closePos}
// Type alias cannot have type parameters. Accept them for robustness but complain.
p.exprLev++
x := p.parseExpr()
p.exprLev--
- if name0, _ := x.(*ast.Ident); p.parseTypeParams() && name0 != nil && p.tok != token.RBRACK {
+ if name0, _ := x.(*ast.Ident); p.allowGenerics() && name0 != nil && p.tok != token.RBRACK {
// generic type [T any];
p.parseGenericType(spec, lbrack, name0)
} else {
--- /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).
+// TODO(rfindley): Improve error recover here. In these cases go/parser error
+// recovery is worse than cmd/compile/internal/syntax, and unnecessary type
+// declarations had to be inserted to force synchronization.
+type _[t] t
+type _[~ /* ERROR "expected operand" */ t] t
+type /* ERROR "expected ']'" */ Sync int // placeholder to synchronize the parser
+type _[t|t] t
+type _[~ /* ERROR "expected operand" */ t|t] t
+type /* ERROR "expected ']'" */ Sync int // placeholder to synchronize the parser
+type _[t| ~ /* ERROR "expected operand" */ t] t
+type /* ERROR "expected ']'" */ Sync int // placeholder to synchronize the parser
+type _[~ /* ERROR "expected operand" */ t|~t] t
+type /* ERROR "expected ']'" */ Sync int // placeholder to synchronize the parser
+
+type _[_ t, t /* ERROR "type parameters must be named" */ ] t
+type _[_ ~t, t /* ERROR "type parameters must be named" */ ] t
+type _[_ t, ~ /* ERROR "type parameters must be named" */ t] t
+type _[_ ~t, ~ /* ERROR "type parameters must be named" */ t] t
+
+type _[_ t|t, t /* ERROR "type parameters must be named" */ |t] t
+type _[_ ~t|t, t /* ERROR "type parameters must be named" */ |t] t
+type _[_ t|t, ~ /* ERROR "type parameters must be named" */ t|t] t
+type _[_ ~t|t, ~ /* ERROR "type parameters must be named" */ t|t] t