mirror of https://github.com/golang/go.git
go/*: change representation of type lists in interface types, adjusted dependent code
Until now, types of type lists in interface types were collected in a single expression list "Types". This made it not possible to gofmt such interfaces while preserving the original layout of type lists. This change represents types of type lists as part of an ast.FieldList. The ast.InterfaceType.Methods field list now represents embedded interfaces, methods, or type list types through ast.Fields. This preserves all position information and thus permits accurate gofmt-ing. The new representation is as follows: For an ast.Field f, if - len(f.Names) == 0 : f.Type is an embedded interface - f.Names[0].Name == "type": f.Type is a type of a type list - otherwise : f represents a method Since "type" is a keyword, a Go field name cannot be "type". Fields of types of type lists that share the same "type" keyword in the source share the same f.Names[0] identifier (named "type"), and the position of that identifier is the position of the "type" keyword in the source. Related changes: - Adjusted go/parser to build the new representation. - Adjusted go/printer and implemented formatting of type lists. (This is still not quite correct if there are comments, but that is fine-tuning). - Adjusted go/types to work with the new representation. - Implemented SplitFieldList and MergeFieldList in go2go translator to easily switch between old and new representation. - Updated documentation. Change-Id: I016e2ce5949bfe294509d703156c41d42cf7084e
This commit is contained in:
parent
e5e519e893
commit
a31f6ae875
|
|
@ -188,11 +188,14 @@ func isDirective(c string) bool {
|
|||
// in a signature.
|
||||
// Field.Names is nil for unnamed parameters (parameter lists which only contain types)
|
||||
// and embedded struct fields. In the latter case, the field name is the type name.
|
||||
// Field.Names contains a single name "type" for elements of interface type lists.
|
||||
// Types belonging to the same type list share the same "type" identifier which also
|
||||
// records the position of that keyword.
|
||||
//
|
||||
type Field struct {
|
||||
Doc *CommentGroup // associated documentation; or nil
|
||||
Names []*Ident // field/method/(type) parameter names; or nil
|
||||
Type Expr // field/method/parameter type or contract name; or nil
|
||||
Names []*Ident // field/method/(type) parameter names, or type "type"; or nil
|
||||
Type Expr // field/method/parameter type, type list type, or contract name; or nil
|
||||
Tag *BasicLit // field tag; or nil
|
||||
Comment *CommentGroup // line comments; or nil
|
||||
}
|
||||
|
|
@ -441,12 +444,10 @@ type (
|
|||
}
|
||||
|
||||
// An InterfaceType node represents an interface type.
|
||||
// The Types list is an experimental extension for interfaces that serve as type bounds (as in contracts).
|
||||
InterfaceType struct {
|
||||
Interface token.Pos // position of "interface" keyword
|
||||
Methods *FieldList // list of methods
|
||||
Types []Expr // list of types (TODO(gri) for now they are all lumped together, loosing syntax info)
|
||||
Incomplete bool // true if (source) methods are missing in the Methods list
|
||||
Methods *FieldList // list of embedded interfaces, methods, or types
|
||||
Incomplete bool // true if (source) methods or types are missing in the Methods list
|
||||
}
|
||||
|
||||
// A MapType node represents a map type.
|
||||
|
|
|
|||
|
|
@ -191,13 +191,13 @@ func (t *translator) instantiateTypeDecl(qid qualifiedIdent, typ *types.Named, a
|
|||
tparams := rtyp.(*ast.CallExpr).Args
|
||||
ta := typeArgsFromExprs(t, astTypes, typeTypes, tparams)
|
||||
newDecl := &ast.FuncDecl{
|
||||
Doc: mast.Doc,
|
||||
Doc: mast.Doc,
|
||||
Recv: &ast.FieldList{
|
||||
Opening: mast.Recv.Opening,
|
||||
List: []*ast.Field{
|
||||
{
|
||||
Doc: mast.Recv.List[0].Doc,
|
||||
Names: []*ast.Ident{
|
||||
Doc: mast.Recv.List[0].Doc,
|
||||
Names: []*ast.Ident{
|
||||
mast.Recv.List[0].Names[0],
|
||||
},
|
||||
Type: newRtype,
|
||||
|
|
@ -788,15 +788,15 @@ func (t *translator) instantiateExpr(ta *typeArgs, e ast.Expr) ast.Expr {
|
|||
Results: results,
|
||||
}
|
||||
case *ast.InterfaceType:
|
||||
methods := t.instantiateFieldList(ta, e.Methods)
|
||||
types, typesChanged := t.instantiateExprList(ta, e.Types)
|
||||
eMethods, eTypes := splitFieldList(e.Methods)
|
||||
methods := t.instantiateFieldList(ta, eMethods)
|
||||
types, typesChanged := t.instantiateExprList(ta, eTypes)
|
||||
if methods == e.Methods && !typesChanged {
|
||||
return e
|
||||
}
|
||||
return &ast.InterfaceType{
|
||||
Interface: e.Interface,
|
||||
Methods: methods,
|
||||
Types: types,
|
||||
Methods: mergeFieldList(methods, types),
|
||||
Incomplete: e.Incomplete,
|
||||
}
|
||||
case *ast.MapType:
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ var config = printer.Config{
|
|||
Tabwidth: 8,
|
||||
}
|
||||
|
||||
|
||||
// isParameterizedFuncDecl reports whether fd is a parameterized function.
|
||||
func isParameterizedFuncDecl(fd *ast.FuncDecl, info *types.Info) bool {
|
||||
if fd.Type.TParams != nil {
|
||||
|
|
@ -182,7 +181,7 @@ func rewriteAST(fset *token.FileSet, importer *Importer, importPath string, file
|
|||
if addImportableName {
|
||||
file.Decls = append(file.Decls,
|
||||
&ast.GenDecl{
|
||||
Tok: token.TYPE,
|
||||
Tok: token.TYPE,
|
||||
Specs: []ast.Spec{
|
||||
&ast.TypeSpec{
|
||||
Name: ast.NewIdent(t.importableName()),
|
||||
|
|
@ -255,7 +254,7 @@ func rewriteAST(fset *token.FileSet, importer *Importer, importPath string, file
|
|||
switch tok {
|
||||
case token.CONST, token.VAR:
|
||||
spec = &ast.ValueSpec{
|
||||
Names: []*ast.Ident{
|
||||
Names: []*ast.Ident{
|
||||
ast.NewIdent("_"),
|
||||
},
|
||||
Values: []ast.Expr{
|
||||
|
|
@ -278,7 +277,7 @@ func rewriteAST(fset *token.FileSet, importer *Importer, importPath string, file
|
|||
}
|
||||
file.Decls = append(file.Decls,
|
||||
&ast.GenDecl{
|
||||
Tok: tok,
|
||||
Tok: tok,
|
||||
Specs: []ast.Spec{spec},
|
||||
})
|
||||
}
|
||||
|
|
@ -536,8 +535,9 @@ func (t *translator) translateExpr(pe *ast.Expr) {
|
|||
t.translateFieldList(e.Params)
|
||||
t.translateFieldList(e.Results)
|
||||
case *ast.InterfaceType:
|
||||
t.translateFieldList(e.Methods)
|
||||
t.translateExprList(e.Types)
|
||||
methods, types := splitFieldList(e.Methods)
|
||||
t.translateFieldList(methods)
|
||||
t.translateExprList(types)
|
||||
case *ast.MapType:
|
||||
t.translateExpr(&e.Key)
|
||||
t.translateExpr(&e.Value)
|
||||
|
|
@ -548,6 +548,42 @@ func (t *translator) translateExpr(pe *ast.Expr) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(iant) refactor code and get rid of this?
|
||||
func splitFieldList(fl *ast.FieldList) (methods *ast.FieldList, types []ast.Expr) {
|
||||
if fl == nil {
|
||||
return
|
||||
}
|
||||
var mfields []*ast.Field
|
||||
for _, f := range fl.List {
|
||||
if len(f.Names) > 0 && f.Names[0].Name == "type" {
|
||||
// type list type
|
||||
types = append(types, f.Type)
|
||||
} else {
|
||||
mfields = append(mfields, f)
|
||||
}
|
||||
}
|
||||
copy := *fl
|
||||
copy.List = mfields
|
||||
methods = ©
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(iant) refactor code and get rid of this?
|
||||
func mergeFieldList(methods *ast.FieldList, types []ast.Expr) (fl *ast.FieldList) {
|
||||
fl = methods
|
||||
if len(types) == 0 {
|
||||
return
|
||||
}
|
||||
if fl == nil {
|
||||
fl = new(ast.FieldList)
|
||||
}
|
||||
name := []*ast.Ident{ast.NewIdent("type")}
|
||||
for _, typ := range types {
|
||||
fl.List = append(fl.List, &ast.Field{Names: name, Type: typ})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// translateExprList translate an expression list from Go with
|
||||
// contracts to Go 1.
|
||||
func (t *translator) translateExprList(el []ast.Expr) {
|
||||
|
|
|
|||
|
|
@ -1053,7 +1053,7 @@ func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field {
|
|||
}
|
||||
} else {
|
||||
// embedded, possibly parameterized interface
|
||||
// (using the enclosing parentheses to distinguish it from a method declaration)
|
||||
// (using enclosing parentheses to distinguish it from a method declaration)
|
||||
typ = p.parseType(true)
|
||||
}
|
||||
p.expectSemi() // call before accessing p.linecomment
|
||||
|
|
@ -1072,16 +1072,21 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
|
|||
pos := p.expect(token.INTERFACE)
|
||||
lbrace := p.expect(token.LBRACE)
|
||||
scope := ast.NewScope(nil) // interface scope
|
||||
var mlist []*ast.Field
|
||||
var tlist []ast.Expr
|
||||
var list []*ast.Field
|
||||
L:
|
||||
for {
|
||||
switch p.tok {
|
||||
case token.IDENT, token.LPAREN:
|
||||
mlist = append(mlist, p.parseMethodSpec(scope))
|
||||
list = append(list, p.parseMethodSpec(scope))
|
||||
case token.TYPE:
|
||||
// all types in a type list share the same field name "type"
|
||||
// (since type is a keyword, a Go program cannot have that field name)
|
||||
name := []*ast.Ident{&ast.Ident{NamePos: p.pos, Name: "type"}}
|
||||
p.next()
|
||||
tlist = append(tlist, p.parseTypeList()...)
|
||||
// add each type as a field named "type"
|
||||
for _, typ := range p.parseTypeList() {
|
||||
list = append(list, &ast.Field{Names: name, Type: typ})
|
||||
}
|
||||
p.expectSemi()
|
||||
default:
|
||||
break L
|
||||
|
|
@ -1093,10 +1098,9 @@ L:
|
|||
Interface: pos,
|
||||
Methods: &ast.FieldList{
|
||||
Opening: lbrace,
|
||||
List: mlist,
|
||||
List: list,
|
||||
Closing: rbrace,
|
||||
},
|
||||
Types: tlist,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -473,10 +473,18 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
|
|||
}
|
||||
p.expr(f.Type)
|
||||
} else { // interface
|
||||
if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
|
||||
// method
|
||||
p.expr(f.Names[0])
|
||||
p.signature(ftyp)
|
||||
if len(f.Names) > 0 {
|
||||
// type list type or method
|
||||
name := f.Names[0] // "type" or method name
|
||||
p.expr(name)
|
||||
if name.Name == "type" {
|
||||
// type list type
|
||||
p.print(blank)
|
||||
p.expr(f.Type)
|
||||
} else {
|
||||
// method
|
||||
p.signature(f.Type.(*ast.FuncType)) // don't print "func"
|
||||
}
|
||||
} else {
|
||||
// embedded interface
|
||||
p.expr(f.Type)
|
||||
|
|
@ -544,19 +552,47 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
|
|||
} else { // interface
|
||||
|
||||
var line int
|
||||
var prev *ast.Ident // previous "type" identifier
|
||||
for i, f := range list {
|
||||
var name *ast.Ident // first name, or nil
|
||||
if len(f.Names) > 0 {
|
||||
name = f.Names[0]
|
||||
}
|
||||
if i > 0 {
|
||||
p.linebreak(p.lineFor(f.Pos()), 1, ignore, p.linesFrom(line) > 0)
|
||||
// don't do a line break (min == 0) if we are printing a list of types
|
||||
// TODO(gri) this doesn't work quite right if the list of types is
|
||||
// spread across multiple lines
|
||||
min := 1
|
||||
if prev != nil && name == prev {
|
||||
min = 0
|
||||
}
|
||||
p.linebreak(p.lineFor(f.Pos()), min, ignore, p.linesFrom(line) > 0)
|
||||
}
|
||||
p.setComment(f.Doc)
|
||||
p.recordLine(&line)
|
||||
if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
|
||||
// method
|
||||
p.expr(f.Names[0])
|
||||
p.signature(ftyp)
|
||||
if name != nil {
|
||||
// type list type or method
|
||||
if name.Name == "type" {
|
||||
// type list type
|
||||
if name == prev {
|
||||
// type is part of a list of types
|
||||
p.print(token.COMMA, blank)
|
||||
} else {
|
||||
// type starts a new list of types
|
||||
p.print(name, blank)
|
||||
}
|
||||
p.expr(f.Type)
|
||||
prev = name
|
||||
} else {
|
||||
// method
|
||||
p.expr(name)
|
||||
p.signature(f.Type.(*ast.FuncType)) // don't print "func"
|
||||
prev = nil
|
||||
}
|
||||
} else {
|
||||
// embedded interface
|
||||
p.expr(f.Type)
|
||||
prev = nil
|
||||
}
|
||||
p.setComment(f.Comment)
|
||||
}
|
||||
|
|
@ -956,7 +992,6 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
|
|||
case *ast.InterfaceType:
|
||||
p.print(token.INTERFACE)
|
||||
p.fieldList(x.Methods, false, x.Incomplete)
|
||||
// TODO(gri) implement printing of type lists
|
||||
|
||||
case *ast.MapType:
|
||||
p.print(token.MAP, token.LBRACK)
|
||||
|
|
|
|||
|
|
@ -36,8 +36,16 @@ type _(type T) interface{}
|
|||
type _(type T) interface {
|
||||
m(T)
|
||||
}
|
||||
type _ interface{ type int } // one-liner
|
||||
type _ interface {
|
||||
// cannot print interface type lists yet
|
||||
// TODO(gri) comments before type lists are not handled correctly yet
|
||||
m1()
|
||||
type int
|
||||
type bool, string, error
|
||||
m2()
|
||||
m3()
|
||||
type float32, float64, complex64, complex128
|
||||
m4()
|
||||
}
|
||||
|
||||
type _(type T) struct{}
|
||||
|
|
|
|||
|
|
@ -36,8 +36,18 @@ type _(type T) interface{}
|
|||
type _(type T) interface{
|
||||
m(T)
|
||||
}
|
||||
type _ interface {
|
||||
type int // cannot print interface type lists yet
|
||||
type _ interface { type int } // one-liner
|
||||
type _ interface{
|
||||
// TODO(gri) comments before type lists are not handled correctly yet
|
||||
m1()
|
||||
type int
|
||||
type bool, string, error
|
||||
m2()
|
||||
m3()
|
||||
type float32,
|
||||
float64,
|
||||
complex64, complex128
|
||||
m4()
|
||||
}
|
||||
|
||||
type _(type T) struct{}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ so we have a better track record. I only switched to this file in Nov 2019, henc
|
|||
----------------------------------------------------------------------------------------------------
|
||||
TODO
|
||||
|
||||
- go/printer: implement printing of type lists
|
||||
- review use of Contract.TParams field - it seems like it's only needed for length checks?
|
||||
- review handling of fields of instantiated generic types (do we need to make them non-parameterized,
|
||||
similar to what we did for the embedded interfaces created by contract embedding?)
|
||||
|
|
|
|||
|
|
@ -77,8 +77,6 @@ idea how to implement that but we can easily type-check it).
|
|||
|
||||
MAJOR KNOWN ISSUES
|
||||
|
||||
- importing of packages exporting generic code is not implemented
|
||||
in the type-checker
|
||||
- various type-specific operations (such as sending a message, type
|
||||
assertions, etc.) on expressions of a generic type don't work yet
|
||||
(but are relatively easy to implement going forward)
|
||||
|
|
@ -86,7 +84,6 @@ MAJOR KNOWN ISSUES
|
|||
better in a real implementation (the subscript numbers on type
|
||||
parameters are there to visually identify different parameters
|
||||
with the same name)
|
||||
- gofmt works only partly with parameterized code
|
||||
|
||||
See also the NOTES file for a more up-to-date documentation of the
|
||||
current state and issues.
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ func WriteExpr(buf *bytes.Buffer, x ast.Expr) {
|
|||
|
||||
case *ast.StructType:
|
||||
buf.WriteString("struct{")
|
||||
writeFieldList(buf, x.Fields, "; ", false)
|
||||
writeFieldList(buf, x.Fields.List, "; ", false)
|
||||
buf.WriteByte('}')
|
||||
|
||||
case *ast.FuncType:
|
||||
|
|
@ -137,14 +137,28 @@ func WriteExpr(buf *bytes.Buffer, x ast.Expr) {
|
|||
writeSigExpr(buf, x)
|
||||
|
||||
case *ast.InterfaceType:
|
||||
// separate type list types from method list
|
||||
// TODO(gri) we can get rid of this extra code if writeExprList does the separation
|
||||
var types []ast.Expr
|
||||
var methods []*ast.Field
|
||||
for _, f := range x.Methods.List {
|
||||
if len(f.Names) > 1 && f.Names[0].Name == "type" {
|
||||
// type list type
|
||||
types = append(types, f.Type)
|
||||
} else {
|
||||
// method or embedded interface
|
||||
methods = append(methods, f)
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString("interface{")
|
||||
writeFieldList(buf, x.Methods, "; ", true)
|
||||
if len(x.Types) > 0 {
|
||||
if len(x.Methods.List) > 0 {
|
||||
writeFieldList(buf, methods, "; ", true)
|
||||
if len(types) > 0 {
|
||||
if len(methods) > 0 {
|
||||
buf.WriteString("; ")
|
||||
}
|
||||
buf.WriteString("type ")
|
||||
writeExprList(buf, x.Types)
|
||||
writeExprList(buf, types)
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
|
||||
|
|
@ -171,7 +185,7 @@ func WriteExpr(buf *bytes.Buffer, x ast.Expr) {
|
|||
|
||||
func writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) {
|
||||
buf.WriteByte('(')
|
||||
writeFieldList(buf, sig.Params, ", ", false)
|
||||
writeFieldList(buf, sig.Params.List, ", ", false)
|
||||
buf.WriteByte(')')
|
||||
|
||||
res := sig.Results
|
||||
|
|
@ -190,12 +204,12 @@ func writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) {
|
|||
|
||||
// multiple or named result(s)
|
||||
buf.WriteByte('(')
|
||||
writeFieldList(buf, res, ", ", false)
|
||||
writeFieldList(buf, res.List, ", ", false)
|
||||
buf.WriteByte(')')
|
||||
}
|
||||
|
||||
func writeFieldList(buf *bytes.Buffer, fields *ast.FieldList, sep string, iface bool) {
|
||||
for i, f := range fields.List {
|
||||
func writeFieldList(buf *bytes.Buffer, list []*ast.Field, sep string, iface bool) {
|
||||
for i, f := range list {
|
||||
if i > 0 {
|
||||
buf.WriteString(sep)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -658,9 +658,11 @@ func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool
|
|||
}
|
||||
|
||||
func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, def *Named) {
|
||||
var types []ast.Expr
|
||||
for _, f := range iface.Methods.List {
|
||||
if len(f.Names) > 0 {
|
||||
// We have a method with name f.Names[0].
|
||||
// We have a method with name f.Names[0], or a type
|
||||
// of a type list (name.Name == "type").
|
||||
// (The parser ensures that there's only one method
|
||||
// and we don't care if a constructed AST has more.)
|
||||
name := f.Names[0]
|
||||
|
|
@ -669,6 +671,11 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
|
|||
continue // ignore
|
||||
}
|
||||
|
||||
if name.Name == "type" {
|
||||
types = append(types, f.Type)
|
||||
continue
|
||||
}
|
||||
|
||||
typ := check.typ(f.Type)
|
||||
sig, _ := typ.(*Signature)
|
||||
if sig == nil {
|
||||
|
|
@ -708,7 +715,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
|
|||
}
|
||||
|
||||
// type constraints
|
||||
ityp.types = check.collectTypeConstraints(iface.Pos(), ityp.types, iface.Types)
|
||||
ityp.types = check.collectTypeConstraints(iface.Pos(), ityp.types, types)
|
||||
|
||||
if len(ityp.methods) == 0 && len(ityp.types) == 0 && len(ityp.embeddeds) == 0 {
|
||||
// empty interface
|
||||
|
|
|
|||
Loading…
Reference in New Issue