-
Notifications
You must be signed in to change notification settings - Fork 239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a clang-specific impl->projection conversion operator #1274
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good - thanks!
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
facepalm
In Clang, the conversion from
producer_convert
toconst producer_ref<I>
does not allow for the move-initialization of
I
viaI(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