This is an archive of the discontinued LLVM Phabricator instance.

[Testing/Support] Remove the const_cast in TakeExpected
ClosedPublic

Authored by labath on Jun 20 2017, 9:02 AM.

Details

Summary

The const_cast in the "const" version of TakeExpected was quite
dangerous, as the function does indeed modify the apparently const
argument.

I assume the reason the const overload was added was to make the
function bind to xvalues(temporaries). That can be also achieved with
rvalue references, so I use that instead.

Using the ASSERT macros on const Expected objects will now become
illegal, but I believe that is correct, as it is not actually possible
to inspect the error stored in an Expected object without modifying it.

Event Timeline

labath created this revision.Jun 20 2017, 9:02 AM
zturner added inline comments.Jun 20 2017, 9:25 AM
include/llvm/Testing/Support/Error.h
34

Should this be std::move(Exp)?

zturner added inline comments.Jun 20 2017, 9:26 AM
include/llvm/Testing/Support/Error.h
34

Actually wait. If we have an rvalue reference overload just delete the lvalue ref overload and put all the code in here.

labath added inline comments.Jun 20 2017, 9:38 AM
include/llvm/Testing/Support/Error.h
34

That won't work because the rvalue reference will not bind to lvalues (Expected<T> foo = bar(); ASSERT_THAT_EXPECTED(foo, Succeeded());).

This does look peculiarly recursive, but it's fine because within the body of this function "Exp" is an lvalue, so it will call the other function. std::move would actually make this recursive.

A possibly cleaner solution would be to have a TakeExpectedImpl function, taking a (lvalue) reference argument, and having both of these delegate to that. (But the only difference there would be that the non-recursivity would be more obvious, and that can also be done with a comment).

zturner edited edge metadata.EditedJun 20 2017, 9:41 AM

Right, but we have the ASSERT_THAT_EXPECTED macro, which can be re-written as ASSERT_THAT(llvm::detail::Expected(std::move(Err), Matcher))

Right, but we have the ASSERT_THAT_EXPECTED macro, which can be re-written as ASSERT_THAT(llvm::detail::Expected(std::move(Err), Matcher))

Ah, that would work, I haven't thought of that.

I am not entirely thrilled by that, as that adds more bloat to the error message ("llvm::detail::TakeExpected(std::move(original_assert_expression)) failed"), but I could go with that.

Right, but we have the ASSERT_THAT_EXPECTED macro, which can be re-written as ASSERT_THAT(llvm::detail::Expected(std::move(Err), Matcher))

Ah, that would work, I haven't thought of that.

I am not entirely thrilled by that, as that adds more bloat to the error message ("llvm::detail::TakeExpected(std::move(original_assert_expression)) failed"), but I could go with that.

I think we can fix that by using a full blown class based implementation of MatcherImpl instead of the MATCHER_P macro. But it was a bit tricky so I didn't attempt it yet.

I am not entirely thrilled by that, as that adds more bloat to the error message ("llvm::detail::TakeExpected(std::move(original_assert_expression)) failed"), but I could go with that.

I think we can fix that by using a full blown class based implementation of MatcherImpl instead of the MATCHER_P macro. But it was a bit tricky so I didn't attempt it yet.

This idea intrigued me, so I tried playing around with it. Unfortunately, I have to report back that I think this is not possible. The first thing that the EXPECT_THAT macro does is pass the value through the testing::internal::PredicateFormatterFromMatcher class, which takes the value as const T &. From this point on, nothing we do can help as the value is already unmodifyable.

I was able to get this working by specializing this class and adding the overloads for other value categories, but this is a pretty big hack, and I think we don't want that. In light of that, I propose to stick to the current implementation. I think we should optimize for error message length, and having an extra TakeExpected overload is a small price to pay for that.

This revision was automatically updated to reflect the committed changes.