Skip to content

Commit 52b18b4

Browse files
authored
[Clang] Reland: Diagnose invalid function types in dependent contexts (#139246)
When forming an invalid function type, we were not diagnosing it if the call was dependent. However, we later rely on the function type to be sensible during argument deduction. We now diagnose anything that is not a potential function type, to avoid constructing bogus call expressions. Fixes #138657 Fixes #115725 Fixes #68852 Fixes #139163
1 parent 2403865 commit 52b18b4

File tree

3 files changed

+125
-1
lines changed

3 files changed

+125
-1
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,7 @@ Bug Fixes to C++ Support
680680
- Improved parser recovery of invalid requirement expressions. In turn, this
681681
fixes crashes from follow-on processing of the invalid requirement. (#GH138820)
682682
- Fixed the handling of pack indexing types in the constraints of a member function redeclaration. (#GH138255)
683+
- Fixed a crash when forming an invalid function type in a dependent context. (#GH138657) (#GH115725) (#GH68852)
683684

684685
Bug Fixes to AST Handling
685686
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/Sema/SemaExpr.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6550,6 +6550,23 @@ ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
65506550
return Call;
65516551
}
65526552

6553+
// Any type that could be used to form a callable expression
6554+
static bool MayBeFunctionType(const ASTContext &Context, const Expr *E) {
6555+
QualType T = E->getType();
6556+
if (T->isDependentType())
6557+
return true;
6558+
6559+
if (T == Context.BoundMemberTy || T == Context.UnknownAnyTy ||
6560+
T == Context.BuiltinFnTy || T == Context.OverloadTy ||
6561+
T->isFunctionType() || T->isFunctionReferenceType() ||
6562+
T->isMemberFunctionPointerType() || T->isFunctionPointerType() ||
6563+
T->isBlockPointerType() || T->isRecordType())
6564+
return true;
6565+
6566+
return isa<CallExpr, DeclRefExpr, MemberExpr, CXXPseudoDestructorExpr,
6567+
OverloadExpr, UnresolvedMemberExpr, UnaryOperator>(E);
6568+
}
6569+
65536570
ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
65546571
MultiExprArg ArgExprs, SourceLocation RParenLoc,
65556572
Expr *ExecConfig, bool IsExecConfig,
@@ -6603,6 +6620,14 @@ ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
66036620
*this, dyn_cast<UnresolvedMemberExpr>(Fn->IgnoreParens()),
66046621
Fn->getBeginLoc());
66056622

6623+
// If the type of the function itself is not dependent
6624+
// check that it is a reasonable as a function, as type deduction
6625+
// later assume the CallExpr has a sensible TYPE.
6626+
if (!MayBeFunctionType(Context, Fn))
6627+
return ExprError(
6628+
Diag(LParenLoc, diag::err_typecheck_call_not_function)
6629+
<< Fn->getType() << Fn->getSourceRange());
6630+
66066631
return CallExpr::Create(Context, Fn, ArgExprs, Context.DependentTy,
66076632
VK_PRValue, RParenLoc, CurFPFeatureOverrides());
66086633
}

clang/test/SemaTemplate/fun-template-def.cpp

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// RUN: %clang_cc1 -fsyntax-only -verify %s
22
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
33
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
4+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 %s
5+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
46

57
// Tests that dependent expressions are always allowed, whereas non-dependent
68
// are checked as usual.
@@ -32,7 +34,7 @@ T f1(T t1, U u1, int i1, T** tpp)
3234
i1 = t1[u1];
3335
i1 *= t1;
3436

35-
i1(u1, t1); // error
37+
i1(u1, t1);
3638
u1(i1, t1);
3739

3840
U u2 = (T)i1;
@@ -60,3 +62,99 @@ void f3() {
6062
f2<int*>(0);
6163
f2<int>(0); // expected-error {{no matching function for call to 'f2'}}
6264
}
65+
66+
#if __cplusplus >= 202002L
67+
namespace GH138657 {
68+
template <auto V> // #gh138657-template-head
69+
class meta {};
70+
template<int N>
71+
class meta<N()> {}; // expected-error {{called object type 'int' is not a function or function point}}
72+
73+
template<int N[1]>
74+
class meta<N()> {}; // expected-error {{called object type 'int *' is not a function or function point}}
75+
76+
template<char* N>
77+
class meta<N()> {}; // expected-error {{called object type 'char *' is not a function or function point}}
78+
79+
struct S {};
80+
template<S>
81+
class meta<S()> {}; // expected-error {{template argument for non-type template parameter is treated as function type 'S ()'}}
82+
// expected-note@#gh138657-template-head {{template parameter is declared here}}
83+
84+
}
85+
86+
namespace GH115725 {
87+
template<auto ...> struct X {};
88+
template<typename T, typename ...Ts> struct A {
89+
template<Ts ...Ns, T *...Ps>
90+
A(X<0(Ps)...>, Ts (*...qs)[Ns]);
91+
// expected-error@-1{{called object type 'int' is not a function or function pointer}}
92+
93+
};
94+
}
95+
96+
namespace GH68852 {
97+
template <auto v>
98+
struct constexpr_value {
99+
template <class... Ts>
100+
constexpr constexpr_value<v(Ts::value...)> call(Ts...) {
101+
//expected-error@-1 {{called object type 'int' is not a function or function pointer}}
102+
return {};
103+
}
104+
};
105+
106+
template <auto v> constexpr static inline auto c_ = constexpr_value<v>{};
107+
// expected-note@-1 {{in instantiation of template}}
108+
auto k = c_<1>; // expected-note {{in instantiation of variable}}
109+
110+
}
111+
112+
#endif
113+
#if __cplusplus >= 201702L
114+
115+
namespace GH138731 {
116+
template <class...>
117+
using void_t = void;
118+
119+
template <class T>
120+
T&& declval();
121+
122+
struct S {
123+
S();
124+
static int f();
125+
static int var;
126+
};
127+
128+
namespace invoke_detail {
129+
130+
template <typename F>
131+
struct traits {
132+
template <typename... A>
133+
using result = decltype(declval<F>()(declval<A>()...));
134+
};
135+
136+
template <typename F, typename... A>
137+
using invoke_result_t = typename traits<F>::template result<A...>;
138+
139+
template <typename Void, typename F, typename... A>
140+
inline constexpr bool is_invocable_v = false;
141+
142+
template <typename F, typename... A>
143+
inline constexpr bool
144+
is_invocable_v<void_t<invoke_result_t<F, A...>>, F, A...> = true;
145+
146+
}
147+
148+
template <typename F, typename... A>
149+
inline constexpr bool is_invocable_v =
150+
invoke_detail::is_invocable_v<void, F, A...>;
151+
152+
static_assert(!is_invocable_v<int>);
153+
static_assert(!is_invocable_v<int, int>);
154+
static_assert(!is_invocable_v<S>);
155+
static_assert(is_invocable_v<decltype(&S::f)>);
156+
static_assert(!is_invocable_v<decltype(&S::var)>);
157+
158+
}
159+
160+
#endif

0 commit comments

Comments
 (0)