[RFC] Add a conditional no_unique_address attribute

There is a fairly common pattern of EBO which is constrained on is_empty && !is_final. This causes quite a bit of code duplication. The answer to this issue was [[no_unique_address]]. Unfortunately, that doesn’t have the same ABI as is_empty && !is_final, so it can’t be used easily if the ABI can’t change, like in libc++ and libstdc++. Still, libc++ (I don’t know about libstdc++) tries to get the benefits of no_unique_address whenever possible. This has lead to various patterns, like _LIBCPP_COMPRESSED_PAIR and __conditional_no_unique_address. In other places it’s simply not possible to switch to no_unique_address in a meaningful way (e.g. __tuple_leaf), which leaves us with keeping EBO around and having duplicate code. However, there is a relatively simple fix to this: having an attribute which is either equivalent to no_unique_address if a given condition is true, and has no effect otherwise.

For this to be actually useful for libc++ (and probably libstdc++ as well) it would have to be available on both Clang and GCC, which is why I’m proposing this here instead of a Clang patch.

The main questions I have are:

  1. Is this something libstdc++/GCC maintainers would actually be interested in?
  2. Should this be a vendor extension, or do people believe this should be [[no_unique_address(condition)]] (or some other standard spelling) and should be proposed to WG21 instead (and then back-ported by GCC and Clang so standard library implementations can use it)?

CC @jwakely @pinskia @AaronBallman @erichkeane @ldionne @frederick-vs-ja

1 Like

Yes I’ve suggested this for G++. The attribute was intended to provide an ABI-compatible alternative to the EBO, but for many uses in libstdc++ we still need to use partial specializations to select whether to use the attribute or not. A Boolean condition would make life much much easier

I also think an attribute like this would make a lot of sense. I think the best path would to propose it for standardization – given the support here I think it might sail through. And then both Clang and GCC can implement + backport the attribute, as you say.

I like the idea.
However there is a small risk that WG21 would later adopt a somewhat different design? Unlikely but, we never know.

Louis’s suggestion to go to WG21 first is certainly a solution, I feel this could have a fairly quick turn around (and I’d be happy to review and even champion a paper).
Another direction is to come up with a different spelling (like clang::no_unique_address_if or something) - however I’m not sure other vendors would be thrilled with that solution - and it’s painful enough that msvc::no_unique_address exists.

2 Likes

I would love to see a non-ignorable non-attribute spelling specifically to provide a path away from the [[msvc::no_unique_address]] debacle.

1 Like

I have no concerns about a conditional argument for the functionality, that seems like a well-motivated feature.

In terms of the design of it, I think it depends on whether we’re proposing it to WG21 or not. If we’re proposing it to WG21, I’d personally want to see a proposal that 1) deprecates [[no_unique_address]] as an attribute, 2) proposes replacing it with a keyword that works the same way but accepts an optional argument (and heck, might as well call it a DR so the keyword works back to C++20). Basically, this is a chance to correct the problematic design mistake and we might as well see if there’s an appetite for it. I expect WG21 to reject that and demand adding the argument to the existing attribute though, at which point we can implement it under the [[no_unique_address]] spelling because we’ll have the signal from the committee that they want to adopt it in that form. This side-steps needing a [[clang::no_unique_address]] spelling.

If WG21 rejects the idea entirely or we don’t propose it to them at all, then I think we can decide between a keyword or [[clang::no_unique_address]] as part of the RFC, but I would be opposed to extending the standard spelling.

2 Likes

Thanks for the replies, everyone. Given that there seems to be consensus that we should propose it to WG21 I’ll start working on a paper.

Re. making it a keyword, IMO that ship has sailed. The attribute is already widely used, so I don’t really see any compiler remove support for it. I’d rather extend the attribute to our needs instead of fighting over whether it should have been a keyword in the first place. FWIW I’m happy to ask for a poll on whether the paper should be changed to propose making it a keyword, though given that I fully expect that to be rejected I’d rather write the paper in a form that I believe may be accepted.

P.S. Note that a keyword in C++20 wouldn’t actually help us, since we really need the feature all the way back to C++11/03 to allow simplifying the libraries.

1 Like

That’s a reasonable approach too and works for me. I mostly want us to make the attempt to fix the design mistake so we can say we did all we could. :smiley:

