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

Reply via email to