[clang] [Clang] prevent recovery call expression from proceeding with explicit attributes and undeclared templates (PR #107786)

Oleksandr T. via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 27 05:20:41 PDT 2024


https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/107786

>From e9948a1004cc2b486a0422d83e88392754e9f7e9 Mon Sep 17 00:00:00 2001
From: Oleksandr T <oleksandr.tarasiuk at outlook.com>
Date: Mon, 23 Sep 2024 17:17:30 +0300
Subject: [PATCH 1/2] [Clang] prevent recovery call expression from proceeding
 with explicit attributes and undeclared templates

---
 clang/docs/ReleaseNotes.rst                   |  2 ++
 clang/include/clang/Sema/Sema.h               |  4 ++-
 clang/lib/Sema/SemaExpr.cpp                   | 12 +++++--
 clang/lib/Sema/SemaOverload.cpp               |  2 +-
 .../SemaTemplate/recovery-crash-cxx20.cpp     | 32 +++++++++++++++++++
 5 files changed, 48 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/SemaTemplate/recovery-crash-cxx20.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b47e06cb0c5d68..110f75d739c072 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -424,6 +424,8 @@ Bug Fixes to C++ Support
 - Fixed an assertion failure in debug mode, and potential crashes in release mode, when
   diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter.
 - Fixed an assertion failure by adjusting integral to boolean vector conversions (#GH108326)
+- Fixed an assertion failure when invoking recovery call expressions with explicit attributes
+  and undeclared templates. (#GH107047, #GH49093)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index e1c3a99cfa167e..b2eefdbd1c56d1 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6753,7 +6753,9 @@ class Sema final : public SemaBase {
   ///
   /// Return \c true if the error is unrecoverable, or \c false if the caller
   /// should attempt to recover using these lookup results.
-  bool DiagnoseDependentMemberLookup(const LookupResult &R);
+  bool DiagnoseDependentMemberLookup(
+      const LookupResult &R,
+      TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr);
 
   /// Diagnose an empty lookup.
   ///
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 66df9c969256a2..a5770ae8848517 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2385,7 +2385,15 @@ static void emitEmptyLookupTypoDiagnostic(
                          SemaRef.PDiag(NoteID));
 }
 
-bool Sema::DiagnoseDependentMemberLookup(const LookupResult &R) {
+bool Sema::DiagnoseDependentMemberLookup(
+    const LookupResult &R, TemplateArgumentListInfo *ExplicitTemplateArgs) {
+  auto IsTemplated = [](NamedDecl *D) { return D->isTemplated(); };
+  if (ExplicitTemplateArgs && !llvm::all_of(R, IsTemplated)) {
+    Diag(R.getNameLoc(), diag::err_non_template_in_template_id)
+        << R.getLookupName();
+    return true;
+  }
+
   // During a default argument instantiation the CurContext points
   // to a CXXMethodDecl; but we can't apply a this-> fixit inside a
   // function parameter list, hence add an explicit check.
@@ -2487,7 +2495,7 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
           R.resolveKind();
         }
 
-        return DiagnoseDependentMemberLookup(R);
+        return DiagnoseDependentMemberLookup(R, ExplicitTemplateArgs);
       }
 
       R.clear();
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index d304f322aced64..fe9b16e1890c16 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -13860,7 +13860,7 @@ BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
     // enclosing class.
     // FIXME: We should also explain why the candidates found by name lookup
     // were not viable.
-    if (SemaRef.DiagnoseDependentMemberLookup(R))
+    if (SemaRef.DiagnoseDependentMemberLookup(R, ExplicitTemplateArgs))
       return ExprError();
   } else {
     // We had viable candidates and couldn't recover; let the caller diagnose
diff --git a/clang/test/SemaTemplate/recovery-crash-cxx20.cpp b/clang/test/SemaTemplate/recovery-crash-cxx20.cpp
new file mode 100644
index 00000000000000..3651b986dfd989
--- /dev/null
+++ b/clang/test/SemaTemplate/recovery-crash-cxx20.cpp
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
+
+namespace GH49093 {
+  class B {
+  public:
+    static int a() { return 0; }
+    decltype(a< 0 >(0)) test; // expected-error {{'a' does not name a template but is followed by template arguments}}
+  };
+
+  struct C {
+      static int a() { return 0; }
+      decltype(a < 0 > (0)) test; // expected-error {{'a' does not name a template but is followed by template arguments}}
+  };
+
+  void test_is_bool(bool t) {}
+  void test_is_bool(int t) {}
+
+  int main() {
+    B b;
+    test_is_bool(b.test);
+
+    C c;
+    test_is_bool(c.test);
+  }
+}
+
+namespace GH107047 {
+  struct A {
+    static constexpr auto test() { return 1; }
+    static constexpr int s = test< 1 >(); // expected-error {{'test' does not name a template but is followed by template arguments}}
+  };
+}

>From b9a9758f7f613ffc88cd10da16515558493c27c4 Mon Sep 17 00:00:00 2001
From: Oleksandr T <oleksandr.tarasiuk at outlook.com>
Date: Fri, 27 Sep 2024 15:20:00 +0300
Subject: [PATCH 2/2] use template lookup with typo correction for calls with
 explicit arguments

---
 clang/include/clang/Sema/Sema.h               |  4 +---
 clang/lib/Sema/SemaExpr.cpp                   | 22 +++++++++----------
 clang/lib/Sema/SemaOverload.cpp               |  2 +-
 .../SemaTemplate/recovery-crash-cxx20.cpp     | 12 +++++-----
 4 files changed, 19 insertions(+), 21 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index b2eefdbd1c56d1..e1c3a99cfa167e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6753,9 +6753,7 @@ class Sema final : public SemaBase {
   ///
   /// Return \c true if the error is unrecoverable, or \c false if the caller
   /// should attempt to recover using these lookup results.
-  bool DiagnoseDependentMemberLookup(
-      const LookupResult &R,
-      TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr);
+  bool DiagnoseDependentMemberLookup(const LookupResult &R);
 
   /// Diagnose an empty lookup.
   ///
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index a5770ae8848517..271a3534ffb071 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2385,15 +2385,7 @@ static void emitEmptyLookupTypoDiagnostic(
                          SemaRef.PDiag(NoteID));
 }
 
-bool Sema::DiagnoseDependentMemberLookup(
-    const LookupResult &R, TemplateArgumentListInfo *ExplicitTemplateArgs) {
-  auto IsTemplated = [](NamedDecl *D) { return D->isTemplated(); };
-  if (ExplicitTemplateArgs && !llvm::all_of(R, IsTemplated)) {
-    Diag(R.getNameLoc(), diag::err_non_template_in_template_id)
-        << R.getLookupName();
-    return true;
-  }
-
+bool Sema::DiagnoseDependentMemberLookup(const LookupResult &R) {
   // During a default argument instantiation the CurContext points
   // to a CXXMethodDecl; but we can't apply a this-> fixit inside a
   // function parameter list, hence add an explicit check.
@@ -2476,7 +2468,15 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
       LookupCtx ? LookupCtx : (SS.isEmpty() ? CurContext : nullptr);
   while (DC) {
     if (isa<CXXRecordDecl>(DC)) {
-      LookupQualifiedName(R, DC);
+      if (ExplicitTemplateArgs) {
+        if (LookupTemplateName(
+                R, S, SS, Context.getRecordType(cast<CXXRecordDecl>(DC)),
+                /*EnteringContext*/ false, TemplateNameIsRequired,
+                /*RequiredTemplateKind*/ nullptr, /*AllowTypoCorrection*/ true))
+          return true;
+      } else {
+        LookupQualifiedName(R, DC);
+      }
 
       if (!R.empty()) {
         // Don't give errors about ambiguities in this lookup.
@@ -2495,7 +2495,7 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
           R.resolveKind();
         }
 
-        return DiagnoseDependentMemberLookup(R, ExplicitTemplateArgs);
+        return DiagnoseDependentMemberLookup(R);
       }
 
       R.clear();
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index db7373e9de0c9c..0c1e054f7c30a4 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -13837,7 +13837,7 @@ BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
     // enclosing class.
     // FIXME: We should also explain why the candidates found by name lookup
     // were not viable.
-    if (SemaRef.DiagnoseDependentMemberLookup(R, ExplicitTemplateArgs))
+    if (SemaRef.DiagnoseDependentMemberLookup(R))
       return ExprError();
   } else {
     // We had viable candidates and couldn't recover; let the caller diagnose
diff --git a/clang/test/SemaTemplate/recovery-crash-cxx20.cpp b/clang/test/SemaTemplate/recovery-crash-cxx20.cpp
index 3651b986dfd989..e092ab802e459e 100644
--- a/clang/test/SemaTemplate/recovery-crash-cxx20.cpp
+++ b/clang/test/SemaTemplate/recovery-crash-cxx20.cpp
@@ -3,13 +3,13 @@
 namespace GH49093 {
   class B {
   public:
-    static int a() { return 0; }
-    decltype(a< 0 >(0)) test; // expected-error {{'a' does not name a template but is followed by template arguments}}
+    static int a() { return 0; } // expected-note {{declared as a non-template here}}
+    decltype(a< 0 >(0)) test;    // expected-error {{'a' does not refer to a template}}
   };
 
   struct C {
-      static int a() { return 0; }
-      decltype(a < 0 > (0)) test; // expected-error {{'a' does not name a template but is followed by template arguments}}
+      static int a() { return 0; } // expected-note {{declared as a non-template here}}
+      decltype(a < 0 > (0)) test;  // expected-error {{'a' does not refer to a template}}
   };
 
   void test_is_bool(bool t) {}
@@ -26,7 +26,7 @@ namespace GH49093 {
 
 namespace GH107047 {
   struct A {
-    static constexpr auto test() { return 1; }
-    static constexpr int s = test< 1 >(); // expected-error {{'test' does not name a template but is followed by template arguments}}
+    static constexpr auto test() { return 1; } // expected-note {{declared as a non-template here}}
+    static constexpr int s = test< 1 >();      // expected-error {{'test' does not refer to a template}}
   };
 }



More information about the cfe-commits mailing list