fmtlib / fmt

A modern formatting library
https://fmt.dev
Other
19.84k stars 2.42k forks source link

Check range_begin is dereferenceable #3964

Closed Arghnews closed 1 month ago

Arghnews commented 1 month ago

Fixes issue #3839

An Eigen 3.4 2x2 matrix has a begin member function that returns void and a static_assert that evaluates to false (as you're not meant to call begin on a non-1d matrix). However this doesn't play nicely with the SFINAE checks at the moment in fmt as they simply perform a SFINAE check that the expression T{}.begin() is valid, which evaluating to void is (even though it's of course not a valid type for an iterator):

https://github.com/fmtlib/fmt/blob/75e892420ed84a058f0a494d693a6baf5212eac4/include/fmt/ranges.h#L100-L104


To model a range more strictly to eliminate this case:

If we look at the c++20 concept for a range, it requires ranges::begin on the type, and that requires the iterator type satisfy input_or_output_iterator, which specifically notes that begin returns: The exposition-only concept /*can-reference*/ is satisfied if and only if the type is referenceable (in particular, not void).

So just check we can deref the result of that type to fix this case, and not incorrectly identify a type with void begin/end functions as a range. Note we don't want to do this for the type returned by end, as (in c++ >= 20) it may be a different sentinel type that is not dereferenceable