Skip to content

Commit ad29ee5

Browse files
authored
[clang][bytecode] Always initialize scope before conditional operator (#171133)
Otherwise, the scope might be (lazily) initialized in one of the arms of the conditional operator, which means the will NOT be intialized in the other arm. Fixes #170981
1 parent 86c5539 commit ad29ee5

File tree

3 files changed

+37
-9
lines changed

3 files changed

+37
-9
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2503,20 +2503,22 @@ bool Compiler<Emitter>::VisitAbstractConditionalOperator(
25032503
const Expr *TrueExpr = E->getTrueExpr();
25042504
const Expr *FalseExpr = E->getFalseExpr();
25052505

2506+
if (std::optional<bool> BoolValue = getBoolValue(Condition)) {
2507+
if (*BoolValue)
2508+
return this->delegate(TrueExpr);
2509+
return this->delegate(FalseExpr);
2510+
}
2511+
2512+
// Force-init the scope, which creates a InitScope op. This is necessary so
2513+
// the scope is not only initialized in one arm of the conditional operator.
2514+
this->VarScope->forceInit();
25062515
// The TrueExpr and FalseExpr of a conditional operator do _not_ create a
25072516
// scope, which means the local variables created within them unconditionally
25082517
// always exist. However, we need to later differentiate which branch was
25092518
// taken and only destroy the varibles of the active branch. This is what the
25102519
// "enabled" flags on local variables are used for.
25112520
llvm::SaveAndRestore LAAA(this->VarScope->LocalsAlwaysEnabled,
25122521
/*NewValue=*/false);
2513-
2514-
if (std::optional<bool> BoolValue = getBoolValue(Condition)) {
2515-
if (*BoolValue)
2516-
return this->delegate(TrueExpr);
2517-
return this->delegate(FalseExpr);
2518-
}
2519-
25202522
bool IsBcpCall = false;
25212523
if (const auto *CE = dyn_cast<CallExpr>(Condition->IgnoreParenCasts());
25222524
CE && CE->getBuiltinCallee() == Builtin::BI__builtin_constant_p) {

clang/lib/AST/ByteCode/Compiler.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,7 @@ template <class Emitter> class VariableScope {
505505

506506
virtual bool emitDestructors(const Expr *E = nullptr) { return true; }
507507
virtual bool destroyLocals(const Expr *E = nullptr) { return true; }
508+
virtual void forceInit() {}
508509
VariableScope *getParent() const { return Parent; }
509510
ScopeKind getKind() const { return Kind; }
510511

@@ -534,7 +535,6 @@ template <class Emitter> class LocalScope : public VariableScope<Emitter> {
534535
this->Ctx->emitDestroy(*Idx, SourceInfo{});
535536
removeStoredOpaqueValues();
536537
}
537-
538538
/// Explicit destruction of local variables.
539539
bool destroyLocals(const Expr *E = nullptr) override {
540540
if (!Idx)
@@ -558,10 +558,22 @@ template <class Emitter> class LocalScope : public VariableScope<Emitter> {
558558
this->Ctx->Descriptors[*Idx].emplace_back(Local);
559559
}
560560

561+
/// Force-initialize this scope. Usually, scopes are lazily initialized when
562+
/// the first local variable is created, but in scenarios with conditonal
563+
/// operators, we need to ensure scope is initialized just in case one of the
564+
/// arms will create a local and the other won't. In such a case, the
565+
/// InitScope() op would be part of the arm that created the local.
566+
void forceInit() override {
567+
if (!Idx) {
568+
Idx = static_cast<unsigned>(this->Ctx->Descriptors.size());
569+
this->Ctx->Descriptors.emplace_back();
570+
this->Ctx->emitInitScope(*Idx, {});
571+
}
572+
}
573+
561574
bool emitDestructors(const Expr *E = nullptr) override {
562575
if (!Idx)
563576
return true;
564-
assert(!this->Ctx->Descriptors[*Idx].empty());
565577

566578
// Emit destructor calls for local variables of record
567579
// type with a destructor.

clang/test/AST/ByteCode/cxx20.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,3 +1211,17 @@ namespace DyamicCast {
12111211
constexpr const X *p = &y;
12121212
constexpr const Y *q = dynamic_cast<const Y*>(p);
12131213
}
1214+
1215+
namespace ConditionalTemporaries {
1216+
class F {
1217+
public:
1218+
constexpr F(int a ) {this->a = a;}
1219+
constexpr ~F() {}
1220+
int a;
1221+
};
1222+
constexpr int foo(bool b) {
1223+
return b ? F{12}.a : F{13}.a;
1224+
}
1225+
static_assert(foo(false)== 13);
1226+
static_assert(foo(true)== 12);
1227+
}

0 commit comments

Comments
 (0)