For any schema that contains an array of non-nullable boolean, schemagen emits a std::vector<bool> and code of the form
return&v->back();
inside RootBuilder::engage. std::vector<bool> is the bit-packed specialization: back() returns a prvalue proxy (std::vector<bool>::reference, i.e. _Bit_reference), so taking its address is ill-formed. The generated header fails to compile on both GCC and Clang, and schemagen itself exits 0 (no GenError), so the breakage is only discovered later at C++ compile time.
Root cause
contrib/schemagen/weaseljson_schemagen.py:
Emitter.base_cpp (lines 541-554): a boolean element type maps to "bool" (line 546), so an array of bools produces std::vector<bool> at line 553 (storage_cpp(..., False) -> std::vector<bool>).
Emitter._engage (lines 772-783): for every registered array kind with a non-nullable element it emits return &v->back(); (line 782). This is only valid if back() returns a real reference — which it does not for std::vector<bool>.
Reproduction
schema.json:
{"type":"array","items":{"type":"boolean"}}
Commands:
python3 contrib/schemagen/weaseljson_schemagen.py schema.json -o out.h # exits 0
g++ -std=c++20 -fsyntax-only -I include -I . any.cpp # any TU including out.h
Generated fragment (out.h):
caseKind::Arr0:{auto*v=(std::vector<bool>*)f.dest;if(fresh)v->emplace_back();return&v->back();// line ~392
}
GCC 16:
error: taking address of rvalue [-fpermissive]
392 | return &v->back();
| ~~~~~~~^~
warning: returning pointer to temporary [-Wreturn-local-addr]
Clang 21:
error: taking the address of a temporary object of type 'reference' (aka '_Bit_reference') [-Waddress-of-temporary]
Scope (all verified):
root-level array of bools: fails (this repro)
array of bools as an object field: {"properties": {"flags": {"type": "array", "items": {"type": "boolean"}}}} — fails
{"type": "boolean"} as a plain member or root scalar — compiles (uses &o->x / &result_)
nullable items {"type": ["boolean", "null"]} (std::vector<std::optional<bool>>) — compiles, because engage uses auto &e = v->back(); ... return &*e; there
So the bug hits exactly the non-nullable-bool-items case: the very common flags: [true, false] schema.
Impact
Any valid JSON Schema containing an array of booleans produces a generated header that does not compile, with schemagen reporting success (exit code 0). README's contract says generation rejects anything that cannot map onto C++ structs ("Only the subset of JSON Schema that maps naturally onto C++ structs is supported. Anything else is rejected at generation time"); here an unmappable case slips through to compile time.
Note that even if it were forced to compile with -fpermissive, the semantics would still be broken: cbBool does *(bool *)engage(f, true) = value;, which would write through a pointer to the destroyed temporary proxy — so the fix likely needs to reject std::vector<bool> element types at generation time (matching the existing "no natural C++ struct mapping" policy) or change the element storage (e.g. std::vector<char>/std::deque<bool>), not merely change the cast. The existing in-repo tests don't cover this (example.schema.json contains no bool arrays; test_gen.cpp / nullable schemas use int/string arrays).
## Summary
For any schema that contains an **array of non-nullable `boolean`**, schemagen emits a `std::vector<bool>` and code of the form
```cpp
return &v->back();
```
inside `RootBuilder::engage`. `std::vector<bool>` is the bit-packed specialization: `back()` returns a *prvalue proxy* (`std::vector<bool>::reference`, i.e. `_Bit_reference`), so taking its address is ill-formed. The generated header fails to compile on both GCC and Clang, and `schemagen` itself exits 0 (no `GenError`), so the breakage is only discovered later at C++ compile time.
## Root cause
`contrib/schemagen/weaseljson_schemagen.py`:
- `Emitter.base_cpp` (lines 541-554): a boolean element type maps to `"bool"` (line 546), so an array of bools produces `std::vector<bool>` at line 553 (`storage_cpp(..., False)` -> `std::vector<bool>`).
- `Emitter._engage` (lines 772-783): for every registered array kind with a non-nullable element it emits `return &v->back();` (line 782). This is only valid if `back()` returns a real reference — which it does not for `std::vector<bool>`.
## Reproduction
`schema.json`:
```json
{
"type": "array",
"items": { "type": "boolean" }
}
```
Commands:
```sh
python3 contrib/schemagen/weaseljson_schemagen.py schema.json -o out.h # exits 0
g++ -std=c++20 -fsyntax-only -I include -I . any.cpp # any TU including out.h
```
Generated fragment (`out.h`):
```cpp
case Kind::Arr0: {
auto *v = (std::vector<bool> *)f.dest;
if (fresh) v->emplace_back();
return &v->back(); // line ~392
}
```
GCC 16:
```
error: taking address of rvalue [-fpermissive]
392 | return &v->back();
| ~~~~~~~^~
warning: returning pointer to temporary [-Wreturn-local-addr]
```
Clang 21:
```
error: taking the address of a temporary object of type 'reference' (aka '_Bit_reference') [-Waddress-of-temporary]
```
Scope (all verified):
- root-level array of bools: fails (this repro)
- array of bools as an object field: `{"properties": {"flags": {"type": "array", "items": {"type": "boolean"}}}}` — fails
- nested arrays `{"type": "array", "items": {"type": "array", "items": {"type": "boolean"}}}` — fails
- nullable root variant `{"type": ["array", "null"], "items": {"type": "boolean"}}` — fails (same `std::vector<bool>` engage path)
- `{"type": "boolean"}` as a plain member or root scalar — compiles (uses `&o->x` / `&result_`)
- **nullable** items `{"type": ["boolean", "null"]}` (`std::vector<std::optional<bool>>`) — compiles, because `engage` uses `auto &e = v->back(); ... return &*e;` there
So the bug hits exactly the non-nullable-bool-items case: the very common `flags: [true, false]` schema.
## Impact
Any valid JSON Schema containing an array of booleans produces a generated header that does not compile, with schemagen reporting success (exit code 0). README's contract says generation rejects anything that cannot map onto C++ structs ("Only the subset of JSON Schema that maps naturally onto C++ structs is supported. Anything else is rejected at generation time"); here an unmappable case slips through to compile time.
Note that even if it were forced to compile with `-fpermissive`, the semantics would still be broken: `cbBool` does `*(bool *)engage(f, true) = value;`, which would write through a pointer to the destroyed temporary proxy — so the fix likely needs to reject `std::vector<bool>` element types at generation time (matching the existing "no natural C++ struct mapping" policy) or change the element storage (e.g. `std::vector<char>`/`std::deque<bool>`), not merely change the cast. The existing in-repo tests don't cover this (example.schema.json contains no bool arrays; test_gen.cpp / nullable schemas use int/string arrays).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
For any schema that contains an array of non-nullable
boolean, schemagen emits astd::vector<bool>and code of the forminside
RootBuilder::engage.std::vector<bool>is the bit-packed specialization:back()returns a prvalue proxy (std::vector<bool>::reference, i.e._Bit_reference), so taking its address is ill-formed. The generated header fails to compile on both GCC and Clang, andschemagenitself exits 0 (noGenError), so the breakage is only discovered later at C++ compile time.Root cause
contrib/schemagen/weaseljson_schemagen.py:Emitter.base_cpp(lines 541-554): a boolean element type maps to"bool"(line 546), so an array of bools producesstd::vector<bool>at line 553 (storage_cpp(..., False)->std::vector<bool>).Emitter._engage(lines 772-783): for every registered array kind with a non-nullable element it emitsreturn &v->back();(line 782). This is only valid ifback()returns a real reference — which it does not forstd::vector<bool>.Reproduction
schema.json:Commands:
Generated fragment (
out.h):GCC 16:
Clang 21:
Scope (all verified):
{"properties": {"flags": {"type": "array", "items": {"type": "boolean"}}}}— fails{"type": "array", "items": {"type": "array", "items": {"type": "boolean"}}}— fails{"type": ["array", "null"], "items": {"type": "boolean"}}— fails (samestd::vector<bool>engage path){"type": "boolean"}as a plain member or root scalar — compiles (uses&o->x/&result_){"type": ["boolean", "null"]}(std::vector<std::optional<bool>>) — compiles, becauseengageusesauto &e = v->back(); ... return &*e;thereSo the bug hits exactly the non-nullable-bool-items case: the very common
flags: [true, false]schema.Impact
Any valid JSON Schema containing an array of booleans produces a generated header that does not compile, with schemagen reporting success (exit code 0). README's contract says generation rejects anything that cannot map onto C++ structs ("Only the subset of JSON Schema that maps naturally onto C++ structs is supported. Anything else is rejected at generation time"); here an unmappable case slips through to compile time.
Note that even if it were forced to compile with
-fpermissive, the semantics would still be broken:cbBooldoes*(bool *)engage(f, true) = value;, which would write through a pointer to the destroyed temporary proxy — so the fix likely needs to rejectstd::vector<bool>element types at generation time (matching the existing "no natural C++ struct mapping" policy) or change the element storage (e.g.std::vector<char>/std::deque<bool>), not merely change the cast. The existing in-repo tests don't cover this (example.schema.json contains no bool arrays; test_gen.cpp / nullable schemas use int/string arrays).