This is an archive of the discontinued LLVM Phabricator instance.

Add `__reference_binds_to_temporary` trait for checking safe reference initialization.
ClosedPublic

Authored by EricWF on Feb 14 2017, 3:19 AM.

Details

Summary

The STL types std::pair and std::tuple can both store reference types. However their constructors cannot adequately check if the initialization of reference types is safe. For example:

std::tuple<std::tuple<int> const&> t = 42;
// The stored reference is already dangling.

Libc++ has a best effort attempts in tuple to diagnose this, but they're not able to handle all valid cases (If I'm not mistaken). For example initialization of a reference from the result of a class's conversion operator. Libc++ would benefit from having a builtin traits which can provide a much better implementation.

This patch introduce the __reference_binds_to_temporary(T, U) trait that determines whether a reference of type T bound to an expression of type U would bind to a materialized temporary object.

Note that the trait simply returns false if T is not a reference type instead of reporting it as an error.

static_assert(__is_constructible(int const&, long));
static_assert(__reference_binds_to_temporary(int const&, long));

Diff Detail

Repository
rL LLVM

Event Timeline

EricWF created this revision.Feb 14 2017, 3:19 AM
EricWF edited the summary of this revision. (Show Details)Feb 14 2017, 3:29 AM
EricWF added a reviewer: majnemer.
EricWF added a subscriber: compnerd.
majnemer added inline comments.Feb 14 2017, 1:19 PM
include/clang/Basic/TokenKinds.def
418 ↗(On Diff #88341)

Should this trait be grouped here?

rsmith edited edge metadata.Feb 14 2017, 2:19 PM

I don't like this name; it sounds too much like you're asking whether a certain direct-initialization is possible, which is what __is_constructible does. I also don't like the idea of combining an "is this type direct-initializable from this list of arguments" check with a reference-specific side-check; it seems better to expose the underlying check itself and allow the user to figure out how they want to combine the checks.

Perhaps we could introduce a __reference_binds_to_temporary(T, U) trait, where T is required to be a reference type, that determines whether a reference of type T bound to an expression of type U would bind to a materialized temporary object (or subobject thereof)?

I don't like this name; it sounds too much like you're asking whether a certain direct-initialization is possible, which is what __is_constructible does. I also don't like the idea of combining an "is this type direct-initializable from this list of arguments" check with a reference-specific side-check; it seems better to expose the underlying check itself and allow the user to figure out how they want to combine the checks.

Perhaps we could introduce a __reference_binds_to_temporary(T, U) trait, where T is required to be a reference type, that determines whether a reference of type T bound to an expression of type U would bind to a materialized temporary object (or subobject thereof)?

That all sounds great to me. Thanks for the comments @rsmith. I'll update it tomorrow with the changes.

EricWF updated this revision to Diff 88794.Feb 16 2017, 3:20 PM
EricWF retitled this revision from Add `__is_direct_constructible` trait for checking safe reference initialization. to Add `__reference_binds_to_temporary` trait for checking safe reference initialization..
EricWF edited the summary of this revision. (Show Details)

Address all of @rsmith's comments.

EricWF updated this revision to Diff 88795.Feb 16 2017, 3:21 PM

Remove test code that snuck in.

rsmith added inline comments.Nov 20 2017, 4:53 PM
lib/Sema/SemaExprCXX.cpp
4567 ↗(On Diff #88795)

"alongside" is a single word.

test/SemaCXX/type-traits.cpp
2102–2116 ↗(On Diff #88795)

Please also check things like:

`__reference_binds_to_temporary(const int&, ConvertsToRef<long, long&>)`

(That is, a user-defined conversion to a reference type followed by converting and materializing a temporary.)

EricWF marked an inline comment as done.Nov 21 2017, 1:40 AM
EricWF added inline comments.
test/SemaCXX/type-traits.cpp
2102–2116 ↗(On Diff #88795)

Isn't that exactly what the test on the specified line does? LongRef is ConvertsToRef<long, long&>

EricWF updated this revision to Diff 123741.Nov 21 2017, 1:59 AM
  • Address spelling errors pointed out during review.
rsmith accepted this revision.Jan 11 2018, 3:22 PM

Please also document this trait in docs/LanguageExtensions.rst.

include/clang/Basic/TypeTraits.h
91 ↗(On Diff #123741)

Looks like you reformatted (and reindented) this whole file. If you meant to do that, please commit it as a separate change.

lib/Parse/ParseDeclCXX.cpp
1407–1432 ↗(On Diff #123741)

This list should only contain keywords that libstdc++ or libc++ has at one point defined as class templates; your trait should not be added here.

lib/Parse/ParseExpr.cpp
903 ↗(On Diff #123741)

Likewise.

This revision is now accepted and ready to land.Jan 11 2018, 3:22 PM
EricWF marked 3 inline comments as done.Jan 11 2018, 3:29 PM

@rsmith The trait is already documented in LanguageExtensions.rst, so I'll assumed you missed that and are not trying to suggest more documentation is needed.

include/clang/Basic/TypeTraits.h
91 ↗(On Diff #123741)

Woops!

lib/Parse/ParseDeclCXX.cpp
1410 ↗(On Diff #123741)

Woops, Clang-format seems to chewed this up.

EricWF updated this revision to Diff 129544.Jan 11 2018, 3:43 PM
EricWF marked an inline comment as done.

Address inline comments.

This revision was automatically updated to reflect the committed changes.

@rsmith The trait is already documented in LanguageExtensions.rst, so I'll assumed you missed that and are not trying to suggest more documentation is needed.

Yes, sorry, not sure how I missed that! :)