This is an archive of the discontinued LLVM Phabricator instance.

Stop claiming we support [[carries_dependency]]
Needs RevisionPublic

Authored by aaron.ballman on Feb 9 2023, 11:26 AM.

Details

Reviewers
erichkeane
rsmith
hubert.reinterpretcast
cor3ntin
Group Reviewers
Restricted Project
Summary

The [[carries_dependency]] attribute was added to the standard in C++11, but implementations have (uniformly, as best I can tell) been ignoring this attribute for the past decade. In Clang, we would parse the attribute and otherwise do nothing useful with it (no additional analyses, no communication of the attribute to the backend, etc). As far as I know, we have no future plans to implement the semantics of the attribute. However, __has_cpp_attribute(carries_dependency) returns a nonzero value which suggests we actually do support the attribute and do useful things with it, especially because we documented that it might improve performance.

This removes all support for [[carries_dependency]] and treats it as an unknown attribute instead (the same as we do for other standard attributes we don't support, like [[no_unique_address]] on some targets). We now return zero from __has_cpp_attribute(carries_dependency), which matches the behavior of GCC.

I can find no evidence of user code that should be impacted by this. All uses of the GNU version of the attribute were in compiler test suites: https://sourcegraph.com/search?q=context:global+__attribute__%28%28carries_dependency%29%29+-file:.*test.*+-file:clang&patternType=standard&sm=1&groupBy=repo All uses of the C++ version of the attribute are also in compiler implementations or documentation: https://sourcegraph.com/search?q=context:global+%5B%5Bcarries_dependency%5D%5D+-file:.*test.*+-file:clang&patternType=standard&sm=1&groupBy=repo

Incidentally, this also helps people in WG21 build a case for deprecating and removing this attribute in a future revision of C++.

Diff Detail

Event Timeline

aaron.ballman created this revision.Feb 9 2023, 11:26 AM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 9 2023, 11:26 AM
Herald added a subscriber: jdoerfert. · View Herald Transcript
aaron.ballman requested review of this revision.Feb 9 2023, 11:26 AM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 9 2023, 11:26 AM
cor3ntin accepted this revision.Feb 9 2023, 11:30 AM
cor3ntin added a subscriber: cor3ntin.

Thanks for doing this Aaron.
Did you look in libc++ if they used it anywhere? (I assume they don't)

clang/docs/ReleaseNotes.rst
131
This revision is now accepted and ready to land.Feb 9 2023, 11:30 AM

Thanks for doing this Aaron.
Did you look in libc++ if they used it anywhere? (I assume they don't)

I'm not aware of any uses, and I would be quite surprised if it was used anywhere given that apparently nobody properly implemented it. IIUC this attribute is supposed to improve performance in some way, and we normally require a benchmark to show that some change actually does that if it claims to.

erichkeane requested changes to this revision.Feb 9 2023, 4:54 PM

The guidance from EWG this week and in the past was that we are always required to 'parse and diagnose appertainment' of standard attributes, but not to enable __has_cpp_attribute unless we actually 'do' something with it. I intend/suggest we add a condition to the CXX tag of 'is supported' with some sort of conditional for checking diagnostic and O level (or just straight 'false' in this case).

clang/lib/Sema/SemaDecl.cpp
3324

Unfortunately EWG guidance doesn't let us do this, we still have to enforce appertainment/etc, even if we claim not to support this.

4038

Same here, we still have to carry it around.

clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.depend/p1.cpp
7

These tests still need to be here, __has_cpp_attribute or not.

This revision now requires changes to proceed.Feb 9 2023, 4:54 PM

The guidance from EWG this week and in the past was that we are always required to 'parse and diagnose appertainment' of standard attributes, but not to enable __has_cpp_attribute unless we actually 'do' something with it. I intend/suggest we add a condition to the CXX tag of 'is supported' with some sort of conditional for checking diagnostic and O level (or just straight 'false' in this case).

I've been thinking about the distinction here as well. I'm leaning towards the idea of Introducing a feature test macro for each standard attribute (e.g., __cpp_attr_carries_dependency that, If defined, indicates that the implementation is 1) aware of the attribute (won't diagnose uses of it as an unknown attribute) and 2) will perform syntax and appertainment checks. Conformance would require defining the feature test macro for corresponding standard versions (unless EWG can be convinced that standard attributes are optional). __has_cpp_attribute(XXX) would, of course, only be non-zero if the corresponding __cpp_attr_XXX feature test macro is defined.

hubert.reinterpretcast requested changes to this revision.Feb 9 2023, 6:17 PM
hubert.reinterpretcast added inline comments.
clang/test/Preprocessor/has_attribute.cpp
51

I'd trust this when I see the change to [cpp.cond]/6 arrive in CWG.

I think it may be an option to use the gnu++* modes to do deliberately non-conforming things, but I believe there should be an RFC for that first.

aaron.ballman marked an inline comment as done.Feb 16 2023, 10:25 AM

The guidance from EWG this week and in the past was that we are always required to 'parse and diagnose appertainment' of standard attributes, but not to enable __has_cpp_attribute unless we actually 'do' something with it. I intend/suggest we add a condition to the CXX tag of 'is supported' with some sort of conditional for checking diagnostic and O level (or just straight 'false' in this case).

I'm aware of EWG's guidance, and EWG (and WG21) are aware of Clang's position regarding conformance in this area. It's unfortunate that EWG didn't consider implementer feedback or sibling committee feedback to be sufficiently compelling, but at the end of the day, we are the implementers not EWG, and we need to do what's best for us. This implementation is conforming to what the standard says. We're obligated to issue a diagnostic for the situations you identified and we do -- "attribute ignored" is a diagnostic that conveys exactly what we intend it to convey. Maybe someday we will improve conformance in this area but it is explicitly not a priority given that we're still trying to work on C++20 features three years after that standard was released. I suspect we'll get around to this particular "issue" roughly never (given the work, risks, and benefits) which is why WG21 was told this was a burden for us and that we would be conforming in this manner.

To be clear about this change specifically -- this is a consistency change. Our existing policy has always been that we do not silently ignore attributes unless they're actively harmful to diagnose as being ignored. [[carries_dependency]] isn't even used in practice, so it does not fall into the "harmful to diagnose" category. To date, we've silently accepted it without any intention of implementing the performance improvements it suggests are possible, and that's against our usual policy. What's more, this makes our behavior more self-consistent -- we already do syntactic ignorability of other standard attributes, like [[no_unique_address]] on some targets.

As we saw when we switched our default mode to C++17, compile time overhead is only getting worse with every release of C++. We do not need to keep the (admittedly small) compile time overhead on every C++ compilation to check for requirements of an attribute we will then ignore entirely. "You're using this wrong but we do nothing with it" is user-hostile behavior for attributes which are inherently non-portable (even the standard ones). I don't expect this to have a measurable impact on compile time overhead in practice, but it's hard to argue that there's value in testing every function parameter of every function declaration to see if it perhaps is using [[carries_dependency]] wrong. However, for future attributes like [[assume]], the expense of syntactic checking the argument expression is significant and nontrivial, and we should be self-consistent. (I expect we'll be one of the later C++ implementations to adopt that attribute given it cannot be *semantically* ignored due to potential for template instantiations, changing ABI, etc. We were bitten by WG21 adopting [[no_unique_address]] for C++20 and [[assume]] has already had at least one implementer push back on implementing it, so there's a very real chance we'll be in the same boat again.)

There's ample evidence that total ignorability is not unexpected behavior in practice, either -- the idiomatic way of using attributes is to use feature testing macros to provide various levels of fallback behavior. Note how the fallback clauses do not provide any syntactic checking for the attribute arguments and are defined in a way that semantic appertainment issues are silently ignored as well:

https://github.com/mozilla/gecko-dev/blob/master/third_party/highway/hwy/base.h#L159
https://github.com/pytorch/pytorch/blob/master/c10/util/Deprecated.h#L22
https://github.com/boostorg/config/blob/master/include/boost/config/detail/suffix.hpp#L701

Mozilla, pytorch, and boost are not doing anything unusual here and they are all examples of quite popular C++ projects; it seems to be rare that fallback code ever checks that the syntax of the argument is valid or that it’s being applied to the correct thing.

Given that these changes are conforming, make us more self-consistent with other C++ standard attributes we don't implement, and makes it less likely to introduce header incompatibilities with C (which has reaffirmed the need for syntactic ignorability of attribute for more than one of their implementations as well), I think we should move forward with these changes to [[carries_dependency]] regardless of EWG's guidance. We're following what we're obligated to follow for the standard requirements and WG21 is aware of this. Effectively, I believe our policy should be "follow the C rules for attributes and ignorability" because that does what we need at the moment; we can always revisit the decision later if we find we have the bandwidth.

Specific to this review, does anyone have evidence that we have actual users of [[carries_dependency]] who will be materially impacted by this change?

clang/test/Preprocessor/has_attribute.cpp
51

This is consistent with how we handle [[no_unique_address]] (see line 59).

clang/test/Preprocessor/has_attribute.cpp
51

Is that driven by compatibility concerns on the platform though? For example, we might be doing something on line 59 to match MSVC.

Do we know if GCC intends to make a similar change for carries_dependency? It may help to be more explicit about why our situation is different from GCC's.

Until or unless a C++ DR permits us to define __has_cpp_attribute(carries_dependency) to any value other than 200809L, we have a conformance requirement to macro-expand this to that value. CWG2695 only adds a note, so it changes nothing in this regard. Similarly, we have a conformance requirement to emit a diagnostic on invalid uses of the attribute, and per our policy for -pedantic, we should produce an error for invalid uses under -pedantic-errors and not reject valid uses in that mode. So I think we still need all of the functionality that this patch removes for conformance reasons, for now at least. Maybe WG21 would be receptive to a paper making support of [[carries_dependency]] optional.

Conformance aside, the meaning of [[carries_dependency]] on a particular platform is an ABI requirement. On every platform that we support, the psABI does not specify any different handling for [[carries_dependency]] functions, so we *are* implementing the attribute completely and correctly on those targets, it just happens to be vacuous. If there were a target that defined a different ABI rule, or was anticipated to add such a different ABI rule (eg, due to some future PowerPC or ARM ABI change), then it would make sense to stop claiming support on those platforms until we implemented the behavior (I have the behavior of [[no_unique_address]] on MS ABI in mind here particularly). But I think it's not helpful to users to say we don't support it because it doesn't happen to make a difference on the current target, just it's not helpful to say we don't support [[no_unique_address]] just because it doesn't affect struct layout on a particular target, or -- more to the point -- it wouldn't be helpful to say we don't support consume memory ordering just because we don't get any benefit from it on the current target compared to acquire (and as a result, we lower consume to acquire). I think it's much better to let people unconditionally add [[carries_dependency]] (or [[no_unique_address]], or to use consume) when they mean it; that way, if they compile their code on both Clang and some other compiler that supports a target where the attribute does something, then they'll get the behavior they asked for. We shouldn't be encouraging people to wrap uses of standard attributes in macros.

That said, even though I think we should claim (vacuous) support for such cases, I think it would be reasonable to warn developers that they have no effect. For example, we could add a special-case sub-group of -Wignored-attribute to tell developers that "carries_dependency attribute has no effect", because they might be combining it with consume and expecting improved performance. It's probably even reasonable to turn such a warning on by default.

aaron.ballman marked an inline comment as done.Feb 21 2023, 11:06 AM

Until or unless a C++ DR permits us to define __has_cpp_attribute(carries_dependency) to any value other than 200809L, we have a conformance requirement to macro-expand this to that value. CWG2695 only adds a note, so it changes nothing in this regard.

We don't yet have a C++ DR for this, but we do have EWG direction that such a DR is needed. At the end of the day, our behavior in Clang is inconsistent and this patch fixes that inconsistency. __has_cpp_attribute(no_unique_address) returns 0 on some targets for Clang already today. That cannot change without the risk of silently breaking ABI for users. This is why EWG is moving in the direction of making the value returned by the feature test macro be a matter of QoI. So in terms of behaving consistently, we're faced with a choice: potentially break users by returning nonzero for no_unique_address or stop returning nonzero for carries_dependency. GCC has always returned 0 for carries_dependency. Returning 0 makes Clang consistent with GCC for this specific attribute and makes Clang consistent with itself in terms of standard attributes.

Similarly, we have a conformance requirement to emit a diagnostic on invalid uses of the attribute, and per our policy for -pedantic, we should produce an error for invalid uses under -pedantic-errors and not reject valid uses in that mode. So I think we still need all of the functionality that this patch removes for conformance reasons, for now at least. Maybe WG21 would be receptive to a paper making support of [[carries_dependency]] optional.

The behavior of -pedantic-errors is an interesting point. We *conform* with an "attribute ignored" diagnostic as far as the standard is concerned (so there's no problem about conformance requirements) but it doesn't meet our usual behavior of giving an error in -pedantic-errors mode. We have quite a few other instances of missing pedantic errors and it'd be nice to not make that worse. However, given that we are missing plenty of other pedantic error diagnostics, I'm not certain that observation changes a whole lot in practice.

Conformance aside, the meaning of [[carries_dependency]] on a particular platform is an ABI requirement. On every platform that we support, the psABI does not specify any different handling for [[carries_dependency]] functions, so we *are* implementing the attribute completely and correctly on those targets, it just happens to be vacuous. If there were a target that defined a different ABI rule, or was anticipated to add such a different ABI rule (eg, due to some future PowerPC or ARM ABI change), then it would make sense to stop claiming support on those platforms until we implemented the behavior (I have the behavior of [[no_unique_address]] on MS ABI in mind here particularly). But I think it's not helpful to users to say we don't support it because it doesn't happen to make a difference on the current target, just it's not helpful to say we don't support [[no_unique_address]] just because it doesn't affect struct layout on a particular target, or -- more to the point -- it wouldn't be helpful to say we don't support consume memory ordering just because we don't get any benefit from it on the current target compared to acquire (and as a result, we lower consume to acquire). I think it's much better to let people unconditionally add [[carries_dependency]] (or [[no_unique_address]], or to use consume) when they mean it; that way, if they compile their code on both Clang and some other compiler that supports a target where the attribute does something, then they'll get the behavior they asked for. We shouldn't be encouraging people to wrap uses of standard attributes in macros.

Errr, I have exactly the opposite opinion as you on this. I do not think it helps users to claim vacuous support for an attribute -- that's a response only a compiler writer/committee member could appreciate. In my experience, users want to know "does the attribute do what it says on the tin" not "can the compiler trivially claim conformance". [[carries_dependency]] is an optimization hint; that we don't attempt to even pass the information along to the backend to try to vary optimization behavior means we're not doing what the attribute says on the tin. And given that standard attributes are inherently not portable nor backwards compatible, I think it's very reasonable to encourage people to wrap their use in macros.

Otherwise, what's the point to __has_cpp_attribute returning *anything* for a standard attribute? Users can check __cplusplus instead if the only answer they need to get from the feature test macro is "what version of the standard are you using?".

That said, even though I think we should claim (vacuous) support for such cases, I think it would be reasonable to warn developers that they have no effect. For example, we could add a special-case sub-group of -Wignored-attribute to tell developers that "carries_dependency attribute has no effect", because they might be combining it with consume and expecting improved performance. It's probably even reasonable to turn such a warning on by default.

I continue to believe the best approach here is the honest approach. We're not obligated to implement this attribute, we don't currently implement this attribute in any meaningful way, and so the best approach is to handle it the same as any other attribute we don't know or care about.

Until or unless a C++ DR permits us to define __has_cpp_attribute(carries_dependency) to any value other than 200809L, we have a conformance requirement to macro-expand this to that value. CWG2695 only adds a note, so it changes nothing in this regard.

We don't yet have a C++ DR for this, but we do have EWG direction that such a DR is needed. At the end of the day, our behavior in Clang is inconsistent and this patch fixes that inconsistency. __has_cpp_attribute(no_unique_address) returns 0 on some targets for Clang already today. That cannot change without the risk of silently breaking ABI for users. This is why EWG is moving in the direction of making the value returned by the feature test macro be a matter of QoI. So in terms of behaving consistently, we're faced with a choice: potentially break users by returning nonzero for no_unique_address or stop returning nonzero for carries_dependency. GCC has always returned 0 for carries_dependency. Returning 0 makes Clang consistent with GCC for this specific attribute and makes Clang consistent with itself in terms of standard attributes.

Similarly, we have a conformance requirement to emit a diagnostic on invalid uses of the attribute, and per our policy for -pedantic, we should produce an error for invalid uses under -pedantic-errors and not reject valid uses in that mode. So I think we still need all of the functionality that this patch removes for conformance reasons, for now at least. Maybe WG21 would be receptive to a paper making support of [[carries_dependency]] optional.

The behavior of -pedantic-errors is an interesting point. We *conform* with an "attribute ignored" diagnostic as far as the standard is concerned (so there's no problem about conformance requirements) but it doesn't meet our usual behavior of giving an error in -pedantic-errors mode. We have quite a few other instances of missing pedantic errors and it'd be nice to not make that worse. However, given that we are missing plenty of other pedantic error diagnostics, I'm not certain that observation changes a whole lot in practice.

Conformance aside, the meaning of [[carries_dependency]] on a particular platform is an ABI requirement. On every platform that we support, the psABI does not specify any different handling for [[carries_dependency]] functions, so we *are* implementing the attribute completely and correctly on those targets, it just happens to be vacuous. If there were a target that defined a different ABI rule, or was anticipated to add such a different ABI rule (eg, due to some future PowerPC or ARM ABI change), then it would make sense to stop claiming support on those platforms until we implemented the behavior (I have the behavior of [[no_unique_address]] on MS ABI in mind here particularly). But I think it's not helpful to users to say we don't support it because it doesn't happen to make a difference on the current target, just it's not helpful to say we don't support [[no_unique_address]] just because it doesn't affect struct layout on a particular target, or -- more to the point -- it wouldn't be helpful to say we don't support consume memory ordering just because we don't get any benefit from it on the current target compared to acquire (and as a result, we lower consume to acquire). I think it's much better to let people unconditionally add [[carries_dependency]] (or [[no_unique_address]], or to use consume) when they mean it; that way, if they compile their code on both Clang and some other compiler that supports a target where the attribute does something, then they'll get the behavior they asked for. We shouldn't be encouraging people to wrap uses of standard attributes in macros.

Errr, I have exactly the opposite opinion as you on this. I do not think it helps users to claim vacuous support for an attribute -- that's a response only a compiler writer/committee member could appreciate. In my experience, users want to know "does the attribute do what it says on the tin" not "can the compiler trivially claim conformance". [[carries_dependency]] is an optimization hint; that we don't attempt to even pass the information along to the backend to try to vary optimization behavior means we're not doing what the attribute says on the tin. And given that standard attributes are inherently not portable nor backwards compatible, I think it's very reasonable to encourage people to wrap their use in macros.

Otherwise, what's the point to __has_cpp_attribute returning *anything* for a standard attribute? Users can check __cplusplus instead if the only answer they need to get from the feature test macro is "what version of the standard are you using?".

That said, even though I think we should claim (vacuous) support for such cases, I think it would be reasonable to warn developers that they have no effect. For example, we could add a special-case sub-group of -Wignored-attribute to tell developers that "carries_dependency attribute has no effect", because they might be combining it with consume and expecting improved performance. It's probably even reasonable to turn such a warning on by default.

I continue to believe the best approach here is the honest approach. We're not obligated to implement this attribute, we don't currently implement this attribute in any meaningful way, and so the best approach is to handle it the same as any other attribute we don't know or care about.

The last bit (intent of __has_cpp_attribute being that we 'do something useful with it') was the intent of the room in EWG. Note the term 'useful' was not used in the polls thanks to some CWG folks invading and telling us that 'useful' had meaning apart from what we meant :) That said, the subtlety of "we do exactly what you expect, which is NOTHING!" for carries-dependency wasn't brought up in the room.

It is ALSO the vote taken in EWG to make setting the value optional, see the poll "Ask CWG to change the definition of __has_cpp_attribute to permit 0, and place the suggested values in the 'recommended practice' of the individual attributes.", which gained consensus (https://github.com/cplusplus/papers/issues/1212).

Updated patch limited to changing the feature test macro value would match Varna meeting outcome (changes to allow macro to be 0 accepted as DR).

clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.depend/p1.cpp