Skip to content

Commit d8352e9

Browse files
SirraideAaronBallman
authored andcommitted
[Clang] [Sema] Handle placeholders in '.*' expressions (#83103)
When analysing whether we should handle a binary expression as an overloaded operator call or a builtin operator, we were calling `checkPlaceholderForOverload()`, which takes care of any placeholders that are not overload sets—which would usually make sense since those need to be handled as part of overload resolution. Unfortunately, we were also doing that for `.*`, which is not overloadable, and then proceeding to create a builtin operator anyway, which would crash if the RHS happened to be an unresolved overload set (due hitting an assertion in `CreateBuiltinBinOp()`—specifically, in one of its callees—in the `.*` case that makes sure its arguments aren’t placeholders). This pr instead makes it so we check for *all* placeholders early if the operator is `.*`. It’s worth noting that, 1. In the `.*` case, we now additionally also check for *any* placeholders (not just non-overload-sets) in the LHS; this shouldn’t make a difference, however—at least I couldn’t think of a way to trigger the assertion with an overload set as the LHS of `.*`; it is worth noting that the assertion in question would also complain if the LHS happened to be of placeholder type, though. 2. There is another case in which we also don’t perform overload resolution—namely `=` if the LHS is not of class or enumeration type after handling non-overload-set placeholders—as in the `.*` case, but similarly to 1., I first couldn’t think of a way of getting this case to crash, and secondly, `CreateBuiltinBinOp()` doesn’t seem to care about placeholders in the LHS or RHS in the `=` case (from what I can tell, it, or rather one of its callees, only checks that the LHS is not a pseudo-object type, but those will have already been handled by the call to `checkPlaceholderForOverload()` by the time we get to this function), so I don’t think this case suffers from the same problem. This fixes #53815. --------- Co-authored-by: Aaron Ballman <[email protected]>
1 parent eb9bc02 commit d8352e9

File tree

3 files changed

+40
-5
lines changed

3 files changed

+40
-5
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,8 @@ Bug Fixes to C++ Support
11031103
(`#82258 <https://github.com/llvm/llvm-project/issues/82258>`_)
11041104
- Correctly immediate-escalate lambda conversion functions.
11051105
(`#82258 <https://github.com/llvm/llvm-project/issues/82258>`_)
1106+
- Fix a crash when an unresolved overload set is encountered on the RHS of a ``.*`` operator.
1107+
(`#53815 <https://github.com/llvm/llvm-project/issues/53815>`_)
11061108

11071109
Bug Fixes to AST Handling
11081110
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/Sema/SemaOverload.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14470,6 +14470,23 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
1447014470
CurFPFeatureOverrides());
1447114471
}
1447214472

14473+
// If this is the .* operator, which is not overloadable, just
14474+
// create a built-in binary operator.
14475+
if (Opc == BO_PtrMemD) {
14476+
auto CheckPlaceholder = [&](Expr *&Arg) {
14477+
ExprResult Res = CheckPlaceholderExpr(Arg);
14478+
if (Res.isUsable())
14479+
Arg = Res.get();
14480+
return !Res.isUsable();
14481+
};
14482+
14483+
// CreateBuiltinBinOp() doesn't like it if we tell it to create a '.*'
14484+
// expression that contains placeholders (in either the LHS or RHS).
14485+
if (CheckPlaceholder(Args[0]) || CheckPlaceholder(Args[1]))
14486+
return ExprError();
14487+
return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
14488+
}
14489+
1447314490
// Always do placeholder-like conversions on the RHS.
1447414491
if (checkPlaceholderForOverload(*this, Args[1]))
1447514492
return ExprError();
@@ -14489,11 +14506,6 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
1448914506
if (Opc == BO_Assign && !Args[0]->getType()->isOverloadableType())
1449014507
return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
1449114508

14492-
// If this is the .* operator, which is not overloadable, just
14493-
// create a built-in binary operator.
14494-
if (Opc == BO_PtrMemD)
14495-
return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
14496-
1449714509
// Build the overload set.
1449814510
OverloadCandidateSet CandidateSet(OpLoc, OverloadCandidateSet::CSK_Operator,
1449914511
OverloadCandidateSet::OperatorRewriteInfo(

clang/test/SemaCXX/gh53815.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
2+
// expected-no-diagnostics
3+
4+
// Check that we don't crash due to forgetting to check for placeholders
5+
// in the RHS of '.*'.
6+
7+
template <typename Fn>
8+
static bool has_explicitly_named_overload() {
9+
return requires { Fn().*&Fn::operator(); };
10+
}
11+
12+
int main() {
13+
has_explicitly_named_overload<decltype([](auto){})>();
14+
}
15+
16+
template <typename Fn>
17+
constexpr bool has_explicitly_named_overload_2() {
18+
return requires { Fn().*&Fn::operator(); };
19+
}
20+
21+
static_assert(!has_explicitly_named_overload_2<decltype([](auto){})>());

0 commit comments

Comments
 (0)