Skip to content

[clang-tidy] Portability Template Virtual Member Function Check #110099

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

Merged
merged 7 commits into from
Oct 10, 2024

Conversation

isuckatcs
Copy link
Member

Per C++ [temp.inst#11]: "It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated."

In the following snippets the virtual member function is not instantiated by gcc and clang, but it is instantiated by MSVC, so while the snippet is accepted by the former compilers, it is rejected by the latter. (See godbolt)

template<typename T>
struct CrossPlatformError {
    virtual ~CrossPlatformError() = default;

    static void used() {}

    virtual void unused() {
        T MSVCError = this;
    };
};

int main() {
    CrossPlatformError<int>::used();
    return 0;
}

Cross-platform projects that need to support MSVC on Windows might see compiler errors because certain virtual member functions are instantiated, which are not instantiated by other compilers on other platforms. This check highlights such virtual member functions.

@llvmbot
Copy link
Member

llvmbot commented Sep 26, 2024

@llvm/pr-subscribers-clang-tidy

Author: None (isuckatcs)

Changes

Per C++ [temp.inst#<!-- -->11]: "It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated."

In the following snippets the virtual member function is not instantiated by gcc and clang, but it is instantiated by MSVC, so while the snippet is accepted by the former compilers, it is rejected by the latter. (See godbolt)

template&lt;typename T&gt;
struct CrossPlatformError {
    virtual ~CrossPlatformError() = default;

    static void used() {}

    virtual void unused() {
        T MSVCError = this;
    };
};

int main() {
    CrossPlatformError&lt;int&gt;::used();
    return 0;
}

Cross-platform projects that need to support MSVC on Windows might see compiler errors because certain virtual member functions are instantiated, which are not instantiated by other compilers on other platforms. This check highlights such virtual member functions.


Full diff: https://github.com/llvm/llvm-project/pull/110099.diff

8 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/portability/CMakeLists.txt (+1)
  • (modified) clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp (+3)
  • (added) clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp (+49)
  • (added) clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h (+36)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+7)
  • (modified) clang-tools-extra/docs/clang-tidy/checks/list.rst (+1)
  • (added) clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst (+33)
  • (added) clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp (+134)
diff --git a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
index 01a86d686daa76..df08cf2c8e292c 100644
--- a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
@@ -9,6 +9,7 @@ add_clang_library(clangTidyPortabilityModule
   RestrictSystemIncludesCheck.cpp
   SIMDIntrinsicsCheck.cpp
   StdAllocatorConstCheck.cpp
+  TemplateVirtualMemberFunctionCheck.cpp
 
   LINK_LIBS
   clangTidy
diff --git a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
index b3759a754587d7..316b98b46cf3f2 100644
--- a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
@@ -12,6 +12,7 @@
 #include "RestrictSystemIncludesCheck.h"
 #include "SIMDIntrinsicsCheck.h"
 #include "StdAllocatorConstCheck.h"
+#include "TemplateVirtualMemberFunctionCheck.h"
 
 namespace clang::tidy {
 namespace portability {
@@ -25,6 +26,8 @@ class PortabilityModule : public ClangTidyModule {
         "portability-simd-intrinsics");
     CheckFactories.registerCheck<StdAllocatorConstCheck>(
         "portability-std-allocator-const");
+    CheckFactories.registerCheck<TemplateVirtualMemberFunctionCheck>(
+        "portability-template-virtual-member-function");
   }
 };
 
diff --git a/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp
new file mode 100644
index 00000000000000..6b7b50798798fa
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp
@@ -0,0 +1,49 @@
+//===--- TemplateVirtualMemberFunctionCheck.cpp - clang-tidy
+//-------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TemplateVirtualMemberFunctionCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::portability {
+
+void TemplateVirtualMemberFunctionCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(classTemplateSpecializationDecl().bind("specialization"),
+                     this);
+}
+
+void TemplateVirtualMemberFunctionCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *MatchedDecl =
+      Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("specialization");
+
+  if (MatchedDecl->isExplicitSpecialization())
+    return;
+
+  for (auto &&Method : MatchedDecl->methods()) {
+    if (!Method->isVirtual())
+      continue;
+
+    if (const auto *Dtor = llvm::dyn_cast<CXXDestructorDecl>(Method);
+        Dtor && Dtor->isDefaulted())
+      continue;
+
+    if (!Method->isUsed()) {
+      diag(Method->getLocation(),
+           "unspecified virtual member function instantiation; the virtual "
+           "member function is not instantiated but it might be with a "
+           "different compiler");
+      diag(MatchedDecl->getPointOfInstantiation(), "template instantiated here",
+           DiagnosticIDs::Note);
+    }
+  }
+}
+
+} // namespace clang::tidy::portability
diff --git a/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h
new file mode 100644
index 00000000000000..9d7918dbdba612
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h
@@ -0,0 +1,36 @@
+//===--- TemplateVirtualMemberFunctionCheck.h - clang-tidy ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::portability {
+
+/// Detects C++ [temp.inst#11]: "It is unspecified whether or not an
+/// implementation implicitly instantiates a virtual member function of a class
+/// template if the virtual member function would not otherwise be
+/// instantiated."
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/portability/template-virtual-member-function.html
+class TemplateVirtualMemberFunctionCheck : public ClangTidyCheck {
+public:
+  TemplateVirtualMemberFunctionCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus;
+  }
+};
+
+} // namespace clang::tidy::portability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 8f7b0b5333f3a1..81e83c66273ed0 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -103,6 +103,13 @@ Improvements to clang-tidy
 New checks
 ^^^^^^^^^^
 
