aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--prog/encoding.go13
-rw-r--r--prog/expr.go12
-rw-r--r--prog/minimization.go2
-rw-r--r--prog/rand.go2
-rw-r--r--prog/test/fuzz_test.go3
-rw-r--r--prog/types.go10
-rw-r--r--prog/validation.go14
7 files changed, 41 insertions, 15 deletions
diff --git a/prog/encoding.go b/prog/encoding.go
index b5d5114ca..e925e7918 100644
--- a/prog/encoding.go
+++ b/prog/encoding.go
@@ -257,9 +257,13 @@ func (target *Target) Deserialize(data []byte, mode DeserializeMode) (*Prog, err
// This validation is done even in non-debug mode because deserialization
// procedure does not catch all bugs (e.g. mismatched types).
// And we can receive bad programs from corpus and hub.
- if err := prog.validate(); err != nil {
+ if err := prog.validateWithOpts(validationOptions{
+ // Don't validate auto-set conditional fields. We'll patch them later.
+ ignoreTransient: true,
+ }); err != nil {
return nil, err
}
+ p.fixupConditionals(prog)
if p.autos != nil {
p.fixupAutos(prog)
}
@@ -1154,6 +1158,13 @@ func (p *parser) fixupAutos(prog *Prog) {
}
}
+func (p *parser) fixupConditionals(prog *Prog) {
+ for _, c := range prog.Calls {
+ // Only overwrite transient union fields.
+ c.setDefaultConditions(p.target, true)
+ }
+}
+
func (p *parser) Scan() bool {
if p.e != nil || len(p.data) == 0 {
return false
diff --git a/prog/expr.go b/prog/expr.go
index 5ecbef8eb..38358b214 100644
--- a/prog/expr.go
+++ b/prog/expr.go
@@ -174,7 +174,7 @@ func matchingUnionArgs(typ *UnionType, finder ArgFinder) []int {
func (p *Prog) checkConditions() error {
for _, c := range p.Calls {
- err := c.checkConditions(p.Target)
+ err := c.checkConditions(p.Target, false)
if err != nil {
return err
}
@@ -184,12 +184,15 @@ func (p *Prog) checkConditions() error {
var ErrViolatedConditions = errors.New("conditional fields rules violation")
-func (c *Call) checkConditions(target *Target) error {
+func (c *Call) checkConditions(target *Target, ignoreTransient bool) error {
var ret error
makeArgFinder := argFinderConstructor(target, c)
forEachStaleUnion(target, c, makeArgFinder,
func(a *UnionArg, t *UnionType, okIndices []int) {
+ if ignoreTransient && a.transient {
+ return
+ }
ret = fmt.Errorf("%w union %s field is #%d(%s), but %v satisfy conditions",
ErrViolatedConditions, t.Name(), a.Index, t.Fields[a.Index].Name,
okIndices)
@@ -197,7 +200,7 @@ func (c *Call) checkConditions(target *Target) error {
return ret
}
-func (c *Call) setDefaultConditions(target *Target) bool {
+func (c *Call) setDefaultConditions(target *Target, transientOnly bool) bool {
var anyReplaced bool
// Replace stale conditions with the default values of their correct types.
for {
@@ -205,6 +208,9 @@ func (c *Call) setDefaultConditions(target *Target) bool {
makeArgFinder := argFinderConstructor(target, c)
forEachStaleUnion(target, c, makeArgFinder,
func(unionArg *UnionArg, unionType *UnionType, okIndices []int) {
+ if transientOnly && !unionArg.transient {
+ return
+ }
// If several union options match, take the first one.
idx := okIndices[0]
field := unionType.Fields[idx]
diff --git a/prog/minimization.go b/prog/minimization.go
index a9864aca2..1760d180f 100644
--- a/prog/minimization.go
+++ b/prog/minimization.go
@@ -281,7 +281,7 @@ func minimizeInt(ctx *minimizeArgsCtx, arg Arg, path string) bool {
// By mutating an integer, we risk violating conditional fields.
// If the fields are patched, the minimization process must be restarted.
- patched := ctx.call.setDefaultConditions(ctx.p.Target)
+ patched := ctx.call.setDefaultConditions(ctx.p.Target, false)
if ctx.pred(ctx.p, ctx.callIndex0) {
*ctx.p0 = ctx.p
ctx.triedPaths[path] = true
diff --git a/prog/rand.go b/prog/rand.go
index aca3163bd..742dbaa7c 100644
--- a/prog/rand.go
+++ b/prog/rand.go
@@ -886,7 +886,7 @@ func (a *UnionType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*C
if a.isConditional() {
// Conditions may reference other fields that may not have already
// been generated. We'll fill them in later.
- return a.DefaultTransientArg(dir), nil
+ return a.DefaultArg(dir), nil
}
index := r.Intn(len(a.Fields))
optType, optDir := a.Fields[index].Type, a.Fields[index].Dir(dir)
diff --git a/prog/test/fuzz_test.go b/prog/test/fuzz_test.go
index f01905ea3..2d063c469 100644
--- a/prog/test/fuzz_test.go
+++ b/prog/test/fuzz_test.go
@@ -30,6 +30,9 @@ test$res2()
test$res2()
test$res2()
`,
+ `r=test$res0()
+test$recur2(&(293324893027559)={r})
+`,
} {
t.Logf("test #%v: %q", i, data)
inp := []byte(data)[:len(data):len(data)]
diff --git a/prog/types.go b/prog/types.go
index 00440163b..4e9d8455e 100644
--- a/prog/types.go
+++ b/prog/types.go
@@ -735,13 +735,9 @@ func (t *UnionType) String() string {
func (t *UnionType) DefaultArg(dir Dir) Arg {
idx := t.defaultField()
f := t.Fields[idx]
- return MakeUnionArg(t, dir, f.DefaultArg(f.Dir(dir)), idx)
-}
-
-func (t *UnionType) DefaultTransientArg(dir Dir) Arg {
- unionArg := t.DefaultArg(dir).(*UnionArg)
- unionArg.transient = true
- return unionArg
+ arg := MakeUnionArg(t, dir, f.DefaultArg(f.Dir(dir)), idx)
+ arg.transient = t.isConditional()
+ return arg
}
func (t *UnionType) defaultField() int {
diff --git a/prog/validation.go b/prog/validation.go
index 13b7c33db..f38dcd1e8 100644
--- a/prog/validation.go
+++ b/prog/validation.go
@@ -21,15 +21,25 @@ func (p *Prog) debugValidate() {
}
}
+func (p *Prog) validate() error {
+ return p.validateWithOpts(validationOptions{})
+}
+
type validCtx struct {
target *Target
+ opts validationOptions
args map[Arg]bool
uses map[Arg]Arg
}
-func (p *Prog) validate() error {
+type validationOptions struct {
+ ignoreTransient bool
+}
+
+func (p *Prog) validateWithOpts(opts validationOptions) error {
ctx := &validCtx{
target: p.Target,
+ opts: opts,
args: make(map[Arg]bool),
uses: make(map[Arg]Arg),
}
@@ -65,7 +75,7 @@ func (ctx *validCtx) validateCall(c *Call) error {
return err
}
}
- if err := c.checkConditions(ctx.target); err != nil {
+ if err := c.checkConditions(ctx.target, ctx.opts.ignoreTransient); err != nil {
return err
}
return ctx.validateRet(c)