This is an archive of the discontinued LLVM Phabricator instance.

[clang] Perform implicit lvalue-to-rvalue cast with new interpreter
ClosedPublic

Authored by tbaeder on Aug 18 2022, 7:11 AM.

Details

Summary

Extra bonus patch.

The EvaluateAsRValue() documentation mentions that an implicit
lvalue-to-rvalue cast is being performed if the result is an lvalue.
However, that was not being done if the new constant interpreter was in
use.

Just always do it.

This touches ExprConstant.cpp, but is a NFC patch if the new constant interpreter is not in use.

Diff Detail

Event Timeline

tbaeder created this revision.Aug 18 2022, 7:11 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 18 2022, 7:11 AM
tbaeder requested review of this revision.Aug 18 2022, 7:11 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 18 2022, 7:11 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
shafik added inline comments.Aug 18 2022, 2:44 PM
clang/lib/AST/ExprConstant.cpp
14938

Curious why these two checks don't go w/ the ::Evaluate(Result, Info, E) below.

tbaeder added inline comments.Aug 18 2022, 10:46 PM
clang/lib/AST/ExprConstant.cpp
14938

No real reason I think, it's just that from looking at them, they seem to make sense to me in the general case.

tbaeder updated this revision to Diff 455792.Aug 25 2022, 9:39 PM

Can I get another opinion on this before the weekend?

erichkeane accepted this revision.Aug 26 2022, 6:24 AM

Would be great if we had a better test here... is there anything we can do to validate this is happening other than checking for that one note?

This revision is now accepted and ready to land.Aug 26 2022, 6:24 AM

Would be great if we had a better test here... is there anything we can do to validate this is happening other than checking for that one note?

+1 to this request, but the changes themselves LGTM otherwise

Would be great if we had a better test here... is there anything we can do to validate this is happening other than checking for that one note?

EvaluateAsRValue is called from Expr::EvaluateAsRValue(), so I think it would be possible to write a unittest for this. But I think that would be a lot of effort just to test this. There is even unittests/AST/EvaluateAsRValueTest.cpp already, but it tests the wrong thing :(

Would be great if we had a better test here... is there anything we can do to validate this is happening other than checking for that one note?

EvaluateAsRValue is called from Expr::EvaluateAsRValue(), so I think it would be possible to write a unittest for this. But I think that would be a lot of effort just to test this. There is even unittests/AST/EvaluateAsRValueTest.cpp already, but it tests the wrong thing :(

The existing test coverage being wrong seems like all the more reason to add correct test coverage. LValue to RValue conversions are important to get right (lol here's a wonderful demonstration of where we didn't bother to see if we got it right that I accidentally stumbled into when trying to give you a constexpr test case: https://godbolt.org/z/bdxbers3M), especially because they're going to impact which overload gets called when picking between an && and & overload.

Would be great if we had a better test here... is there anything we can do to validate this is happening other than checking for that one note?

EvaluateAsRValue is called from Expr::EvaluateAsRValue(), so I think it would be possible to write a unittest for this. But I think that would be a lot of effort just to test this. There is even unittests/AST/EvaluateAsRValueTest.cpp already, but it tests the wrong thing :(

The existing test coverage being wrong seems like all the more reason to add correct test coverage. LValue to RValue conversions are important to get right (lol here's a wonderful demonstration of where we didn't bother to see if we got it right that I accidentally stumbled into when trying to give you a constexpr test case: https://godbolt.org/z/bdxbers3M), especially because they're going to impact which overload gets called when picking between an && and & overload.

To be clear, can I land this patch without the unittest? I tried adding this to EvaluateAsRValueTest.cpp but I just run into other problems in the new interpreter :) So more unittests would definitely be good.

@aaron.ballman Can you comment on my last question? I'd like to land this patch since the others depend on it.

@aaron.ballman Can you comment on my last question? I'd like to land this patch since the others depend on it.

Sorry for the delay!

The existing test coverage being wrong seems like all the more reason to add correct test coverage. LValue to RValue conversions are important to get right (lol here's a wonderful demonstration of where we didn't bother to see if we got it right that I accidentally stumbled into when trying to give you a constexpr test case: https://godbolt.org/z/bdxbers3M), especially because they're going to impact which overload gets called when picking between an && and & overload.

To be clear, can I land this patch without the unittest? I tried adding this to EvaluateAsRValueTest.cpp but I just run into other problems in the new interpreter :) So more unittests would definitely be good.

On the one hand, I'm not comfortable landing this without adequate testing, especially because you want to build more stuff on top of it. I think we need to make sure the foundational bits of evaluation are rock solid before building things on top of them. However, on the other hand, this is foundational enough that it's hard to test this bit without other parts of the interpreter being ready. So my preference is to try to add test coverage even if it's failing currently because other parts of the interpreter aren't ready yet. e.g., (imaginary test case, feel free to use better ones)

template <typename Ty, typename Uy>
struct is_same { static constexpr bool value = false; };

template <typename Ty>
struct is_same<Ty, Ty> { static constexpr bool value = true; };

const volatile int i = 0;
// Test that lvalue to rvalue conversion ends up stripping top-level qualifiers.
// FIXME: this isn't correct yet because we don't have support for qualifier conversions yet.
static_assert(is_same<decltype(+i), int>::value, "oh no!"); // expected-error {{static assertion failed: oh no!}}

This way, we can start getting extensive test cases written while thinking about the situations specific to lvalue to rvalue conversion (or whatever else is being implemented at the moment) instead of trying to come back and remember to write them later.

Is that reasonable, or is the interpreter in such a state where even this kind of testing is basically impossible to come up with?

On the one hand, I'm not comfortable landing this without adequate testing, especially because you want to build more stuff on top of it. I think we need to make sure the foundational bits of evaluation are rock solid before building things on top of them. However, on the other hand, this is foundational enough that it's hard to test this bit without other parts of the interpreter being ready. So my preference is to try to add test coverage even if it's failing currently because other parts of the interpreter aren't ready yet. e.g., (imaginary test case, feel free to use better ones)

template <typename Ty, typename Uy>
struct is_same { static constexpr bool value = false; };

template <typename Ty>
struct is_same<Ty, Ty> { static constexpr bool value = true; };

const volatile int i = 0;
// Test that lvalue to rvalue conversion ends up stripping top-level qualifiers.
// FIXME: this isn't correct yet because we don't have support for qualifier conversions yet.
static_assert(is_same<decltype(+i), int>::value, "oh no!"); // expected-error {{static assertion failed: oh no!}}

This way, we can start getting extensive test cases written while thinking about the situations specific to lvalue to rvalue conversion (or whatever else is being implemented at the moment) instead of trying to come back and remember to write them later.

Is that reasonable, or is the interpreter in such a state where even this kind of testing is basically impossible to come up with?

Soo... I was gonna tell you that the test case is obviously not going to work right now, since record types aren't implemented at all. But the test case actually works just fine. However, it also works without the lvalue-to-rvalue conversion in EvaluteAsRValue. Both with the old and the new interpreter.

I'm going to add an actual unit test that fails without the conversion. But it only checks a very simple case, i.e. that a DeclRefExpr is returned as an integer and not the lvalue.

tbaeder updated this revision to Diff 457838.Sep 3 2022, 11:09 PM

Add a unit test.

aaron.ballman accepted this revision.Sep 7 2022, 6:26 AM

LGTM, this wasn't quite what I had in mind, but I can see how my suggestion was going to be far too tricky to try to implement as a lit test. Thanks for adding the unit test coverage!