+- New :doc:`portability-template-virtual-member-function
+  <clang-tidy/checks/portability/template-virtual-member-function>` check.
+
+  Detects C++ [temp.inst#11]: "It is unspecified whether or not an implementation 
+  implicitly instantiates a virtual member function of a class template if the 
+  virtual member function would not otherwise be instantiated."
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 1909d7b8d8e246..b7fc78de79eadd 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -346,6 +346,7 @@ Clang-Tidy Checks
    :doc:`portability-restrict-system-includes <portability/restrict-system-includes>`, "Yes"
    :doc:`portability-simd-intrinsics <portability/simd-intrinsics>`,
    :doc:`portability-std-allocator-const <portability/std-allocator-const>`,
+   :doc:`portability-template-virtual-member-function <portability/template-virtual-member-function>`,
    :doc:`readability-avoid-const-params-in-decls <readability/avoid-const-params-in-decls>`, "Yes"
    :doc:`readability-avoid-nested-conditional-operator <readability/avoid-nested-conditional-operator>`,
    :doc:`readability-avoid-return-with-void-value <readability/avoid-return-with-void-value>`, "Yes"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst b/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst
new file mode 100644
index 00000000000000..3e22d967734fa1
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst
@@ -0,0 +1,33 @@
+.. title:: clang-tidy - portability-template-virtual-member-function
+
+portability-template-virtual-member-function
+============================================
+Per C++ ``[temp.inst#11]``: "It is unspecified whether or not an implementation 
+implicitly instantiates a virtual member function of a class template if the virtual 
+member function would not otherwise be instantiated."
+
+In the following snippets the virtual member function is not instantiated by gcc and clang,
+but it is instantiated by MSVC, so while the snippet is accepted by the former compilers,
+it is rejected by the latter.
+
+.. code:: c++
+
+    template<typename T>
+    struct CrossPlatformError {
+        virtual ~CrossPlatformError() = default;
+        
+        static void used() {}
+
+        virtual void unused() {
+            T MSVCError = this;
+        };
+    };
+
+    int main() {
+        CrossPlatformError<int>::used();
+        return 0;
+    }
+
+Cross-platform projects that need to support MSVC on Windows might see compiler errors
+because certain virtual member functions are instantiated, which are not instantiated 
+by other compilers on other platforms. This check highlights such virtual member functions.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp b/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp
new file mode 100644
index 00000000000000..59503e46b9bf3f
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp
@@ -0,0 +1,134 @@
+// RUN: %check_clang_tidy %s portability-template-virtual-member-function %t
+namespace UninstantiatedVirtualMember {
+template<typename T>
+struct CrossPlatformError {
+    virtual ~CrossPlatformError() = default;
+
+    static void used() {}
+
+    // CHECK-MESSAGES: [[#@LINE+1]]:18: warning: unspecified virtual member function instantiation
+    virtual void unused() {
+        T MSVCError = this;
+    };
+};
+
+int main() {
+    // CHECK-MESSAGES: [[#@LINE+1]]:5: note: template instantiated here
+    CrossPlatformError<int>::used();
+    return 0;
+}
+} // namespace UninstantiatedVirtualMember
+
+namespace UninstantiatedVirtualMembers {
+template<typename T>
+struct CrossPlatformError {
+    virtual ~CrossPlatformError() = default;
+
+    static void used() {}
+
+    // CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
+    // CHECK-MESSAGES: [[#@LINE+13]]:5: note: template instantiated here
+    virtual void unused() {
+        T MSVCError = this;
+    };
+
+    // CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
+    // CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
+    virtual void unused2() {
+        T MSVCError = this;
+    };
+};
+
+int main() {
+    CrossPlatformError<int>::used();
+    return 0;
+}
+} // namespace UninstantiatedVirtualMembers
+
+namespace UninstantiatedVirtualDestructor {
+template<typename T>
+struct CrossPlatformError {
+    // CHECK-MESSAGES: [[#@LINE+2]]:13: warning: unspecified virtual member function instantiation
+    // CHECK-MESSAGES: [[#@LINE+9]]:5: note: template instantiated here
+    virtual ~CrossPlatformError() {
+        T MSVCError = this;
+    };
+
+    static void used() {}
+};
+
+int main() {
+    CrossPlatformError<int>::used();
+    return 0;
+}
+} // namespace UninstantiatedVirtualDestructor
+
+namespace MultipleImplicitInstantiations {
+template<typename T>
+struct CrossPlatformError {
+    virtual ~CrossPlatformError() = default;
+
+    static void used() {}
+
+    // CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
+    // CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
+    virtual void unused() {
+        T MSVCError = this;
+    };
+};
+
+int main() {
+    CrossPlatformError<int>::used();
+    CrossPlatformError<float>::used();
+    CrossPlatformError<long>::used();
+    return 0;
+}
+} // namespace MultipleImplicitInstantiations
+
+namespace SomeImplicitInstantiationError {
+template <typename T> struct CrossPlatformError {
+    virtual ~CrossPlatformError() = default;
+
+    static void used() {}
+
+    // CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
+    // CHECK-MESSAGES: [[#@LINE+5]]:5: note: template instantiated here
+    virtual void unused(){};
+};
+
+int main() {
+    CrossPlatformError<int>::used();
+    CrossPlatformError<float> NoError;
+    return 0;
+}
+} // namespace SomeImplicitInstantiationError
+
+namespace InstantiatedVirtualMemberFunctions {
+template<typename T>
+struct NoError {
+    virtual ~NoError() {};
+    virtual void unused() {};
+    virtual void unused2() {};
+    virtual void unused3() {};
+};
+
+int main() {
+    NoError<int> Ne;
+    return 0;
+}
+} // namespace InstantiatedVirtualMemberFunctions
+
+namespace UninstantiatedNonVirtualMemberFunctions {
+template<typename T>
+struct NoError {
+    static void used() {};
+    void unused() {};
+    void unused2() {};
+    void unused3() {};
+};
+
+int main() {
+    NoError<int>::used();
+    return 0;
+}
+} // namespace UninstantiatedNonVirtualMemberFunctions

@llvmbot
Copy link
Member

llvmbot commented Sep 26, 2024

@llvm/pr-subscribers-clang-tools-extra

Author: None (isuckatcs)

Changes

Per C++ [temp.inst#<!-- -->11]: "It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated."

In the following snippets the virtual member function is not instantiated by gcc and clang, but it is instantiated by MSVC, so while the snippet is accepted by the former compilers, it is rejected by the latter. (See godbolt)

template&lt;typename T&gt;
struct CrossPlatformError {
    virtual ~CrossPlatformError() = default;

    static void used() {}

    virtual void unused() {
        T MSVCError = this;
    };
};

int main() {
    CrossPlatformError&lt;int&gt;::used();
    return 0;
}

Cross-platform projects that need to support MSVC on Windows might see compiler errors because certain virtual member functions are instantiated, which are not instantiated by other compilers on other platforms. This check highlights such virtual member functions.


Full diff: https://github.com/llvm/llvm-project/pull/110099.diff

8 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/portability/CMakeLists.txt (+1)
  • (modified) clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp (+3)
  • (added) clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp (+49)
  • (added) clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h (+36)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+7)
  • (modified) clang-tools-extra/docs/clang-tidy/checks/list.rst (+1)
  • (added) clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst (+33)
  • (added) clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp (+134)
diff --git a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
index 01a86d686daa76..df08cf2c8e292c 100644
--- a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
@@ -9,6 +9,7 @@ add_clang_library(clangTidyPortabilityModule
   RestrictSystemIncludesCheck.cpp
   SIMDIntrinsicsCheck.cpp
   StdAllocatorConstCheck.cpp
+  TemplateVirtualMemberFunctionCheck.cpp
 
   LINK_LIBS
   clangTidy
diff --git a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
index b3759a754587d7..316b98b46cf3f2 100644
--- a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
@@ -12,6 +12,7 @@
 #include "RestrictSystemIncludesCheck.h"
 #include "SIMDIntrinsicsCheck.h"
 #include "StdAllocatorConstCheck.h"
+#include "TemplateVirtualMemberFunctionCheck.h"
 
 namespace clang::tidy {
 namespace portability {
@@ -25,6 +26,8 @@ class PortabilityModule : public ClangTidyModule {
         "portability-simd-intrinsics");
     CheckFactories.registerCheck<StdAllocatorConstCheck>(
         "portability-std-allocator-const");
+    CheckFactories.registerCheck<TemplateVirtualMemberFunctionCheck>(
+        "portability-template-virtual-member-function");
   }
 };
 
diff --git a/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp
new file mode 100644
index 00000000000000..6b7b50798798fa
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.cpp
@@ -0,0 +1,49 @@
+//===--- TemplateVirtualMemberFunctionCheck.cpp - clang-tidy
+//-------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TemplateVirtualMemberFunctionCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::portability {
+
+void TemplateVirtualMemberFunctionCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(classTemplateSpecializationDecl().bind("specialization"),
+                     this);
+}
+
+void TemplateVirtualMemberFunctionCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *MatchedDecl =
+      Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("specialization");
+
+  if (MatchedDecl->isExplicitSpecialization())
+    return;
+
+  for (auto &&Method : MatchedDecl->methods()) {
+    if (!Method->isVirtual())
+      continue;
+
+    if (const auto *Dtor = llvm::dyn_cast<CXXDestructorDecl>(Method);
+        Dtor && Dtor->isDefaulted())
+      continue;
+
+    if (!Method->isUsed()) {
+      diag(Method->getLocation(),
+           "unspecified virtual member function instantiation; the virtual "
+           "member function is not instantiated but it might be with a "
+           "different compiler");
+      diag(MatchedDecl->getPointOfInstantiation(), "template instantiated here",
+           DiagnosticIDs::Note);
+    }
+  }
+}
+
+} // namespace clang::tidy::portability
diff --git a/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h
new file mode 100644
index 00000000000000..9d7918dbdba612
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/portability/TemplateVirtualMemberFunctionCheck.h
@@ -0,0 +1,36 @@
+//===--- TemplateVirtualMemberFunctionCheck.h - clang-tidy ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::portability {
+
+/// Detects C++ [temp.inst#11]: "It is unspecified whether or not an
+/// implementation implicitly instantiates a virtual member function of a class
+/// template if the virtual member function would not otherwise be
+/// instantiated."
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/portability/template-virtual-member-function.html
+class TemplateVirtualMemberFunctionCheck : public ClangTidyCheck {
+public:
+  TemplateVirtualMemberFunctionCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus;
+  }
+};
+
+} // namespace clang::tidy::portability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_TEMPLATEVIRTUALMEMBERFUNCTIONCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 8f7b0b5333f3a1..81e83c66273ed0 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -103,6 +103,13 @@ Improvements to clang-tidy
 New checks
 ^^^^^^^^^^
 
