[clang] c58bc24 - [Clang] Only compare template params of potential overload after checking their decl context (#78139)

via cfe-commits cfe-commits at lists.llvm.org
Mon Jan 15 15:56:46 PST 2024


Author: cor3ntin
Date: 2024-01-16T00:56:42+01:00
New Revision: c58bc24fcf678c55b0bf522be89eff070507a005

URL: https://github.com/llvm/llvm-project/commit/c58bc24fcf678c55b0bf522be89eff070507a005
DIFF: https://github.com/llvm/llvm-project/commit/c58bc24fcf678c55b0bf522be89eff070507a005.diff

LOG: [Clang] Only compare template params of potential overload after checking their decl context (#78139)

Fixes a regression from 69066ab3 in which we compared the template lists
of potential overloads before checkings their declaration contexts.

This would cause a crash when doing constraint substitution as part of
that template check, because we would try to refer to not yet
instantiated entities (the underlying cause is unclear).

This patch reorders (again) when we look at template parameter so we
don't do it when checkings friends in different lexical contexts.

Fixes #77953
Fixes #78101

Added: 
    clang/test/Modules/GH77953.cpp

Modified: 
    clang/lib/Sema/SemaOverload.cpp
    clang/test/CXX/over/over.load/p2-0x.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 31111222e043787..37c62b306b3cd3f 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1259,6 +1259,40 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
   if ((OldTemplate == nullptr) != (NewTemplate == nullptr))
     return true;
 
+  // Is the function New an overload of the function Old?
+  QualType OldQType = SemaRef.Context.getCanonicalType(Old->getType());
+  QualType NewQType = SemaRef.Context.getCanonicalType(New->getType());
+
+  // Compare the signatures (C++ 1.3.10) of the two functions to
+  // determine whether they are overloads. If we find any mismatch
+  // in the signature, they are overloads.
+
+  // If either of these functions is a K&R-style function (no
+  // prototype), then we consider them to have matching signatures.
+  if (isa<FunctionNoProtoType>(OldQType.getTypePtr()) ||
+      isa<FunctionNoProtoType>(NewQType.getTypePtr()))
+    return false;
+
+  const auto *OldType = cast<FunctionProtoType>(OldQType);
+  const auto *NewType = cast<FunctionProtoType>(NewQType);
+
+  // The signature of a function includes the types of its
+  // parameters (C++ 1.3.10), which includes the presence or absence
+  // of the ellipsis; see C++ DR 357).
+  if (OldQType != NewQType && OldType->isVariadic() != NewType->isVariadic())
+    return true;
+
+  // For member-like friends, the enclosing class is part of the signature.
+  if ((New->isMemberLikeConstrainedFriend() ||
+       Old->isMemberLikeConstrainedFriend()) &&
+      !New->getLexicalDeclContext()->Equals(Old->getLexicalDeclContext()))
+    return true;
+
+  // Compare the parameter lists.
+  // This can only be done once we have establish that friend functions
+  // inhabit the same context, otherwise we might tried to instantiate
+  // references to non-instantiated entities during constraint substitution.
+  // GH78101.
   if (NewTemplate) {
     // C++ [temp.over.link]p4:
     //   The signature of a function template consists of its function
@@ -1296,34 +1330,6 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
       return true;
   }
 
-  // Is the function New an overload of the function Old?
-  QualType OldQType = SemaRef.Context.getCanonicalType(Old->getType());
-  QualType NewQType = SemaRef.Context.getCanonicalType(New->getType());
-
-  // Compare the signatures (C++ 1.3.10) of the two functions to
-  // determine whether they are overloads. If we find any mismatch
-  // in the signature, they are overloads.
-
-  // If either of these functions is a K&R-style function (no
-  // prototype), then we consider them to have matching signatures.
-  if (isa<FunctionNoProtoType>(OldQType.getTypePtr()) ||
-      isa<FunctionNoProtoType>(NewQType.getTypePtr()))
-    return false;
-
-  const FunctionProtoType *OldType = cast<FunctionProtoType>(OldQType);
-  const FunctionProtoType *NewType = cast<FunctionProtoType>(NewQType);
-
-  // The signature of a function includes the types of its
-  // parameters (C++ 1.3.10), which includes the presence or absence
-  // of the ellipsis; see C++ DR 357).
-  if (OldQType != NewQType && OldType->isVariadic() != NewType->isVariadic())
-    return true;
-
-  // For member-like friends, the enclosing class is part of the signature.
-  if ((New->isMemberLikeConstrainedFriend() ||
-       Old->isMemberLikeConstrainedFriend()) &&
-      !New->getLexicalDeclContext()->Equals(Old->getLexicalDeclContext()))
-    return true;
   const auto *OldMethod = dyn_cast<CXXMethodDecl>(Old);
   const auto *NewMethod = dyn_cast<CXXMethodDecl>(New);
 

diff  --git a/clang/test/CXX/over/over.load/p2-0x.cpp b/clang/test/CXX/over/over.load/p2-0x.cpp
index 8fd9a1ce1e87ab4..94185963ff5c751 100644
--- a/clang/test/CXX/over/over.load/p2-0x.cpp
+++ b/clang/test/CXX/over/over.load/p2-0x.cpp
@@ -55,4 +55,31 @@ static_assert(not test<type<2>&>);
 static_assert(test<type<2>&&>);
 
 }
+
+namespace GH78101 {
+
+template<typename T, typename U, int i>
+concept True = true;
+
+template<typename T, int I>
+struct Template {
+    static constexpr int i = I;
+    friend constexpr auto operator+(True<T, i> auto f) {
+        return i;
+    }
+};
+
+template<int I>
+struct Template<float, I> {
+    static constexpr int i = I;
+    friend constexpr auto operator+(True<float, i> auto f) {
+        return i;
+    }
+};
+
+Template<void, 4> f{};
+static_assert(+Template<float, 5>{} == 5);
+
+}
+
 #endif

diff  --git a/clang/test/Modules/GH77953.cpp b/clang/test/Modules/GH77953.cpp
new file mode 100644
index 000000000000000..aaca8153c3d2121
--- /dev/null
+++ b/clang/test/Modules/GH77953.cpp
@@ -0,0 +1,28 @@
+// From https://github.com/llvm/llvm-project/issues/77953
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/a.cppm -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 -fmodule-file=a=%t/a.pcm %t/b.cppm
+
+//--- a.cppm
+export module a;
+
+template<typename, typename>
+concept c = true;
+
+export template<typename... Ts>
+struct a {
+	template<typename... Us> requires(... and c<Ts, Us>)
+	friend bool operator==(a, a<Us...>) {
+		return true;
+	}
+};
+
+template struct a<>;
+
+//--- b.cppm
+import a;
+
+template struct a<int>;


        


More information about the cfe-commits mailing list