I don’t see that as an issue because we can always provide a __ version of the keyword to extend it back even farther.

2 Likes

Sure. Can I have __requires and __concept? :stuck_out_tongue:

I don’t see a reason why not… I fear we’ve gated a ton of stuff on “if CPlusPlus20” for concepts/etc, but I’d be in favor of trying (at least experimentally) extending this via alternate keyword.

I would agree with that except for the fact that Microsoft has never, and will never, implement [[no_unique_address]] in a meaningful form due to reasonable ABI concerns. And since Clang targets the Microsoft ABI on Windows, that means Clang won’t benefit from your proposal across all of its supported targets.

The options should definitely be polled, but I would urge pushing for a complete solution. If WG21 then wants to water it down, so be it. But I think there is enough evidence that [[no_unique_address]] has failed as a portable syntax despite demonstrable demand for the feature by programmers willing to use [[msvc::no_unique_address]] hidden behind a macro. Personally, I think WG21 will listen to that argument. Just focus on macros being the only correct and meaningfully way to use [[no_unique_address]] and WG21 will probably be happy to invent yet another Turing complete language that allows you to use reflection to manipulate class layout manually with no_unique_address provided as a shortcut keyword.

Perhaps there is an obvious reason why its not feasible, or otherwise a bad idea, but given the similarity between this request and Extending `[[clang::lifetimebound]]` to take a condition , it seems at least worth asking if there is a more general solution to this problem space. If two attributes need a condition, they are unlikely to be the last.

For example, would a generic wrapping attribute be possible like [[clang::conditional(<condition>, [[attribute_to_include]])]] (or some other spelling)?

Or if nesting attributes is not kosher, some other syntax like __conditional_attribute(<condition>, [[attribute_to_include]])?

“will never” is incorrect here, I believe. There are still some ideas about an ABI-breaking vNext for MSVC – they just keep getting pushed off to the next release each time, but it’s still filed under “someday” AFAIU. Once that happens, MSVC would implement [[no_unique_address]] correctly.

CC @STL_MSFT

It’s still possible vNext will happen (we have issues and a wiki page tracking what we would do in it). However, I don’t expect it to happen before I retire. Perhaps I’ll be pleasantly surprised.

3 Likes

This strikes me as a very interesting idea. And indeed, we’ve run into wanting to condition other attributes too — like [[nodiscard]] which unlike the other two we’re talking about already has a form that takes another parameter.

1 Like

I’ve been considering writing a paper for this myself, and I think WG21-first makes most sense given that there is no vendor-specific solution yet. For me, the motivation would have been conditional deprecation of specific template specializations via [[deprecated]].

There are so many attributes that we should really have a general solution to this, not just add one bool after another in various places. I’ve also not seen anyone mention this yet: a general solution can and should also cover annotations (new in C++26), and that may be hugely useful in the future.

@philnik please let me know if you want any help with the paper and/or the WG21 process.

My initial idea for a syntax would have been:

// attributes
[[if (constant-expression) deprecated("reason")]]
// or
[[deprecated ("reason") requires (constant-expression)]]
// annotations
[[if (constant-expression) = 1 + 1]]
[[= 1 + 1 requires (constant-expression)]]

An important consideration is how you conditionally apply several attributes/annotations at the same time, if that’s supported at all. [[if (b) x, y, z]] seems intuitive, [[x, y, z requires (b)]] perhaps less so. I think you definitely want the ability to slap multiple attributes conditonally on there, like

[[if (cond) no_unique_address, msvc::no_unique_address]]

Yeah, I think I agree that this really needs a more general approach. [[nodiscard]] is also something we might want to apply conditionally, and there are probably many more.

If you’re interested I’d be happy to work together on a paper. Feel free to DM me.

I’m not 100% sold on this syntax, since it seems a bit ambiguous to me what exactly the if applies to. This should probably not be discussed here though, since it’s getting a bit off-topic.

There are barely a handful of standard attributes, attributes can have expression as arguments,
no_unique_address(bool) is perfectly cromulent.

The only thing we need here, is that the standard accepts to do that.
If you want to add a boolean attribute to deprecated (i doubt there is a motivation), go for it.

Does everything need to be maximally over-engineered?

1 Like

I agree if clang and gcc can agree on an approach I don’t think we need to wait for wg21 here.