+- New :doc:`portability-template-virtual-member-function
+  <clang-tidy/checks/portability/template-virtual-member-function>` check.
+
+  Detects C++ [temp.inst#11]: "It is unspecified whether or not an implementation 
+  implicitly instantiates a virtual member function of a class template if the 
+  virtual member function would not otherwise be instantiated."
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 1909d7b8d8e246..b7fc78de79eadd 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -346,6 +346,7 @@ Clang-Tidy Checks
    :doc:`portability-restrict-system-includes <portability/restrict-system-includes>`, "Yes"
    :doc:`portability-simd-intrinsics <portability/simd-intrinsics>`,
    :doc:`portability-std-allocator-const <portability/std-allocator-const>`,
+   :doc:`portability-template-virtual-member-function <portability/template-virtual-member-function>`,
    :doc:`readability-avoid-const-params-in-decls <readability/avoid-const-params-in-decls>`, "Yes"
    :doc:`readability-avoid-nested-conditional-operator <readability/avoid-nested-conditional-operator>`,
    :doc:`readability-avoid-return-with-void-value <readability/avoid-return-with-void-value>`, "Yes"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst b/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst
new file mode 100644
index 00000000000000..3e22d967734fa1
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/portability/template-virtual-member-function.rst
@@ -0,0 +1,33 @@
+.. title:: clang-tidy - portability-template-virtual-member-function
+
+portability-template-virtual-member-function
+============================================
+Per C++ ``[temp.inst#11]``: "It is unspecified whether or not an implementation 
+implicitly instantiates a virtual member function of a class template if the virtual 
+member function would not otherwise be instantiated."
+
+In the following snippets the virtual member function is not instantiated by gcc and clang,
+but it is instantiated by MSVC, so while the snippet is accepted by the former compilers,
+it is rejected by the latter.
+
+.. code:: c++
+
+    template<typename T>
+    struct CrossPlatformError {
+        virtual ~CrossPlatformError() = default;
+        
+        static void used() {}
+
+        virtual void unused() {
+            T MSVCError = this;
+        };
+    };
+
+    int main() {
+        CrossPlatformError<int>::used();
+        return 0;
+    }
+
+Cross-platform projects that need to support MSVC on Windows might see compiler errors
+because certain virtual member functions are instantiated, which are not instantiated 
+by other compilers on other platforms. This check highlights such virtual member functions.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp b/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp
new file mode 100644
index 00000000000000..59503e46b9bf3f
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/portability/template-virtual-member-function.cpp
@@ -0,0 +1,134 @@
+// RUN: %check_clang_tidy %s portability-template-virtual-member-function %t
+namespace UninstantiatedVirtualMember {
+template<typename T>
+struct CrossPlatformError {
+    virtual ~CrossPlatformError() = default;
+
+    static void used() {}
+
+    // CHECK-MESSAGES: [[#@LINE+1]]:18: warning: unspecified virtual member function instantiation
+    virtual void unused() {
+        T MSVCError = this;
+    };
+};
+
+int main() {
+    // CHECK-MESSAGES: [[#@LINE+1]]:5: note: template instantiated here
+    CrossPlatformError<int>::used();
+    return 0;
+}
+} // namespace UninstantiatedVirtualMember
+
+namespace UninstantiatedVirtualMembers {
+template<typename T>
+struct CrossPlatformError {
+    virtual ~CrossPlatformError() = default;
+
+    static void used() {}
+
+    // CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
+    // CHECK-MESSAGES: [[#@LINE+13]]:5: note: template instantiated here
+    virtual void unused() {
+        T MSVCError = this;
+    };
+
+    // CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
+    // CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
+    virtual void unused2() {
+        T MSVCError = this;
+    };
+};
+
+int main() {
+    CrossPlatformError<int>::used();
+    return 0;
+}
+} // namespace UninstantiatedVirtualMembers
+
+namespace UninstantiatedVirtualDestructor {
+template<typename T>
+struct CrossPlatformError {
+    // CHECK-MESSAGES: [[#@LINE+2]]:13: warning: unspecified virtual member function instantiation
+    // CHECK-MESSAGES: [[#@LINE+9]]:5: note: template instantiated here
+    virtual ~CrossPlatformError() {
+        T MSVCError = this;
+    };
+
+    static void used() {}
+};
+
+int main() {
+    CrossPlatformError<int>::used();
+    return 0;
+}
+} // namespace UninstantiatedVirtualDestructor
+
+namespace MultipleImplicitInstantiations {
+template<typename T>
+struct CrossPlatformError {
+    virtual ~CrossPlatformError() = default;
+
+    static void used() {}
+
+    // CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
+    // CHECK-MESSAGES: [[#@LINE+7]]:5: note: template instantiated here
+    virtual void unused() {
+        T MSVCError = this;
+    };
+};
+
+int main() {
+    CrossPlatformError<int>::used();
+    CrossPlatformError<float>::used();
+    CrossPlatformError<long>::used();
+    return 0;
+}
+} // namespace MultipleImplicitInstantiations
+
+namespace SomeImplicitInstantiationError {
+template <typename T> struct CrossPlatformError {
+    virtual ~CrossPlatformError() = default;
+
+    static void used() {}
+
+    // CHECK-MESSAGES: [[#@LINE+2]]:18: warning: unspecified virtual member function instantiation
+    // CHECK-MESSAGES: [[#@LINE+5]]:5: note: template instantiated here
+    virtual void unused(){};
+};
+
+int main() {
+    CrossPlatformError<int>::used();
+    CrossPlatformError<float> NoError;
+    return 0;
+}
+} // namespace SomeImplicitInstantiationError
+
+namespace InstantiatedVirtualMemberFunctions {
+template<typename T>
+struct NoError {
+    virtual ~NoError() {};
+    virtual void unused() {};
+    virtual void unused2() {};
+    virtual void unused3() {};
+};
+
+int main() {
+    NoError<int> Ne;
+    return 0;
+}
+} // namespace InstantiatedVirtualMemberFunctions
+
+namespace UninstantiatedNonVirtualMemberFunctions {
+template<typename T>
+struct NoError {
+    static void used() {};
+    void unused() {};
+    void unused2() {};
+    void unused3() {};
+};
+
+int main() {
+    NoError<int>::used();
+    return 0;
+}
+} // namespace UninstantiatedNonVirtualMemberFunctions

Copy link

⚠️ We detected that you are using a GitHub private e-mail address to contribute to the repo.
Please turn off Keep my email addresses private setting in your account.
See LLVM Discourse for more information.

@isuckatcs isuckatcs requested a review from 5chmidti September 27, 2024 00:49
Copy link
Contributor

@5chmidti 5chmidti left a comment

Choose a reason for hiding this comment

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

LGTM, but let's keep this open for others to review as well

@isuckatcs
Copy link
Member Author

@EugeneZelenko can you please take a look at the changes once again and let us know if you're happy with them?

@EugeneZelenko
Copy link
Contributor

@isuckatcs: I mostly check documentation, so will be good idea if somebody from active developers will take a look in addition to @5chmidti.

@isuckatcs
Copy link
Member Author

I see. Hopefully someone will take a look then and we can merge the patch.

@isuckatcs isuckatcs requested a review from NagyDonat October 10, 2024 00:30
Copy link
Contributor

@NagyDonat NagyDonat left a comment

Choose a reason for hiding this comment

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

Looks good to me as well.

@isuckatcs
Copy link
Member Author

Thank you for the review!

@isuckatcs isuckatcs merged commit 6d8e966 into llvm:main Oct 10, 2024
10 checks passed
DanielCChen pushed a commit to DanielCChen/llvm-project that referenced this pull request Oct 16, 2024
…#110099)

Introduced a new check that finds cases when an uninstantiated virtual member function in a template class causes cross-compiler incompatibility.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants