Add OIIO_NODISCARD to ImageInput create/open/read methods#5111
Add OIIO_NODISCARD to ImageInput create/open/read methods#5111mvanhorn wants to merge 2 commits intoAcademySoftwareFoundation:mainfrom
Conversation
6ac7875 to
50ed282
Compare
|
Did you not run the CI before submitting the PR? Many of the CI job variants are failing -- for the obvious reason that OIIO itself calls these functions, often without checking the return code (I know, bad), so those will now be errors and break our build. Changes to the function declarations to add these annotations must be accompanied by changes to the call sites where we have up until now neglected the return values. That's part of why I wanted to go slowly, a few calls at a time, because we'll want to carefully review and ensure that all the places that we must add the error checks are handling those previously-unchecked errors sensibly, so we want small, easily reviewable PRs for that sweeping set of changes. As for why some job variants pass and others fail, I suspect that is reflecting which compiler, version, and C++ standard they are using. |
|
I like the idea of migrating to heavy use of nodiscard, but I fear how much user code will have broken builds as a result (as our own was, revealed by the CI!). Maybe there's a way to gently transition people? I think there are two classes of return values we may wish to annotate as "nodiscard":
Definitely for case 1, we should not hesitate to annotate with OIIO_NODISCARD now. But for case 2, I wonder what people think of the merits of creating a second macro #ifndef OIIO_CHECK_DISCARDED_ERROR_RETURN
# define OIIO_CHECK_DISCARDED_ERROR_RETURN 0
#endif
#if OIIO_CHECK_DISCARDED_ERROR_RETURN
# define OIIO_NODISCARD_ERROR OIIO_NODISCARD
#else
# define OIIO_NODISCARD_ERROR
#endifSo what this would do is:
So, for example, a method like Opinions? |
It looks like clang is always flagging the nodiscard errors, but gcc never is. We should look into why this is the case -- I would have expected more recent gcc versions to also turn them into errors. Have we done something wrong, or perhaps is something else needed for clang? |
|
You're right - I should have run CI before submitting. Apologies for the noise. The two-tier approach makes sense. I'll rework this to:
On the clang vs gcc question: gcc only treats I'll push the reworked version with call site fixes shortly. |
|
I prefer OIIO_NODISCARD_ERROR rather than OIIO_NODISCARD_GENTLE, in case we later identify other cases (aside from error status returns) that we want to independently select as gentle or harsh. |
|
Good call on OIIO_NODISCARD_ERROR - clearer that it's specific to error-status returns and leaves room for other nodiscard categories later. Will use that naming in the rework. |
Just a gentle reminder in case this slipped your mind. There is no rush, of course. I just wanted to make sure it wasn't a case of your already having done it but forgotten to re-push. |
50ed282 to
b6cfc38
Compare
|
Reworked in b6cfc38. Two-tier approach as discussed:
Also fixed unchecked return values at three internal call sites: null check after Used |
Per @lgritz feedback, use a two-tier approach: - OIIO_NODISCARD (hard [[nodiscard]]) on create() and open(), where ignoring the return is always a bug. - OIIO_NODISCARD_ERROR (initially a no-op) on read_image() and read_scanlines(), where ignoring is sloppy but not catastrophic. This allows a gentle transition for downstream callers. Also fix unchecked return values at internal call sites: - imageioplugin.cpp: add null check after ImageInput::create() - jpegoutput.cpp: check open() return and propagate failure - py_imagebuf.cpp: check create()/open() return before writing Refs: AcademySoftwareFoundation#4781 Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
b6cfc38 to
5b166ef
Compare
lgritz
left a comment
There was a problem hiding this comment.
This is looking great. I made a few minor suggestions.
I see that there are still some tests failing, I don't quite understand why. I believe this is somehow changing control flow in such a way that it changes what errors are printed in some case?
src/python/py_imagebuf.cpp
Outdated
| if (!out || !out->open("temp.png", altered_spec)) | ||
| return py::none(); |
There was a problem hiding this comment.
I think this changes the behavior -- instead of returning an empty 'bytes' object, now it returns none.
| auto inp = ImageInput::create(f.first); | ||
| lock.lock(); | ||
| if (inp->supports("procedural")) | ||
| if (inp && inp->supports("procedural")) |
src/jpeg.imageio/jpegoutput.cpp
Outdated
| if (!open(out_name, orig_out_spec)) | ||
| return false; |
There was a problem hiding this comment.
It used to be that even if it fails, it would call close(), below (though erroneously returning 'true'). Can we guarantee that every possible failure of open() leaves it in a closed state? Or should we structure this more like
bool ok = open(...);
// the comments
close();
return ok;
and that would exactly replicate what we did before, except for correctly returning false upon failure to open?
| // OIIO_NODISCARD_ERROR is for functions returning error status (bool) where | ||
| // ignoring the return is a bad practice but not always catastrophic. This is | ||
| // initially a no-op to allow a gentle transition; it can later be defined as | ||
| // [[nodiscard]] once downstream callers have been updated. | ||
| #ifndef OIIO_NODISCARD_ERROR | ||
| # define OIIO_NODISCARD_ERROR /* nothing for now */ | ||
| #endif | ||
|
|
There was a problem hiding this comment.
Let's structure this a little differently, so that a simple on/off can turn on the right behavior and not just leave it to change this line for future versions:
| // OIIO_NODISCARD_ERROR is for functions returning error status (bool) where | |
| // ignoring the return is a bad practice but not always catastrophic. This is | |
| // initially a no-op to allow a gentle transition; it can later be defined as | |
| // [[nodiscard]] once downstream callers have been updated. | |
| #ifndef OIIO_NODISCARD_ERROR | |
| # define OIIO_NODISCARD_ERROR /* nothing for now */ | |
| #endif | |
| // OIIO_NODISCARD_ERROR is for functions returning error status (bool) where | |
| // ignoring the return is a bad practice but not always catastrophic. | |
| // | |
| // OIIO_NODISCARD_ERROR_ENABLE, if nonzero, enables OIIO_NODISCARD_ERROR to | |
| // take effect. The default is to disable it for OIIO < 3.3, and enable it for | |
| // OIIO >= 3.3. But `-DOIIO_NODISCARD_ERROR_ENABLE=...` can be used to | |
| // override the default, for example to flag discarded errors in older | |
| // versions of OIIO, or to disable the warnings in future versions of OIIO. | |
| #ifndef OIIO_NODISCARD_ERROR_ENABLE | |
| # if OIIO_VERSION_LESS(3, 3, 0) | |
| # define OIIO_NODISCARD_ERROR_ENABLE 0 /* disable for now */ | |
| # else | |
| # define OIIO_NODISCARD_ERROR_ENABLE 1 /* enable for OIIO >= 3.3 */ | |
| # endif | |
| #endif | |
| #if OIIO_NODISCARD_ERROR_DEFAULT | |
| # define OIIO_NODISCARD_ERROR OIIO_NODISCARD | |
| #else | |
| # define OIIO_NODISCARD /* nothing */ | |
| #endif | |
|
Addressed in 3dafe19:
Relying on CI for verification (C++ project, no local build). The test failures should resolve since the py_imagebuf behavioral change and the jpegoutput close-path change were the likely culprits. |
…l changes - platform.h: Replace simple no-op macro with version-gated OIIO_NODISCARD_ERROR_ENABLE that defaults to disabled for OIIO < 3.3 and enabled for >= 3.3, overridable via -D flag - py_imagebuf.cpp: Restore returning empty py::bytes() instead of py::none() on open failure to preserve backward compatibility - jpegoutput.cpp: Restructure open/close flow to guarantee close() is always called even when open() fails, matching previous behavior - imageio.h: Apply clang-format to NODISCARD_ERROR-annotated functions Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
3dafe19 to
0c93039
Compare
Summary
Adds
OIIO_NODISCARDtoImageInput::create(),open(),read_image(), andread_scanlines()methods. Callers ignoring return values from these methods now get a compiler warning.Why this matters
As noted in #4781, it's easy to accidentally ignore the bool return from
read_image()oropen(). These methods return false on failure, and silently proceeding with bad data leads to hard-to-debug issues.create()returns a unique_ptr that should never be discarded.Changes
src/include/OpenImageIO/imageio.h: AddedOIIO_NODISCARDto 14 method declarations in theImageInputclass, coveringcreate()(2 overloads),open()(4 overloads),read_image()(4 overloads), andread_scanlines()(4 overloads).Scoped to
ImageInputonly per @lgritz's guidance to start with one class and iterate.Testing
Partial fix for #4781
This contribution was developed with AI assistance (Claude Code).