From f5442bde55872d703f184f8617329f706bad8149 Mon Sep 17 00:00:00 2001 From: Necip Fazil Yildiran Date: Mon, 10 Aug 2020 14:43:38 +0000 Subject: pkg, prog: add per-field direction attribute --- pkg/ast/ast.go | 1 + pkg/ast/clone.go | 1 + pkg/ast/format.go | 22 ++++----- pkg/ast/parser.go | 19 ++++++-- pkg/ast/testdata/errors.txt | 13 ++++++ pkg/ast/walk.go | 3 ++ pkg/compiler/attrs.go | 4 ++ pkg/compiler/check.go | 24 +++++++++- pkg/compiler/gen.go | 22 ++++++++- pkg/compiler/testdata/all.txt | 10 ++++- pkg/compiler/testdata/errors.txt | 10 +++++ pkg/compiler/testdata/errors2.txt | 94 ++++++++++++++++++++++++++++++++++++--- 12 files changed, 199 insertions(+), 24 deletions(-) (limited to 'pkg') diff --git a/pkg/ast/ast.go b/pkg/ast/ast.go index c0707aca9..66dfc11bd 100644 --- a/pkg/ast/ast.go +++ b/pkg/ast/ast.go @@ -223,6 +223,7 @@ type Field struct { Pos Pos Name *Ident Type *Type + Attrs []*Type NewBlock bool // separated from previous fields by a new line Comments []*Comment } diff --git a/pkg/ast/clone.go b/pkg/ast/clone.go index ece4ba594..23e4f421c 100644 --- a/pkg/ast/clone.go +++ b/pkg/ast/clone.go @@ -168,6 +168,7 @@ func (n *Field) Clone() Node { Pos: n.Pos, Name: n.Name.Clone().(*Ident), Type: n.Type.Clone().(*Type), + Attrs: cloneTypes(n.Attrs), NewBlock: n.NewBlock, Comments: cloneComments(n.Comments), } diff --git a/pkg/ast/format.go b/pkg/ast/format.go index 3803d1e66..6b16f4015 100644 --- a/pkg/ast/format.go +++ b/pkg/ast/format.go @@ -113,11 +113,7 @@ func (n *Call) serialize(w io.Writer) { fmt.Fprintf(w, " %v", fmtType(n.Ret)) } if len(n.Attrs) != 0 { - fmt.Fprintf(w, " (") - for i, t := range n.Attrs { - fmt.Fprintf(w, "%v%v", comma(i, ""), fmtType(t)) - } - fmt.Fprintf(w, ")") + fmt.Fprintf(w, " %v", fmtTypeList(n.Attrs, "(", ")")) } fmt.Fprintf(w, "\n") } @@ -148,13 +144,17 @@ func (n *Struct) serialize(w io.Writer) { for tabs := len(f.Name.Name)/tabWidth + 1; tabs < maxTabs; tabs++ { fmt.Fprintf(w, "\t") } - fmt.Fprintf(w, "%v\n", fmtType(f.Type)) + fmt.Fprintf(w, "%v", fmtType(f.Type)) + if len(f.Attrs) != 0 { + fmt.Fprintf(w, "\t%v", fmtTypeList(f.Attrs, "(", ")")) + } + fmt.Fprintf(w, "\n") } for _, com := range n.Comments { fmt.Fprintf(w, "#%v\n", com.Text) } fmt.Fprintf(w, "%c", closing) - if attrs := fmtTypeList(n.Attrs); attrs != "" { + if attrs := fmtTypeList(n.Attrs, "[", "]"); attrs != "" { fmt.Fprintf(w, " %v", attrs) } fmt.Fprintf(w, "\n") @@ -197,20 +197,20 @@ func fmtType(t *Type) string { for _, c := range t.Colon { v += ":" + fmtType(c) } - v += fmtTypeList(t.Args) + v += fmtTypeList(t.Args, "[", "]") return v } -func fmtTypeList(args []*Type) string { +func fmtTypeList(args []*Type, opening, closing string) string { if len(args) == 0 { return "" } w := new(bytes.Buffer) - fmt.Fprintf(w, "[") + fmt.Fprint(w, opening) for i, t := range args { fmt.Fprintf(w, "%v%v", comma(i, ""), fmtType(t)) } - fmt.Fprintf(w, "]") + fmt.Fprint(w, closing) return w.String() } diff --git a/pkg/ast/parser.go b/pkg/ast/parser.go index 025c5dcc2..20898169e 100644 --- a/pkg/ast/parser.go +++ b/pkg/ast/parser.go @@ -290,7 +290,7 @@ func (p *parser) parseCall(name *Ident) *Call { } p.consume(tokLParen) for p.tok != tokRParen { - c.Args = append(c.Args, p.parseField()) + c.Args = append(c.Args, p.parseField(false)) p.expect(tokComma, tokRParen) p.tryConsume(tokComma) } @@ -376,7 +376,7 @@ func (p *parser) parseStruct(name *Ident) *Struct { str.Comments = comments break } - fld := p.parseField() + fld := p.parseField(true) fld.NewBlock = newBlock fld.Comments = comments str.Fields = append(str.Fields, fld) @@ -403,13 +403,24 @@ func (p *parser) parseCommentBlock() []*Comment { return comments } -func (p *parser) parseField() *Field { +func (p *parser) parseField(parseAttrs bool) *Field { name := p.parseIdent() - return &Field{ + + field := &Field{ Pos: name.Pos, Name: name, Type: p.parseType(), } + + if parseAttrs && p.tryConsume(tokLParen) { + field.Attrs = append(field.Attrs, p.parseType()) + for p.tryConsume(tokComma) { + field.Attrs = append(field.Attrs, p.parseType()) + } + p.consume(tokRParen) + } + + return field } func (p *parser) parseType() *Type { diff --git a/pkg/ast/testdata/errors.txt b/pkg/ast/testdata/errors.txt index 30af16d9a..463158756 100644 --- a/pkg/ast/testdata/errors.txt +++ b/pkg/ast/testdata/errors.txt @@ -30,6 +30,7 @@ call() ("attr1") call() (42) call() ( ### unexpected '\n', expecting int, identifier, string call() () ### unexpected ')', expecting int, identifier, string +call(foo int32 (attr)) ### unexpected '(', expecting ',', ')' define FOO bar @@ -81,3 +82,15 @@ type templ_struct0[A, B] { typ const[A, int16] data B } [align_4] + +s4 { + f0 int8 (attr) + f1 int8 (attr1, attr2[arg1, "arg2"]) + f2 int8 ("attr1") +} + +s5 { + f0 int8 ( ### unexpected '\n', expecting int, identifier, string + +s6 { + f0 int8 () ### unexpected ')', expecting int, identifier, string \ No newline at end of file diff --git a/pkg/ast/walk.go b/pkg/ast/walk.go index fe64b0676..84a3c67c3 100644 --- a/pkg/ast/walk.go +++ b/pkg/ast/walk.go @@ -118,6 +118,9 @@ func (n *Type) walk(cb func(Node)) { func (n *Field) walk(cb func(Node)) { cb(n.Name) cb(n.Type) + for _, a := range n.Attrs { + cb(a) + } for _, c := range n.Comments { cb(c) } diff --git a/pkg/compiler/attrs.go b/pkg/compiler/attrs.go index c0fa04211..5de19f884 100644 --- a/pkg/compiler/attrs.go +++ b/pkg/compiler/attrs.go @@ -23,9 +23,13 @@ var ( attrVarlen = &attrDesc{Name: "varlen"} attrSize = &attrDesc{Name: "size", HasArg: true} attrAlign = &attrDesc{Name: "align", HasArg: true} + attrIn = &attrDesc{Name: "in"} + attrOut = &attrDesc{Name: "out"} + attrInOut = &attrDesc{Name: "inout"} structAttrs = makeAttrs(attrPacked, attrSize, attrAlign) unionAttrs = makeAttrs(attrVarlen, attrSize) + fieldAttrs = makeAttrs(attrIn, attrOut, attrInOut) callAttrs = make(map[string]*attrDesc) ) diff --git a/pkg/compiler/check.go b/pkg/compiler/check.go index 9bb8c1c0c..468b6e35e 100644 --- a/pkg/compiler/check.go +++ b/pkg/compiler/check.go @@ -168,6 +168,14 @@ func (comp *compiler) checkStructFields(n *ast.Struct, typ, name string) { if len(n.Fields) < 1 { comp.error(n.Pos, "%v %v has no fields, need at least 1 field", typ, name) } + for _, f := range n.Fields { + attrs := comp.parseAttrs(fieldAttrs, f, f.Attrs) + + if attrs[attrIn]+attrs[attrOut]+attrs[attrInOut] > 1 { + _, typ, _ := f.Info() + comp.error(f.Pos, "%v has multiple direction attributes", typ) + } + } } func (comp *compiler) checkFieldGroup(fields []*ast.Field, what, ctx string) { @@ -267,6 +275,16 @@ func (comp *compiler) checkAttributeValues() { desc.CheckConsts(comp, n, attr) } } + // Check each field's attributes. + st := decl.(*ast.Struct) + for _, f := range st.Fields { + for _, attr := range f.Attrs { + desc := fieldAttrs[attr.Ident] + if desc.CheckConsts != nil { + desc.CheckConsts(comp, f, attr) + } + } + } } } } @@ -614,7 +632,11 @@ func (comp *compiler) checkTypeCtors(t *ast.Type, dir prog.Dir, isArg bool, } checked[key] = true for _, fld := range s.Fields { - comp.checkTypeCtors(fld.Type, dir, false, ctors, inputs, checked) + fldDir, fldHasDir := comp.genFieldDir(fld) + if !fldHasDir { + fldDir = dir + } + comp.checkTypeCtors(fld.Type, fldDir, false, ctors, inputs, checked) } return } diff --git a/pkg/compiler/gen.go b/pkg/compiler/gen.go index d4834e40e..bd487432a 100644 --- a/pkg/compiler/gen.go +++ b/pkg/compiler/gen.go @@ -441,10 +441,28 @@ func (comp *compiler) genFieldArray(fields []*ast.Field, argSizes []uint64) []pr return res } +func (comp *compiler) genFieldDir(f *ast.Field) (prog.Dir, bool) { + attrs := comp.parseAttrs(fieldAttrs, f, f.Attrs) + switch { + case attrs[attrIn] != 0: + return prog.DirIn, true + case attrs[attrOut] != 0: + return prog.DirOut, true + case attrs[attrInOut] != 0: + return prog.DirInOut, true + default: + return prog.DirIn, false + } +} + func (comp *compiler) genField(f *ast.Field, argSize uint64) prog.Field { + dir, hasDir := comp.genFieldDir(f) + return prog.Field{ - Name: f.Name.Name, - Type: comp.genType(f.Type, argSize), + Name: f.Name.Name, + Type: comp.genType(f.Type, argSize), + HasDirection: hasDir, + Direction: dir, } } diff --git a/pkg/compiler/testdata/all.txt b/pkg/compiler/testdata/all.txt index b9e144d73..17b7c838d 100644 --- a/pkg/compiler/testdata/all.txt +++ b/pkg/compiler/testdata/all.txt @@ -276,7 +276,15 @@ s4 { f2 int8 } [size[19]] -foo_s0(a ptr[in, s0], b ptr[in, s1], c ptr[in, s2], d ptr[in, s4]) +s5 { + f_in0 int32 (in) + f_in1 int32[0:1] (in) + f_out int32 (out) + f_inout0 int32 (inout) + f_inout1 int32[0:1] (inout) +} + +foo_s0(a ptr[in, s0], b ptr[in, s1], c ptr[in, s2], d ptr[in, s4], e ptr[in, s5]) # Unions. diff --git a/pkg/compiler/testdata/errors.txt b/pkg/compiler/testdata/errors.txt index cc0f47508..d3658d239 100644 --- a/pkg/compiler/testdata/errors.txt +++ b/pkg/compiler/testdata/errors.txt @@ -346,3 +346,13 @@ struct$fmt0 { f0 fmt[dec, int8:3] ### unexpected ':', only struct fields can be bitfields f1 int32:-1 ### bitfield of size 18446744073709551615 is too large for base type of size 32 } + +struct$perfielddir { + f0 int32 (in, in) ### duplicate arg/field f0 attribute in + f1 int32 (out, out) ### duplicate arg/field f1 attribute out + f2 int32 (inout, inout) ### duplicate arg/field f2 attribute inout + f3 int32 (in, out) ### arg/field has multiple direction attributes + f4 int32 (in, inout) ### arg/field has multiple direction attributes + f5 int32 (out, inout) ### arg/field has multiple direction attributes + f6 int32 (in, out, inout) ### arg/field has multiple direction attributes +} \ No newline at end of file diff --git a/pkg/compiler/testdata/errors2.txt b/pkg/compiler/testdata/errors2.txt index 68b8a66df..c862358cf 100644 --- a/pkg/compiler/testdata/errors2.txt +++ b/pkg/compiler/testdata/errors2.txt @@ -176,11 +176,26 @@ resource r104[int8] resource r105[int8] resource r106[int8] ### resource r106 can't be created (never mentioned as a syscall return value or output argument/field) resource r107[int8] ### resource r107 can't be created (never mentioned as a syscall return value or output argument/field) -resource r108[int8] ### resource r108 is never used as an input(such resources are not useful) - -foo$300(a0 r100, a1 r101, a2 r102, a3 r103, a4 r104) -foo$301(a ptr[out, array[r103]], b ptr[in, s300], c r107) r104 -foo$302() r108 +resource r108[int8] ### resource r108 can't be created (never mentioned as a syscall return value or output argument/field) +resource r109[int8] ### resource r109 can't be created (never mentioned as a syscall return value or output argument/field) +resource r110[int8] +resource r111[int8] +resource r112[int8] ### resource r112 can't be created (never mentioned as a syscall return value or output argument/field) +resource r113[int8] +resource r114[int8] +resource r115[int8] ### resource r115 can't be created (never mentioned as a syscall return value or output argument/field) +resource r116[int8] ### resource r116 is never used as an input(such resources are not useful) +resource r117[int8] ### resource r117 is never used as an input(such resources are not useful) +resource r118[int8] ### resource r118 is never used as an input(such resources are not useful) +resource r119[int8] ### resource r119 is never used as an input(such resources are not useful) +resource r120[int8] ### resource r120 can't be created (never mentioned as a syscall return value or output argument/field) +resource r121[int8] + +foo$300(a0 r100, a1 r101, a2 r102, a3 r103, a4 r104, a5 r105, a6 r106, a7 r107, a8 r108) +foo$301(a0 r109, a1 r110, a2 r111, a3 r112, a4 r113, a5 r114, a6 r115, a7 r120, a8 r121) +foo$302(a ptr[out, array[r103]], b ptr[in, s300], c r107) r104 +foo$303(a ptr[in, s302], b ptr[in, s303], c ptr[in, s304], d ptr[out, s305], e ptr[inout, s306], f ptr[inout, s307], g ptr[in, s308], h ptr[out, s310]) +foo$304(a ptr[out, r117], b ptr[in, s312], c ptr[in, s313], d ptr[inout, u100]) r116 s300 { f1 ptr[inout, s301] @@ -191,6 +206,75 @@ s301 { f2 r105 } +s302 { + f1 r108 +} + +s303 { + f1 r109 (in) +} + +# ptr to struct is in, field is out (valid, resource can be created) +s304 { + f1 r110 (out) +} + +# ptr to struct is out, field is in (error, resource can't be created) +s305 { + f1 r111 (in) +} + +# ptr to struct is inout, field is inout (valid, resource can be created) +s306 { + f1 r111 (inout) +} + +# ptr to struct is inout, fields are in (error) and out (valid) +s307 { + f1 r112 (in) + f2 r113 (out) +} + +# recurse the out field inside two levels of in ptrs (valid, resource can be created) +s308 { + f1 ptr[in, s309] +} + +s309 { + f1 ptr[out, r114] +} + +# recurse the in field inside two levels of out ptrs (error, resource can't be created) +s310 { + f1 ptr[out, s311] +} + +s311 { + f1 ptr[in, r115] +} + +# ptr to struct is in, field is out (error, never used as an input) +s312 { + f1 r118 (out) +} + +# recurse the out field inside two levels of in ptrs (error, never used as an input) +s313 { + f1 ptr[in, s314] +} + +s314 { + f1 r119 (out) +} + +u100 [ + f1 r120 (in) + f2 r121 (out) +] + +# TODO: Two instances of the same resource might exist in the same structure as +# both in and out. How common is this and how to handle this? + # Varlen field tests. s400 { -- cgit mrf-deployment