[clang] [Clang] Eagerly instantiate used constexpr function upon definition. (PR #73463)

via cfe-commits cfe-commits at lists.llvm.org
Sun Nov 26 13:57:21 PST 2023


https://github.com/cor3ntin created https://github.com/llvm/llvm-project/pull/73463

Despite CWG2497 not being resolved, it is reasonable to expect the following code to compile (and which is supported by other compilers)

```cpp
  template<typename T> constexpr T f();
  constexpr int g() { return f<int>(); } // #1
  template<typename T> constexpr T f() { return 123; }
  int k[g()];
  // #2
```

To that end, we eagerly instantiate all referenced specializations of constexpr functions when they are defined.

We maintain a map of (pattern, [instantiations]) independant of `PendingInstantiations` to avoid having to iterate that list after each function definition.

We should apply the same logic to constexpr variables, but I wanted to keep the PR small.

Fixes #73232

>From ea676509938a3260eaef0963099e2299a787e58b Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Sun, 26 Nov 2023 22:47:51 +0100
Subject: [PATCH] [Clang] Eagerly instantiate used constexpr function upon
 definition.

Despite CWG2497 not being resolved, it is reasonable to expect the following
code to compile (and which is supported by other compilers)

```cpp
  template<typename T> constexpr T f();
  constexpr int g() { return f<int>(); } // #1
  template<typename T> constexpr T f() { return 123; }
  int k[g()];
  // #2
```

To that end, we eagerly instantiate all referenced
specializations of constexpr functions when they are defined.

We maintain a map of (pattern, [instantiations]) independant of
`PendingInstantiations` to avoid having to iterate that list after
each function definition.

We should apply the same logic to constexpr variables,
but I wanted to keep the PR small.

Fixes #73232
---
 clang/docs/ReleaseNotes.rst                   |  5 ++++
 clang/include/clang/Sema/Sema.h               | 11 +++++++
 clang/lib/Sema/SemaDecl.cpp                   |  3 ++
 clang/lib/Sema/SemaExpr.cpp                   |  9 ++++--
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  | 17 +++++++++++
 .../SemaCXX/cxx2b-consteval-propagate.cpp     |  8 +++--
 .../instantiate-used-constexpr-function.cpp   | 30 +++++++++++++++++++
 7 files changed, 78 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/SemaTemplate/instantiate-used-constexpr-function.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 29a06d0f713f588..b34b66ec7dcabc2 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -762,6 +762,11 @@ Bug Fixes to C++ Support
   completes (except deduction guides). Fixes:
   (`#59827 <https://github.com/llvm/llvm-project/issues/59827>`_)
 
+- Clang now immediately instantiates function template specializations
+  at the end of the definition of the corresponding function template
+  when the definition appears after the first point of instantiation.
+  (`#73232 <https://github.com/llvm/llvm-project/issues/73232>`_)
+
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
 - Fixed an import failure of recursive friend class template.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f7c9d0e2e6412b7..091e1e3b4c1fd64 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -59,6 +59,7 @@
 #include "clang/Sema/TypoCorrection.h"
 #include "clang/Sema/Weak.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/SmallBitVector.h"
 #include "llvm/ADT/SmallPtrSet.h"
@@ -10078,6 +10079,12 @@ class Sema final {
   /// but have not yet been performed.
   std::deque<PendingImplicitInstantiation> PendingInstantiations;
 
+  /// Track constexpr functions referenced before they are (lexically) defined.
+  /// The key is the pattern, associated with a list of specialisations that
+  /// need to be instantiated when the pattern is defined.
+  llvm::DenseMap<NamedDecl *, SmallVector<NamedDecl *>>
+      PendingInstantiationsOfConstexprEntities;
+
   /// Queue of implicit template instantiations that cannot be performed
   /// eagerly.
   SmallVector<PendingImplicitInstantiation, 1> LateParsedInstantiations;
@@ -10396,6 +10403,10 @@ class Sema final {
                                      bool Recursive = false,
                                      bool DefinitionRequired = false,
                                      bool AtEndOfTU = false);
+
+  void InstantiateFunctionTemplateSpecializations(
+      SourceLocation PointOfInstantiation, FunctionDecl *Template);
+
   VarTemplateSpecializationDecl *BuildVarTemplateInstantiation(
       VarTemplateDecl *VarTemplate, VarDecl *FromVar,
       const TemplateArgumentList &TemplateArgList,
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 23dd8ae15c16583..1030ba0d21b1906 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16255,6 +16255,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
   if (FD && !FD->isDeleted())
     checkTypeSupport(FD->getType(), FD->getLocation(), FD);
 
+  if (FD && FD->isConstexpr() && FD->isTemplated())
+    InstantiateFunctionTemplateSpecializations(FD->getEndLoc(), FD);
+
   return dcl;
 }
 
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index fc39d6149c1cc65..37b0d0eed35845c 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -19047,12 +19047,17 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
               CodeSynthesisContexts.size())
             PendingLocalImplicitInstantiations.push_back(
                 std::make_pair(Func, PointOfInstantiation));
-          else if (Func->isConstexpr())
+          else if (Func->isConstexpr()) {
             // Do not defer instantiations of constexpr functions, to avoid the
             // expression evaluator needing to call back into Sema if it sees a
             // call to such a function.
             InstantiateFunctionDefinition(PointOfInstantiation, Func);
-          else {
+            if (!Func->isDefined()) {
+              PendingInstantiationsOfConstexprEntities
+                  [Func->getTemplateInstantiationPattern()->getCanonicalDecl()]
+                      .push_back(Func);
+            }
+          } else {
             Func->setInstantiationIsPending(true);
             PendingInstantiations.push_back(
                 std::make_pair(Func, PointOfInstantiation));
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 08f4ba00fc9f7de..9afbf139a1c2b88 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -6481,6 +6481,23 @@ void Sema::PerformPendingInstantiations(bool LocalOnly) {
     PendingInstantiations.swap(delayedPCHInstantiations);
 }
 
+// Instantiate all referenced specializations of the given function template
+// definition. This make sure that function template that are defined after the
+// point of instantiation of their used can be evaluated after they are defined.
+// see CWG2497.
+void Sema::InstantiateFunctionTemplateSpecializations(
+    SourceLocation PointOfInstantiation, FunctionDecl *Tpl) {
+  auto It =
+      PendingInstantiationsOfConstexprEntities.find(Tpl->getCanonicalDecl());
+  if (It == PendingInstantiationsOfConstexprEntities.end())
+    return;
+  for (NamedDecl *Fun : It->second) {
+    InstantiateFunctionDefinition(PointOfInstantiation,
+                                  cast<FunctionDecl>(Fun));
+  }
+  PendingInstantiationsOfConstexprEntities.erase(It);
+}
+
 void Sema::PerformDependentDiagnostics(const DeclContext *Pattern,
                        const MultiLevelTemplateArgumentList &TemplateArgs) {
   for (auto *DD : Pattern->ddiags()) {
diff --git a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
index 531a62622873357..a2b4039a31db778 100644
--- a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
+++ b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
@@ -105,13 +105,15 @@ template <typename T>
 constexpr int f(T t);
 
 auto a = &f<char>;
-auto b = &f<int>; // expected-error {{immediate function 'f<int>' used before it is defined}} \
-                  // expected-note {{in instantiation of function template specialization}}
+auto b = &f<int>; // expected-error {{immediate function 'f<int>' used before it is defined}}
 
 template <typename T>
 constexpr int f(T t) { // expected-note {{'f<int>' defined here}}
     return id(t); // expected-note {{'f<int>' is an immediate function because its body contains a call to a consteval function 'id' and that call is not a constant expression}}
-}
+} // expected-note {{in instantiation of function template specialization}}
+
+
+
 }
 
 namespace constructors {
diff --git a/clang/test/SemaTemplate/instantiate-used-constexpr-function.cpp b/clang/test/SemaTemplate/instantiate-used-constexpr-function.cpp
new file mode 100644
index 000000000000000..57353d681ac7708
--- /dev/null
+++ b/clang/test/SemaTemplate/instantiate-used-constexpr-function.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+namespace GH73232  {
+
+template <typename _CharT>
+struct basic_string {
+  constexpr void _M_construct();
+  constexpr basic_string() {
+    _M_construct();
+  }
+};
+
+basic_string<char> a;
+
+template <typename _CharT>
+constexpr void basic_string<_CharT>::_M_construct(){}
+constexpr basic_string<char> z{};
+
+template <typename T>
+constexpr void g(T);
+
+constexpr int f() { g(0); return 0; }
+
+template <typename T>
+constexpr void g(T) {}
+
+constexpr int z = f();
+
+}



More information about the cfe-commits mailing list