Format checking needs argument types during parsing
P2757R0 proposes closing a compile-time checking gap for dynamic formatting parameters without requiring their runtime values.
A format string can be known at compile time while one of its type errors still escapes to runtime. Barry Revzin's January 7 paper, P2757R0, identifies a particular gap involving dynamic width. The parser can establish that a width argument exists without knowing whether that argument has an appropriate type.
The paper builds on the compile-time format checking developed in Victor Zverovich's P2216R3. That earlier work connects known format strings to argument types through constant evaluation. P2757R0 asks how the parse context can expose enough additional type information to check dynamic parameters as well.
I find this a useful example of C++'s abstraction tradeoff. Templates can move a check out of the running program, but only if the library carries the necessary information to the point where it performs that check. The existence of a compile-time parser does not mean every relevant type is visible to it.
Dynamic width does not require a compile-time value
The motivating call formats a string using a width supplied by another argument. A numeric width is meaningful. A string containing the characters of a number is still a string, and its type is inappropriate for that parameter.
The valid form can be written as follows:
#include <format>
#include <string>
std::string padded()
{
return std::format("{:>{}}", "hello", 10);
}
The final argument supplies the width. Replacing the integer argument with "10" produces the paper's problematic case. The characters resemble a numeric value, but formatting does not make that pointer-to-character argument into an integral width.
Rejecting that case requires the argument's type, not its value. A width can remain dynamic while the compiler knows that the variable supplying it is integral. This distinction is the reason a stronger compile-time check is possible without requiring all formatted data to be constant expressions.
The current parse context records argument counts and indexing information. That lets it reject a reference to a missing argument and enforce indexing rules. It does not expose the type information needed to reject every inappropriate dynamic argument.
Type erasure need not discard every useful property
Revzin describes the existing solution in the {fmt} library. Its compile-time context stores a compact classification of argument types. It does not retain the actual argument values. That is enough to decide whether a dynamic width has an accepted integral type.
The distinction matters for both implementation size and evaluation time. Passing arbitrary values into a parse operation would create a different interface and would often be impossible during constant evaluation. A compact type classification supplies the property needed by this check without exposing those values.
The paper also explains why the check belongs inside a helper whose effects occur only during constant evaluation. A formatter's parser can run during compile-time checking and again during runtime formatting. Repeating a type check at runtime would add work after the relevant property had already been established.
My preference is for the library to encode that phase distinction. Requiring every custom formatter author to remember an additional constant-evaluation guard would create repeated opportunities for inconsistent behavior. A helper with the intended semantics makes the obligation easier to satisfy.
Custom formatters need a general check
An integral-width check covers standard formatting cases, but user-defined formatters can have other dynamic parameters. A custom parameter might accept a string or another supported argument category. The proposal therefore considers a general facility that lets the formatter state permitted types.
The paper compares exposing an enumeration with offering a function template. It chooses the template approach, named check_dynamic_spec, with accepted types supplied as template arguments. Convenience functions cover integral, arithmetic, and string categories.
That choice keeps the requested type set at the call site. It also avoids accepting an arbitrary user predicate. Revzin identifies side effects and user-provided execution as concerns with a more open-ended check. The proposed operation has a narrow purpose, establishing whether the supplied argument category belongs to a permitted set.
There is a syntactic cost for dependent contexts. Calling a member template can require the template disambiguator. The paper acknowledges this awkwardness while preferring it to specifying and exposing another enumeration. That is a concrete tradeoff between interface vocabulary and use-site syntax.
Construction is an implementation responsibility
P2757R0 also proposes removing the specified constructor for basic_format_parse_context. The rationale is that the implementation should construct these contexts, and the existing constructor cannot supply the extra state needed for the proposed checks.
This connects an apparently small API cleanup to the checking mechanism. If implementations own construction, they can prepare a context with compile-time type information while preserving the interface that formatter parsers receive. A custom formatter need not construct or understand that internal representation.
The paper remains a proposal. It does not establish that an installed standard library offers the new check helpers today. Its contribution is to identify information already available at the formatting call and describe a controlled way to make that information available during parsing.
The resulting distinction is precise. Dynamic values can remain runtime data while their types participate in compile-time validation. P2757R0 targets that gap for formatting parameters, allowing a parser to reject a string used as a width before a formatting operation ever runs.