Author: Arthur O'Dwyer Date: 2021-09-11T13:44:51-05:00 New Revision: 2b4cad5e471c60edae528979fa5f3edde844ac34
URL: https://github.com/llvm/llvm-project/commit/2b4cad5e471c60edae528979fa5f3edde844ac34 DIFF: https://github.com/llvm/llvm-project/commit/2b4cad5e471c60edae528979fa5f3edde844ac34.diff LOG: [clang] Enable the special enable_if_t diagnostics for libc++'s __enable_if_t as well. This comes from lengthy discussion between Quuxplusone and ldionne over on D108216. Right now, libc++ uses a "SCARY metaprogramming" version of _EnableIf that bypasses all of Clang's clever diagnostic stuff and thus produces bad diagnostics. My recent benchmarks ( https://quuxplusone.github.io/blog/2021/09/04/enable-if-benchmark/ ) have determined that the SCARYness is not buying us any speedup; therefore we are happy to drop it and go back to using the standard std::enable_if for all our SFINAE needs. However, we don't want to type out typename std::enable_if<X>::type all over the library; we want to use an alias template. And we can't use std::enable_if_t because we need a solution that works in C++11, and we do not provide std::enable_if_t in C++11. Therefore, D109435 switches us from SCARY `_EnableIf` to a normal `__enable_if_t` (at least in C++11 mode, and possibly everywhere for consistency). Simultaneously, this Clang patch enables the good diagnostics for `__enable_if_t`. We don't need to enable good diagnostics for `_EnableIf` because the name `_EnableIf` has only ever been used for the SCARY version where the good diagnostics don't trigger anyway. (Btw, this existing code is all sorts of broken, theoretically speaking. I filed https://bugs.llvm.org/show_bug.cgi?id=51696 about it last week. So if someone wants to use this PR as an excuse to go down the rabbit hole and fix it for real, that would be cool too.) Differential Revision: https://reviews.llvm.org/D109411 Added: clang/test/SemaTemplate/diagnose-enable-if-t.cpp Modified: clang/lib/Sema/SemaTemplate.cpp Removed: ################################################################################ diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 6682b17f4e146..bfe69a45f5951 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -3511,8 +3511,10 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD, } /// Determine whether this alias template is "enable_if_t". +/// libc++ >=14 uses "__enable_if_t" in C++11 mode. static bool isEnableIfAliasTemplate(TypeAliasTemplateDecl *AliasTemplate) { - return AliasTemplate->getName().equals("enable_if_t"); + return AliasTemplate->getName().equals("enable_if_t") || + AliasTemplate->getName().equals("__enable_if_t"); } /// Collect all of the separable terms in the given condition, which diff --git a/clang/test/SemaTemplate/diagnose-enable-if-t.cpp b/clang/test/SemaTemplate/diagnose-enable-if-t.cpp new file mode 100644 index 0000000000000..3cf142907cd7f --- /dev/null +++ b/clang/test/SemaTemplate/diagnose-enable-if-t.cpp @@ -0,0 +1,96 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s + +namespace std { + inline namespace __1 { + template<bool, class = void> struct enable_if {}; + template<class T> struct enable_if<true, T> { using type = T; }; + template<bool B, class T = void> using enable_if_t = typename enable_if<B, T>::type; + } +} + +namespace similar_to_user_code { + // expected-note@+2 {{candidate template ignored: requirement 'sizeof(char) != 1' was not satisfied [with T = char]}} + template<class T, class = std::enable_if_t<sizeof(T) != 1>> + void f(T, short); + + // expected-note@+2 {{candidate template ignored: requirement 'sizeof(char) != 1' was not satisfied [with T = char]}} + template<class T, std::enable_if_t<sizeof(T) != 1>* = nullptr> + void f(T, int); + + // expected-note@+2 {{candidate template ignored: requirement 'sizeof(char) != 1' was not satisfied [with T = char]}} + template<class T> + std::enable_if_t<sizeof(T) != 1, void> f(T, long); + + void test() { + f('x', 0); // expected-error{{no matching function}} + } +} + +namespace similar_to_libcxx_version_14 { + template<bool, class = void> struct enable_if {}; + template<class T> struct enable_if<true, T> { using type = T; }; + template<bool B, class T = void> using __enable_if_t = typename enable_if<B, T>::type; + + // expected-note@+2 {{candidate template ignored: requirement 'sizeof(char) != 1' was not satisfied [with T = char]}} + template<class T, class = __enable_if_t<sizeof(T) != 1>> + void f(T, short); + + // expected-note@+2 {{candidate template ignored: requirement 'sizeof(char) != 1' was not satisfied [with T = char]}} + template<class T, __enable_if_t<sizeof(T) != 1>* = nullptr> + void f(T, int); + + // expected-note@+2 {{candidate template ignored: requirement 'sizeof(char) != 1' was not satisfied [with T = char]}} + template<class T> + __enable_if_t<sizeof(T) != 1, void> f(T, long); + + void test() { + f('x', 0); // expected-error{{no matching function}} + } +} + +namespace similar_to_libcxx_version_13 { + template<bool> struct _MetaBase {}; + template<> struct _MetaBase<true> { template<class R> using _EnableIfImpl = R; }; + template<bool B, class T = void> using _EnableIf = typename _MetaBase<B>::template _EnableIfImpl<T>; + + // expected-note@+2 {{no member named '_EnableIfImpl'}} + template<class T, class = _EnableIf<sizeof(T) != 1>> + void f(T, short); + + // expected-note@+2 {{no member named '_EnableIfImpl'}} + template<class T, _EnableIf<sizeof(T) != 1>* = nullptr> + void f(T, int); + + // expected-note@+2 {{no member named '_EnableIfImpl'}} + template<class T> + _EnableIf<sizeof(T) != 1, void> f(T, long); + + void test() { + f('x', 0); // expected-error{{no matching function}} + } +} + +namespace not_all_names_are_magic { + template<bool, class = void> struct enable_if {}; + template<class T> struct enable_if<true, T> { using type = T; }; + template<bool B, class T = void> using a_pony = typename enable_if<B, T>::type; + + // expected-note@-2 {{candidate template ignored: disabled by 'enable_if' [with T = char]}} + template<class T, class = a_pony<sizeof(T) != 1>> + void f(T, short); + + // expected-note@-6 {{candidate template ignored: disabled by 'enable_if' [with T = char]}} + template<class T, a_pony<sizeof(T) != 1>* = nullptr> + void f(T, int); + + // expected-note@-10 {{candidate template ignored: disabled by 'enable_if' [with T = char]}} + template<class T> + a_pony<sizeof(T) != 1, void> f(T, long); + + void test() { + f('x', 0); // expected-error{{no matching function}} + } +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits