diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 21e339370cd4..386d0f655c52 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1420,85 +1420,6 @@ caveats to this use of name mangling: Query for this feature with ``__has_extension(attribute_overloadable)``. -Controlling Overload Resolution -=============================== - -Clang introduces the ``enable_if`` attribute, which can be placed on function -declarations to control which overload is selected based on the values of the -function's arguments. When combined with the -:ref:`overloadable` attribute, this feature is also -available in C. - -.. code-block:: c++ - - int isdigit(int c); - int isdigit(int c) __attribute__((enable_if(c <= -1 || c > 255, "chosen when 'c' is out of range"))) __attribute__((unavailable("'c' must have the value of an unsigned char or EOF"))); - - void foo(char c) { - isdigit(c); - isdigit(10); - isdigit(-10); // results in a compile-time error. - } - -The enable_if attribute takes two arguments, the first is an expression written -in terms of the function parameters, the second is a string explaining why this -overload candidate could not be selected to be displayed in diagnostics. The -expression is part of the function signature for the purposes of determining -whether it is a redeclaration (following the rules used when determining -whether a C++ template specialization is ODR-equivalent), but is not part of -the type. - -The enable_if expression is evaluated as if it were the body of a -bool-returning constexpr function declared with the arguments of the function -it is being applied to, then called with the parameters at the callsite. If the -result is false or could not be determined through constant expression -evaluation, then this overload will not be chosen and the provided string may -be used in a diagnostic if the compile fails as a result. - -Because the enable_if expression is an unevaluated context, there are no global -state changes, nor the ability to pass information from the enable_if -expression to the function body. For example, suppose we want calls to -strnlen(strbuf, maxlen) to resolve to strnlen_chk(strbuf, maxlen, size of -strbuf) only if the size of strbuf can be determined: - -.. code-block:: c++ - - __attribute__((always_inline)) - static inline size_t strnlen(const char *s, size_t maxlen) - __attribute__((overloadable)) - __attribute__((enable_if(__builtin_object_size(s, 0) != -1))), - "chosen when the buffer size is known but 'maxlen' is not"))) - { - return strnlen_chk(s, maxlen, __builtin_object_size(s, 0)); - } - -Multiple enable_if attributes may be applied to a single declaration. In this -case, the enable_if expressions are evaluated from left to right in the -following manner. First, the candidates whose enable_if expressions evaluate to -false or cannot be evaluated are discarded. If the remaining candidates do not -share ODR-equivalent enable_if expressions, the overload resolution is -ambiguous. Otherwise, enable_if overload resolution continues with the next -enable_if attribute on the candidates that have not been discarded and have -remaining enable_if attributes. In this way, we pick the most specific -overload out of a number of viable overloads using enable_if. - -.. code-block:: c++ - - void f() __attribute__((enable_if(true, ""))); // #1 - void f() __attribute__((enable_if(true, ""))) __attribute__((enable_if(true, ""))); // #2 - - void g(int i, int j) __attribute__((enable_if(i, ""))); // #1 - void g(int i, int j) __attribute__((enable_if(j, ""))) __attribute__((enable_if(true))); // #2 - -In this example, a call to f() is always resolved to #2, as the first enable_if -expression is ODR-equivalent for both declarations, but #1 does not have another -enable_if expression to continue evaluating, so the next round of evaluation has -only a single candidate. In a call to g(1, 1), the call is ambiguous even though -#2 has more enable_if attributes, because the first enable_if expressions are -not ODR-equivalent. - -Query for this feature with ``__has_attribute(enable_if)``. - Initializer lists for complex numbers in C ========================================== diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 2d984949b06f..8a28549f87e8 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -627,7 +627,7 @@ def EnableIf : InheritableAttr { let Subjects = SubjectList<[Function]>; let Args = [ExprArgument<"Cond">, StringArgument<"Message">]; let TemplateDependent = 1; - let Documentation = [Undocumented]; + let Documentation = [EnableIfDocs]; } def ExtVectorType : Attr { diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index af73b8e711ea..01877c5b6027 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -75,4 +75,84 @@ A function declared as ``[[noreturn]]`` shall not return to its caller. The compiler will generate a diagnostic for a function declared as ``[[noreturn]]`` that appears to be capable of returning to its caller. }]; -} \ No newline at end of file +} + +def EnableIfDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``enable_if`` attribute can be placed on function declarations to control +which overload is selected based on the values of the function's arguments. +When combined with the ``overloadable`` attribute, this feature is also +available in C. + +.. code-block:: c++ + + int isdigit(int c); + int isdigit(int c) __attribute__((enable_if(c <= -1 || c > 255, "chosen when 'c' is out of range"))) __attribute__((unavailable("'c' must have the value of an unsigned char or EOF"))); + + void foo(char c) { + isdigit(c); + isdigit(10); + isdigit(-10); // results in a compile-time error. + } + +The enable_if attribute takes two arguments, the first is an expression written +in terms of the function parameters, the second is a string explaining why this +overload candidate could not be selected to be displayed in diagnostics. The +expression is part of the function signature for the purposes of determining +whether it is a redeclaration (following the rules used when determining +whether a C++ template specialization is ODR-equivalent), but is not part of +the type. + +The enable_if expression is evaluated as if it were the body of a +bool-returning constexpr function declared with the arguments of the function +it is being applied to, then called with the parameters at the callsite. If the +result is false or could not be determined through constant expression +evaluation, then this overload will not be chosen and the provided string may +be used in a diagnostic if the compile fails as a result. + +Because the enable_if expression is an unevaluated context, there are no global +state changes, nor the ability to pass information from the enable_if +expression to the function body. For example, suppose we want calls to +strnlen(strbuf, maxlen) to resolve to strnlen_chk(strbuf, maxlen, size of +strbuf) only if the size of strbuf can be determined: + +.. code-block:: c++ + + __attribute__((always_inline)) + static inline size_t strnlen(const char *s, size_t maxlen) + __attribute__((overloadable)) + __attribute__((enable_if(__builtin_object_size(s, 0) != -1))), + "chosen when the buffer size is known but 'maxlen' is not"))) + { + return strnlen_chk(s, maxlen, __builtin_object_size(s, 0)); + } + +Multiple enable_if attributes may be applied to a single declaration. In this +case, the enable_if expressions are evaluated from left to right in the +following manner. First, the candidates whose enable_if expressions evaluate to +false or cannot be evaluated are discarded. If the remaining candidates do not +share ODR-equivalent enable_if expressions, the overload resolution is +ambiguous. Otherwise, enable_if overload resolution continues with the next +enable_if attribute on the candidates that have not been discarded and have +remaining enable_if attributes. In this way, we pick the most specific +overload out of a number of viable overloads using enable_if. + +.. code-block:: c++ + + void f() __attribute__((enable_if(true, ""))); // #1 + void f() __attribute__((enable_if(true, ""))) __attribute__((enable_if(true, ""))); // #2 + + void g(int i, int j) __attribute__((enable_if(i, ""))); // #1 + void g(int i, int j) __attribute__((enable_if(j, ""))) __attribute__((enable_if(true))); // #2 + +In this example, a call to f() is always resolved to #2, as the first enable_if +expression is ODR-equivalent for both declarations, but #1 does not have another +enable_if expression to continue evaluating, so the next round of evaluation has +only a single candidate. In a call to g(1, 1), the call is ambiguous even though +#2 has more enable_if attributes, because the first enable_if expressions are +not ODR-equivalent. + +Query for this feature with ``__has_attribute(enable_if)``. + }]; +}