Author: Stanislav Bardyuk Date: 2026-10-16T15:09:30+02:00 New Revision: 74158edd0fabd0a05f7d23b495120ade188b20da
URL: https://github.com/llvm/llvm-project/commit/74158edd0fabd0a05f7d23b495120ade188b20da DIFF: https://github.com/llvm/llvm-project/commit/74158edd0fabd0a05f7d23b495120ade188b20da.diff LOG: [Clang] Fix crash classifying a dependent call to a non-callable value (#220110) Clang asserts in Type::castAs<FunctionType> (via CallExpr::getCallReturnType) when a template argument contains a call whose callee has already been substituted to a plain value while an argument is still dependent, e.g. ```cpp template <auto V> struct W { static constexpr auto value = V; template <class T> auto f(T) -> W<value(T::value)>; }; template struct W<42>; ``` When W<42> is instantiated, the callee `value` becomes an int but T::value is still dependent, so BuildCallExpr creates a dependent CallExpr (the MayBeFunctionType guard from #139246 intentionally lets DeclRefExpr callees through so the error is deferred to instantiation). Checking the template argument against the `auto` NTTP then classifies the expression, and getCallReturnType has no case for a type-dependent call whose callee type is not a function, so it hits the castAs assertion. Handle it the same way the record-callee (#68078) and dependent-builtin (#210524) cases already are: return DependentTy. The check goes after the pointer-stripping branch so a callee of pointer-to-non-function type (same crash, e.g. `p(T::value)` with `const int *p`) is covered too. The invalid call is still diagnosed when the member actually gets instantiated (as a substitution failure), and the accepted-and-deferred behavior matches GCC. Fixes #218323 --------- Co-authored-by: Shengxin Pei <[email protected]> Added: Modified: clang/docs/ReleaseNotes.md clang/lib/AST/Expr.cpp clang/test/SemaTemplate/fun-template-def.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 3f0c48cf36644..d6080cd648a39 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -1013,6 +1013,9 @@ features cannot lower the translation-unit ABI level; - Fixed a bug where Clang failed to resolve a reference to an overloaded conversion function when one of the overloads was non-template. (#GH230682) +- Fixed a crash when classifying a dependent call whose callee has already + been substituted to a value of non-callable type. (#GH218323) + #### Bug Fixes to AST Handling - Fixed a non-deterministic ordering of unused local typedefs that made diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 6b9aef5d479a6..1ba2ccdf697f9 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1660,6 +1660,9 @@ QualType CallExpr::getCallReturnType(const ASTContext &Ctx) const { return Ctx.DependentTy; } + if (isTypeDependent() && !CalleeType->isFunctionType()) + return Ctx.DependentTy; + const FunctionType *FnType = CalleeType->castAs<FunctionType>(); return FnType->getReturnType(); } diff --git a/clang/test/SemaTemplate/fun-template-def.cpp b/clang/test/SemaTemplate/fun-template-def.cpp index b0d0580d5ba80..7aed80266ff97 100644 --- a/clang/test/SemaTemplate/fun-template-def.cpp +++ b/clang/test/SemaTemplate/fun-template-def.cpp @@ -121,6 +121,45 @@ template struct S<1>; // expected-note {{in instantiation}} } +namespace GH218323 { +template <auto V> struct W { + static constexpr auto value = V; + template <class T> auto f(T) -> W<value(T::value)>; // #gh218323-f + template <class T> auto g(T) -> W<int(T::type)>; // #gh218323-g + template <class T> auto h(T) -> W<value(T::type)>; // #gh218323-h + template <class... Ts> auto p(Ts...) -> W<value(Ts::value...)>; +}; +template struct W<42>; + +template <auto V> struct WPtr { + static constexpr int arr[1] = {V}; + static constexpr const int *p = arr; + template <class T> auto f(T) -> WPtr<p(T::value)>; +}; +template struct WPtr<42>; + +struct HasValue { static constexpr int value = 1; }; +struct HasAlias { using type = int; }; +void use(W<42> w) { + w.f(HasValue{}); // expected-error {{no matching member function for call to 'f'}} + // expected-note@#gh218323-f {{candidate template ignored: substitution failure [with T = HasValue]: called object type 'int' is not a function or function pointer}} + w.g(HasAlias{}); // expected-error {{no matching member function for call to 'g'}} + // expected-note@#gh218323-g {{candidate template ignored: substitution failure [with T = HasAlias]: missing 'typename' prior to dependent type name 'GH218323::HasAlias::type'}} + w.h(HasAlias{}); // expected-error {{no matching member function for call to 'h'}} + // expected-note@#gh218323-h {{candidate template ignored: substitution failure [with T = HasAlias]: missing 'typename' prior to dependent type name 'GH218323::HasAlias::type'}} +} + +template <auto V> struct WTypename { // #gh218323-WTypename + static constexpr auto value = V; + template <class T> auto f(T) -> WTypename<int(typename T::type)>; // expected-error {{template argument for non-type template parameter is treated as function type 'int (typename T::type)'}} + // expected-note@#gh218323-WTypename {{template parameter is declared here}} + template <class T> auto g(T) -> WTypename<typename T::type(value)>; +}; +template struct WTypename<42>; +void use_typename(WTypename<42> w) { + w.g(HasAlias{}); +} +} #endif #if __cplusplus >= 201702L _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
