Add a clang-specific impl->projection conversion operator#1274
Add a clang-specific impl->projection conversion operator#1274kennykerr merged 5 commits intomicrosoft:mastermicrosoft/cppwinrt:masterfrom
Conversation
The conversion from `producer_convert` to `const producer_ref` does not improve const-correctness, and in compliant compilers¹ prevents the conversion of `producer_convert` (ala `winrt::implements<D, I>`) to `producer_ref<T>`'s base type `T`. In brief², this is because conversion acts as rvalue reference construction via `T(T&&)`; a `const`-qualified rvalue cannot bind an rvalue reference. The `const`ness here doesn't impart any additional safety, either: - It contains only an untyped pointer, the constness of which does not restrict its holder from any additional access to either the implementation or projected types. - It exists to convert to its base type T, and it will do so by copy. That copy will contain (hidden) the same pointer, but will often not be `const`-qualified; any code that holds it will likely have unfettered access to that In short, it's a `const`-qualified temporary that exists to convert a `winrt::implements<D, I>` from non-`const` `D` to non-`const` `I`. Given that holding on to a `producer_ref` is in violation of the C++/WinRT `impl` namespace contract _anyway_, be it `const`-qualified or not, this should be a safe change to make. Closes microsoft#1273 ¹ http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2077 ² https://stackoverflow.com/a/70654432
|
/cc @DefaultRyan @oldnewthing as mentioned in #1273. |
|
(I'll dig in on the ASAN failures, which didn't reproduce when I ran the test suite locally.) |
|
Edit: With the included |
|
Well, it looks like that is a load-bearing In short, here's what happens during conversion from With
|
|
Yes, the |
|
My next couple avenues of exploration are...
I know that the last one would be a dealbreaker, but would you be amenable to it if it didn't impact the golden path MSVC scenario? |
|
Presumably we don't want to inject spurious ref count bumps into Clang builds either but if it only impacts cases where a ref count is needed anyway, such as |
|
Okay, so, here's my contention. I've dug and dug and I can't find a way to eliminate the I'd like to propose two things:
See, at the end of the day... all I need is for my code to appear to be well-formed and compile. I'm not shipping a product based on this, but right now we would need to fix up ~151 sites across our codebase (and annotate them with a comment explaining why the normal C++/WinRT way doesn't work so they don't get reverted) and in every one of those cases introduce our own extra If you'd be amenable to a gated "make the code work, sub-optimally, if the user opts in" experience... I'd be happy to write it up. |
|
Hey, thanks for looking into it. Obviously, compiling sub-optimally is better than not compiling at all. Can we just hide it behind an |
|
I'm totally cool with that. Thanks @kennykerr. I'll update this PR inplace. |
|
Alright, updated the PR title, body and contents. |
|
Can you add some tests for this? |
|
A little surprised this isn't already covered, unless a test is disabled for clang? |
(cherry picked from commit 675f8b7)
|
Good call. I added a test, unconditional for all compilers. 😄 I could not find one that had been disabled for clang that would have exercised this. |
| // uniform initialization relies on IStringable(IStringable&&), | ||
| // which requires non-const rvalue semantics. | ||
| com_ptr<Foo> foo = make_self<Foo>(); | ||
| IStringable stringable{ foo.as<IStringable>() }; |
There was a problem hiding this comment.
| IStringable stringable{ foo.as<IStringable>() }; | |
| IStringable stringable{ *foo }; |
Isn't something like this required to actually invoke the implicit conversion operator? Otherwise you're just calling the explicit as method provided by com_ptr which in turn calls QI.
In Clang, the conversion from
producer_converttoconst producer_ref<I>does not allow for the move-initialization of
IviaI(I&&)(implicitor explicit) due to a language misspecification¹.
In brief², we cannot offer zero-cost initialization of projected types
from implementation types in all meaningful cases in projects compiled
with clang.
Until it improves (which is tracked in llvm/llvm-project#17056), this
conversion operator will allow for C++/WinRT code to compile with Clang.
Closes #1273
¹ http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2077 documents
the language standard bug that results in this.
² In non-brief, https://stackoverflow.com/a/70654432