Skip to content

Commit ea92860

Browse files
committed
should hopefully fix GCC CI
1 parent 2a0342c commit ea92860

File tree

1 file changed

+11
-21
lines changed

1 file changed

+11
-21
lines changed

sus/string/__private/format_to_stream.h

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ concept ConvertibleFrom = std::convertible_to<From, To>;
3131

3232
template <class T, class Char, class U>
3333
concept StreamCanReceiveString =
34+
/// Ensure that we don't accidentally recursively check `U`.
3435
!std::same_as<std::remove_cvref_t<T>, std::remove_cvref_t<U>> &&
3536
requires(T& t, const std::basic_string<Char> s) {
3637
// Check ConvertibleFrom as std streams return std::basic_ostream&, which the
@@ -60,30 +61,19 @@ S& format_to_stream(S& os, const std::basic_string<Char>& s) {
6061
///
6162
/// # Implementation Notes
6263
///
63-
/// The `Type` argument is encoded as a template argument for the GCC compiler
64-
/// because it does not reject the overload when the `Type` does not match
65-
/// otherwise, and then ends up recursively trying to solve
66-
/// `StreamCanReceiveString<S, char>`. On the first attempt to solve
67-
/// `StreamCanReceiveString<S, char>`, it tries to call this overload with
68-
/// `std::string` which is *not* `Type` and yet it considers it a valid
69-
/// overload, so it tries to again solve `StreamCanReceiveString<S, char>` which
70-
/// is now recursive. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99599.
71-
///
72-
/// Actually it gets worse. If the `Type` has template parameters, we can't use
73-
/// `std::same_as<Type<Params..>> Sus_ValueType` as the compiler can't
74-
/// infer the `Params...` there, even though `Sus_ValueType` appears in the
75-
/// parameter list. So we _have_ to put the real type in the parameter list.
76-
/// But also in this case GCC does the same thing as the others and does not
77-
/// include the function in the overload set if the non-concept type doesn't
78-
/// match. So we can revert back to the usual incantation then.
79-
//
64+
/// The `U` template type parameter allows `_sus_format_to_stream` to be a
65+
/// hidden friend when `Type` is not a class template. Directly using `Type`
66+
/// means that the function is immediately instantiated, and we'll get a
67+
/// compile-time error unless `fmt::formatter` is specialised before `Type`'s
68+
/// definition, since `fmt::is_formattable` doesn't know it's a formattable type
69+
/// at this point in the code. This isn't a problem for primary class templates,
70+
/// but it is problematic for regular classes and full specialisations. Using a
71+
/// dependent type `U` defers instantiation until `operator<<` is first used.
72+
/// We need to constrain `U` to be the same type as `Type`, otherwise it will
73+
/// be ambiguous as to which overload we want.
8074
// clang-format off
8175
#define _sus_format_to_stream(Type) \
8276
template< \
83-
/* Inserts `std::same_as<Type> Sus_ValueType` if required for GCC. */ \
84-
sus_if_gcc( \
85-
_sus_format_to_stream_parameter_concept(Type) \
86-
) \
8777
sus::string::__private::StreamCanReceiveString<char, Type> Sus_StreamType, \
8878
std::same_as<Type> U = Type \
8979
> \

0 commit comments

Comments
 (0)