This is an archive of the discontinued LLVM Phabricator instance.

[libc++] Avoid creating temporaries in unary expressions involving valarray
ClosedPublic

Authored by ldionne on May 5 2022, 9:32 AM.

Details

Summary

Currently, unary expressions involving valarray will create a temporary.
This leads to dangling references in expressions like -a * b, because
-a is a temporary and the resulting expression will refer to it. This
patch fixes the problem by creating a lazy expression to perform the unary
operation instead of eagerly creating a temporary valarray. This is
permitted by the Standard.

rdar://90152242

Diff Detail

Event Timeline

ldionne created this revision.May 5 2022, 9:32 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 5 2022, 9:32 AM
ldionne requested review of this revision.May 5 2022, 9:32 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 5 2022, 9:32 AM
Herald added a reviewer: Restricted Project. · View Herald Transcript
ldionne updated this revision to Diff 427358.May 5 2022, 9:35 AM

Fix typo in test.

philnik added a subscriber: philnik.May 5 2022, 9:42 AM

Isn't this ABI breaking? Or is it in this case OK for some reason?

libcxx/include/valarray
926–930

Add _LIBCPP_HIDE_FROM_ABI?

3306–3307
Mordante added inline comments.
libcxx/test/std/numerics/numarray/template.valarray/valarray.unary/bit_not.pass.cpp
59

C++03 fails due to these initializer lists.

62

Can you also test the contents of the resulting array? Same for the other tests.

ldionne marked 4 inline comments as done.May 30 2022, 1:07 PM
ldionne added a subscriber: Restricted Project.

Isn't this ABI breaking? Or is it in this case OK for some reason?

It is technically an ABI break. However, the only way I can think of being broken by this is to depend on the exact type of an expression involving valarray (such as decltype(!a && b)). Furthermore, if that were the case, it would almost surely show up as a link-time error, not a runtime error (it would be a runtime error if a function's return type suddenly changed as a result of this change). Given that the current behavior is extremely broken and that valarray doesn't see all that much use in the wild, I believe that while the risk of breakage is non-zero, the risk-to-reward ratio makes it a no brainer to make the change. I'll add a release note, though.

Note that in particular, I am erring towards not introducing an ABI macro for this because it fixes a bug and anyone having used these methods non-trivially is probably broken anyways. Please let me know if you think that's reasonable. Also, pinging libc++ vendors in case they have something to add.

libcxx/include/valarray
926–930

I'll use _LIBCPP_INLINE_VISIBILITY for consistency with the rest of the file.

ldionne updated this revision to Diff 432976.May 30 2022, 1:18 PM
ldionne marked an inline comment as done.

Address comments.

Isn't this ABI breaking? Or is it in this case OK for some reason?

It is technically an ABI break. However, the only way I can think of being broken by this is to depend on the exact type of an expression involving valarray (such as decltype(!a && b)). [...]

It's also worth noting that the Standard does not mandate that the result of !a & friends be std::valarray itself. Hence, if someone has been relying on that at their ABI boundary, they've been really playing with fire (most likely unknowingly). However, like I said, I think this is unlikely to bite anyone -- one would need an unfortunate combination of auto-deduced return types and unary expressions on valarray at an ABI boundary to get bitten.

Isn't this ABI breaking? Or is it in this case OK for some reason?

It is technically an ABI break. However, the only way I can think of being broken by this is to depend on the exact type of an expression involving valarray (such as decltype(!a && b)). [...]

It's also worth noting that the Standard does not mandate that the result of !a & friends be std::valarray itself. Hence, if someone has been relying on that at their ABI boundary, they've been really playing with fire (most likely unknowingly). However, like I said, I think this is unlikely to bite anyone -- one would need an unfortunate combination of auto-deduced return types and unary expressions on valarray at an ABI boundary to get bitten.

Wouldn't one be also bitten if they explicitly instantiated an instance of valarray with a program-defined type and made the instantiated member functions available as part of their ABI interface?

Wouldn't one be also bitten if they explicitly instantiated an instance of valarray with a program-defined type and made the instantiated member functions available as part of their ABI interface?

Yes, although that comment can be made whenever we add _LIBCPP_HIDE_FROM_ABI (aka _LIBCPP_INLINE_VISIBILITY) to a method, since that causes the method to be excluded from any explicit instantiation. Concretely, explicitly instantiating standard library types is very brittle (some other gotchas are described here), and fortunately very few people do it. My intuition tells me that the intersection of people using valarray with a user-defined type *and* explicitly instantiating it as part of their ABI is probably empty, but I could be wrong.

IMO, it's a better tradeoff to fix this bug (which will bite unsuspecting users doing normal stuff) and potentially break users who use valarray in technically-valid-yet-niche ways, than to leave the bug here to preserve other more niche use cases. Especially since the breakage should be at link time for most of these valid-but-niche use cases. If someone disagrees, let's have a discussion, I'm entirely open to reconsidering this.

If there's no additional discussion by EOW, I'll assume that folks are OK with this direction.

ldionne accepted this revision.Jun 6 2022, 7:16 AM
This revision is now accepted and ready to land.Jun 6 2022, 7:16 AM