[clang] [Clang][Sema] fix outline member function template with default align crash (PR #80288)

Qizhi Hu via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 1 05:09:07 PST 2024


https://github.com/jcsxky created https://github.com/llvm/llvm-project/pull/80288

Try to fix [issue](https://github.com/llvm/llvm-project/issues/68490 ) and some extented problem. Root cause of current issue is that error handling in instantiation of function parameter with default initialization on sizeof or align expression. When instance an out-of-line template member function, depth of `TemplateTypeParmDecl` in default initialization doesn't change while depth of other template parameter does and this will lead to some template parameter uninstanced. Also, sometime it will leader to wrong instantiation when it uses the template parameter of the template class.
Fix it by add template args of context. This will make MultiLevelTemplateArgumentList::getNumLevels matching the depth of template parameter. Testcase with some static_assert demonstrates the template parameter has been instanced correctly.
But, the default initialization of lambda expression compiles failed when only checking if the member function is out-of-line. We should check the `PrimaryFunctionTemplateDecl` of the funtion if it's out-of-line.

>From b9360998d234cccda4f632067cc9aa62f5593021 Mon Sep 17 00:00:00 2001
From: huqizhi <huqizhi at feysh.com>
Date: Thu, 1 Feb 2024 20:54:46 +0800
Subject: [PATCH] [Clang][Sema] fix outline member function template with
 default align crash

---
 clang/docs/ReleaseNotes.rst                   |   4 +
 clang/lib/Sema/SemaTemplateInstantiate.cpp    |  14 +-
 clang/test/SemaTemplate/default-parm-init.cpp | 190 ++++++++++++++++++
 3 files changed, 206 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/SemaTemplate/default-parm-init.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ec9e3ef07057f..263d64d610950 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -182,6 +182,10 @@ Bug Fixes to C++ Support
   and (`#79745 <https://github.com/llvm/llvm-project/issues/79745>`_)
 - Fix incorrect code generation caused by the object argument of ``static operator()`` and ``static operator[]`` calls not being evaluated.
   Fixes (`#67976 <https://github.com/llvm/llvm-project/issues/67976>`_)
+- Fix a crash when specializing an out-of-line member function with a default
+  parameter where we did an incorrect specialization of the initialization of
+  the default parameter.
+  Fixes (`#68490 <https://github.com/llvm/llvm-project/issues/68490>`_)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index e12186d7d82f8..c107702aeba1c 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -3049,6 +3049,7 @@ bool Sema::SubstDefaultArgument(
     //   default argument expression appears.
     ContextRAII SavedContext(*this, FD);
     std::unique_ptr<LocalInstantiationScope> LIS;
+    MultiLevelTemplateArgumentList NewTemplateArgs = TemplateArgs;
 
     if (ForCallExpr) {
       // When instantiating a default argument due to use in a call expression,
@@ -3061,11 +3062,20 @@ bool Sema::SubstDefaultArgument(
           /*ForDefinition*/ false);
       if (addInstantiatedParametersToScope(FD, PatternFD, *LIS, TemplateArgs))
         return true;
+      const FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
+      if (PrimaryTemplate && PrimaryTemplate->isOutOfLine()) {
+        TemplateArgumentList *CurrentTemplateArgumentList =
+            TemplateArgumentList::CreateCopy(getASTContext(),
+                                             TemplateArgs.getInnermost());
+        NewTemplateArgs = getTemplateInstantiationArgs(
+            FD, FD->getDeclContext(), /*Final=*/false,
+            CurrentTemplateArgumentList, /*RelativeToPrimary=*/true);
+      }
     }
 
     runWithSufficientStackSpace(Loc, [&] {
-      Result = SubstInitializer(PatternExpr, TemplateArgs,
-                                /*DirectInit*/false);
+      Result = SubstInitializer(PatternExpr, NewTemplateArgs,
+                                /*DirectInit*/ false);
     });
   }
   if (Result.isInvalid())
diff --git a/clang/test/SemaTemplate/default-parm-init.cpp b/clang/test/SemaTemplate/default-parm-init.cpp
new file mode 100644
index 0000000000000..6ad69c39d93dc
--- /dev/null
+++ b/clang/test/SemaTemplate/default-parm-init.cpp
@@ -0,0 +1,190 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+// expected-no-diagnostics
+
+namespace std {
+
+template<typename Signature> class function;
+
+template<typename R, typename... Args> class invoker_base {
+public: 
+  virtual ~invoker_base() { } 
+  virtual R invoke(Args...) = 0; 
+  virtual invoker_base* clone() = 0;
+};
+
+template<typename F, typename R, typename... Args> 
+class functor_invoker : public invoker_base<R, Args...> {
+public: 
+  explicit functor_invoker(const F& f) : f(f) { } 
+  R invoke(Args... args) { return f(args...); } 
+  functor_invoker* clone() { return new functor_invoker(f); }
+
+private:
+  F f;
+};
+
+template<typename R, typename... Args>
+class function<R (Args...)> {
+public: 
+  typedef R result_type;
+  function() : invoker (0) { }
+  function(const function& other) : invoker(0) { 
+    if (other.invoker)
+      invoker = other.invoker->clone();
+  }
+
+  template<typename F> function(const F& f) : invoker(0) {
+    invoker = new functor_invoker<F, R, Args...>(f);
+  }
+
+  ~function() { 
+    if (invoker)
+      delete invoker;
+  }
+
+  function& operator=(const function& other) { 
+    function(other).swap(*this); 
+    return *this;
+  }
+
+  template<typename F> 
+  function& operator=(const F& f) {
+    function(f).swap(*this); 
+    return *this;
+  }
+
+  void swap(function& other) { 
+    invoker_base<R, Args...>* tmp = invoker; 
+    invoker = other.invoker; 
+    other.invoker = tmp;
+  }
+
+  result_type operator()(Args... args) const { 
+    return invoker->invoke(args...);
+  }
+
+private: 
+  invoker_base<R, Args...>* invoker;
+};
+
+}
+
+template<typename TemplateParam>
+struct Problem{
+  template<typename FunctionTemplateParam>
+  constexpr int FuncAlign(int param = alignof(FunctionTemplateParam));
+
+  template<typename FunctionTemplateParam>
+  constexpr int FuncSizeof(int param = sizeof(FunctionTemplateParam));
+
+  template<typename FunctionTemplateParam>
+  constexpr int FuncAlign2(int param = alignof(TemplateParam));
+
+  template<typename FunctionTemplateParam>
+  constexpr int FuncSizeof2(int param = sizeof(TemplateParam));
+};
+
+template<typename TemplateParam>
+struct Problem<TemplateParam*>{
+  template<typename FunctionTemplateParam>
+  constexpr int FuncAlign(int param = alignof(FunctionTemplateParam));
+
+  template<typename FunctionTemplateParam>
+  constexpr int FuncSizeof(int param = sizeof(FunctionTemplateParam));
+
+  template<typename FunctionTemplateParam>
+  constexpr int FuncAlign2(int param = alignof(TemplateParam));
+
+  template<typename FunctionTemplateParam>
+  constexpr int FuncSizeof2(int param = sizeof(TemplateParam));
+};
+
+template<typename TemplateParam>
+template<typename FunctionTemplateParam>
+constexpr int Problem<TemplateParam*>::FuncAlign(int param) {
+	return 2U*param;
+}
+
+template<typename TemplateParam>
+template<typename FunctionTemplateParam>
+constexpr int Problem<TemplateParam*>::FuncSizeof(int param) {
+    return 2U*param;
+}
+
+template<typename TemplateParam>
+template<typename FunctionTemplateParam>
+constexpr int Problem<TemplateParam*>::FuncAlign2(int param) {
+	return 2U*param;
+}
+
+template<typename TemplateParam>
+template<typename FunctionTemplateParam>
+constexpr int Problem<TemplateParam*>::FuncSizeof2(int param) {
+	return 2U*param;
+}
+
+template <>
+template<typename FunctionTemplateParam>
+constexpr int Problem<int>::FuncAlign(int param) {
+	return param;
+}
+
+template <>
+template<typename FunctionTemplateParam>
+constexpr int Problem<int>::FuncSizeof(int param) {
+	return param;
+}
+
+template <>
+template<typename FunctionTemplateParam>
+constexpr int Problem<int>::FuncAlign2(int param) {
+	return param;
+}
+
+template <>
+template<typename FunctionTemplateParam>
+constexpr int Problem<int>::FuncSizeof2(int param) {
+	return param;
+}
+
+void foo(){
+    Problem<int> p = {};
+    static_assert(p.FuncAlign<char>() == alignof(char));
+    static_assert(p.FuncSizeof<char>() == sizeof(char));
+    static_assert(p.FuncAlign2<char>() == alignof(int));
+    static_assert(p.FuncSizeof2<char>() == sizeof(int));
+    Problem<short*> q = {};
+    static_assert(q.FuncAlign<char>() == 2U * alignof(char));
+    static_assert(q.FuncSizeof<char>() == 2U * sizeof(char));
+    static_assert(q.FuncAlign2<char>() == 2U *alignof(short));
+    static_assert(q.FuncSizeof2<char>() == 2U * sizeof(short));
+}
+
+template <typename T>
+class A {
+ public:
+  void run(
+    std::function<void(T&)> f1 = [](auto&&) {},
+    std::function<void(T&)> f2 = [](auto&&) {});
+ private:
+  class Helper {
+   public:
+    explicit Helper(std::function<void(T&)> f2) : f2_(f2) {}
+    std::function<void(T&)> f2_;
+  };
+};
+
+template <typename T>
+void A<T>::run(std::function<void(T&)> f1,
+               std::function<void(T&)> f2) {
+  Helper h(f2);
+}
+
+struct B {};
+
+int main() {
+    A<B> a;
+    a.run([&](auto& l) {});
+    return 0;
+}
\ No newline at end of file



More information about the cfe-commits mailing list