Skip to content
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

Improve Vtable implementations #1740

Merged
merged 10 commits into from
May 6, 2022
Merged

Conversation

rylev
Copy link
Contributor

@rylev rylev commented May 6, 2022

note: since codegen was run on this PR, it's easier to review commit by commit as code gen is always runs in a separate commit

This PR slightly simplifies vtable implementations while also making them a bit more type and memory safe:

  • Change IUnknownImpl::get_impl to return a reference to the actual implementation type which is not specified as an associated type. This avoids passing a void pointer around which prevents the compiler from doing some type and borrow checking.
  • Also change IUnknownImpl::get_impl to take a &self instead of &mut self. Com interfaces are non-exclusive references to a shared vtable so &mut self is just wrong and is UB.

This change is easiest to see when comparing COM vtable method implementations before and after this change:

Before

unsafe extern "system" fn Name<Identity: ::windows::core::IUnknownImpl, Impl: ILearningModelVariableDescriptorPreview_Impl, const OFFSET: isize>(this: *mut ::core::ffi::c_void, result__: *mut ::core::mem::ManuallyDrop<::windows::core::HSTRING>) -> ::windows::core::HRESULT {
  let this = (this as *mut ::windows::core::RawPtr).offset(OFFSET) as *mut Identity;
  let this = (*this).get_impl() as *mut Impl;
  match (*this).Name() {
  // ...

After

unsafe extern "system" fn Name<Identity: ::windows::core::IUnknownImpl<Impl = Impl>, Impl: ILearningModelVariableDescriptorPreview_Impl, const OFFSET: isize>(this: *mut ::core::ffi::c_void, result__: *mut ::core::mem::ManuallyDrop<::windows::core::HSTRING>) -> ::windows::core::HRESULT {
  let this = (this as *const *const ()).offset(OFFSET) as *const Identity;
  let this = (*this).get_impl();
  match this.Name() {
  // ...

One additional change I believe we can make is changing the this pointers to *const instead of *mut.

@rylev rylev requested review from eholk and kennykerr May 6, 2022 11:00
unsafe extern "system" fn #name<#(#constraints)* Identity: ::windows::core::IUnknownImpl, Impl: #impl_ident<#(#generics)*>, const OFFSET: isize> #vtbl_signature {
let this = (this as *mut ::windows::core::RawPtr).offset(OFFSET) as *mut Identity;
let this = (*this).get_impl() as *mut Impl;
unsafe extern "system" fn #name<#(#constraints)* Identity: ::windows::core::IUnknownImpl<Impl = Impl>, Impl: #impl_ident<#(#generics)*>, const OFFSET: isize> #vtbl_signature {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you help me understand what the IUnknownImpl<Impl = Impl> syntax means?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! So the IUnknownImpl trait now has an associated type with called Impl which is meant to be the implementation backing the COM object, and is what is returned fromIUnknownImpl::get_impl. You can see the definition of that here.

The Identity: IUnknownImpl<Impl = Impl> (yes, that's a lot of impl) syntax means the generic parameter Identity must implement the trait IUknownImpl with an associated type that is itself the generic parameter Impl. So the Impl = Impl says "the associated type of the IUnknownImpl trait is called Impl and we're saying it must be whatever the generic parameter Impl is.

This would probably be clearer if we used different names for the associated type and the generic parameter. Normally generic parameters are single letters like T, U, etc. so the syntax would end up looking like Identity: IUnknownImpl<Impl = I>.

Does that make sense? Any thoughts on how we can tweak names to make this clearer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a simplified version of what we're doing you can use to play around with how the syntax works.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it - thanks! Yes, that's a lot of impl, but then this isn't something that devs need to interact with so perhaps not that important. I'm far more interested in find a way to drop the Impl from names like IStringable_Impl that devs need to utter...

@rylev rylev merged commit e0cfbb3 into microsoft:master May 6, 2022
@rylev rylev deleted the iunknown-improvements2 branch May 6, 2022 15:10
@kennykerr
Copy link
Collaborator

Thanks Ryan, this certainly feels less hacky. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants