Skip to content

Commit af2bb8f

Browse files
authored
[Clang] Correctly handle allocations in the condition of a if constexpr (#146890)
Deal with the following scenario ```cpp struct S { char* c = new char; constexpr ~S() { delete c; } }; if constexpr((S{}, true)){}; ``` There were two issues - We need to produce a full expression _before_ evaluating the condition (otherwise, automatic variables are never destroyed) - We need to preserve the evaluation context of the condition while doing the semantics analysis for it (lest it is evaluated in a non-constant-evaluated context) Fixes #120197 Fixes #134820
1 parent ad20dc0 commit af2bb8f

File tree

7 files changed

+96
-18
lines changed

7 files changed

+96
-18
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,7 @@ Bug Fixes to C++ Support
899899
- Fixed a crash when constant evaluating some explicit object member assignment operators. (#GH142835)
900900
- Fixed an access checking bug when substituting into concepts (#GH115838)
901901
- Fix a bug where private access specifier of overloaded function not respected. (#GH107629)
902+
- Correctly handle allocations in the condition of a ``if constexpr``.(#GH120197) (#GH134820)
902903

903904
Bug Fixes to AST Handling
904905
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7723,12 +7723,12 @@ class Sema final : public SemaBase {
77237723

77247724
class ConditionResult {
77257725
Decl *ConditionVar;
7726-
FullExprArg Condition;
7726+
ExprResult Condition;
77277727
bool Invalid;
77287728
std::optional<bool> KnownValue;
77297729

77307730
friend class Sema;
7731-
ConditionResult(Sema &S, Decl *ConditionVar, FullExprArg Condition,
7731+
ConditionResult(Sema &S, Decl *ConditionVar, ExprResult Condition,
77327732
bool IsConstexpr)
77337733
: ConditionVar(ConditionVar), Condition(Condition), Invalid(false) {
77347734
if (IsConstexpr && Condition.get()) {
@@ -7739,7 +7739,7 @@ class Sema final : public SemaBase {
77397739
}
77407740
}
77417741
explicit ConditionResult(bool Invalid)
7742-
: ConditionVar(nullptr), Condition(nullptr), Invalid(Invalid),
7742+
: ConditionVar(nullptr), Condition(Invalid), Invalid(Invalid),
77437743
KnownValue(std::nullopt) {}
77447744

77457745
public:

clang/lib/Parse/ParseExprCXX.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,15 +1931,13 @@ Parser::ParseCXXCondition(StmtResult *InitStmt, SourceLocation Loc,
19311931
return ParseCXXCondition(nullptr, Loc, CK, MissingOK);
19321932
}
19331933

1934-
ExprResult Expr = [&] {
1935-
EnterExpressionEvaluationContext Eval(
1936-
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated,
1937-
/*LambdaContextDecl=*/nullptr,
1938-
/*ExprContext=*/Sema::ExpressionEvaluationContextRecord::EK_Other,
1939-
/*ShouldEnter=*/CK == Sema::ConditionKind::ConstexprIf);
1940-
// Parse the expression.
1941-
return ParseExpression(); // expression
1942-
}();
1934+
EnterExpressionEvaluationContext Eval(
1935+
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated,
1936+
/*LambdaContextDecl=*/nullptr,
1937+
/*ExprContext=*/Sema::ExpressionEvaluationContextRecord::EK_Other,
1938+
/*ShouldEnter=*/CK == Sema::ConditionKind::ConstexprIf);
1939+
1940+
ExprResult Expr = ParseExpression();
19431941

19441942
if (Expr.isInvalid())
19451943
return Sema::ConditionError();

clang/lib/Sema/SemaExpr.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20636,6 +20636,7 @@ Sema::ConditionResult Sema::ActOnCondition(Scope *S, SourceLocation Loc,
2063620636
break;
2063720637

2063820638
case ConditionKind::ConstexprIf:
20639+
// Note: this might produce a FullExpr
2063920640
Cond = CheckBooleanCondition(Loc, SubExpr, true);
2064020641
break;
2064120642

@@ -20648,13 +20649,13 @@ Sema::ConditionResult Sema::ActOnCondition(Scope *S, SourceLocation Loc,
2064820649
{SubExpr}, PreferredConditionType(CK));
2064920650
if (!Cond.get())
2065020651
return ConditionError();
20651-
}
20652-
// FIXME: FullExprArg doesn't have an invalid bit, so check nullness instead.
20653-
FullExprArg FullExpr = MakeFullExpr(Cond.get(), Loc);
20654-
if (!FullExpr.get())
20652+
} else if (Cond.isUsable() && !isa<FullExpr>(Cond.get()))
20653+
Cond = ActOnFinishFullExpr(Cond.get(), Loc, /*DiscardedValue*/ false);
20654+
20655+
if (!Cond.isUsable())
2065520656
return ConditionError();
2065620657

20657-
return ConditionResult(*this, nullptr, FullExpr,
20658+
return ConditionResult(*this, nullptr, Cond,
2065820659
CK == ConditionKind::ConstexprIf);
2065920660
}
2066020661

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4357,7 +4357,8 @@ Sema::ConditionResult Sema::ActOnConditionVariable(Decl *ConditionVar,
43574357
CheckConditionVariable(cast<VarDecl>(ConditionVar), StmtLoc, CK);
43584358
if (E.isInvalid())
43594359
return ConditionError();
4360-
return ConditionResult(*this, ConditionVar, MakeFullExpr(E.get(), StmtLoc),
4360+
E = ActOnFinishFullExpr(E.get(), /*DiscardedValue*/ false);
4361+
return ConditionResult(*this, ConditionVar, E,
43614362
CK == ConditionKind::ConstexprIf);
43624363
}
43634364

@@ -4417,6 +4418,12 @@ ExprResult Sema::CheckCXXBooleanCondition(Expr *CondExpr, bool IsConstexpr) {
44174418
if (!IsConstexpr || E.isInvalid() || E.get()->isValueDependent())
44184419
return E;
44194420

4421+
E = ActOnFinishFullExpr(E.get(), E.get()->getExprLoc(),
4422+
/*DiscardedValue*/ false,
4423+
/*IsConstexpr*/ true);
4424+
if (E.isInvalid())
4425+
return E;
4426+
44204427
// FIXME: Return this value to the caller so they don't need to recompute it.
44214428
llvm::APSInt Cond;
44224429
E = VerifyIntegerConstantExpression(

clang/lib/Sema/TreeTransform.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4561,6 +4561,13 @@ bool TreeTransform<Derived>::TransformExprs(Expr *const *Inputs,
45614561
template <typename Derived>
45624562
Sema::ConditionResult TreeTransform<Derived>::TransformCondition(
45634563
SourceLocation Loc, VarDecl *Var, Expr *Expr, Sema::ConditionKind Kind) {
4564+
4565+
EnterExpressionEvaluationContext Eval(
4566+
SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated,
4567+
/*LambdaContextDecl=*/nullptr,
4568+
/*ExprContext=*/Sema::ExpressionEvaluationContextRecord::EK_Other,
4569+
/*ShouldEnter=*/Kind == Sema::ConditionKind::ConstexprIf);
4570+
45644571
if (Var) {
45654572
VarDecl *ConditionVar = cast_or_null<VarDecl>(
45664573
getDerived().TransformDefinition(Var->getLocation(), Var));

clang/test/SemaCXX/cxx2a-constexpr-dynalloc.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,3 +242,67 @@ void f() {
242242
}
243243

244244
}
245+
246+
namespace GH134820 {
247+
struct S {
248+
char* c = new char;
249+
constexpr ~S() {
250+
delete c;
251+
}
252+
int i = 0;
253+
};
254+
255+
int f() {
256+
if constexpr((S{}, true)) { // expected-warning{{left operand of comma operator has no effect}}
257+
return 1;
258+
}
259+
if constexpr(S s; (S{}, true)) { // expected-warning{{left operand of comma operator has no effect}}
260+
return 1;
261+
}
262+
if constexpr(S s; (s, true)) { // expected-warning{{left operand of comma operator has no effect}}
263+
return 1;
264+
}
265+
if constexpr(constexpr int _ = S{}.i; true) {
266+
return 1;
267+
}
268+
return 0;
269+
}
270+
271+
template <typename T>
272+
int f2() {
273+
if constexpr((T{}, true)) { // expected-warning{{left operand of comma operator has no effect}}
274+
return 1;
275+
}
276+
if constexpr(T s; (T{}, true)) { // expected-warning{{left operand of comma operator has no effect}}
277+
return 1;
278+
}
279+
if constexpr(T s; (s, true)) { // expected-warning{{left operand of comma operator has no effect}}
280+
return 1;
281+
}
282+
if constexpr(constexpr int _ = T{}.i; true) {
283+
return 1;
284+
}
285+
return 0;
286+
}
287+
288+
void test() {
289+
f2<S>(); // expected-note {{in instantiation}}
290+
}
291+
292+
}
293+
namespace GH120197{
294+
struct NonTrivialDtor {
295+
NonTrivialDtor() = default;
296+
NonTrivialDtor(const NonTrivialDtor&) = default;
297+
NonTrivialDtor(NonTrivialDtor&&) = default;
298+
NonTrivialDtor& operator=(const NonTrivialDtor&) = default;
299+
NonTrivialDtor& operator=(NonTrivialDtor&&) = default;
300+
constexpr ~NonTrivialDtor() noexcept {}
301+
};
302+
303+
static_assert(((void)NonTrivialDtor{}, true)); // passes
304+
305+
void f() {
306+
if constexpr ((void)NonTrivialDtor{}, true) {}
307+
}
308+
}

0 commit comments

Comments
 (0)