Skip to content

exclude_from_explicit_instantiation doesn't seem to exclude virtual methods, causing problems after 84216d1 #66909

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

Open
zmodem opened this issue Sep 20, 2023 · 2 comments
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc.

Comments

@zmodem
Copy link
Collaborator

zmodem commented Sep 20, 2023

Consider:

C:\src\llvm-project>cat \src\temp\a.cc
template <typename>
struct Fruit {
  __attribute((exclude_from_explicit_instantiation)) Fruit() {}

  virtual void __attribute((exclude_from_explicit_instantiation)) draw() {}
};

extern template struct Fruit<int>;

int main() {
  Fruit<int> f;
  return 0;
}

C:\src\llvm-project>build\bin\clang-cl \src\temp\a.cc -fuse-ld=lld
lld-link: error: undefined symbol: public: virtual void __cdecl Fruit<int>::draw(void)
>>> referenced by c:\src\temp\a-6ec007.obj:(const Fruit<int>::`vftable')
clang-cl: error: linker command failed with exit code 1 (use -v to see invocation)

It seems that despite the exclude_from_explicit_instantiation attribute on Fruit<>::draw, the explicit instantiation decl prevents the definition from being emitted when referenced by the vtable. Instead the definition of Fruit<>::draw will only get emitted once there's an explicit instantiation definition.

However, after 84216d1 that stopped working across DLL boundaries, since the method will no longer be dllimport/export.

It seems the exclusion from explicit instantiation is not working completely.

@zmodem zmodem added the clang:codegen IR generation bugs: mangling, exceptions, etc. label Sep 20, 2023
@llvmbot
Copy link
Member

llvmbot commented Sep 20, 2023

@llvm/issue-subscribers-clang-codegen

Consider:
C:\src\llvm-project&gt;cat \src\temp\a.cc
template &lt;typename&gt;
struct Fruit {
  __attribute((exclude_from_explicit_instantiation)) Fruit() {}

  virtual void __attribute((exclude_from_explicit_instantiation)) draw() {}
};

extern template struct Fruit&lt;int&gt;;

int main() {
  Fruit&lt;int&gt; f;
  return 0;
}

C:\src\llvm-project&gt;build\bin\clang-cl \src\temp\a.cc -fuse-ld=lld
lld-link: error: undefined symbol: public: virtual void __cdecl Fruit&lt;int&gt;::draw(void)
&gt;&gt;&gt; referenced by c:\src\temp\a-6ec007.obj:(const Fruit&lt;int&gt;::`vftable')
clang-cl: error: linker command failed with exit code 1 (use -v to see invocation)

It seems that despite the exclude_from_explicit_instantiation attribute on Fruit&lt;&gt;::draw, the explicit instantiation decl prevents the definition from being emitted when referenced by the vtable. Instead the definition of Fruit&lt;&gt;::draw will only get emitted once there's an explicit instantiation definition.

However, after 84216d1 that stopped working across DLL boundaries, since the method will no longer be dllimport/export.

It seems the exclusion from explicit instantiation is not working completely.

zmodem added a commit that referenced this issue Sep 20, 2023
…licit_instantiation members during explicit instantiation (#65961)"

This uncovered a problem with virtual methods and
exclude_from_explicit_instantiation, see
#66909

Reverting until that's fixed.

> This is a continuation of https://reviews.llvm.org/D155713
>
> Fixes #40363

This reverts commit 84216d1.
@zmodem
Copy link
Collaborator Author

zmodem commented Sep 21, 2023

I think the crux of the problem is in Sema::DefineUsedVTables() (which in our case gets called after InstantiateFunctionDefinition of Fruit<int>::Fruit):

      // If we have a class with no key function that is the subject
      // of an explicit instantiation declaration, suppress the
      // vtable; it will live with the explicit instantiation
      // definition.

That does not hold in our case: because the Fruit constructor is marked exclude_from_explicit_instantiation we will emit that constructor, and it will try to emit the vtable.

But because Sema::DefineUsedVTables() decided not to emit the vtable, it has not marked Fruit<int>::draw() as referenced as it normally would (MarkVirtualMembersReferenced), and so the vtable ends up referencing an undefined function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc.
Projects
None yet
Development

No branches or pull requests

2 participants