Bug 60512 - would be useful if gcc implemented __has_feature similarly to clang
Summary: would be useful if gcc implemented __has_feature similarly to clang
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: unknown
: P3 enhancement
Target Milestone: ---
Assignee: Alex Coplan
URL:
Keywords:
Depends on:
Blocks: 90709 109882
  Show dependency treegraph
 
Reported: 2014-03-12 19:24 UTC by Evan Teran
Modified: 2023-12-13 21:51 UTC (History)
8 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2016-06-07 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Evan Teran 2014-03-12 19:24:40 UTC
I was recently thinking about how GCC sets __cplusplus to 201103L when -std=c++11 yet doesn't (and may not ever) fully support every single corner case of c++11.

I agree with the notion that __cplusplus == 201103L is a statement of intent, not 100% conformance. But how should developers write code that attempts to use features which aren't necessarily implemented yet, but may be in the near future or other compilers.

It occurred to me that the ideal solution may not be to test __cplusplus at all, but instead to test for specific features. I find that that clang's approach to this seems to hit the sweet spot. I'm sure the GCC folk are aware of the feature, but I'll reiterate for reference. In clang:

"[This] function-like macro take a single identifier argument that is the name of a feature. __has_feature evaluates to 1 if the feature is both supported by Clang and standardized in the current language standard or 0 if not"

So while GCC doesn't support C++11 attributes yet, we could still code for it today wrapping it like so.

    #if __has_feature(cxx_attributes)
    #endif

if it also supported the __has_feature (and family) suite of macros. Perhaps this macro is mostly of interest to library writers and not a huge deal to most developers, but I believe that it would be a tool that is *very* useful when it turns out that you do need it.
Comment 2 Andrew Pinski 2014-03-12 19:35:58 UTC
>So while GCC doesn't support C++11 attributes yet

Wrong, it does support them, in fact for a while now.
Comment 3 Evan Teran 2014-03-12 19:52:13 UTC
@Andrew,

I hadn't seen that they were supported, I stand corrected. However that particular feature was for example purposes. Any unsupported feature would do.

@Marc,

A quick read of that page shows a pretty good solution. Has any word towards implementing its recommendations been done?

Thanks for the *very* quick response.
Comment 4 Marc Glisse 2014-03-12 20:16:09 UTC
(In reply to Evan Teran from comment #3)
> Any unsupported feature would do.

All the core features are at least partially supported, only some ABI-breaking library changes have been postponed.

> A quick read of that page shows a pretty good solution. Has any word towards
> implementing its recommendations been done?

I don't think so. Feel free to start. Defining a bunch of macros seems like a nice and easy way to start contributing to gcc ;-)
Comment 5 Manuel López-Ibáñez 2016-06-07 18:23:47 UTC
AFAIU, it will be welcome if someone implements it.
Comment 6 Jonathan Wakely 2016-06-08 08:43:34 UTC
(In reply to Manuel López-Ibáñez from comment #5)
> AFAIU, it will be welcome if someone implements it.

I'm not sure about that. As Marc pointed out in comment 1, the C++ committee's recommendation is to test for feature test macros being defined, not use the non-standard __has_feature extension. So the correct way to check for C++11 attributes would be:

  #if __cpp_attributes
  ...
  #endif

If the compiler defines that macro then it means it supports the feature. This works perfectly without needing __has_feature.

N.B. the related __has_include extension provides functionality that can't be done with just macros, and it is now part of C++17 (see http://wg21.link/p0061) and is supported by GCC since version 5.1, as is the __has_cpp_attribute described by SD-6.

I see no need to add __has_feature given that it contradicts the recommendations of the C++ committee, and that GCC already defines the recommended feature test macros that can be used without __has_feature.
Comment 7 Jonathan Wakely 2016-06-08 08:50:25 UTC
N.B. checking #if __cpp_attributes works correctly with all versions of GCC (with a true result since 4.9.2 and false otherwise), whereas adding a new __has_feature extension and using #if __has_feature(attributes) would only give the right answer for GCC releases that support the feature.
Comment 8 Jonathan Wakely 2016-06-08 08:51:35 UTC
(In reply to Jonathan Wakely from comment #7)
> N.B. checking #if __cpp_attributes works correctly with all versions of GCC
> (with a true result since 4.9.2 and false otherwise), whereas adding a new
> __has_feature extension and using #if __has_feature(attributes) would only
> give the right answer for GCC releases that support the feature.

s/support the feature/support the __has_feature extension/
Comment 9 Alex Coplan 2023-01-03 16:41:01 UTC
(In reply to Jonathan Wakely from comment #6)
> (In reply to Manuel López-Ibáñez from comment #5)
> > AFAIU, it will be welcome if someone implements it.
> 
> I see no need to add __has_feature given that it contradicts the
> recommendations of the C++ committee, and that GCC already defines the
> recommended feature test macros that can be used without __has_feature.

I think it's worth noting that __has_feature can be used to check for more than just C++ language features. To quote the clang documentation (https://clang.llvm.org/docs/LanguageExtensions.html):

> Another use of __has_feature is to check for compiler features not related to the language standard, such as e.g. AddressSanitizer.

LLVM users can check whether ASan is enabled using __has_feature(address_sanitizer). Similarly one can query e.g. __has_feature(thread_sanitizer).

I think it would be good for the alignment between GCC and LLVM if GCC were to implement this extension.
Comment 10 Jonathan Wakely 2023-01-04 11:40:28 UTC
But why is that better than GCC's alternative, just defining macros like __SANITIZE_ADDRESS__ and __SANITIZE_THREAD__?

Libstdc++ does this:

// Macro indicating that TSAN is in use.
#if __SANITIZE_THREAD__
#  define _GLIBCXX_TSAN 1
#elif defined __has_feature
# if __has_feature(thread_sanitizer)
#  define _GLIBCXX_TSAN 1
# endif
#endif

ISTM that it would be simpler if clang defined those macros too, because checking for both __has_feature and then using __has_feature takes more work than just checking a macro.

There's currently no macro for UBsan AFAIK, but one could be added.
Comment 11 Jessica Clarke 2023-01-06 14:50:33 UTC
Macros and __has_feature are equally expressive, sure, but why should Clang change what it’s been doing from the start because GCC doesn’t want to be compatible with how it’s always done it? It seems a bit rude to expect Clang to change when it was the one to define how these worked first and GCC took its implementation. It’s not like it’s a complicated thing for GCC to implement, and it should really have done so when it added sanitizer support in order to be fully compatible rather than do things differently and force users to support both ways in their code (which, to this day, isn’t reliably done, so there is code out there that only works with Clang’s sanitizers).
Comment 12 Jason Merrill 2023-02-02 16:43:43 UTC
I agree that __has_feature seems redundant with feature test macros, but it does seem reasonable to implement for portability.
Comment 13 Alex Coplan 2023-04-11 16:39:48 UTC
Clang recognizes the "cxx_defaulted_functions" feature to detect whether "= default" functions are supported.

It's clear that __has_feature (cxx_defaulted_functions) should evaluate to 1 for -std=c++11 and above. It's less clear whether GCC intends to support "= default" functions in C++98 mode as an extension, and therefore whether __has_extension (cxx_defaulted_functions) should evaluate to 1 with -std=c++98.

I noticed that e.g. we accept:

struct S {
    int x;
    S(int a) : x(a) {}
    S() = default;
};
void f() {
    S s;
}

with -std=c++98 and emit a -Wc++11-extensions warning. This suggests that "= default" might be supported as an extension, but it's not clear. By contrast, it seems that deleted functions (detected with "cxx_deleted_functions") aren't supported in C++98 mode, since e.g.

struct S {
    S() = delete;
};
void f() {
    S s;
}

doesn't get rejected with -std=c++98. It would be good to get some input from C++ maintainers on the cxx_defaulted_functions case.
Comment 14 Alex Coplan 2023-05-05 16:31:00 UTC
I'm working on this.
Comment 15 Iain Sandoe 2023-05-05 18:03:13 UTC
(In reply to Alex Coplan from comment #14)
> I'm working on this.

I will rebase my WIP and push it somewhere - [ it has __has_feature and __has_extension components .. but likely bit rotten with changes to libcpp ]
(sorry for being slow to do this - releases tie up all my Darwin time.
Comment 16 Iain Sandoe 2023-05-05 18:19:27 UTC
(In reply to Iain Sandoe from comment #15)
> (In reply to Alex Coplan from comment #14)
> > I'm working on this.
> 
> I will rebase my WIP and push it somewhere - [ it has __has_feature and
> __has_extension components .. but likely bit rotten with changes to libcpp ]
> (sorry for being slow to do this - releases tie up all my Darwin time.

https://github.com/iains/gcc-git/tree/master-wip-has-extension

(it might not even build - I was hoping to have some time to refresh it - but better publish now in case it's useful in any way )

AFAIR the main blocker to progress was trying to decide how to represent the target/language/language version dependencies of the features and extensions (there's a rudimentary scheme at the moment but it probably is not flexible enough)
Comment 17 Iain Sandoe 2023-05-05 20:26:42 UTC
(In reply to Iain Sandoe from comment #16)
> (In reply to Iain Sandoe from comment #15)
> > (In reply to Alex Coplan from comment #14)
> > > I'm working on this.
> > 
> > I will rebase my WIP and push it somewhere - [ it has __has_feature and
> > __has_extension components .. but likely bit rotten with changes to libcpp ]
> > (sorry for being slow to do this - releases tie up all my Darwin time.
> 
> https://github.com/iains/gcc-git/tree/master-wip-has-extension
> 
> (it might not even build - I was hoping to have some time to refresh it -
> but better publish now in case it's useful in any way )

heh, despite that I've not done anything to it since 2019 actually it builds and the tests pass - at least for C.  Anyway, see what you think and how it lines up with your intended impl.
Comment 18 Alex Coplan 2023-05-09 10:37:10 UTC
(In reply to Iain Sandoe from comment #16)
> 
> AFAIR the main blocker to progress was trying to decide how to represent the
> target/language/language version dependencies of the features and extensions
> (there's a rudimentary scheme at the moment but it probably is not flexible
> enough)

Can you elaborate on this a little? In particular I'd be interested to hear about cases where the target matters.
Comment 19 Iain Sandoe 2023-05-09 10:57:26 UTC
(In reply to Alex Coplan from comment #18)
> (In reply to Iain Sandoe from comment #16)
> > 
> > AFAIR the main blocker to progress was trying to decide how to represent the
> > target/language/language version dependencies of the features and extensions
> > (there's a rudimentary scheme at the moment but it probably is not flexible
> > enough)
> 
> Can you elaborate on this a little? In particular I'd be interested to hear
> about cases where the target matters.

the first commit in my WIP branch is "generic support" (i.e. features that are [intended to be] common to all targets)

the second commit identifies a set of features that (I would want to be) supported on Darwin, but maybe not relevant to other targets.

Of course, we hope the first set to be the largest.

Targets like Darwin and Windows are more likely to need such things - where compatibility with external ABI and system tools means we're trying to comply with design decisions out of our control.

However, it is presumably conceivable that (say) variable length vector features could be relevant to aarch64 and risc-v but not elsewhere (I'm speculating here, so no concrete example or evidence).
Comment 20 Iain Sandoe 2023-05-09 11:13:00 UTC
although, I guess, we could have one table and somehow include the target in predicates if appropriate...
Comment 21 Alex Coplan 2023-05-09 12:14:01 UTC
(In reply to Iain Sandoe from comment #17)
> 
> heh, despite that I've not done anything to it since 2019 actually it builds
> and the tests pass - at least for C.  Anyway, see what you think and how it
> lines up with your intended impl.

Thanks. I've posted what I came up with so far as an RFC here: https://gcc.gnu.org/pipermail/gcc-patches/2023-May/617878.html
Comment 22 GCC Commits 2023-11-27 10:46:26 UTC
The master branch has been updated by Alex Coplan <acoplan@gcc.gnu.org>:

https://gcc.gnu.org/g:06280a906cb3dc80cf5e07cf3335b758848d488d

commit r14-5873-g06280a906cb3dc80cf5e07cf3335b758848d488d
Author: Alex Coplan <alex.coplan@arm.com>
Date:   Fri Mar 17 16:30:51 2023 +0000

    c-family: Implement __has_feature and __has_extension [PR60512]
    
    This patch implements clang's __has_feature and __has_extension in GCC.
    Currently the patch aims to implement all documented features (and some
    undocumented ones) following the documentation at
    https://clang.llvm.org/docs/LanguageExtensions.html with the exception
    of the legacy features for C++ type traits.  These are omitted, since as
    the clang documentation notes, __has_builtin is the correct "modern" way
    to query for these (which GCC already implements).
    
    gcc/c-family/ChangeLog:
    
            PR c++/60512
            * c-common.cc (struct hf_feature_info): New.
            (c_common_register_feature): New.
            (init_has_feature): New.
            (has_feature_p): New.
            * c-common.h (c_common_has_feature): New.
            (c_family_register_lang_features): New.
            (c_common_register_feature): New.
            (has_feature_p): New.
            * c-lex.cc (init_c_lex): Plumb through has_feature callback.
            (c_common_has_builtin): Generalize and move common part ...
            (c_common_lex_availability_macro): ... here.
            (c_common_has_feature): New.
            * c-ppoutput.cc (init_pp_output): Plumb through has_feature.
    
    gcc/c/ChangeLog:
    
            PR c++/60512
            * c-lang.cc (c_family_register_lang_features): New.
            * c-objc-common.cc (struct c_feature_info): New.
            (c_register_features): New.
            * c-objc-common.h (c_register_features): New.
    
    gcc/cp/ChangeLog:
    
            PR c++/60512
            * cp-lang.cc (c_family_register_lang_features): New.
            * cp-objcp-common.cc (struct cp_feature_selector): New.
            (cp_feature_selector::has_feature): New.
            (struct cp_feature_info): New.
            (cp_register_features): New.
            * cp-objcp-common.h (cp_register_features): New.
    
    gcc/ChangeLog:
    
            PR c++/60512
            * doc/cpp.texi: Document __has_{feature,extension}.
    
    gcc/objc/ChangeLog:
    
            PR c++/60512
            * objc-act.cc (struct objc_feature_info): New.
            (objc_nonfragile_abi_p): New.
            (objc_common_register_features): New.
            * objc-act.h (objc_common_register_features): New.
            * objc-lang.cc (c_family_register_lang_features): New.
    
    gcc/objcp/ChangeLog:
    
            PR c++/60512
            * objcp-lang.cc (c_family_register_lang_features): New.
    
    libcpp/ChangeLog:
    
            PR c++/60512
            * include/cpplib.h (struct cpp_callbacks): Add has_feature.
            (enum cpp_builtin_type): Add BT_HAS_{FEATURE,EXTENSION}.
            * init.cc: Add __has_{feature,extension}.
            * macro.cc (_cpp_builtin_macro_text): Handle
            BT_HAS_{FEATURE,EXTENSION}.
    
    gcc/testsuite/ChangeLog:
    
            PR c++/60512
            * c-c++-common/has-feature-common.c: New test.
            * c-c++-common/has-feature-pedantic.c: New test.
            * g++.dg/ext/has-feature.C: New test.
            * gcc.dg/asan/has-feature-asan.c: New test.
            * gcc.dg/has-feature.c: New test.
            * gcc.dg/ubsan/has-feature-ubsan.c: New test.
            * obj-c++.dg/has-feature.mm: New test.
            * objc.dg/has-feature.m: New test.
    
    Co-Authored-By: Iain Sandoe <iain@sandoe.co.uk>
Comment 23 Alex Coplan 2023-11-27 11:08:39 UTC
Implemented for GCC 14.
Comment 24 Eric Gallager 2023-12-11 21:53:57 UTC
(In reply to Alex Coplan from comment #23)
> Implemented for GCC 14.

Thanks! Could you add a note to gcc-14/changes.html in gcc-wwwdocs so that people can be aware of it?