https://github.com/akash-manna-sky created
https://github.com/llvm/llvm-project/pull/221723
Fixes #189146
A call to a class object that goes through a conversion function to a function
pointer ("surrogate call") asserted when that conversion function had been
brought into the class with a using-declaration, e.g. `using M::operator
auto;`. The surrogate loop in `BuildCallToObjectOfClassType` unwraps the
`UsingShadowDecl` to get the conversion function but records the shadow as the
candidate's `FoundDecl`, so the two are not the same pointer and the assertion
fires. It wasn't only the assertion, though: the line before it passed
`FoundDecl` to `DiagnoseUseOfDecl`, which skips all of its function checks on a
shadow decl, and surrogates never go through `OR_Deleted` since they carry no
`Function`. So with the assertion relaxed, a deleted conversion function
reached through a using-declaration would have compiled without complaint.
The surrogate branch now diagnoses use of the conversion function itself rather
than the found declaration, which is what `BuildCXXCastArgument` already does
for the ordinary user-defined conversion path, and the assertion compares
against the underlying declaration. Access checking keeps using `FoundDecl`,
since the using-declaration's access is the one that matters. For conversion
functions declared directly in the class the two pointers are identical, so
nothing changes there.
>From 4302e1d4cd05b1aa8822fb7b712816cbb97e659c Mon Sep 17 00:00:00 2001
From: Akash Manna <[email protected]>
Date: Mon, 7 Sep 2026 18:34:24 +0530
Subject: [PATCH] [clang] Fix surrogate call through a using-declared
conversion function
Fixes #189146
When a call to a class object is resolved through a conversion function
to a function pointer that was brought into the class by a
using-declaration, the candidate's FoundDecl is the UsingShadowDecl
while the conversion function itself is the shadow's target. The
surrogate branch of BuildCallToObjectOfClassType asserted the two were
pointer-identical and passed FoundDecl to DiagnoseUseOfDecl, which does
nothing useful for a shadow decl, so a deleted conversion function
reached this way was never diagnosed.
Diagnose use of the conversion function itself, matching
BuildCXXCastArgument, and have the assertion compare against the
underlying declaration.
---
clang/docs/ReleaseNotes.md | 5 +++
clang/lib/Sema/SemaOverload.cpp | 7 ++--
clang/test/SemaCXX/conversion-function.cpp | 38 ++++++++++++++++++++++
3 files changed, 47 insertions(+), 3 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a49971adef86f..b37a16248ab0a 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -646,6 +646,11 @@ features cannot lower the translation-unit ABI level;
- Fixed a crash when a coroutine keyword appeared inside a mem-initializer on a
function that is not a constructor. (#GH194298)
+- Fixed an assertion when a call to a class object was resolved through a
+ conversion function to a function pointer that was introduced into the class
+ by a using-declaration (e.g. `using Base::operator auto;`). Such a conversion
+ function is now also diagnosed if it is deleted. (#GH189146)
+
#### Bug Fixes to AST Handling
- Fixed a non-deterministic ordering of unused local typedefs that made
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 106ddb90ed9dc..6e42af3956fe3 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -16812,12 +16812,13 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr
*Obj,
= cast<CXXConversionDecl>(
Best->Conversions[0].UserDefined.ConversionFunction);
+ // FoundDecl may be a UsingShadowDecl naming the conversion function.
+ assert(Conv == Best->FoundDecl.getDecl()->getUnderlyingDecl() &&
+ "Found Decl & conversion-to-functionptr should be same, right?!");
CheckMemberOperatorAccess(LParenLoc, Object.get(), nullptr,
Best->FoundDecl);
- if (DiagnoseUseOfDecl(Best->FoundDecl, LParenLoc))
+ if (DiagnoseUseOfDecl(Conv, LParenLoc))
return ExprError();
- assert(Conv == Best->FoundDecl.getDecl() &&
- "Found Decl & conversion-to-functionptr should be same, right?!");
// We selected one of the surrogate functions that converts the
// object parameter to a function pointer. Perform the conversion
// on the object argument, then let BuildCallExpr finish the job.
diff --git a/clang/test/SemaCXX/conversion-function.cpp
b/clang/test/SemaCXX/conversion-function.cpp
index e00553fbb20a3..1a09a527b750c 100644
--- a/clang/test/SemaCXX/conversion-function.cpp
+++ b/clang/test/SemaCXX/conversion-function.cpp
@@ -519,3 +519,41 @@ struct S {
**operator char(); // expected-error {{cannot specify any part of a return
type in the declaration of a conversion function; put the complete type after
'operator'}}
};
}
+
+#if __cplusplus >= 201402L
+namespace GH189146 {
+ struct M {
+ template <class T> static constexpr int static_foo(T) { return 5; }
+ template <class T> operator T() { return T{}; }
+ constexpr operator auto() { return &static_foo<int>; }
+ };
+
+ struct N : M {
+ using M::operator auto;
+ };
+
+ template <class T> constexpr int test() {
+ return T{}(3);
+ }
+ static_assert(test<M>() == 5, "");
+ static_assert(test<N>() == 5, "");
+
+ using FP = int (*)(int);
+ constexpr int bar(int) { return 7; }
+ struct P {
+ constexpr operator FP() const { return &bar; }
+ };
+ struct Q : P {
+ using P::operator FP;
+ };
+ static_assert(Q{}(0) == 7, "");
+
+ struct R {
+ operator FP() const = delete; // expected-note {{has been explicitly
marked deleted here}}
+ };
+ struct S : R {
+ using R::operator FP;
+ };
+ int s = S{}(0); // expected-error {{attempt to use a deleted function}}
+}
+#endif
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits