@@ -788,37 +788,61 @@ class out {
788
788
#define CPP2_UFCS_REMPARENS (...) __VA_ARGS__
789
789
790
790
// Ideally, the expression `CPP2_UFCS_IS_NOTHROW` expands to
791
- // is in the _noexcept-specifier_ of the UFCS lambda.
791
+ // is in the _noexcept-specifier_ of the UFCS lambda, but without 'std::declval' .
792
792
// To workaround [GCC bug 101043](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101043),
793
793
// we instead make it a template parameter of the UFCS lambda.
794
794
// But using a template parameter, Clang also ICEs on an application.
795
795
// So we use these `NOTHROW` macros to fall back to the ideal for when not using GCC.
796
796
#define CPP2_UFCS_IS_NOTHROW (QUALID,TEMPKW,...) \
797
- requires { requires requires { std::declval<Obj>().CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__ (std::declval<Params>()...); }; \
798
- requires noexcept (std::declval<Obj>().CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__ (std::declval<Params>()...)); } \
797
+ requires { requires requires { std::declval<Obj>().CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__ (std::declval<Params>()...); }; \
798
+ requires noexcept (std::declval<Obj>().CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__ (std::declval<Params>()...)); } \
799
799
|| requires { requires !requires { std::declval<Obj>().CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__ (std::declval<Params>()...); }; \
800
800
requires noexcept (CPP2_UFCS_REMPARENS QUALID __VA_ARGS__ (std::declval<Obj>(), std::declval<Params>()...)); }
801
- #define CPP2_UFCS_IS_NOTHROW_PARAM (QUALID,TEMPKW, ...) /* empty*/
802
- #define CPP2_UFCS_IS_NOTHROW_ARG (QUALID,TEMPKW,...) CPP2_UFCS_IS_NOTHROW(QUALID,TEMPKW,__VA_ARGS__)
801
+ #define CPP2_UFCS_IS_NOTHROW_PARAM (...) /* empty*/
802
+ #define CPP2_UFCS_IS_NOTHROW_ARG (QUALID,TEMPKW,...) CPP2_UFCS_IS_NOTHROW(QUALID,TEMPKW,__VA_ARGS__)
803
803
#if defined(__GNUC__) && !defined(__clang__)
804
- #undef CPP2_UFCS_IS_NOTHROW_PARAM
805
- #undef CPP2_UFCS_IS_NOTHROW_ARG
804
+ #undef CPP2_UFCS_IS_NOTHROW_PARAM
805
+ #undef CPP2_UFCS_IS_NOTHROW_ARG
806
806
#define CPP2_UFCS_IS_NOTHROW_PARAM (QUALID,TEMPKW,...) , bool IsNothrow = CPP2_UFCS_IS_NOTHROW(QUALID,TEMPKW,__VA_ARGS__)
807
- #define CPP2_UFCS_IS_NOTHROW_ARG (QUALID,TEMPKW, ...) IsNothrow
807
+ #define CPP2_UFCS_IS_NOTHROW_ARG (...) IsNothrow
808
808
#if __GNUC__ < 11
809
- #undef CPP2_UFCS_IS_NOTHROW_PARAM
810
- #undef CPP2_UFCS_IS_NOTHROW_ARG
809
+ #undef CPP2_UFCS_IS_NOTHROW_PARAM
810
+ #undef CPP2_UFCS_IS_NOTHROW_ARG
811
811
#define CPP2_UFCS_IS_NOTHROW_PARAM (...) /* empty*/
812
812
#define CPP2_UFCS_IS_NOTHROW_ARG (...) false // GCC 10 UFCS is always potentially-throwing.
813
813
#endif
814
814
#endif
815
815
816
+ // Ideally, the expression `CPP2_UFCS_CONSTRAINT_ARG` expands to
817
+ // is in the _requires-clause_ of the UFCS lambda.
818
+ // To workaround an MSVC bug within a member function 'F' where UFCS is also for 'F'
819
+ // (<https://github.com/hsutter/cppfront/pull/506#issuecomment-1826086952>),
820
+ // we instead make it a template parameter of the UFCS lambda.
821
+ // But using a template parameter, Clang also ICEs and GCC rejects a local 'F'.
822
+ // Also, Clang rejects the SFINAE test case when using 'std::declval'.
823
+ // So we use these `CONSTRAINT` macros to fall back to the ideal for when not using MSVC.
824
+ #define CPP2_UFCS_CONSTRAINT_PARAM (...) /* empty*/
825
+ #define CPP2_UFCS_CONSTRAINT_ARG (QUALID,TEMPKW,...) \
826
+ requires { CPP2_FORWARD (obj).CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__ (CPP2_FORWARD (params)...); } \
827
+ || requires { CPP2_UFCS_REMPARENS QUALID __VA_ARGS__ (CPP2_FORWARD (obj), CPP2_FORWARD (params)...); }
828
+ #if defined(_MSC_VER)
829
+ #undef CPP2_UFCS_CONSTRAINT_PARAM
830
+ #undef CPP2_UFCS_CONSTRAINT_ARG
831
+ #define CPP2_UFCS_CONSTRAINT_PARAM (QUALID,TEMPKW,...) , bool IsViable = \
832
+ requires { std::declval<Obj>().CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__ (std::declval<Params>()...); } \
833
+ || requires { CPP2_UFCS_REMPARENS QUALID __VA_ARGS__ (std::declval<Obj>(), std::declval<Params>()...); }
834
+ #define CPP2_UFCS_CONSTRAINT_ARG (...) IsViable
835
+ #endif
836
+
816
837
#define CPP2_UFCS_ (LAMBDADEFCAPT,QUALID,TEMPKW,...) \
817
- [LAMBDADEFCAPT]<typename Obj, typename ... Params CPP2_UFCS_IS_NOTHROW_PARAM (QUALID,TEMPKW,__VA_ARGS__)> \
838
+ [LAMBDADEFCAPT]< \
839
+ typename Obj, typename ... Params \
840
+ CPP2_UFCS_IS_NOTHROW_PARAM (QUALID,TEMPKW,__VA_ARGS__) \
841
+ CPP2_UFCS_CONSTRAINT_PARAM (QUALID,TEMPKW,__VA_ARGS__) \
842
+ > \
818
843
CPP2_LAMBDA_NO_DISCARD (Obj&& obj, Params&& ...params) CPP2_FORCE_INLINE_LAMBDA_CLANG \
819
844
noexcept (CPP2_UFCS_IS_NOTHROW_ARG(QUALID,TEMPKW,__VA_ARGS__)) CPP2_FORCE_INLINE_LAMBDA -> decltype (auto ) \
820
- requires requires { CPP2_FORWARD (obj).CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__ (CPP2_FORWARD (params)...); } \
821
- || requires { CPP2_UFCS_REMPARENS QUALID __VA_ARGS__ (CPP2_FORWARD (obj), CPP2_FORWARD (params)...); } { \
845
+ requires CPP2_UFCS_CONSTRAINT_ARG(QUALID,TEMPKW,__VA_ARGS__) { \
822
846
if constexpr (requires { CPP2_FORWARD (obj).CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__ (CPP2_FORWARD (params)...); }) { \
823
847
return CPP2_FORWARD (obj).CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__ (CPP2_FORWARD (params)...); \
824
848
} else { \
0 commit comments