schemagen: enum class Kind : uint8_t overflows for schemas with more than 256 object/array kinds - generated header does not compile #67

Open
opened 2026-09-13 20:53:28 +00:00 by weaselbot · 0 comments
Member

contrib/schemagen/weaseljson_schemagen.py, Emitter._kind_enum() (line 726) emits

  enum class Kind : uint8_t { Root1, P0, P1, ... };

with one enumerator per generated object struct, one per distinct array kind (Arr0, Arr1, ...) and, for scalar roots, RootScalar. There is no capacity check, so once the schema produces more than 256 kinds the generated header declares enumerator values that do not fit in uint8_t and the header cannot be compiled. schemagen itself exits 0 with no diagnostic.

Reproduction

repro.json - a root object with 256 object-valued properties (each property generates a distinct struct, so together with the root that is 257 kinds):

{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "p0": {"type": "object", "additionalProperties": false, "properties": {"x": {"type": "integer"}}},
    ...
    "p255": {"type": "object", "additionalProperties": false, "properties": {"x": {"type": "integer"}}}
  }
}
$ python3 weaseljson_schemagen.py repro.json -o out256.h --namespace repro
$ echo $?
0
$ printf '#include "out256.h"\nint main(){ repro::Root r{}; (void)r; }\n' > use.cpp
$ g++ -std=c++20 -c use.cpp
out256.h:1595:1458: error: enumerator value '256' is outside the range of underlying type 'uint8_t' {aka 'unsigned char'}
 1595 |   enum class Kind : uint8_t { Root1, P0, P1, ..., P255 };

GCC also emits cascading error: duplicate case value diagnostics in slotInfo()/matchKey() afterwards, i.e. the overflowing enumerator values alias existing kinds.

The ceiling is exact: with 255 object-valued properties (256 kinds total) the same schema compiles cleanly; with 256 (257 kinds) it fails. Array-valued properties count too: 255 object properties + 1 array property is also 257 kinds and fails.

Expected behavior

Either the generated header compiles for any schema schemagen accepts, or schemagen rejects the schema at generation time with a clear GenError (consistent with how other unsupported constructs are handled). Note the analogous scale ceiling fixed for the seen bitset in issue #3 (objects with >32 properties), which was resolved by widening to std::vector<uint64_t>; the Kind underlying type needs the same treatment.

Suggested fix

Widen the underlying type (e.g. enum class Kind : uint16_t), and/or raise a GenError when the number of kinds exceeds what the emitted type can hold.

Impact

Any legal JSON Schema whose document contains more than 256 distinct object/array types (large OpenAPI-derived schemas, for example) produces a header that fails to compile, with no warning at generation time.

Not covered by #59/#60/#62/#64/#66 (those are identifier-syntax, keyword, std::vector<bool>, and NUL-escaping defects), nor by closed #21 (which was about name collisions with Root/Kind, not the enum's range).

`contrib/schemagen/weaseljson_schemagen.py`, `Emitter._kind_enum()` (line 726) emits ```cpp enum class Kind : uint8_t { Root1, P0, P1, ... }; ``` with one enumerator per generated object struct, one per distinct array kind (`Arr0`, `Arr1`, ...) and, for scalar roots, `RootScalar`. There is no capacity check, so once the schema produces more than 256 kinds the generated header declares enumerator values that do not fit in `uint8_t` and the header cannot be compiled. schemagen itself exits 0 with no diagnostic. ## Reproduction `repro.json` - a root object with 256 object-valued properties (each property generates a distinct struct, so together with the root that is 257 kinds): ```json { "type": "object", "additionalProperties": false, "properties": { "p0": {"type": "object", "additionalProperties": false, "properties": {"x": {"type": "integer"}}}, ... "p255": {"type": "object", "additionalProperties": false, "properties": {"x": {"type": "integer"}}} } } ``` ```sh $ python3 weaseljson_schemagen.py repro.json -o out256.h --namespace repro $ echo $? 0 $ printf '#include "out256.h"\nint main(){ repro::Root r{}; (void)r; }\n' > use.cpp $ g++ -std=c++20 -c use.cpp out256.h:1595:1458: error: enumerator value '256' is outside the range of underlying type 'uint8_t' {aka 'unsigned char'} 1595 | enum class Kind : uint8_t { Root1, P0, P1, ..., P255 }; ``` GCC also emits cascading `error: duplicate case value` diagnostics in `slotInfo()`/`matchKey()` afterwards, i.e. the overflowing enumerator values alias existing kinds. The ceiling is exact: with **255** object-valued properties (256 kinds total) the same schema compiles cleanly; with **256** (257 kinds) it fails. Array-valued properties count too: 255 object properties + 1 array property is also 257 kinds and fails. ## Expected behavior Either the generated header compiles for any schema schemagen accepts, or schemagen rejects the schema at generation time with a clear `GenError` (consistent with how other unsupported constructs are handled). Note the analogous scale ceiling fixed for the `seen` bitset in issue #3 (objects with >32 properties), which was resolved by widening to `std::vector<uint64_t>`; the `Kind` underlying type needs the same treatment. ## Suggested fix Widen the underlying type (e.g. `enum class Kind : uint16_t`), and/or raise a `GenError` when the number of kinds exceeds what the emitted type can hold. ## Impact Any legal JSON Schema whose document contains more than 256 distinct object/array types (large OpenAPI-derived schemas, for example) produces a header that fails to compile, with no warning at generation time. Not covered by #59/#60/#62/#64/#66 (those are identifier-syntax, keyword, `std::vector<bool>`, and NUL-escaping defects), nor by closed #21 (which was about *name collisions* with `Root`/`Kind`, not the enum's range).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: weaselab/weaseljson#67