diff options
Diffstat (limited to 'pkg/compiler')
| -rw-r--r-- | pkg/compiler/check.go | 18 | ||||
| -rw-r--r-- | pkg/compiler/compiler.go | 12 | ||||
| -rw-r--r-- | pkg/compiler/gen.go | 2 | ||||
| -rw-r--r-- | pkg/compiler/testdata/all.txt | 19 | ||||
| -rw-r--r-- | pkg/compiler/testdata/errors2.txt | 23 |
5 files changed, 57 insertions, 17 deletions
diff --git a/pkg/compiler/check.go b/pkg/compiler/check.go index 8a1a7b7e9..d693fa790 100644 --- a/pkg/compiler/check.go +++ b/pkg/compiler/check.go @@ -949,7 +949,7 @@ func (comp *compiler) checkStructRecursion(checked map[string]bool, n *ast.Struc str += fmt.Sprintf("%v.%v -> ", elem.Struct, elem.Field) } str += name - comp.error(path[0].Pos, "recursive declaration: %v (mark some pointers as opt)", str) + comp.error(path[0].Pos, "recursive declaration: %v (mark some pointers as opt, or use variable-length arrays)", str) checked[name] = true return } @@ -975,6 +975,9 @@ func (comp *compiler) recurseField(checked map[string]bool, t *ast.Type, path [] if desc == typePtr && base.IsOptional { return // optional pointers prune recursion } + if desc == typeArray && len(args) == 1 { + return // variable-length arrays prune recursion + } for i, arg := range args { if desc.Args[i].Type == typeArgType { isArg := false @@ -1425,8 +1428,19 @@ func (comp *compiler) isVarlen(t *ast.Type) bool { } func (comp *compiler) isZeroSize(t *ast.Type) bool { + // We can recurse here for a struct that recursively contains itself in an array. + // In such case it's safe to say that it is zero size, because if all other fields are zero size, + // then the struct is indeed zero size (even if it contains several versions of itself transitively). + // If there are any other "normal" fields, then we will still correctly conclude that the whole struct + // is not zero size. + if comp.recursiveQuery[t] { + return true + } + comp.recursiveQuery[t] = true desc, args, _ := comp.getArgsBase(t, false) - return desc.ZeroSize != nil && desc.ZeroSize(comp, t, args) + res := desc.ZeroSize != nil && desc.ZeroSize(comp, t, args) + delete(comp.recursiveQuery, t) + return res } func (comp *compiler) checkVarlen(n *ast.Struct) { diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 3f10ae5fe..978255219 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -65,6 +65,7 @@ func createCompiler(desc *ast.Description, target *targets.Target, eh ast.ErrorH structVarlen: make(map[string]bool), structTypes: make(map[string]prog.Type), structFiles: make(map[*ast.Struct]map[string]ast.Pos), + recursiveQuery: make(map[ast.Node]bool), builtinConsts: map[string]uint64{ "PTR_SIZE": target.PtrSize, }, @@ -135,11 +136,12 @@ type compiler struct { usedTypedefs map[string]bool brokenTypedefs map[string]bool - structVarlen map[string]bool - structTypes map[string]prog.Type - structFiles map[*ast.Struct]map[string]ast.Pos - builtinConsts map[string]uint64 - fileMeta map[string]Meta + structVarlen map[string]bool + structTypes map[string]prog.Type + structFiles map[*ast.Struct]map[string]ast.Pos + builtinConsts map[string]uint64 + fileMeta map[string]Meta + recursiveQuery map[ast.Node]bool } type warn struct { diff --git a/pkg/compiler/gen.go b/pkg/compiler/gen.go index 104c2899b..a6fd938a9 100644 --- a/pkg/compiler/gen.go +++ b/pkg/compiler/gen.go @@ -224,6 +224,7 @@ func (comp *compiler) layoutType(typ prog.Type, padded map[prog.Type]bool) { if padded[typ] { return } + padded[typ] = true switch t := typ.(type) { case *prog.ArrayType: comp.layoutType(t.Elem, padded) @@ -244,7 +245,6 @@ func (comp *compiler) layoutType(typ prog.Type, padded map[prog.Type]bool) { if !typ.Varlen() && typ.Size() == sizeUnassigned { panic("size unassigned") } - padded[typ] = true } func (comp *compiler) layoutArray(t *prog.ArrayType) { diff --git a/pkg/compiler/testdata/all.txt b/pkg/compiler/testdata/all.txt index 098da4604..b19b85980 100644 --- a/pkg/compiler/testdata/all.txt +++ b/pkg/compiler/testdata/all.txt @@ -373,3 +373,22 @@ union$conditional3 [ ] conditional(a ptr[in, struct$conditional]) + +# Struct recusrion via arrays. + +recursive_struct_call(a ptr[in, recursive_struct], b ptr[in, recursive_struct3]) + +recursive_struct { + f0 array[recursive_struct] + f1 recursive_struct2 + f2 array[recursive_struct2] +} [packed] + +recursive_struct2 { + f0 int32 + f1 array[recursive_struct] +} + +recursive_struct3 { + f0 array[recursive_struct3] +} diff --git a/pkg/compiler/testdata/errors2.txt b/pkg/compiler/testdata/errors2.txt index 71e2aa5ad..9bdccb8fe 100644 --- a/pkg/compiler/testdata/errors2.txt +++ b/pkg/compiler/testdata/errors2.txt @@ -16,7 +16,7 @@ foo$1(a0 r0, a1 r1, a2 r2) # Recursive structs/unions. sr1 { - f1 sr1 ### recursive declaration: sr1.f1 -> sr1 (mark some pointers as opt) + f1 sr1 ### recursive declaration: sr1.f1 -> sr1 (mark some pointers as opt, or use variable-length arrays) } sr2 { @@ -25,12 +25,12 @@ sr2 { } sr3 { - f1 ptr[in, sr3] ### recursive declaration: sr3.f1 -> sr3 (mark some pointers as opt) + f1 ptr[in, sr3] ### recursive declaration: sr3.f1 -> sr3 (mark some pointers as opt, or use variable-length arrays) } sr4 { f1 ptr[in, sr3] - f2 array[ptr[in, sr5], 4] ### recursive declaration: sr4.f2 -> sr5.f2 -> sr6.f1 -> sr4 (mark some pointers as opt) + f2 array[ptr[in, sr5], 4] ### recursive declaration: sr4.f2 -> sr5.f2 -> sr6.f1 -> sr4 (mark some pointers as opt, or use variable-length arrays) } sr5 [ @@ -51,26 +51,31 @@ type templ_sr[T] { } sr8 { - f templ_sr[sr8] ### recursive declaration: sr8.f -> templ_sr[sr8].f -> sr8 (mark some pointers as opt) + f templ_sr[sr8] ### recursive declaration: sr8.f -> templ_sr[sr8].f -> sr8 (mark some pointers as opt, or use variable-length arrays) } sr9 { - f templ_sr[ptr[in, sr9]] ### recursive declaration: sr9.f -> templ_sr[ptr[in, sr9]].f -> sr9 (mark some pointers as opt) + f templ_sr[ptr[in, sr9]] ### recursive declaration: sr9.f -> templ_sr[ptr[in, sr9]].f -> sr9 (mark some pointers as opt, or use variable-length arrays) +} + +sr10 { + f0 array[sr10, 10] ### recursive declaration: sr10.f0 -> sr10 (mark some pointers as opt, or use variable-length arrays) } use_sr { u2 u2 - u3 u3 + u3 u3 s3 s3 s4 s4 s6 s6 s8 s8 - sr1 sr1 + sr1 sr1 sr2 sr2 sr5 sr5 sr7 sr7 - sr8 sr8 - sr9 sr9 + sr8 sr8 + sr9 sr9 + sr10 sr10 s400 s400 s401 s401 u400 u400 |
