Add warnings as another status code category
diff --git a/cmd/wuffs-c/internal/cgen/base/base-public.h b/cmd/wuffs-c/internal/cgen/base/base-public.h index fd481d5..3a70040 100644 --- a/cmd/wuffs-c/internal/cgen/base/base-public.h +++ b/cmd/wuffs-c/internal/cgen/base/base-public.h
@@ -99,10 +99,10 @@ // -------- -// A status is either NULL (meaning OK or no error) or string message. That -// message is human-readable, for programmers, but it is not for end users. It -// is not localized, and does not contain additional contextual information -// such as a source filename. +// A status is either NULL (meaning OK) or a string message. That message is +// human-readable, for programmers, but it is not for end users. It is not +// localized, and does not contain additional contextual information such as a +// source filename. // // Status strings are statically allocated and should never be free'd. They can // be compared by the == operator and not just by strcmp. @@ -112,7 +112,7 @@ static inline bool // wuffs_base__status__is_error(wuffs_base__status z) { - return z && (*z != '$'); + return z && (*z == '?'); } static inline bool // @@ -125,6 +125,11 @@ return z && (*z == '$'); } +static inline bool // +wuffs_base__status__is_warning(wuffs_base__status z) { + return z && (*z != '$') && (*z != '?'); +} + // -------- // Flicks are a unit of time. One flick (frame-tick) is 1 / 705_600_000 of a
diff --git a/cmd/wuffs-c/internal/cgen/cgen.go b/cmd/wuffs-c/internal/cgen/cgen.go index 54d8837..e2a2692 100644 --- a/cmd/wuffs-c/internal/cgen/cgen.go +++ b/cmd/wuffs-c/internal/cgen/cgen.go
@@ -116,9 +116,11 @@ if z == "" { continue } - pre := "error" + pre := "warning" if z[0] == '$' { pre = "suspension" + } else if z[0] == '?' { + pre = "error" } b.printf("const char* wuffs_base__%s__%s = \"%sbase: %s\";\n", pre, cName(z, ""), z[:1], z[1:]) @@ -185,7 +187,15 @@ } func statusMsgIsError(msg string) bool { - return (len(msg) == 0) || (msg[0] != '$') + return (len(msg) != 0) && (msg[0] == '?') +} + +func statusMsgIsSuspension(msg string) bool { + return (len(msg) != 0) && (msg[0] == '$') +} + +func statusMsgIsWarning(msg string) bool { + return (len(msg) == 0) || (msg[0] != '$' && msg[0] != '?') } type buffer []byte @@ -247,8 +257,10 @@ if err := expandBangBangInsert(buf, baseBasePublicH, map[string]func(*buffer) error{ "// !! INSERT wuffs_base__status names.\n": func(b *buffer) error { for _, z := range builtin.StatusList { - pre := "error" - if !statusMsgIsError(z) { + pre := "warning" + if statusMsgIsError(z) { + pre = "error" + } else if statusMsgIsSuspension(z) { pre = "suspension" } b.printf("extern const char* wuffs_base__%s__%s;\n", pre, cName(z, ""))
diff --git a/cmd/wuffs-c/internal/cgen/data.go b/cmd/wuffs-c/internal/cgen/data.go index 13ef72d..a3a8605 100644 --- a/cmd/wuffs-c/internal/cgen/data.go +++ b/cmd/wuffs-c/internal/cgen/data.go
@@ -22,7 +22,7 @@ "nt.com/nothings/stb/master/docs/stb_howto.txt\n#ifdef WUFFS_CONFIG__STATIC_FUNCTIONS\n#define WUFFS_BASE__MAYBE_STATIC static\n#else\n#define WUFFS_BASE__MAYBE_STATIC\n#endif\n\n// Clang also defines \"__GNUC__\".\n#if defined(__GNUC__)\n#define WUFFS_BASE__WARN_UNUSED_RESULT __attribute__((warn_unused_result))\n#else\n#define WUFFS_BASE__WARN_UNUSED_RESULT\n#endif\n\n// wuffs_base__empty_struct is used when a Wuffs function returns an empty\n// struct. In C, if a function f returns void, you can't say \"x = f()\", but in\n// Wuffs, if a function g returns empty, you can say \"y = g()\".\ntypedef struct {\n // private_impl is a placeholder field. It isn't explicitly used, except that\n // without it, the sizeof a struct with no fields can differ across C/C++\n // compilers, and it is undefined behavior in C99. For example, gcc says that\n // the sizeof an empty struct is 0, and g++ says that it is 1. This leads to\n // ABI incompatibility if a Wuffs .c file is processed by one compiler and\n // its .h file with another compiler.\n " + "//\n // Instead, we explicitly insert an otherwise unused field, so that the\n // sizeof this struct is always 1.\n uint8_t private_impl;\n} wuffs_base__empty_struct;\n\n// wuffs_base__utility is a placeholder receiver type. It enables what Java\n// calls static methods, as opposed to regular methods.\ntypedef struct {\n // private_impl is a placeholder field. It isn't explicitly used, except that\n // without it, the sizeof a struct with no fields can differ across C/C++\n // compilers, and it is undefined behavior in C99. For example, gcc says that\n // the sizeof an empty struct is 0, and g++ says that it is 1. This leads to\n // ABI incompatibility if a Wuffs .c file is processed by one compiler and\n // its .h file with another compiler.\n //\n // Instead, we explicitly insert an otherwise unused field, so that the\n // sizeof this struct is always 1.\n uint8_t private_impl;\n} wuffs_base__utility;\n\n" + "" + - "// --------\n\n// A status is either NULL (meaning OK or no error) or string message. That\n// message is human-readable, for programmers, but it is not for end users. It\n// is not localized, and does not contain additional contextual information\n// such as a source filename.\n//\n// Status strings are statically allocated and should never be free'd. They can\n// be compared by the == operator and not just by strcmp.\ntypedef const char* wuffs_base__status;\n\n// !! INSERT wuffs_base__status names.\n\nstatic inline bool //\nwuffs_base__status__is_error(wuffs_base__status z) {\n return z && (*z != '$');\n}\n\nstatic inline bool //\nwuffs_base__status__is_ok(wuffs_base__status z) {\n return z == NULL;\n}\n\nstatic inline bool //\nwuffs_base__status__is_suspension(wuffs_base__status z) {\n return z && (*z == '$');\n}\n\n" + + "// --------\n\n// A status is either NULL (meaning OK) or a string message. That message is\n// human-readable, for programmers, but it is not for end users. It is not\n// localized, and does not contain additional contextual information such as a\n// source filename.\n//\n// Status strings are statically allocated and should never be free'd. They can\n// be compared by the == operator and not just by strcmp.\ntypedef const char* wuffs_base__status;\n\n// !! INSERT wuffs_base__status names.\n\nstatic inline bool //\nwuffs_base__status__is_error(wuffs_base__status z) {\n return z && (*z == '?');\n}\n\nstatic inline bool //\nwuffs_base__status__is_ok(wuffs_base__status z) {\n return z == NULL;\n}\n\nstatic inline bool //\nwuffs_base__status__is_suspension(wuffs_base__status z) {\n return z && (*z == '$');\n}\n\nstatic inline bool //\nwuffs_base__status__is_warning(wuffs_base__status z) {\n return z && (*z != '$') && (*z != '?');\n}\n\n" + "" + "// --------\n\n// Flicks are a unit of time. One flick (frame-tick) is 1 / 705_600_000 of a\n// second. See https://github.com/OculusVR/Flicks\ntypedef int64_t wuffs_base__flicks;\n\n#define WUFFS_BASE__FLICKS_PER_SECOND ((uint64_t)705600000)\n#define WUFFS_BASE__FLICKS_PER_MILLISECOND ((uint64_t)705600)\n\n" + "" +
diff --git a/cmd/wuffs-c/internal/cgen/expr.go b/cmd/wuffs-c/internal/cgen/expr.go index 17163af..bc66e1d 100644 --- a/cmd/wuffs-c/internal/cgen/expr.go +++ b/cmd/wuffs-c/internal/cgen/expr.go
@@ -219,8 +219,10 @@ b.writes("wuffs_base__") if statusMsgIsError(msg) { b.writes("error__") - } else { + } else if statusMsgIsSuspension(msg) { b.writes("suspension__") + } else { + b.writes("warning__") } b.writes(cName(msg, "")) }
diff --git a/cmd/wuffs-c/internal/cgen/statement.go b/cmd/wuffs-c/internal/cgen/statement.go index 51968b1..a056ae3 100644 --- a/cmd/wuffs-c/internal/cgen/statement.go +++ b/cmd/wuffs-c/internal/cgen/statement.go
@@ -315,6 +315,7 @@ if retExpr.Operator() == t.IDStatus { msg, _ := t.Unescape(retExpr.Ident().Str(g.tm)) isError = statusMsgIsError(msg) + isOK = statusMsgIsWarning(msg) } // TODO: check that retExpr has no call-suspendibles. if err := g.writeExpr( @@ -335,9 +336,10 @@ b.writes("goto ok;") } else { g.currFunk.hasGotoOK = true - b.writes("if (!status) { goto ok; }" + + // TODO: the "goto exit"s can be "goto ok". + b.writes("if (wuffs_base__status__is_error(status)) { goto exit; }" + "else if (wuffs_base__status__is_suspension(status)) { " + - "status = wuffs_base__error__cannot_return_a_suspension; } goto exit;") + "status = wuffs_base__error__cannot_return_a_suspension; goto exit; } goto ok;") } return nil }
diff --git a/doc/changelog.md b/doc/changelog.md index 0ff0993..a1a85cd 100644 --- a/doc/changelog.md +++ b/doc/changelog.md
@@ -37,6 +37,7 @@ - Removed closed-for-read/write built-in status codes. - Changed the string messages for built-in status codes. - Changed `error "foo"` to `status "?foo"`. +- Added warnings as another status code category. - Made the status type a `const char *`, not an `int32_t`. - Disallowed `__double_underscore` prefixed names. - Moved the base38 package from `lang/base38` to `lib/base38`.
diff --git a/example/gifplayer/gifplayer.c b/example/gifplayer/gifplayer.c index e23f113..8baf804 100644 --- a/example/gifplayer/gifplayer.c +++ b/example/gifplayer/gifplayer.c
@@ -321,7 +321,7 @@ wuffs_base__status z = wuffs_gif__decoder__decode_frame_config(&dec, &fc, src_reader); if (z) { - if (z == wuffs_base__suspension__end_of_data) { + if (z == wuffs_base__warning__end_of_data) { break; } return z; @@ -337,7 +337,7 @@ z = wuffs_gif__decoder__decode_frame(&dec, &pb, 0, 0, src_reader, ((wuffs_base__slice_u8){})); if (z) { - if (z == wuffs_base__suspension__end_of_data) { + if (z == wuffs_base__warning__end_of_data) { break; } return z;
diff --git a/fuzz/c/std/gif_fuzzer.c b/fuzz/c/std/gif_fuzzer.c index 750790a..c3821a0 100644 --- a/fuzz/c/std/gif_fuzzer.c +++ b/fuzz/c/std/gif_fuzzer.c
@@ -97,7 +97,7 @@ z = wuffs_gif__decoder__decode_frame(&dec, &pb, 0, 0, src_reader, ((wuffs_base__slice_u8){})); if (z) { - if ((z != wuffs_base__suspension__end_of_data) || !seen_ok) { + if ((z != wuffs_base__warning__end_of_data) || !seen_ok) { ret = z; } goto exit;
diff --git a/lang/builtin/builtin.go b/lang/builtin/builtin.go index f47f71f..e41718c 100644 --- a/lang/builtin/builtin.go +++ b/lang/builtin/builtin.go
@@ -20,7 +20,7 @@ ) var StatusList = [...]string{ - "$end of data", + "!end of data", "$short read", "$short write", "?bad argument (length too short)", @@ -225,6 +225,7 @@ "status.is_error() bool", "status.is_ok() bool", "status.is_suspension() bool", + "status.is_warning() bool", // ---- frame_config // Duration's upper bound is the maximum possible i64 value.
diff --git a/release/c/wuffs-unsupported-snapshot.h b/release/c/wuffs-unsupported-snapshot.h index 167889e..89e8eeb 100644 --- a/release/c/wuffs-unsupported-snapshot.h +++ b/release/c/wuffs-unsupported-snapshot.h
@@ -123,16 +123,16 @@ // -------- -// A status is either NULL (meaning OK or no error) or string message. That -// message is human-readable, for programmers, but it is not for end users. It -// is not localized, and does not contain additional contextual information -// such as a source filename. +// A status is either NULL (meaning OK) or a string message. That message is +// human-readable, for programmers, but it is not for end users. It is not +// localized, and does not contain additional contextual information such as a +// source filename. // // Status strings are statically allocated and should never be free'd. They can // be compared by the == operator and not just by strcmp. typedef const char* wuffs_base__status; -extern const char* wuffs_base__suspension__end_of_data; +extern const char* wuffs_base__warning__end_of_data; extern const char* wuffs_base__suspension__short_read; extern const char* wuffs_base__suspension__short_write; extern const char* wuffs_base__error__bad_argument_length_too_short; @@ -148,7 +148,7 @@ static inline bool // wuffs_base__status__is_error(wuffs_base__status z) { - return z && (*z != '$'); + return z && (*z == '?'); } static inline bool // @@ -161,6 +161,11 @@ return z && (*z == '$'); } +static inline bool // +wuffs_base__status__is_warning(wuffs_base__status z) { + return z && (*z != '$') && (*z != '?'); +} + // -------- // Flicks are a unit of time. One flick (frame-tick) is 1 / 705_600_000 of a @@ -3616,7 +3621,7 @@ #if !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__BASE) -const char* wuffs_base__suspension__end_of_data = "$base: end of data"; +const char* wuffs_base__warning__end_of_data = "!base: end of data"; const char* wuffs_base__suspension__short_read = "$base: short read"; const char* wuffs_base__suspension__short_write = "$base: short write"; const char* wuffs_base__error__bad_argument_length_too_short = @@ -4514,12 +4519,13 @@ } if (!wuffs_base__status__is_suspension(v_z)) { status = v_z; - if (!status) { - goto ok; + if (wuffs_base__status__is_error(status)) { + goto exit; } else if (wuffs_base__status__is_suspension(status)) { status = wuffs_base__error__cannot_return_a_suspension; + goto exit; } - goto exit; + goto ok; } v_written = ((wuffs_base__slice_u8){ .ptr = a_dst.private_impl.mark, @@ -6603,10 +6609,8 @@ } } if (self->private_impl.f_end_of_data) { - while (true) { - status = wuffs_base__suspension__end_of_data; - WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(4); - } + status = wuffs_base__warning__end_of_data; + goto ok; } v_blend = 0; if (!self->private_impl.f_gc_has_transparent_index) {
diff --git a/std/gif/decode_gif.wuffs b/std/gif/decode_gif.wuffs index e7de4ef..1fcea16 100644 --- a/std/gif/decode_gif.wuffs +++ b/std/gif/decode_gif.wuffs
@@ -205,9 +205,7 @@ // This is a new "if", not an "else", because the calls above can modify // this.end_of_data. if this.end_of_data { - while true { - yield status "$end of data" - } + return status "!end of data" } var blend base.u8 = 0
diff --git a/test/c/std/gif.c b/test/c/std/gif.c index 82c48b9..95f5b45 100644 --- a/test/c/std/gif.c +++ b/test/c/std/gif.c
@@ -223,7 +223,7 @@ while (true) { z = wuffs_gif__decoder__decode_frame_config(&dec, &fc, src_reader); if (z) { - if (z == wuffs_base__suspension__end_of_data) { + if (z == wuffs_base__warning__end_of_data) { break; } return z; @@ -428,9 +428,9 @@ wuffs_base__io_reader src_reader = wuffs_base__io_buffer__reader(&src); wuffs_base__status z = wuffs_gif__decoder__decode_frame( &dec, &pb, 0, 0, src_reader, ((wuffs_base__slice_u8){})); - if (z != wuffs_base__suspension__end_of_data) { + if (z != wuffs_base__warning__end_of_data) { FAIL("decode_frame: got \"%s\", want \"%s\"", z, - wuffs_base__suspension__end_of_data); + wuffs_base__warning__end_of_data); return false; } if (src.ri != src.wi) { @@ -557,9 +557,9 @@ for (i = 0; i < 3; i++) { z = wuffs_gif__decoder__decode_frame(&dec, &pb, 0, 0, src_reader, ((wuffs_base__slice_u8){})); - if (z != wuffs_base__suspension__end_of_data) { + if (z != wuffs_base__warning__end_of_data) { FAIL("decode_frame: got \"%s\", want \"%s\"", z, - wuffs_base__suspension__end_of_data); + wuffs_base__warning__end_of_data); return false; } } @@ -769,7 +769,7 @@ if (!z) { want++; continue; - } else if (z == wuffs_base__suspension__end_of_data) { + } else if (z == wuffs_base__warning__end_of_data) { end_of_data = true; continue; } @@ -902,7 +902,7 @@ } z = wuffs_gif__decoder__decode_frame_config(&dec, NULL, src_reader); - if (z != wuffs_base__suspension__end_of_data) { + if (z != wuffs_base__warning__end_of_data) { FAIL("decode_frame_config EOD: \"%s\"", z); return false; } @@ -923,9 +923,6 @@ return false; } - // TODO: delete this hack to get out of the end-of-data loop. - dec.private_impl.c_decode_frame_config[0].coro_susp_point = 0; - int j; for (j = i + 1; j < 4; j++) { wuffs_base__frame_config fc = ((wuffs_base__frame_config){}); @@ -953,7 +950,7 @@ } z = wuffs_gif__decoder__decode_frame_config(&dec, NULL, src_reader); - if (z != wuffs_base__suspension__end_of_data) { + if (z != wuffs_base__warning__end_of_data) { FAIL("decode_frame_config EOD #%d: \"%s\"", i, z); return false; }