Skip to content

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

Open
@zmodem

Description

@zmodem
Collaborator

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.

Activity

llvmbot

llvmbot commented on Sep 20, 2023

@llvmbot
Member

@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

zmodem commented on Sep 21, 2023

@zmodem
CollaboratorAuthor

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    clang:codegenIR generation bugs: mangling, exceptions, etc.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @zmodem@llvmbot

        Issue actions

          `exclude_from_explicit_instantiation` doesn't seem to exclude virtual methods, causing problems after 84216d1 · Issue #66909 · llvm/llvm-project