Skip to content

Commit c0185ff

Browse files
committed
[clang] Don't typo-fix an expression in a SFINAE context.
If this is a SFINAE context, then continuing to look up names (in particular, to treat a non-function as a function, and then do ADL) might too-eagerly complete a type that it's not safe to complete right now. We should just say "okay, that's a substitution failure" and not do any more work than absolutely required. Fixes llvm#52970. Differential Revision: https://reviews.llvm.org/D117603
1 parent f6ce456 commit c0185ff

File tree

3 files changed

+94
-24
lines changed

3 files changed

+94
-24
lines changed

clang/lib/Sema/Sema.cpp

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2556,32 +2556,36 @@ bool Sema::tryToRecoverWithCall(ExprResult &E, const PartialDiagnostic &PD,
25562556
bool (*IsPlausibleResult)(QualType)) {
25572557
SourceLocation Loc = E.get()->getExprLoc();
25582558
SourceRange Range = E.get()->getSourceRange();
2559-
2560-
QualType ZeroArgCallTy;
25612559
UnresolvedSet<4> Overloads;
2562-
if (tryExprAsCall(*E.get(), ZeroArgCallTy, Overloads) &&
2563-
!ZeroArgCallTy.isNull() &&
2564-
(!IsPlausibleResult || IsPlausibleResult(ZeroArgCallTy))) {
2565-
// At this point, we know E is potentially callable with 0
2566-
// arguments and that it returns something of a reasonable type,
2567-
// so we can emit a fixit and carry on pretending that E was
2568-
// actually a CallExpr.
2569-
SourceLocation ParenInsertionLoc = getLocForEndOfToken(Range.getEnd());
2570-
bool IsMV = IsCPUDispatchCPUSpecificMultiVersion(E.get());
2571-
Diag(Loc, PD) << /*zero-arg*/ 1 << IsMV << Range
2572-
<< (IsCallableWithAppend(E.get())
2573-
? FixItHint::CreateInsertion(ParenInsertionLoc, "()")
2574-
: FixItHint());
2575-
if (!IsMV)
2576-
notePlausibleOverloads(*this, Loc, Overloads, IsPlausibleResult);
2577-
2578-
// FIXME: Try this before emitting the fixit, and suppress diagnostics
2579-
// while doing so.
2580-
E = BuildCallExpr(nullptr, E.get(), Range.getEnd(), None,
2581-
Range.getEnd().getLocWithOffset(1));
2582-
return true;
2583-
}
25842560

2561+
// If this is a SFINAE context, don't try anything that might trigger ADL
2562+
// prematurely.
2563+
if (!isSFINAEContext()) {
2564+
QualType ZeroArgCallTy;
2565+
if (tryExprAsCall(*E.get(), ZeroArgCallTy, Overloads) &&
2566+
!ZeroArgCallTy.isNull() &&
2567+
(!IsPlausibleResult || IsPlausibleResult(ZeroArgCallTy))) {
2568+
// At this point, we know E is potentially callable with 0
2569+
// arguments and that it returns something of a reasonable type,
2570+
// so we can emit a fixit and carry on pretending that E was
2571+
// actually a CallExpr.
2572+
SourceLocation ParenInsertionLoc = getLocForEndOfToken(Range.getEnd());
2573+
bool IsMV = IsCPUDispatchCPUSpecificMultiVersion(E.get());
2574+
Diag(Loc, PD) << /*zero-arg*/ 1 << IsMV << Range
2575+
<< (IsCallableWithAppend(E.get())
2576+
? FixItHint::CreateInsertion(ParenInsertionLoc,
2577+
"()")
2578+
: FixItHint());
2579+
if (!IsMV)
2580+
notePlausibleOverloads(*this, Loc, Overloads, IsPlausibleResult);
2581+
2582+
// FIXME: Try this before emitting the fixit, and suppress diagnostics
2583+
// while doing so.
2584+
E = BuildCallExpr(nullptr, E.get(), Range.getEnd(), None,
2585+
Range.getEnd().getLocWithOffset(1));
2586+
return true;
2587+
}
2588+
}
25852589
if (!ForceComplain) return false;
25862590

25872591
bool IsMV = IsCPUDispatchCPUSpecificMultiVersion(E.get());

clang/lib/Sema/SemaExprMember.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1645,6 +1645,9 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
16451645
<< BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange()
16461646
<< FixItHint::CreateReplacement(OpLoc, "->");
16471647

1648+
if (S.isSFINAEContext())
1649+
return ExprError();
1650+
16481651
// Recurse as an -> access.
16491652
IsArrow = true;
16501653
return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,

clang/test/SemaTemplate/pr52970.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
2+
// RUN: %clang_cc1 -fsyntax-only -std=c++14 -verify %s
3+
// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s
4+
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify=cxx20 %s
5+
// expected-no-diagnostics
6+
7+
struct Incomplete;
8+
template <class T> struct Holder { T t; };
9+
10+
namespace DotFollowingFunctionName {
11+
struct Good {
12+
struct Nested {
13+
int b;
14+
} a;
15+
};
16+
17+
struct Bad {
18+
Holder<Incomplete> a();
19+
};
20+
21+
template <class T>
22+
constexpr auto f(T t) -> decltype((t.a.b, true)) { return true; }
23+
constexpr bool f(...) { return false; }
24+
25+
static_assert(DotFollowingFunctionName::f(Good{}), "");
26+
static_assert(!DotFollowingFunctionName::f(Bad{}), "");
27+
28+
#if __cplusplus >= 202002L
29+
template <class T>
30+
concept C = requires(T t) { t.a.b; };
31+
// cxx20-note@-1 {{because 't.a.b' would be invalid: reference to non-static member function must be called}}
32+
33+
static_assert(C<Good>);
34+
static_assert(!C<Bad>);
35+
static_assert(C<Bad>); // cxx20-error {{static_assert failed}}
36+
// cxx20-note@-1 {{because 'DotFollowingFunctionName::Bad' does not satisfy 'C'}}
37+
#endif
38+
} // namespace DotFollowingFunctionName
39+
40+
namespace DotFollowingPointer {
41+
struct Good {
42+
int begin();
43+
};
44+
using Bad = Holder<Incomplete> *;
45+
46+
template <class T>
47+
constexpr auto f(T t) -> decltype((t.begin(), true)) { return true; }
48+
constexpr bool f(...) { return false; }
49+
50+
static_assert(DotFollowingPointer::f(Good{}), "");
51+
static_assert(!DotFollowingPointer::f(Bad{}), "");
52+
53+
#if __cplusplus >= 202002L
54+
template <class T>
55+
concept C = requires(T t) { t.begin(); };
56+
// cxx20-note@-1 {{because 't.begin()' would be invalid: member reference type 'Holder<Incomplete> *' is a pointer}}
57+
58+
static_assert(C<Good>);
59+
static_assert(!C<Bad>);
60+
static_assert(C<Bad>); // cxx20-error {{static_assert failed}}
61+
// cxx20-note@-1 {{because 'DotFollowingPointer::Bad' (aka 'Holder<Incomplete> *') does not satisfy 'C'}}
62+
#endif
63+
} // namespace DotFollowingPointer

0 commit comments

Comments
 (0)