[clang] [Clang] Fix handling of overloads differing only by constraints and ref-qualifiers (PR #192018)
Corentin Jabot via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 14 07:43:03 PDT 2026
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/192018
>From 7124eafc69f02363f9289e6bb83d4d95dbf5138d Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 14 Apr 2026 09:57:57 +0200
Subject: [PATCH 1/2] [Clang] Fix handling of overloads differing only by
constraints and ref-qualifiers.
We ashould only error about inconsistent qualifiers
if the functions are actually overloads.
Fixes #120812
---
clang/docs/ReleaseNotes.rst | 1 +
clang/lib/Sema/SemaOverload.cpp | 26 +++++++++++++++++---------
clang/test/CXX/drs/cwg24xx.cpp | 11 +++++++++++
3 files changed, 29 insertions(+), 9 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3e2d287d1eb1f..9e4699d642332 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -434,6 +434,7 @@ Bug Fixes to C++ Support
- We no longer caches invalid variable specializations. (#GH132592)
- Fixed an incorrect template argument deduction when matching packs of template
template parameters when one of its parameters is also a pack. (#GH181166)
+- Clang no longer errors on overloads with different ref-qualifiers and constraints. (#GH120812)
- Fixed a crash when a default argument is passed to an explicit object parameter. (#GH176639)
- Fixed an alias template CTAD crash.
- Fixed a crash when diagnosing an invalid static member function with an explicit object parameter (#GH177741)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 11e771bc240f1..170699085ea11 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1521,6 +1521,12 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
return false;
};
+ // We look at the parameters first, as it is the common case.
+ // However we should not emit diagnostic before checking
+ // the overloads do not differ by constraints or other discriminant.
+ bool ShouldDiagnoseInconsistentRefQualifiers = false;
+ bool HaveInconsistentQualifiers = false;
+
if (OldMethod && OldMethod->isExplicitObjectMemberFunction())
OldParamsOffset++;
if (NewMethod && NewMethod->isExplicitObjectMemberFunction())
@@ -1557,17 +1563,20 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
}(OldMethod, NewMethod);
if (!HaveCorrespondingObjectParameters) {
- if (DiagnoseInconsistentRefQualifiers())
- return true;
+ ShouldDiagnoseInconsistentRefQualifiers = true;
// CWG2554
// and, if at least one is an explicit object member function, ignoring
// object parameters
if (!UseOverrideRules || (!NewMethod->isExplicitObjectMemberFunction() &&
!OldMethod->isExplicitObjectMemberFunction()))
- return true;
+ HaveInconsistentQualifiers = true;
}
}
+ if (NewMethod && OldMethod && OldMethod->isImplicitObjectMemberFunction() &&
+ NewMethod->isImplicitObjectMemberFunction())
+ ShouldDiagnoseInconsistentRefQualifiers = true;
+
if (!UseOverrideRules &&
New->getTemplateSpecializationKind() != TSK_ExplicitSpecialization) {
AssociatedConstraint NewRC = New->getTrailingRequiresClause(),
@@ -1582,12 +1591,6 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
return true;
}
- if (NewMethod && OldMethod && OldMethod->isImplicitObjectMemberFunction() &&
- NewMethod->isImplicitObjectMemberFunction()) {
- if (DiagnoseInconsistentRefQualifiers())
- return true;
- }
-
// Though pass_object_size is placed on parameters and takes an argument, we
// consider it to be a function-level modifier for the sake of function
// identity. Either the function has one or more parameters with
@@ -1612,6 +1615,11 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
return true;
}
+ if ((ShouldDiagnoseInconsistentRefQualifiers &&
+ DiagnoseInconsistentRefQualifiers()) ||
+ HaveInconsistentQualifiers)
+ return true;
+
// At this point, it is known that the two functions have the same signature.
if (SemaRef.getLangOpts().CUDA && ConsiderCudaAttrs) {
// Don't allow overloading of destructors. (In theory we could, but it
diff --git a/clang/test/CXX/drs/cwg24xx.cpp b/clang/test/CXX/drs/cwg24xx.cpp
index b27c6b823940c..0a6a05c125451 100644
--- a/clang/test/CXX/drs/cwg24xx.cpp
+++ b/clang/test/CXX/drs/cwg24xx.cpp
@@ -212,5 +212,16 @@ struct T : S {
virtual void k() &;
virtual void l() &&;
};
+#if __cplusplus >= 202002L
+
+// We should not error on inconsistent ref-qualifiers if the functions
+// have different constraints.
+template <unsigned R> struct type {
+ void func() const requires(R == 0);
+ void func() & requires(R == 1);
+};
+
+#endif
+
#endif
}
>From 213b47c591caebf61477a44d9a3930e47da55645 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 14 Apr 2026 16:38:16 +0200
Subject: [PATCH 2/2] [Clang] Add default arguments to the parameter mapping.
We were not adding default argument to the parameter mapping
as such, two constraint which only differed by the mapping of
a parameter only referenced in a default argument were considered
identical.
Fixes #188640
---
clang/docs/ReleaseNotes.rst | 2 +-
clang/include/clang/AST/ASTContext.h | 4 +++
clang/lib/AST/ASTContext.cpp | 4 +--
clang/lib/Sema/SemaConcept.cpp | 19 ++++++++++++
clang/test/SemaTemplate/concepts.cpp | 45 ++++++++++++++++++++++++++++
5 files changed, 71 insertions(+), 3 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9e4699d642332..e5040109edcf3 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -386,7 +386,7 @@ Bug Fixes in This Version
-------------------------
- Fixed atomic boolean compound assignment; the conversion back to atomic bool would be miscompiled. (#GH33210)
-
+- Correctly handle default template argument when establishing subsumption. (#GH188640)
- Fixed a failed assertion in the preprocessor when ``__has_embed`` parameters are missing parentheses. (#GH175088)
- Fix lifetime extension of temporaries in for-range-initializers in templates. (#GH165182)
- Fixed a preprocessor crash in ``__has_cpp_attribute`` on incomplete scoped attributes. (#GH178098)
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index ba1b58489c327..a412ef91ef16d 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3071,6 +3071,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
TemplateName getCanonicalTemplateName(TemplateName Name,
bool IgnoreDeduced = false) const;
+ /// Return the default argument of a template parameter, if one exists.
+ const TemplateArgument *
+ getDefaultTemplateArgumentOrNone(const NamedDecl *P) const;
+
/// Determine whether the given template names refer to the same
/// template.
bool hasSameTemplateName(const TemplateName &X, const TemplateName &Y,
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index ee7f823b014b2..f73f613a2d35a 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -7305,8 +7305,8 @@ ASTContext::getNameForTemplate(TemplateName Name,
llvm_unreachable("bad template name kind!");
}
-static const TemplateArgument *
-getDefaultTemplateArgumentOrNone(const NamedDecl *P) {
+const TemplateArgument *
+ASTContext::getDefaultTemplateArgumentOrNone(const NamedDecl *P) const {
auto handleParam = [](auto *TP) -> const TemplateArgument * {
if (!TP->hasDefaultArgument())
return nullptr;
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 3f04922a5647e..dfab7c919a303 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -2105,6 +2105,25 @@ void SubstituteParameterMappings::buildParameterMapping(
SemaRef.MarkUsedTemplateParameters(Args->arguments(),
/*Depth=*/0, OccurringIndices);
}
+
+ // If a parameter is only referenced in a default template argument,
+ // we need to add it to the mapping explicitly.
+ {
+ llvm::SmallVector<TemplateArgument> DefaultArgs;
+ for (unsigned I = TemplateParams->getMinRequiredArguments();
+ I < TemplateParams->size(); I++) {
+ const NamedDecl *Param = TemplateParams->getParam(I);
+ if (Param->isParameterPack())
+ break;
+ const TemplateArgument *Arg =
+ SemaRef.getASTContext().getDefaultTemplateArgumentOrNone(Param);
+ assert(Arg && "expected a default argument");
+ DefaultArgs.emplace_back(std::move(*Arg));
+ }
+ SemaRef.MarkUsedTemplateParameters(DefaultArgs, /*Depth=*/0,
+ OccurringIndices);
+ }
+
unsigned Size = OccurringIndices.count();
// When the constraint is independent of any template parameters,
// we build an empty mapping so that we can distinguish these cases
diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index ac80d16b4ccf8..c8b5cbac82f89 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -1835,3 +1835,48 @@ namespace GH191016 {
};
void test(){ S<int> s; }
}
+
+
+namespace GH188640 {
+
+namespace Ex1 {
+template <typename T> constexpr bool CC = true;
+
+template <typename V, typename U = V>
+concept C = CC<U>;
+
+template <typename T>
+constexpr int f()
+ requires C<T> && C<T *>
+{
+ return 21;
+}
+
+template <typename T>
+void f()
+ requires C<T>;
+
+void g() { static_assert(f<void>() == 21); }
+
+} // namespace Ex1
+
+namespace VAR {
+template <auto N> constexpr bool CC = true;
+template <auto V, auto U = V>
+concept C = CC<U>;
+
+template <auto V>
+constexpr int f()
+ requires C<V> && C<V + 1>
+{
+ return 42;
+}
+
+template <auto N>
+int f()
+ requires C<N>;
+
+void g() { static_assert(f<1>() == 42); }
+} // namespace VAR
+
+} // namespace GH188640
More information about the cfe-commits
mailing list