Keep the MinGW test objects below the linker's section limit

The MinGW clang job links with a GNU ld that cannot relocate an object
with more than 32767 sections. unit-regression2, unit-regression3 and
unit-msgpack went over it:

- At -O0, the parser and binary reader for parse(), accept() and
  from_*() still emitted the stubs that stand in for recovering, and
  the parser referenced its recovery helpers from code that never runs.
  The stubs are now always inlined, and the parser reaches its helpers
  through the same allow_recovery tag as the stubs, so that this code
  is not even instantiated there. This removes about 300 sections from
  unit-msgpack.
- Every sax_parse call instantiates the parser and binary reader that
  recover from errors. The tests that call it move from
  unit-regression2.cpp (#3989) and unit-regression3.cpp (#5676) into
  unit-sax_parse.cpp, like unit-explicit_instantiation.cpp got a file
  of its own (#5511), and CONTRIBUTING.md points there.

Section counts of the MinGW Debug objects (clang 19, -g0), develop / PR:
unit-msgpack 32281 / 32558, unit-regression2 31272 / 31278,
unit-regression3 32392 / 27462, unit-sax_parse - / 19836.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-08 10:36:16 +02:00
1 parent d1e6439187
commit f8ff0942b2
7 files changed
+863 -679

No files matched your search

+86 -14
View File
@@ -14777,6 +14777,7 @@ class binary_reader
/// @copydoc skip_unsupported_bson_element
template < bool Recover = AllowRecovery, enable_if_t < !Recover, int > = 0 >
JSON_HEDLEY_ALWAYS_INLINE
constexpr std::false_type skip_unsupported_bson_element(const char_int_type /*element_type*/) const noexcept
{
return {};
@@ -18199,6 +18200,7 @@ class binary_reader
/// @copydoc recover_high_precision_number
template < bool Recover = AllowRecovery, enable_if_t < !Recover, int > = 0 >
JSON_HEDLEY_ALWAYS_INLINE
constexpr std::false_type recover_high_precision_number(const std::vector<char>& /*number_vector*/, const std::size_t /*remaining*/ = 0) const noexcept
{
return {};
@@ -19308,6 +19310,7 @@ class binary_reader
@return false, so that the caller stops reading
*/
template<typename Exception>
JSON_HEDLEY_ALWAYS_INLINE
bool report_error(const std::size_t position, const std::string& last_token, const Exception& ex)
{
close_requested = sax->parse_error(position, last_token, ex);
@@ -19344,6 +19347,7 @@ class binary_reader
/// @copydoc repair_requested
template < bool Recover = AllowRecovery, enable_if_t < !Recover, int > = 0 >
JSON_HEDLEY_ALWAYS_INLINE
constexpr std::false_type repair_requested() const noexcept
{
return {};
@@ -19368,6 +19372,7 @@ class binary_reader
/// @copydoc value_failed
template < bool Recover = AllowRecovery, enable_if_t < !Recover, int > = 0 >
JSON_HEDLEY_ALWAYS_INLINE
constexpr std::false_type value_failed() const noexcept
{
return {};
@@ -19418,6 +19423,7 @@ class binary_reader
/// @copydoc close_open_containers
template < bool Recover = AllowRecovery, enable_if_t < !Recover, int > = 0 >
JSON_HEDLEY_ALWAYS_INLINE
void close_open_containers() const noexcept {}
/*!
@@ -19453,6 +19459,7 @@ class binary_reader
/// @copydoc resync
template < bool Recover = AllowRecovery, enable_if_t < !Recover, int > = 0 >
JSON_HEDLEY_ALWAYS_INLINE
constexpr std::false_type resync() const noexcept
{
return {};
@@ -20207,7 +20214,7 @@ class parser
}
// recover: keep what can be read of the token
recover_token();
recover_token(allow_recovery);
if (last_token != token_type::uninitialized)
{
// a string or a number
@@ -20216,7 +20223,7 @@ class parser
if (states.empty())
{
// look for the value after the garbage
if (!skip_to_value())
if (!skip_to_value(allow_recovery))
{
return false;
}
@@ -20250,12 +20257,12 @@ class parser
// there is no value
return false;
}
if (!recover_missing_value(sax, states))
if (!recover_missing_value(sax, states, allow_recovery))
{
return false;
}
// the state evaluation reads the token again
m_lexer.unget_token();
unget_token(allow_recovery);
skip_to_state_evaluation = true;
continue;
}
@@ -20276,7 +20283,7 @@ class parser
if (states.empty())
{
// look for the value after the garbage
if (!skip_to_value())
if (!skip_to_value(allow_recovery))
{
return false;
}
@@ -20288,12 +20295,12 @@ class parser
get_token();
continue;
}
if (!recover_missing_value(sax, states))
if (!recover_missing_value(sax, states, allow_recovery))
{
return false;
}
// the state evaluation reads the token again
m_lexer.unget_token();
unget_token(allow_recovery);
skip_to_state_evaluation = true;
continue;
}
@@ -20355,7 +20362,7 @@ class parser
if (last_token == token_type::end_of_input)
{
// the input ends inside the array
return close_containers(sax, states);
return close_containers(sax, states, allow_recovery);
}
if (last_token == token_type::end_object)
{
@@ -20441,7 +20448,7 @@ class parser
if (last_token == token_type::end_of_input)
{
// the input ends inside the object
return close_containers(sax, states);
return close_containers(sax, states, allow_recovery);
}
if (last_token == token_type::end_array)
{
@@ -20477,6 +20484,7 @@ class parser
}
/// the parser for parse() and accept() never recovers: stop parsing
JSON_HEDLEY_ALWAYS_INLINE
static std::false_type continue_after(std::false_type /*step*/, bool& /*skip_to_state_evaluation*/) noexcept
{
return {};
@@ -20532,8 +20540,8 @@ class parser
@param[in] value the value that is not finite
@return whether to continue parsing
*/
template<typename SAX, typename AllowRecovery>
bool overflow_error(SAX* sax, const number_float_t value, AllowRecovery allow_recovery)
template<typename SAX>
bool overflow_error(SAX* sax, const number_float_t value, std::true_type allow_recovery)
{
if (!report_error(sax, out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr), allow_recovery))
{
@@ -20542,6 +20550,14 @@ class parser
return sax->number_float(value, m_lexer.get_string());
}
/// @copydoc overflow_error
template<typename SAX>
JSON_HEDLEY_ALWAYS_INLINE
std::false_type overflow_error(SAX* sax, const number_float_t /*value*/, std::false_type allow_recovery)
{
return report_error(sax, out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr), allow_recovery);
}
/*!
@brief report a missing key, or a missing name separator (:) after the
key; the parser for parse() and accept() never recovers
@@ -20551,6 +20567,7 @@ class parser
@return std::false_type, see report_error()
*/
template<typename SAX>
JSON_HEDLEY_ALWAYS_INLINE
std::false_type key_error(SAX* sax, std::false_type allow_recovery, const bool key_read)
{
return report_error(sax, parse_error::create(101, m_lexer.get_position(), key_read
@@ -20615,6 +20632,7 @@ class parser
for recovering is not generated
*/
template<typename SAX, typename Exception>
JSON_HEDLEY_ALWAYS_INLINE
std::false_type report_error(SAX* sax, const Exception& ex, std::false_type /*allow_recovery*/)
{
error_reported = true;
@@ -20665,9 +20683,54 @@ class parser
return last_token;
}
// The functions below are called from sax_parse_internal() after an error
// was reported. The parser for parse() and accept() stopped there, so for
// it they are stubs: the code for recovering is then not even referenced,
// and unoptimized builds do not emit it either.
/// @copydoc recover_token()
JSON_HEDLEY_ALWAYS_INLINE
token_type recover_token(std::true_type /*allow_recovery*/)
{
return recover_token();
}
/// @copydoc recover_token()
JSON_HEDLEY_ALWAYS_INLINE
static std::false_type recover_token(std::false_type /*allow_recovery*/) noexcept
{
return {};
}
/// return the token that was read last to the lexer (see lexer::unget_token())
JSON_HEDLEY_ALWAYS_INLINE
void unget_token(std::true_type /*allow_recovery*/)
{
m_lexer.unget_token();
}
/// @copydoc unget_token(std::true_type)
JSON_HEDLEY_ALWAYS_INLINE
static void unget_token(std::false_type /*allow_recovery*/) noexcept {}
/// @copydoc skip_to_value
JSON_HEDLEY_ALWAYS_INLINE
static std::false_type skip_to_value(std::false_type /*allow_recovery*/) noexcept
{
return {};
}
/// @copydoc recover_missing_value
template<typename SAX>
JSON_HEDLEY_ALWAYS_INLINE
static std::false_type recover_missing_value(SAX* /*sax*/, const std::vector<bool>& /*states*/, std::false_type /*allow_recovery*/) noexcept
{
return {};
}
/// pass the end events of all open containers
template<typename SAX>
bool close_containers(SAX* sax, std::vector<bool>& states)
bool close_containers(SAX* sax, std::vector<bool>& states, std::true_type /*allow_recovery*/)
{
while (!states.empty())
{
@@ -20681,12 +20744,20 @@ class parser
return true;
}
/// @copydoc close_containers
template<typename SAX>
JSON_HEDLEY_ALWAYS_INLINE
static std::false_type close_containers(SAX* /*sax*/, std::vector<bool>& /*states*/, std::false_type /*allow_recovery*/) noexcept
{
return {};
}
/*!
@brief read tokens until one begins a value, skipping everything before
the top-level value
@return whether a value begins with last_token
*/
bool skip_to_value()
bool skip_to_value(std::true_type /*allow_recovery*/)
{
while (true)
{
@@ -20794,7 +20865,7 @@ class parser
ends there just ends.
*/
template<typename SAX>
bool recover_missing_value(SAX* sax, const std::vector<bool>& states)
bool recover_missing_value(SAX* sax, const std::vector<bool>& states, std::true_type /*allow_recovery*/)
{
JSON_ASSERT(!states.empty());
if (!states.back() || last_token == token_type::value_separator)
@@ -20900,6 +20971,7 @@ class parser
/// the parser for parse() and accept() never recovers (and does not come
/// here, as report_error() returned false)
template<typename SAX>
JSON_HEDLEY_ALWAYS_INLINE
std::false_type recover_member(SAX* /*sax*/, std::false_type /*allow_recovery*/) const noexcept
{
return {};