aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorNecip Fazil Yildiran <necip@google.com>2020-08-10 14:43:38 +0000
committerDmitry Vyukov <dvyukov@google.com>2020-08-13 13:50:25 +0200
commitf5442bde55872d703f184f8617329f706bad8149 (patch)
tree14e121020d6aacd857b80351f01546818f19a74b /pkg
parentee7cb8b69583db417b187b53f4765c3a403cd4cf (diff)
pkg, prog: add per-field direction attribute
Diffstat (limited to 'pkg')
-rw-r--r--pkg/ast/ast.go1
-rw-r--r--pkg/ast/clone.go1
-rw-r--r--pkg/ast/format.go22
-rw-r--r--pkg/ast/parser.go19
-rw-r--r--pkg/ast/testdata/errors.txt13
-rw-r--r--pkg/ast/walk.go3
-rw-r--r--pkg/compiler/attrs.go4
-rw-r--r--pkg/compiler/check.go24
-rw-r--r--pkg/compiler/gen.go22
-rw-r--r--pkg/compiler/testdata/all.txt10
-rw-r--r--pkg/compiler/testdata/errors.txt10
-rw-r--r--pkg/compiler/testdata/errors2.txt94
12 files changed, 199 insertions, 24 deletions
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 {