Skip to content

Commit 811b5e8

Browse files
committed
fix(util): workaround MSVC bug for UFCS of 'F' in member 'F'
1 parent 55710ba commit 811b5e8

File tree

3 files changed

+67
-13
lines changed

3 files changed

+67
-13
lines changed

include/cpp2util.h

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -788,37 +788,61 @@ class out {
788788
#define CPP2_UFCS_REMPARENS(...) __VA_ARGS__
789789

790790
// 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'.
792792
// To workaround [GCC bug 101043](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101043),
793793
// we instead make it a template parameter of the UFCS lambda.
794794
// But using a template parameter, Clang also ICEs on an application.
795795
// So we use these `NOTHROW` macros to fall back to the ideal for when not using GCC.
796796
#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>()...)); } \
799799
|| requires { requires !requires { std::declval<Obj>().CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(std::declval<Params>()...); }; \
800800
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__)
803803
#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
806806
#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
808808
#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
811811
#define CPP2_UFCS_IS_NOTHROW_PARAM(...) /*empty*/
812812
#define CPP2_UFCS_IS_NOTHROW_ARG(...) false // GCC 10 UFCS is always potentially-throwing.
813813
#endif
814814
#endif
815815

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+
816837
#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+
> \
818843
CPP2_LAMBDA_NO_DISCARD (Obj&& obj, Params&& ...params) CPP2_FORCE_INLINE_LAMBDA_CLANG \
819844
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__) { \
822846
if constexpr (requires{ CPP2_FORWARD(obj).CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(CPP2_FORWARD(params)...); }) { \
823847
return CPP2_FORWARD(obj).CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(CPP2_FORWARD(params)...); \
824848
} else { \

regression-tests/pure2-bugfix-for-ufcs-arguments.cpp2

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,12 @@ t: @struct <T: int, U: int> type = {
6060
f: <V: int> (_: int) -> i32 = 0;
6161
}
6262
} // namespace ns
63+
64+
A: @struct type = {
65+
f: (this) = { }
66+
}
67+
68+
B: @struct type = {
69+
m: A;
70+
f: (this) = m.f();
71+
}

regression-tests/test-results/pure2-bugfix-for-ufcs-arguments.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ template<int T, int U> class t;
1919

2020
}
2121

22+
class A;
23+
24+
25+
#line 68 "pure2-bugfix-for-ufcs-arguments.cpp2"
26+
class B;
27+
2228

2329
//=== Cpp2 type definitions and function declarations ===========================
2430

@@ -68,6 +74,15 @@ template<int T, int U> class t {
6874
};
6975
} // namespace ns
7076

77+
class A {
78+
public: auto f() const& -> void;
79+
};
80+
81+
class B {
82+
public: A m;
83+
public: auto f() const& -> void;
84+
};
85+
7186

7287
//=== Cpp2 function definitions =================================================
7388

@@ -136,3 +151,9 @@ namespace ns {
136151

137152
}
138153

154+
#line 65 "pure2-bugfix-for-ufcs-arguments.cpp2"
155+
auto A::f() const& -> void{}
156+
157+
#line 70 "pure2-bugfix-for-ufcs-arguments.cpp2"
158+
auto B::f() const& -> void { CPP2_UFCS(f)(m); }
159+

0 commit comments

Comments
 (0)