Eric Blake <ebl...@redhat.com> writes: > We want to call the various visit_end_*() functions unconditionally, > so that visitors can release resources tied up since the matching > visit_start_*(). But we also have a requirement for detecting when > an input visitor did not consume everything, so the code allowed > visit_end_*() to set an error. This led to some awkward coding, > since safely setting the error, even if an earlier one had > occurred, while still favoring the earliest error, requires either > multiple error_propagate() calls (and an understanding that unlike > error_setg(), it is safe to propagate onto an already-set error), > or else ternaries such as foo(..., err ? NULL : &err) to ignore > secondary errors if an earlier error is already known. > > Splitting the functionality allows slightly cleaner idioms. Now, > clients must call visit_check_struct() if all earlier actions > have been successful for one last chance at flagging excess > input, while visit_end_struct() is unconditional and does not > set any further errors. > > As it is an API change, everything must be changed in one commit > (there is no way to break this into smaller pieces): both the > generated code and other existing clients, as well as the various > visitor backends. > > Signed-off-by: Eric Blake <ebl...@redhat.com> > > --- > TODO: There is some redundancy in the testsuite, with multiple > files hand-writing their own visit_type_TestStruct() instead > of letting the qapi generators create it. Should this be > consolidated as a separate patch?
If generating makes sense, it should be a separate patch. Whether it makes sense I can't say until I see the patch. > Probably won't apply directly to any published commit, so much > as me posting a part of my worktree early to get feedback on > whether the idea even makes sense, given how large the results > end up being. > > v6: new patch, based on discussion of v5 7/46: > > On 09/28/2015 03:14 AM, Markus Armbruster wrote: > >>> One other potential alternative: What if we split visit_end_struct() >>> into two visitor functions, one that checks for success, and the other >>> that is called unconditionally to clean up resources. > >> I think this split could help with writing safe code: in >> visit_check_struct() you can rely on "no error so far", as usual. In >> visit_end_struct(), you can't, but it should be a pure cleanup function, >> where that's quite normal. >> >> Looks like we're getting drawn into visitor contract territory again. >> > > --- > hmp.c | 3 ++- > hw/virtio/virtio-balloon.c | 14 ++++++++------ > include/qapi/visitor-impl.h | 8 +++++--- > include/qapi/visitor.h | 24 ++++++++++++++++++------ > qapi/opts-visitor.c | 17 +++++++++++++++-- > qapi/qapi-dealloc-visitor.c | 6 +++--- > qapi/qapi-visit-core.c | 19 +++++++++++++------ > qapi/qmp-input-visitor.c | 36 +++++++++++++++++++++++++----------- > qapi/qmp-output-visitor.c | 4 ++-- > qapi/string-input-visitor.c | 2 +- > qapi/string-output-visitor.c | 2 +- > qom/object.c | 5 ++--- > scripts/qapi-event.py | 5 +++-- > scripts/qapi-visit.py | 24 ++++++++++++------------ > tests/test-qmp-input-strict.c | 9 +++++---- > tests/test-qmp-input-visitor.c | 9 +++++---- > tests/test-qmp-output-visitor.c | 23 ++++++++++++++++------- > tests/test-visitor-serialization.c | 9 +++++---- > vl.c | 12 +++++++----- > 19 files changed, 148 insertions(+), 83 deletions(-) > > diff --git a/hmp.c b/hmp.c > index 5048eee..b7a42dc 100644 > --- a/hmp.c > +++ b/hmp.c > @@ -1658,8 +1658,9 @@ void hmp_object_add(Monitor *mon, const QDict *qdict) > > object_add(type, id, pdict, opts_get_visitor(ov), &err); > > + visit_check_struct(opts_get_visitor(ov), &err_end); > out_end: > - visit_end_struct(opts_get_visitor(ov), &err_end); > + visit_end_struct(opts_get_visitor(ov)); > if (!err && err_end) { > qmp_object_del(id, NULL); > } Preexisting: calling object_add() before visit_end_struct() is awkward. Can we simplify things now we have separate visit_check_struct() and visit_end_struct()? Call visit_check_struct() before object_add(), bypass object_add() on error, avoiding the need to undo it with qmp_object_del(). > diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c > index c419b17..48867c4 100644 > --- a/hw/virtio/virtio-balloon.c > +++ b/hw/virtio/virtio-balloon.c > @@ -132,14 +132,16 @@ static void balloon_stats_get_all(Object *obj, struct > Visitor *v, visit_start_struct(v, NULL, NULL, "stats", 0, &err); if (err) { goto out_end; } for (i = 0; !err && i < VIRTIO_BALLOON_S_NR; i++) { > visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i], > &err); Preexisting: if visit_type_int64() fails, we need to break the loop. Separate fix. I wonder whether this is a common mistake. Perhaps I can search for it with Coccinelle. > } > - error_propagate(errp, err); > - err = NULL; > - visit_end_struct(v, &err); > + if (!err) { > + visit_check_struct(v, &err); > + } > + visit_end_struct(v); > > out_end: > - error_propagate(errp, err); > - err = NULL; > - visit_end_struct(v, &err); > + if (!err) { > + visit_check_struct(v, &err); > + } > + visit_end_struct(v); > out: > error_propagate(errp, err); > } This is the obvious mechanical change. Loop fix and visit_end_struct() split squashed together could look like this (not even compile-tested): for (i = 0; !err && i < VIRTIO_BALLOON_S_NR; i++) { visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i], &err); if (err) { goto out_inner_end; } } visit_check_struct(v, &err); out_inner_end: visit_end_struct(v); if (!err) { visit_check_struct(v, &err); } out_end: visit_end_struct(v); out: error_propagate(errp, err); > diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h > index 090b19b..c53d4d3 100644 > --- a/include/qapi/visitor-impl.h > +++ b/include/qapi/visitor-impl.h > @@ -28,21 +28,23 @@ struct Visitor > * currently visit structs). */ > void (*start_struct)(Visitor *v, void **obj, const char *kind, > const char *name, size_t size, Error **errp); > + /* May be NULL; most useful for input visitors. */ > + void (*check_struct)(Visitor *v, Error **errp); > /* Must be provided if start_struct is present. */ > - void (*end_struct)(Visitor *v, Error **errp); > + void (*end_struct)(Visitor *v); > > /* May be NULL; most useful for input visitors. */ > void (*start_implicit_struct)(Visitor *v, void **obj, size_t size, > Error **errp); > /* May be NULL */ > - void (*end_implicit_struct)(Visitor *v, Error **errp); > + void (*end_implicit_struct)(Visitor *v); > > /* Must be set */ > void (*start_list)(Visitor *v, const char *name, Error **errp); > /* Must be set */ > GenericList *(*next_list)(Visitor *v, GenericList **list, Error **errp); > /* Must be set */ > - void (*end_list)(Visitor *v, Error **errp); > + void (*end_list)(Visitor *v); > > /* Must be set, although the helpers input_type_enum() and > * output_type_enum() can be used. */ > diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h > index 6534cca..708f55c 100644 > --- a/include/qapi/visitor.h > +++ b/include/qapi/visitor.h > @@ -56,11 +56,19 @@ typedef struct GenericList > void visit_start_struct(Visitor *v, void **obj, const char *kind, > const char *name, size_t size, Error **errp); > /** > + * Check whether completing a struct is safe. "Safe"? We need to complete the struct visit with visit_end_struct() regardless of what this function returns... > + * Should be called prior to visit_end_struct() if all other intermediate > + * visit steps were successful, to allow the caller one last chance to > + * report errors such as remaining data that was not consumed by the visit. > + */ > +void visit_check_struct(Visitor *v, Error **errp); > +/** > * Complete a struct visit started earlier. > * Must be called after any successful use of visit_start_struct(), > - * even if intermediate processing was skipped due to errors. > + * even if intermediate processing was skipped due to errors, to allow > + * the backend to release any resources. > */ > -void visit_end_struct(Visitor *v, Error **errp); > +void visit_end_struct(Visitor *v); > > /** > * Prepare to visit an implicit struct. > @@ -76,9 +84,12 @@ void visit_start_implicit_struct(Visitor *v, void **obj, > size_t size, > /** > * Complete an implicit struct visit started earlier. > * Must be called after any successful use of visit_start_implicit_struct(), > - * even if intermediate processing was skipped due to errors. > + * even if intermediate processing was skipped due to errors. Unlike > + * visit_end_struct(), there is no need for a prior check call (since > + * an implicit object is a subset of larger object, checking before ending > + * the overall struct is sufficient). > */ > -void visit_end_implicit_struct(Visitor *v, Error **errp); > +void visit_end_implicit_struct(Visitor *v); > > /** > * Prepare to visit an array tied to an object key @name. > @@ -101,9 +112,10 @@ GenericList *visit_next_list(Visitor *v, GenericList > **list, Error **errp); > /** > * Complete the list started earlier. > * Must be called after any successful use of visit_start_list(), > - * even if intermediate processing was skipped due to errors. > + * even if intermediate processing was skipped due to errors, to allow > + * the backend to release any resources. > */ > -void visit_end_list(Visitor *v, Error **errp); > +void visit_end_list(Visitor *v); > > /** > * Check if an optional member @name of an object needs visiting. > diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c > index cd10392..39dc431 100644 > --- a/qapi/opts-visitor.c > +++ b/qapi/opts-visitor.c > @@ -158,7 +158,7 @@ ghr_true(gpointer ign_key, gpointer ign_value, gpointer > ign_user_data) > > > static void > -opts_end_struct(Visitor *v, Error **errp) > +opts_check_struct(Visitor *v, Error **errp) > { > OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); > GQueue *any; if (--ov->depth > 0) { Do we want to decrement ov->depth here? We'll decrement it again in opts_end_struct()... return; } /* we should have processed all (distinct) QemuOpt instances */ any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL); if (any) { const QemuOpt *first; > @@ -175,6 +175,18 @@ opts_end_struct(Visitor *v, Error **errp) > first = g_queue_peek_head(any); > error_setg(errp, QERR_INVALID_PARAMETER, first->name); > } > +} > + > + > +static void > +opts_end_struct(Visitor *v) > +{ > + OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); > + > + if (--ov->depth > 0) { > + return; > + } > + > g_hash_table_destroy(ov->unprocessed_opts); > ov->unprocessed_opts = NULL; > if (ov->fake_id_opt) { > @@ -263,7 +275,7 @@ opts_next_list(Visitor *v, GenericList **list, Error > **errp) > > > static void > -opts_end_list(Visitor *v, Error **errp) > +opts_end_list(Visitor *v) > { > OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); > > @@ -506,6 +518,7 @@ opts_visitor_new(const QemuOpts *opts) > ov = g_malloc0(sizeof *ov); > > ov->visitor.start_struct = &opts_start_struct; > + ov->visitor.check_struct = &opts_check_struct; > ov->visitor.end_struct = &opts_end_struct; > > ov->visitor.start_list = &opts_start_list; > diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c > index 737deab..a2139dd 100644 > --- a/qapi/qapi-dealloc-visitor.c > +++ b/qapi/qapi-dealloc-visitor.c > @@ -67,7 +67,7 @@ static void qapi_dealloc_start_struct(Visitor *v, void > **obj, const char *kind, > qapi_dealloc_push(qov, obj); > } > > -static void qapi_dealloc_end_struct(Visitor *v, Error **errp) > +static void qapi_dealloc_end_struct(Visitor *v) > { > QapiDeallocVisitor *qov = to_qov(v); > void **obj = qapi_dealloc_pop(qov); > @@ -85,7 +85,7 @@ static void qapi_dealloc_start_implicit_struct(Visitor *v, > qapi_dealloc_push(qov, obj); > } > > -static void qapi_dealloc_end_implicit_struct(Visitor *v, Error **errp) > +static void qapi_dealloc_end_implicit_struct(Visitor *v) > { > QapiDeallocVisitor *qov = to_qov(v); > void **obj = qapi_dealloc_pop(qov); > @@ -121,7 +121,7 @@ static GenericList *qapi_dealloc_next_list(Visitor *v, > GenericList **listp, > return NULL; > } > > -static void qapi_dealloc_end_list(Visitor *v, Error **errp) > +static void qapi_dealloc_end_list(Visitor *v) > { > QapiDeallocVisitor *qov = to_qov(v); > void *obj = qapi_dealloc_pop(qov); > diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c > index cbf7780..82d54af 100644 > --- a/qapi/qapi-visit-core.c > +++ b/qapi/qapi-visit-core.c > @@ -23,9 +23,16 @@ void visit_start_struct(Visitor *v, void **obj, const char > *kind, > v->start_struct(v, obj, kind, name, size, errp); > } > > -void visit_end_struct(Visitor *v, Error **errp) > +void visit_check_struct(Visitor *v, Error **errp) > { > - v->end_struct(v, errp); > + if (v->check_struct) { > + v->check_struct(v, errp); > + } > +} > + > +void visit_end_struct(Visitor *v) > +{ > + v->end_struct(v); > } > > void visit_start_implicit_struct(Visitor *v, void **obj, size_t size, > @@ -36,10 +43,10 @@ void visit_start_implicit_struct(Visitor *v, void **obj, > size_t size, > } > } > > -void visit_end_implicit_struct(Visitor *v, Error **errp) > +void visit_end_implicit_struct(Visitor *v) > { > if (v->end_implicit_struct) { > - v->end_implicit_struct(v, errp); > + v->end_implicit_struct(v); > } > } > > @@ -53,9 +60,9 @@ GenericList *visit_next_list(Visitor *v, GenericList > **list, Error **errp) > return v->next_list(v, list, errp); > } > > -void visit_end_list(Visitor *v, Error **errp) > +void visit_end_list(Visitor *v) > { > - v->end_list(v, errp); > + v->end_list(v); > } > > bool visit_start_union(Visitor *v, bool data_present, Error **errp) > diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c > index 5310db5..f10f74f 100644 > --- a/qapi/qmp-input-visitor.c > +++ b/qapi/qmp-input-visitor.c > @@ -88,14 +88,14 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject > *obj, Error **errp) > qiv->nb_stack++; > } > > -/** Only for qmp_input_pop. */ > +/** Only for qmp_input_check. */ Drop the comment instead? Aside: a loop would be more idiomatic C. Leave higher order functions to languages that are actually equipped for the job. > static gboolean always_true(gpointer key, gpointer val, gpointer user_pkey) > { > *(const char **)user_pkey = (const char *)key; > return TRUE; > } > > -static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp) > +static void qmp_input_check(QmpInputVisitor *qiv, Error **errp) > { > assert(qiv->nb_stack > 0); > > @@ -107,6 +107,17 @@ static void qmp_input_pop(QmpInputVisitor *qiv, Error > **errp) > g_hash_table_find(top_ht, always_true, &key); > error_setg(errp, QERR_QMP_EXTRA_MEMBER, key); > } > + } > + } > +} > + > +static void qmp_input_pop(QmpInputVisitor *qiv) > +{ > + assert(qiv->nb_stack > 0); > + > + if (qiv->strict) { > + GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h; > + if (top_ht) { > g_hash_table_unref(top_ht); > } > } > @@ -138,11 +149,18 @@ static void qmp_input_start_struct(Visitor *v, void > **obj, const char *kind, > } > } > > -static void qmp_input_end_struct(Visitor *v, Error **errp) > +static void qmp_input_check_struct(Visitor *v, Error **errp) > { > QmpInputVisitor *qiv = to_qiv(v); > > - qmp_input_pop(qiv, errp); > + qmp_input_check(qiv, errp); qmp_input_check(to_qiv(v)); Alternatively, inline, as this is the only caller. > +} > + > +static void qmp_input_end_struct(Visitor *v) > +{ > + QmpInputVisitor *qiv = to_qiv(v); > + > + qmp_input_pop(qiv); qmp_input_pop(to_qiv(v)); Since all callers pass to_qiv(v), we could move that into the callee. Same for qmp_input_check() then. > } > > static void qmp_input_start_implicit_struct(Visitor *v, void **obj, > @@ -153,10 +171,6 @@ static void qmp_input_start_implicit_struct(Visitor *v, > void **obj, > } > } > > -static void qmp_input_end_implicit_struct(Visitor *v, Error **errp) > -{ > -} > - Further evidence of confusion about the visitor contract. > static void qmp_input_start_list(Visitor *v, const char *name, Error **errp) > { > QmpInputVisitor *qiv = to_qiv(v); > @@ -201,11 +215,11 @@ static GenericList *qmp_input_next_list(Visitor *v, > GenericList **list, > return entry; > } > > -static void qmp_input_end_list(Visitor *v, Error **errp) > +static void qmp_input_end_list(Visitor *v) > { > QmpInputVisitor *qiv = to_qiv(v); > > - qmp_input_pop(qiv, errp); > + qmp_input_pop(qiv); > } Identical to qmp_input_end_struct() now. If we move to_qiv() into qmp_input_pop(), we can use it as callback directly, and drop the two wrappers. > > static void qmp_input_get_next_type(Visitor *v, qtype_code *type, > @@ -332,9 +346,9 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj) > v = g_malloc0(sizeof(*v)); > > v->visitor.start_struct = qmp_input_start_struct; > + v->visitor.check_struct = qmp_input_check_struct; > v->visitor.end_struct = qmp_input_end_struct; > v->visitor.start_implicit_struct = qmp_input_start_implicit_struct; > - v->visitor.end_implicit_struct = qmp_input_end_implicit_struct; > v->visitor.start_list = qmp_input_start_list; > v->visitor.next_list = qmp_input_next_list; > v->visitor.end_list = qmp_input_end_list; > diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c > index f891e72..b9fba7b 100644 > --- a/qapi/qmp-output-visitor.c > +++ b/qapi/qmp-output-visitor.c > @@ -122,7 +122,7 @@ static void qmp_output_start_struct(Visitor *v, void > **obj, const char *kind, > qmp_output_push(qov, dict); > } > > -static void qmp_output_end_struct(Visitor *v, Error **errp) > +static void qmp_output_end_struct(Visitor *v) > { > QmpOutputVisitor *qov = to_qov(v); > qmp_output_pop(qov); > @@ -153,7 +153,7 @@ static GenericList *qmp_output_next_list(Visitor *v, > GenericList **listp, > return list ? list->next : NULL; > } > > -static void qmp_output_end_list(Visitor *v, Error **errp) > +static void qmp_output_end_list(Visitor *v) > { > QmpOutputVisitor *qov = to_qov(v); > qmp_output_pop(qov); > diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c > index bbd6a54..97e00c8 100644 > --- a/qapi/string-input-visitor.c > +++ b/qapi/string-input-visitor.c > @@ -173,7 +173,7 @@ next_list(Visitor *v, GenericList **list, Error **errp) > } > > static void > -end_list(Visitor *v, Error **errp) > +end_list(Visitor *v) > { > StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); > siv->head = true; > diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c > index b86ce2c..b269f39 100644 > --- a/qapi/string-output-visitor.c > +++ b/qapi/string-output-visitor.c > @@ -290,7 +290,7 @@ next_list(Visitor *v, GenericList **list, Error **errp) > } > > static void > -end_list(Visitor *v, Error **errp) > +end_list(Visitor *v) > { > StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); > > diff --git a/qom/object.c b/qom/object.c > index 11cd86b..6d92e01 100644 > --- a/qom/object.c > +++ b/qom/object.c > @@ -1844,10 +1844,9 @@ static void property_get_tm(Object *obj, Visitor *v, > void *opaque, > if (err) { > goto out_end; > } > + visit_check_struct(v, &err); > out_end: > - error_propagate(errp, err); > - err = NULL; > - visit_end_struct(v, errp); > + visit_end_struct(v); > out: > error_propagate(errp, err); > By now the improvement is obvious: the users of this interface get nicer pretty consistently. > diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py > index 720486f..250e753 100644 > --- a/scripts/qapi-event.py > +++ b/scripts/qapi-event.py > @@ -73,13 +73,14 @@ def gen_event_send(name, arg_type): > ret += gen_err_check() > ret += gen_visit_fields(arg_type.members, need_cast=True) > ret += mcgen(''' > - visit_end_struct(v, &err); > + visit_check_struct(v, &err); > if (err) { > goto out; > } > + visit_end_struct(v); > > obj = qmp_output_get_qobject(qov); > - g_assert(obj != NULL); > + g_assert(obj); I prefer the more laconic form myself, but it's an unrelated change. > > qdict_put_obj(qmp, "data", obj); > ''') > diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py > index 1ac5350..c5a32cf 100644 > --- a/scripts/qapi-visit.py > +++ b/scripts/qapi-visit.py > @@ -52,7 +52,7 @@ static void visit_type_implicit_%(c_type)s(Visitor *v, > %(c_type)s **obj, Error * > visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err); > if (!err) { > visit_type_%(c_type)s_fields(v, obj, errp); > - visit_end_implicit_struct(v, &err); > + visit_end_implicit_struct(v); > } > error_propagate(errp, err); > } > @@ -115,9 +115,12 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, > const char *name, Error > visit_start_struct(v, (void **)obj, "%(name)s", name, > sizeof(%(c_name)s), &err); > if (!err) { > if (*obj) { > - visit_type_%(c_name)s_fields(v, obj, errp); > + visit_type_%(c_name)s_fields(v, obj, &err); > } > - visit_end_struct(v, &err); > + if (!err) { > + visit_check_struct(v, &err); > + } > + visit_end_struct(v); > } > error_propagate(errp, err); > } > @@ -147,9 +150,7 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, > const char *name, Error > visit_type_%(c_elt_type)s(v, &native_i->value, NULL, &err); > } > > - error_propagate(errp, err); > - err = NULL; > - visit_end_list(v, &err); > + visit_end_list(v); > out: > error_propagate(errp, err); > } > @@ -208,9 +209,7 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, > const char *name, Error > "%(name)s"); > } > out_obj: > - error_propagate(errp, err); > - err = NULL; > - visit_end_implicit_struct(v, &err); > + visit_end_implicit_struct(v); > out: > error_propagate(errp, err); > } > @@ -297,13 +296,14 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s > **obj, const char *name, Error > default: > abort(); > } > + if (!err) { > + visit_check_struct(v, &err); > + } > out_obj: > error_propagate(errp, err); > err = NULL; > visit_end_union(v, !!(*obj)->data, &err); Should visit_end_union() be similarly split? Or should its Error ** parameter be dropped? As far as I can tell, no visitor implements this method... > - error_propagate(errp, err); > - err = NULL; > - visit_end_struct(v, &err); > + visit_end_struct(v); > out: > error_propagate(errp, err); > } > diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c > index 1c13c8f..da9dafd 100644 > --- a/tests/test-qmp-input-strict.c > +++ b/tests/test-qmp-input-strict.c > @@ -118,11 +118,12 @@ static void visit_type_TestStruct(Visitor *v, > TestStruct **obj, > goto out_end; > } > visit_type_str(v, &(*obj)->string, "string", &err); > - > + if (err) { > + goto out_end; > + } > + visit_check_struct(v, &err); > out_end: > - error_propagate(errp, err); > - err = NULL; > - visit_end_struct(v, &err); > + visit_end_struct(v); > out: > error_propagate(errp, err); > } > diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c > index ef5871b..38b1436 100644 > --- a/tests/test-qmp-input-visitor.c > +++ b/tests/test-qmp-input-visitor.c > @@ -200,11 +200,12 @@ static void visit_type_TestStruct(Visitor *v, > TestStruct **obj, > goto out_end; > } > visit_type_str(v, &(*obj)->string, "string", &err); > - > + if (err) { > + goto out_end; > + } > + visit_check_struct(v, &err); Here's you goto over visit_check_struct()... > out_end: > - error_propagate(errp, err); > - err = NULL; > - visit_end_struct(v, &err); > + visit_end_struct(v); > out: > error_propagate(errp, err); > } > diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c > index 11996d0..f836588 100644 > --- a/tests/test-qmp-output-visitor.c > +++ b/tests/test-qmp-output-visitor.c > @@ -194,10 +194,11 @@ static void visit_type_TestStruct(Visitor *v, > TestStruct **obj, > } > visit_type_str(v, &(*obj)->string, "string", &err); > > + if (!err) { > + visit_check_struct(v, &err); > + } ... but here, you guard it with an if. Either way works, but I'd like us to pick just one for the generators. > out_end: > - error_propagate(errp, err); > - err = NULL; > - visit_end_struct(v, &err); > + visit_end_struct(v); > out: > error_propagate(errp, err); > } > @@ -329,15 +330,23 @@ static void visit_type_TestStructList(Visitor *v, > TestStructList **obj, > const char *name, Error **errp) > { > GenericList *i, **head = (GenericList **)obj; > + Error *err = NULL; > > - visit_start_list(v, name, errp); > + visit_start_list(v, name, &err); > + if (err) { > + goto out; > + } > > - for (*head = i = visit_next_list(v, head, errp); i; i = > visit_next_list(v, &i, errp)) { > + for (*head = i = visit_next_list(v, head, &err); > + !err && i; > + i = visit_next_list(v, &i, &err)) { > TestStructList *native_i = (TestStructList *)i; > - visit_type_TestStruct(v, &native_i->value, NULL, errp); > + visit_type_TestStruct(v, &native_i->value, NULL, &err); > } Is this a silent bug fix? Before your patch, the loop doesn't break on error. I'm not afraid of complex for expressions, but I think this one is unnecessarily complex. Why does visit_type_TestStruct() store to *head? All that buys us is less obviously correct list cleanup in test_visitor_out_list(). Testing err in the for expression works, but the admittedly more verbose visit_type_TestStruct(v, &native_i->value, NULL, &err); if (err) { break; // or goto wherever } would be closer to common Error usage. Matter of taste. Aside: the caller creates identical list elements, which weakens the test needlessly. > > - visit_end_list(v, errp); > + visit_end_list(v); > +out: > + error_propagate(errp, err); > } > > static void test_visitor_out_list(TestOutputVisitorData *data, > diff --git a/tests/test-visitor-serialization.c > b/tests/test-visitor-serialization.c > index fa86cae..dd50d69 100644 > --- a/tests/test-visitor-serialization.c > +++ b/tests/test-visitor-serialization.c > @@ -212,11 +212,12 @@ static void visit_type_TestStruct(Visitor *v, > TestStruct **obj, > goto out_end; > } > visit_type_str(v, &(*obj)->string, "string", &err); > - > + if (err) { > + goto out_end; > + } > + visit_check_struct(v, &err); > out_end: > - error_propagate(errp, err); > - err = NULL; > - visit_end_struct(v, &err); > + visit_end_struct(v); > out: > error_propagate(errp, err); > } > diff --git a/vl.c b/vl.c > index 8d1846c..8b944aa 100644 > --- a/vl.c > +++ b/vl.c > @@ -2796,23 +2796,25 @@ static int object_create(void *opaque, QemuOpts > *opts, Error **errp) > qdict_del(pdict, "qom-type"); > visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err); > if (err) { > - goto out; > + goto obj_out; > } > if (!type_predicate(type)) { > - goto out; > + goto obj_out; > } > > qdict_del(pdict, "id"); > visit_type_str(opts_get_visitor(ov), &id, "id", &err); > if (err) { > - goto out; > + goto obj_out; > } > > object_add(type, id, pdict, opts_get_visitor(ov), &err); > if (err) { > - goto out; > + goto obj_out; > } > - visit_end_struct(opts_get_visitor(ov), &err); > + visit_check_struct(opts_get_visitor(ov), &err); > +obj_out: > + visit_end_struct(opts_get_visitor(ov)); > if (err) { > qmp_object_del(id, NULL); > } Silent bug fix: we now call visit_end_struct() even on error. Impact? Separate patch?