aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/syntax
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2020-10-12 14:07:31 -0700
committerRobert Griesemer <gri@golang.org>2020-10-13 04:38:33 +0000
commitb627988b0ca748a73867ba5fc4cfc70031c028d2 (patch)
treedf4e18f0f902b6fbc425079bfcc1397940e562dd /src/cmd/compile/internal/syntax
parent617b6339170a56a235625f8359730ba710d3ae96 (diff)
downloadgo-b627988b0ca748a73867ba5fc4cfc70031c028d2.tar.gz
go-b627988b0ca748a73867ba5fc4cfc70031c028d2.zip
[dev.typeparams] cmd/compile/internal/syntax: implement parsing of type parameters
Port from dev.go2go prototype branch. The compiler doesn't yet set the syntax.AllowGenerics mode, so parsing of generic code remains disabled. Known issue: The doc strings documenting the specific syntax accepted by parser methods are not all up-to-date. Change-Id: I13d134289fd9330fd0ed7f97c997cca6f23466fd Reviewed-on: https://go-review.googlesource.com/c/go/+/261658 Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Griesemer <gri@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Diffstat (limited to 'src/cmd/compile/internal/syntax')
-rw-r--r--src/cmd/compile/internal/syntax/parser.go643
-rw-r--r--src/cmd/compile/internal/syntax/syntax.go1
2 files changed, 442 insertions, 202 deletions
diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go
index 146f83ed01..126b87b4ee 100644
--- a/src/cmd/compile/internal/syntax/parser.go
+++ b/src/cmd/compile/internal/syntax/parser.go
@@ -459,20 +459,21 @@ func isEmptyFuncDecl(dcl Decl) bool {
// Declarations
// list parses a possibly empty, sep-separated list, optionally
-// followed by sep and enclosed by ( and ) or { and }. open is
-// one of _Lparen, or _Lbrace, sep is one of _Comma or _Semi,
-// and close is expected to be the (closing) opposite of open.
+// followed sep, and closed by close. sep must be one of _Comma
+// or _Semi, and close must be one of _Rparen, _Rbrace, or _Rbrack.
// For each list element, f is called. After f returns true, no
// more list elements are accepted. list returns the position
// of the closing token.
//
-// list = "(" { f sep } ")" |
-// "{" { f sep } "}" . // sep is optional before ")" or "}"
+// list = { f sep } ")" |
+// { f sep } "}" . // "," or ";" is optional before ")", "}" or "]"
//
-func (p *parser) list(open, sep, close token, f func() bool) Pos {
- p.want(open)
+func (p *parser) list(sep, close token, f func() bool) Pos {
+ if debug && (sep != _Comma && sep != _Semi || close != _Rparen && close != _Rbrace && close != _Rbrack) {
+ panic("invalid sep or close argument for list")
+ }
- var done bool
+ done := false
for p.tok != _EOF && p.tok != close && !done {
done = f()
// sep is optional before close
@@ -496,7 +497,8 @@ func (p *parser) appendGroup(list []Decl, f func(*Group) Decl) []Decl {
if p.tok == _Lparen {
g := new(Group)
p.clearPragma()
- p.list(_Lparen, _Semi, _Rparen, func() bool {
+ p.next() // must consume "(" after calling clearPragma!
+ p.list(_Semi, _Rparen, func() bool {
list = append(list, f(g))
return false
})
@@ -566,7 +568,7 @@ func (p *parser) constDecl(group *Group) Decl {
return d
}
-// TypeSpec = identifier [ "=" ] Type .
+// TypeSpec = identifier [ TypeParams ] [ "=" ] Type .
func (p *parser) typeDecl(group *Group) Decl {
if trace {
defer p.trace("typeDecl")()
@@ -578,8 +580,42 @@ func (p *parser) typeDecl(group *Group) Decl {
d.Pragma = p.takePragma()
d.Name = p.name()
- d.Alias = p.gotAssign()
- d.Type = p.typeOrNil()
+ if p.tok == _Lbrack {
+ // array/slice or generic type
+ pos := p.pos()
+ p.next()
+ switch p.tok {
+ case _Rbrack:
+ p.next()
+ d.Type = p.sliceType(pos)
+ case _Name:
+ // array or generic type
+ p.xnest++
+ x := p.expr()
+ p.xnest--
+ if name0, ok := x.(*Name); p.mode&AllowGenerics != 0 && ok && p.tok != _Rbrack {
+ // generic type
+ d.TParamList = p.paramList(name0, _Rbrack)
+ pos := p.pos()
+ if p.gotAssign() {
+ p.syntaxErrorAt(pos, "generic type cannot be alias")
+ }
+ d.Type = p.typeOrNil()
+ } else {
+ // x is the array length expression
+ if debug && x == nil {
+ panic("internal error: nil expression")
+ }
+ d.Type = p.arrayType(pos, x)
+ }
+ default:
+ d.Type = p.arrayType(pos, nil)
+ }
+ } else {
+ d.Alias = p.gotAssign()
+ d.Type = p.typeOrNil()
+ }
+
if d.Type == nil {
d.Type = p.badExpr()
p.syntaxError("in type declaration")
@@ -613,7 +649,7 @@ func (p *parser) varDecl(group *Group) Decl {
return d
}
-// FunctionDecl = "func" FunctionName ( Function | Signature ) .
+// FunctionDecl = "func" FunctionName [ TypeParams ] ( Function | Signature ) .
// FunctionName = identifier .
// Function = Signature FunctionBody .
// MethodDecl = "func" Receiver MethodName ( Function | Signature ) .
@@ -627,8 +663,8 @@ func (p *parser) funcDeclOrNil() *FuncDecl {
f.pos = p.pos()
f.Pragma = p.takePragma()
- if p.tok == _Lparen {
- rcvr := p.paramList()
+ if p.got(_Lparen) {
+ rcvr := p.paramList(nil, _Rparen)
switch len(rcvr) {
case 0:
p.error("method has no receiver")
@@ -647,6 +683,14 @@ func (p *parser) funcDeclOrNil() *FuncDecl {
}
f.Name = p.name()
+ if p.mode&AllowGenerics != 0 && p.got(_Lbrack) {
+ if p.tok == _Rbrack {
+ p.syntaxError("empty type parameter list")
+ p.next()
+ } else {
+ f.TParamList = p.paramList(nil, _Rbrack)
+ }
+ }
f.Type = p.funcType()
if p.tok == _Lbrace {
f.Body = p.funcBody()
@@ -850,13 +894,7 @@ func (p *parser) operand(keep_parens bool) Expr {
// Optimization: Record presence of ()'s only where needed
// for error reporting. Don't bother in other cases; it is
// just a waste of memory and time.
-
- // Parentheses are not permitted on lhs of := .
- // switch x.Op {
- // case ONAME, ONONAME, OPACK, OTYPE, OLITERAL, OTYPESW:
- // keep_parens = true
- // }
-
+ //
// Parentheses are not permitted around T in a composite
// literal T{}. If the next token is a {, assume x is a
// composite literal type T (it may not be, { could be
@@ -879,19 +917,19 @@ func (p *parser) operand(keep_parens bool) Expr {
case _Func:
pos := p.pos()
p.next()
- t := p.funcType()
+ ftyp := p.funcType()
if p.tok == _Lbrace {
p.xnest++
f := new(FuncLit)
f.pos = pos
- f.Type = t
+ f.Type = ftyp
f.Body = p.funcBody()
p.xnest--
return f
}
- return t
+ return ftyp
case _Lbrack, _Chan, _Map, _Struct, _Interface:
return p.type_() // othertype
@@ -971,6 +1009,14 @@ loop:
case _Lbrack:
p.next()
+
+ if p.tok == _Rbrack {
+ // invalid empty instance, slice or index expression; accept but complain
+ p.syntaxError("expecting operand")
+ p.next()
+ break
+ }
+
p.xnest++
var i Expr
@@ -986,6 +1032,20 @@ loop:
p.xnest--
break
}
+
+ if p.mode&AllowGenerics != 0 && p.tok == _Comma {
+ // x[i, ... (instantiated type)
+ // TODO(gri) Suggestion by mdempsky@: Use IndexExpr + ExprList for this case.
+ // Then we can get rid of CallExpr.Brackets.
+ t := new(CallExpr)
+ t.pos = pos
+ t.Fun = x
+ t.ArgList, _ = p.argList(i, _Rbrack)
+ t.Brackets = true
+ x = t
+ p.xnest--
+ break
+ }
}
// x[i:...
@@ -1022,8 +1082,9 @@ loop:
case _Lparen:
t := new(CallExpr)
t.pos = pos
+ p.next()
t.Fun = x
- t.ArgList, t.HasDots = p.argList()
+ t.ArgList, t.HasDots = p.argList(nil, _Rparen)
x = t
case _Lbrace:
@@ -1032,10 +1093,20 @@ loop:
t := unparen(x)
// determine if '{' belongs to a composite literal or a block statement
complit_ok := false
- switch t.(type) {
+ switch t := t.(type) {
case *Name, *SelectorExpr:
if p.xnest >= 0 {
- // x is considered a composite literal type
+ // x is possibly a composite literal type
+ complit_ok = true
+ }
+ case *CallExpr:
+ if t.Brackets && p.xnest >= 0 {
+ // x is possibly a composite literal type
+ complit_ok = true
+ }
+ case *IndexExpr:
+ if p.xnest >= 0 {
+ // x is possibly a composite literal type
complit_ok = true
}
case *ArrayType, *SliceType, *StructType, *MapType:
@@ -1085,7 +1156,8 @@ func (p *parser) complitexpr() *CompositeLit {
x.pos = p.pos()
p.xnest++
- x.Rbrace = p.list(_Lbrace, _Comma, _Rbrace, func() bool {
+ p.want(_Lbrace)
+ x.Rbrace = p.list(_Comma, _Rbrace, func() bool {
// value
e := p.bare_complitexpr()
if p.tok == _Colon {
@@ -1170,26 +1242,10 @@ func (p *parser) typeOrNil() Expr {
// '[' oexpr ']' ntype
// '[' _DotDotDot ']' ntype
p.next()
- p.xnest++
if p.got(_Rbrack) {
- // []T
- p.xnest--
- t := new(SliceType)
- t.pos = pos
- t.Elem = p.type_()
- return t
+ return p.sliceType(pos)
}
-
- // [n]T
- t := new(ArrayType)
- t.pos = pos
- if !p.got(_DotDotDot) {
- t.Len = p.expr()
- }
- p.want(_Rbrack)
- p.xnest--
- t.Elem = p.type_()
- return t
+ return p.arrayType(pos, nil)
case _Chan:
// _Chan non_recvchantype
@@ -1221,7 +1277,7 @@ func (p *parser) typeOrNil() Expr {
return p.interfaceType()
case _Name:
- return p.dotname(p.name())
+ return p.qualifiedName(nil)
case _Lparen:
p.next()
@@ -1233,6 +1289,27 @@ func (p *parser) typeOrNil() Expr {
return nil
}
+func (p *parser) typeInstance(typ Expr) Expr {
+ if trace {
+ defer p.trace("typeInstance")()
+ }
+
+ pos := p.pos()
+ p.want(_Lbrack)
+ if p.tok == _Rbrack {
+ p.error("expecting type")
+ p.next()
+ return typ
+ }
+
+ call := new(CallExpr)
+ call.pos = pos
+ call.Fun = typ
+ call.ArgList, _ = p.argList(nil, _Rbrack)
+ call.Brackets = true
+ return call
+}
+
func (p *parser) funcType() *FuncType {
if trace {
defer p.trace("funcType")()
@@ -1240,12 +1317,41 @@ func (p *parser) funcType() *FuncType {
typ := new(FuncType)
typ.pos = p.pos()
- typ.ParamList = p.paramList()
+ p.want(_Lparen)
+ typ.ParamList = p.paramList(nil, _Rparen)
typ.ResultList = p.funcResult()
return typ
}
+// "[" has already been consumed, and pos is its position.
+// If len != nil it is the already consumed array length.
+func (p *parser) arrayType(pos Pos, len Expr) Expr {
+ if trace {
+ defer p.trace("arrayType")()
+ }
+
+ if len == nil && !p.got(_DotDotDot) {
+ p.xnest++
+ len = p.expr()
+ p.xnest--
+ }
+ p.want(_Rbrack)
+ t := new(ArrayType)
+ t.pos = pos
+ t.Len = len
+ t.Elem = p.type_()
+ return t
+}
+
+// "[" and "]" have already been consumed, and pos is the position of "[".
+func (p *parser) sliceType(pos Pos) Expr {
+ t := new(SliceType)
+ t.pos = pos
+ t.Elem = p.type_()
+ return t
+}
+
func (p *parser) chanElem() Expr {
if trace {
defer p.trace("chanElem")()
@@ -1261,22 +1367,6 @@ func (p *parser) chanElem() Expr {
return typ
}
-func (p *parser) dotname(name *Name) Expr {
- if trace {
- defer p.trace("dotname")()
- }
-
- if p.tok == _Dot {
- s := new(SelectorExpr)
- s.pos = p.pos()
- p.next()
- s.X = name
- s.Sel = p.name()
- return s
- }
- return name
-}
-
// StructType = "struct" "{" { FieldDecl ";" } "}" .
func (p *parser) structType() *StructType {
if trace {
@@ -1287,7 +1377,8 @@ func (p *parser) structType() *StructType {
typ.pos = p.pos()
p.want(_Struct)
- p.list(_Lbrace, _Semi, _Rbrace, func() bool {
+ p.want(_Lbrace)
+ p.list(_Semi, _Rbrace, func() bool {
p.fieldDecl(typ)
return false
})
@@ -1305,9 +1396,56 @@ func (p *parser) interfaceType() *InterfaceType {
typ.pos = p.pos()
p.want(_Interface)
- p.list(_Lbrace, _Semi, _Rbrace, func() bool {
- if m := p.methodDecl(); m != nil {
- typ.MethodList = append(typ.MethodList, m)
+ p.want(_Lbrace)
+ p.list(_Semi, _Rbrace, func() bool {
+ switch p.tok {
+ case _Name:
+ typ.MethodList = append(typ.MethodList, p.methodDecl())
+
+ case _Lparen:
+ p.syntaxError("cannot parenthesize embedded type")
+ f := new(Field)
+ f.pos = p.pos()
+ p.next()
+ f.Type = p.qualifiedName(nil)
+ p.want(_Rparen)
+ typ.MethodList = append(typ.MethodList, f)
+
+ case _Type:
+ if p.mode&AllowGenerics != 0 {
+ // TODO(gri) factor this better
+ type_ := new(Name)
+ type_.pos = p.pos()
+ type_.Value = "type" // cannot have a method named "type"
+ p.next()
+ if p.tok != _Semi && p.tok != _Rbrace {
+ f := new(Field)
+ f.pos = p.pos()
+ f.Name = type_
+ f.Type = p.type_()
+ typ.MethodList = append(typ.MethodList, f)
+ for p.got(_Comma) {
+ f := new(Field)
+ f.pos = p.pos()
+ f.Name = type_
+ f.Type = p.type_()
+ typ.MethodList = append(typ.MethodList, f)
+ }
+ } else {
+ p.syntaxError("expecting type")
+ }
+ break
+ }
+ fallthrough
+
+ default:
+ if p.mode&AllowGenerics != 0 {
+ p.syntaxError("expecting method, interface name, or type list")
+ p.advance(_Semi, _Rbrace, _Type)
+ } else {
+ p.syntaxError("expecting method or interface name")
+ p.advance(_Semi, _Rbrace)
+ }
}
return false
})
@@ -1321,8 +1459,8 @@ func (p *parser) funcResult() []*Field {
defer p.trace("funcResult")()
}
- if p.tok == _Lparen {
- return p.paramList()
+ if p.got(_Lparen) {
+ return p.paramList(nil, _Rparen)
}
pos := p.pos()
@@ -1368,59 +1506,71 @@ func (p *parser) fieldDecl(styp *StructType) {
case _Name:
name := p.name()
if p.tok == _Dot || p.tok == _Literal || p.tok == _Semi || p.tok == _Rbrace {
- // embed oliteral
+ // embedded type
typ := p.qualifiedName(name)
tag := p.oliteral()
p.addField(styp, pos, nil, typ, tag)
- return
+ break
}
- // new_name_list ntype oliteral
+ // name1, name2, ... Type [ tag ]
names := p.nameList(name)
- typ := p.type_()
+ var typ Expr
+
+ // 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 {
+ typ = p.arrayOrTArgs()
+ if typ, ok := typ.(*CallExpr); ok {
+ // embedded type T[P1, P2, ...]
+ typ.Fun = name // name == names[0]
+ tag := p.oliteral()
+ p.addField(styp, pos, nil, typ, tag)
+ break
+ }
+ } else {
+ // T P
+ typ = p.type_()
+ }
+
tag := p.oliteral()
for _, name := range names {
p.addField(styp, name.Pos(), name, typ, tag)
}
- case _Lparen:
+ case _Star:
p.next()
- if p.tok == _Star {
- // '(' '*' embed ')' oliteral
- pos := p.pos()
- p.next()
- typ := newIndirect(pos, p.qualifiedName(nil))
- p.want(_Rparen)
- tag := p.oliteral()
- p.addField(styp, pos, nil, typ, tag)
+ var typ Expr
+ if p.tok == _Lparen {
+ // *(T)
p.syntaxError("cannot parenthesize embedded type")
-
+ p.next()
+ typ = p.qualifiedName(nil)
+ p.got(_Rparen) // no need to complain if missing
} else {
- // '(' embed ')' oliteral
- typ := p.qualifiedName(nil)
- p.want(_Rparen)
- tag := p.oliteral()
- p.addField(styp, pos, nil, typ, tag)
- p.syntaxError("cannot parenthesize embedded type")
+ // *T
+ typ = p.qualifiedName(nil)
}
+ tag := p.oliteral()
+ p.addField(styp, pos, nil, newIndirect(pos, typ), tag)
- case _Star:
+ case _Lparen:
+ p.syntaxError("cannot parenthesize embedded type")
p.next()
- if p.got(_Lparen) {
- // '*' '(' embed ')' oliteral
- typ := newIndirect(pos, p.qualifiedName(nil))
- p.want(_Rparen)
- tag := p.oliteral()
- p.addField(styp, pos, nil, typ, tag)
- p.syntaxError("cannot parenthesize embedded type")
-
+ var typ Expr
+ if p.tok == _Star {
+ // (*T)
+ pos := p.pos()
+ p.next()
+ typ = newIndirect(pos, p.qualifiedName(nil))
} else {
- // '*' embed oliteral
- typ := newIndirect(pos, p.qualifiedName(nil))
- tag := p.oliteral()
- p.addField(styp, pos, nil, typ, tag)
+ // (T)
+ typ = p.qualifiedName(nil)
}
+ p.got(_Rparen) // no need to complain if missing
+ tag := p.oliteral()
+ p.addField(styp, pos, nil, typ, tag)
default:
p.syntaxError("expecting field name or embedded type")
@@ -1428,6 +1578,39 @@ func (p *parser) fieldDecl(styp *StructType) {
}
}
+func (p *parser) arrayOrTArgs() Expr {
+ if trace {
+ defer p.trace("arrayOrTArgs")()
+ }
+
+ pos := p.pos()
+ p.want(_Lbrack)
+ if p.got(_Rbrack) {
+ return p.sliceType(pos)
+ }
+
+ // x [P]E or x[P]
+ args, _ := p.argList(nil, _Rbrack)
+ if len(args) == 1 {
+ if elem := p.typeOrNil(); elem != nil {
+ // x [P]E
+ t := new(ArrayType)
+ t.pos = pos
+ t.Len = args[0]
+ t.Elem = elem
+ return t
+ }
+ }
+
+ // x[P], x[P1, P2], ...
+ t := new(CallExpr)
+ t.pos = pos
+ // t.Fun will be filled in by caller
+ t.ArgList = args
+ t.Brackets = true
+ return t
+}
+
func (p *parser) oliteral() *BasicLit {
if p.tok == _Literal {
b := new(BasicLit)
@@ -1449,51 +1632,93 @@ func (p *parser) methodDecl() *Field {
defer p.trace("methodDecl")()
}
- switch p.tok {
- case _Name:
- name := p.name()
-
- // accept potential name list but complain
- hasNameList := false
- for p.got(_Comma) {
- p.name()
- hasNameList = true
- }
- if hasNameList {
- p.syntaxError("name list not allowed in interface type")
- // already progressed, no need to advance
- }
+ f := new(Field)
+ f.pos = p.pos()
+ name := p.name()
- f := new(Field)
- f.pos = name.Pos()
- if p.tok != _Lparen {
- // packname
- f.Type = p.qualifiedName(name)
- return f
- }
+ // accept potential name list but complain
+ // TODO(gri) We probably don't need this special check anymore.
+ // Nobody writes this kind of code. It's from ancient
+ // Go beginnings.
+ hasNameList := false
+ for p.got(_Comma) {
+ p.name()
+ hasNameList = true
+ }
+ if hasNameList {
+ p.syntaxError("name list not allowed in interface type")
+ // already progressed, no need to advance
+ }
+ switch p.tok {
+ case _Lparen:
+ // method
f.Name = name
f.Type = p.funcType()
- return f
- case _Lparen:
- p.syntaxError("cannot parenthesize embedded type")
- f := new(Field)
- f.pos = p.pos()
- p.next()
- f.Type = p.qualifiedName(nil)
- p.want(_Rparen)
- return f
+ case _Lbrack:
+ if p.mode&AllowGenerics != 0 {
+ // 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).
+ pos := p.pos()
+ p.next()
+
+ // empty type parameter or argument lists are not permitted
+ if p.tok == _Rbrack {
+ // name[]
+ pos := p.pos()
+ p.next()
+ if p.tok == _Lparen {
+ // name[](
+ p.errorAt(pos, "empty type parameter list")
+ f.Name = name
+ f.Type = p.funcType()
+ } else {
+ p.errorAt(pos, "empty type argument list")
+ f.Type = name
+ }
+ break
+ }
+
+ // A type argument list looks like a parameter list with only
+ // types. Parse a parameter list and decide afterwards.
+ list := p.paramList(nil, _Rbrack)
+ if len(list) > 0 && list[0].Name != nil {
+ // generic method
+ f.Name = name
+ f.Type = p.funcType()
+ // TODO(gri) Record list as type parameter list with f.Type
+ // if we want to type-check the generic method.
+ // For now, report an error so this is not a silent event.
+ p.errorAt(pos, "interface method cannot have type parameters")
+ break
+ }
+
+ // embedded instantiated type
+ call := new(CallExpr)
+ call.pos = pos
+ call.Fun = name
+ call.Brackets = true
+ call.ArgList = make([]Expr, len(list))
+ for i := range list {
+ call.ArgList[i] = list[i].Type
+ }
+ f.Type = call
+ break
+ }
+ fallthrough
default:
- p.syntaxError("expecting method or interface name")
- p.advance(_Semi, _Rbrace)
- return nil
+ // embedded type
+ f.Type = p.qualifiedName(name)
}
+
+ return f
}
// ParameterDecl = [ IdentifierList ] [ "..." ] Type .
-func (p *parser) paramDeclOrNil() *Field {
+func (p *parser) paramDeclOrNil(name *Name) *Field {
if trace {
defer p.trace("paramDecl")()
}
@@ -1501,73 +1726,67 @@ func (p *parser) paramDeclOrNil() *Field {
f := new(Field)
f.pos = p.pos()
- switch p.tok {
- case _Name:
- f.Name = p.name()
- switch p.tok {
- case _Name, _Star, _Arrow, _Func, _Lbrack, _Chan, _Map, _Struct, _Interface, _Lparen:
- // sym name_or_type
- f.Type = p.type_()
+ if p.tok == _Name || name != nil {
+ if name == nil {
+ name = p.name()
+ }
- case _DotDotDot:
- // sym dotdotdot
- f.Type = p.dotsType()
+ if p.mode&AllowGenerics != 0 && p.tok == _Lbrack {
+ f.Type = p.arrayOrTArgs()
+ if typ, ok := f.Type.(*CallExpr); ok {
+ typ.Fun = name
+ } else {
+ f.Name = name
+ }
+ return f
+ }
- case _Dot:
+ if p.tok == _Dot {
// name_or_type
- // from dotname
- f.Type = p.dotname(f.Name)
- f.Name = nil
+ f.Type = p.qualifiedName(name)
+ return f
}
- case _Arrow, _Star, _Func, _Lbrack, _Chan, _Map, _Struct, _Interface, _Lparen:
- // name_or_type
- f.Type = p.type_()
-
- case _DotDotDot:
- // dotdotdot
- f.Type = p.dotsType()
-
- default:
- p.syntaxError("expecting )")
- p.advance(_Comma, _Rparen)
- return nil
+ f.Name = name
}
- return f
-}
-
-// ...Type
-func (p *parser) dotsType() *DotsType {
- if trace {
- defer p.trace("dotsType")()
+ if p.tok == _DotDotDot {
+ t := new(DotsType)
+ t.pos = p.pos()
+ p.next()
+ t.Elem = p.typeOrNil()
+ if t.Elem == nil {
+ t.Elem = p.badExpr()
+ p.syntaxError("final argument in variadic function missing type")
+ }
+ f.Type = t
+ return f
}
- t := new(DotsType)
- t.pos = p.pos()
-
- p.want(_DotDotDot)
- t.Elem = p.typeOrNil()
- if t.Elem == nil {
- t.Elem = p.badExpr()
- p.syntaxError("final argument in variadic function missing type")
+ f.Type = p.typeOrNil()
+ if f.Name != nil || f.Type != nil {
+ return f
}
- return t
+ p.syntaxError("expecting )")
+ p.advance(_Comma, _Rparen)
+ return nil
}
// Parameters = "(" [ ParameterList [ "," ] ] ")" .
// ParameterList = ParameterDecl { "," ParameterDecl } .
-func (p *parser) paramList() (list []*Field) {
+// "(" or "[" has already been consumed.
+// If name != nil, it is the first name after "(" or "[".
+func (p *parser) paramList(name *Name, close token) (list []*Field) {
if trace {
defer p.trace("paramList")()
}
- pos := p.pos()
-
- var named int // number of parameters that have an explicit name and type
- p.list(_Lparen, _Comma, _Rparen, func() bool {
- if par := p.paramDeclOrNil(); par != nil {
+ var named int // number of parameters that have an explicit name and type/bound
+ p.list(_Comma, close, func() bool {
+ par := p.paramDeclOrNil(name)
+ name = nil // 1st name was consumed if present
+ if par != nil {
if debug && par.Name == nil && par.Type == nil {
panic("parameter without name or type")
}
@@ -1589,30 +1808,30 @@ func (p *parser) paramList() (list []*Field) {
}
}
} else if named != len(list) {
- // some named => all must be named
- ok := true
+ // some named => all must have names and types
+ var pos Pos // error position (or unknown)
var typ Expr
for i := len(list) - 1; i >= 0; i-- {
if par := list[i]; par.Type != nil {
typ = par.Type
if par.Name == nil {
- ok = false
+ pos = typ.Pos()
n := p.newName("_")
- n.pos = typ.Pos() // correct position
+ n.pos = pos // correct position
par.Name = n
}
} else if typ != nil {
par.Type = typ
} else {
// par.Type == nil && typ == nil => we only have a par.Name
- ok = false
+ pos = par.Name.Pos()
t := p.badExpr()
- t.pos = par.Name.Pos() // correct position
+ t.pos = pos // correct position
par.Type = t
}
}
- if !ok {
- p.syntaxErrorAt(pos, "mixed named and unnamed function parameters")
+ if pos.IsKnown() {
+ p.syntaxErrorAt(pos, "mixed named and unnamed parameters")
}
}
@@ -2209,15 +2428,21 @@ func (p *parser) stmtList() (l []Stmt) {
}
// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
-func (p *parser) argList() (list []Expr, hasDots bool) {
+func (p *parser) argList(arg Expr, close token) (list []Expr, hasDots bool) {
if trace {
defer p.trace("argList")()
}
p.xnest++
- p.list(_Lparen, _Comma, _Rparen, func() bool {
- list = append(list, p.expr())
- hasDots = p.got(_DotDotDot)
+ p.list(_Comma, close, func() bool {
+ if arg == nil {
+ arg = p.expr()
+ }
+ list = append(list, arg)
+ arg = nil
+ if close == _Rparen {
+ hasDots = p.got(_DotDotDot)
+ }
return hasDots
})
p.xnest--
@@ -2275,18 +2500,32 @@ func (p *parser) qualifiedName(name *Name) Expr {
defer p.trace("qualifiedName")()
}
+ var x Expr
switch {
case name != nil:
- // name is provided
+ x = name
case p.tok == _Name:
- name = p.name()
+ x = p.name()
default:
- name = p.newName("_")
+ x = p.newName("_")
p.syntaxError("expecting name")
p.advance(_Dot, _Semi, _Rbrace)
}
- return p.dotname(name)
+ if p.tok == _Dot {
+ s := new(SelectorExpr)
+ s.pos = p.pos()
+ p.next()
+ s.X = x
+ s.Sel = p.name()
+ x = s
+ }
+
+ if p.mode&AllowGenerics != 0 && p.tok == _Lbrack {
+ x = p.typeInstance(x)
+ }
+
+ return x
}
// ExpressionList = Expression { "," Expression } .
diff --git a/src/cmd/compile/internal/syntax/syntax.go b/src/cmd/compile/internal/syntax/syntax.go
index e51b5538b3..f3d4c09ed5 100644
--- a/src/cmd/compile/internal/syntax/syntax.go
+++ b/src/cmd/compile/internal/syntax/syntax.go
@@ -16,6 +16,7 @@ type Mode uint
// Modes supported by the parser.
const (
CheckBranches Mode = 1 << iota // check correct use of labels, break, continue, and goto statements
+ AllowGenerics
)
// Error describes a syntax error. Error implements the error interface.