Skip to content

Commit 8b25820

Browse files
authored
[clang][bytecode] Diagnose copying empty mutable unions (llvm#195529)
We had a special case for copy/move ctors of empty unions. Remove that. Everything else is just so we don't regress diagnostics.
1 parent a9a899d commit 8b25820

6 files changed

Lines changed: 60 additions & 10 deletions

File tree

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6809,12 +6809,13 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
68096809
return false;
68106810
bool IsUnion = R->isUnion();
68116811

6812+
// Union copy and move ctors are special.
68126813
if (IsUnion && Ctor->isCopyOrMoveConstructor()) {
68136814
LocOverrideScope<Emitter> LOS(this, SourceInfo{});
68146815

6815-
if (R->getNumFields() == 0)
6816-
return this->emitRetVoid(Ctor);
6817-
// union copy and move ctors are special.
6816+
// No special case for NumFields == 0 here, so the Memcpy op
6817+
// below also does its checks in those cases.
6818+
68186819
assert(cast<CompoundStmt>(Ctor->getBody())->body_empty());
68196820
if (!this->emitThis(Ctor))
68206821
return false;

clang/lib/AST/ByteCode/Function.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ Function::Function(Program &P, FunctionDeclTy Source, unsigned ArgSize,
2929
Constexpr = F->isConstexpr();
3030
if (const auto *CD = dyn_cast<CXXConstructorDecl>(F)) {
3131
Virtual = CD->isVirtual();
32-
Kind = FunctionKind::Ctor;
32+
Kind = CD->isCopyOrMoveConstructor() ? FunctionKind::CopyOrMoveCtor
33+
: FunctionKind::Ctor;
3334
} else if (const auto *CD = dyn_cast<CXXDestructorDecl>(F)) {
3435
Virtual = CD->isVirtual();
3536
Kind = FunctionKind::Dtor;

clang/lib/AST/ByteCode/Function.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ class Function final {
101101
enum class FunctionKind {
102102
Normal,
103103
Ctor,
104+
CopyOrMoveCtor,
104105
Dtor,
105106
LambdaStaticInvoker,
106107
LambdaCallOperator,
@@ -185,7 +186,13 @@ class Function final {
185186
bool isConstexpr() const { return Constexpr; }
186187

187188
/// Checks if the function is a constructor.
188-
bool isConstructor() const { return Kind == FunctionKind::Ctor; }
189+
bool isConstructor() const {
190+
return Kind == FunctionKind::Ctor || Kind == FunctionKind::CopyOrMoveCtor;
191+
}
192+
bool isCopyOrMoveConstructor() const {
193+
return Kind == FunctionKind::CopyOrMoveCtor;
194+
}
195+
189196
/// Checks if the function is a destructor.
190197
bool isDestructor() const { return Kind == FunctionKind::Dtor; }
191198
/// Checks if the function is copy or move operator.

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,8 @@ static bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
910910
return false;
911911
if (!IsCtorDtor && !CheckLifetime(S, OpPC, Ptr, AK_MemberCall))
912912
return false;
913+
if (!CheckMutable(S, OpPC, Ptr))
914+
return false;
913915
}
914916
return true;
915917
}
@@ -1781,6 +1783,14 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
17811783
if (!CheckInvoke(S, OpPC, ThisPtr,
17821784
Func->isConstructor() || Func->isDestructor()))
17831785
return cleanup();
1786+
1787+
if (Func->isCopyOrMoveOperator() || Func->isCopyOrMoveConstructor()) {
1788+
const Pointer &RVOPtr =
1789+
S.Stk.peek<Pointer>(ThisOffset - align(sizeof(Pointer)));
1790+
if (!CheckInvoke(S, OpPC, RVOPtr, /*IsCtorDtor=*/true))
1791+
return cleanup();
1792+
}
1793+
17841794
if (!Func->isConstructor() && !Func->isDestructor() &&
17851795
!CheckActive(S, OpPC, ThisPtr, AK_MemberCall))
17861796
return false;

clang/lib/AST/ByteCode/InterpFrame.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,6 @@ static bool shouldSkipInBacktrace(const Function *F) {
150150
MD && MD->getParent()->isAnonymousStructOrUnion())
151151
return true;
152152

153-
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FD);
154-
Ctor && Ctor->isDefaulted() && Ctor->isTrivial() &&
155-
Ctor->isCopyOrMoveConstructor() && Ctor->inits().empty())
156-
return true;
157-
158153
return false;
159154
}
160155

clang/test/AST/ByteCode/records.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1964,3 +1964,39 @@ namespace FieldLifetimeNotStarted {
19641964
// both-note {{declared here}} \
19651965
// both-note {{in implicit default constructor for 'FieldLifetimeNotStarted::R' first required here}}
19661966
}
1967+
1968+
namespace EmptyRecords {
1969+
struct E1 {} e1;
1970+
union E2 {} e2; // both-note 4{{here}}
1971+
struct E3 : E1 {} e3;
1972+
1973+
template<typename E>
1974+
constexpr int f(E &a, int kind) {
1975+
switch (kind) {
1976+
case 0: { E e(a); return 0; } // both-note {{read}} \
1977+
// both-note {{in call}}
1978+
case 1: { E e(static_cast<E&&>(a)); return 0; } // both-note {{read}} \
1979+
// both-note {{in call}}
1980+
case 2: { E e; e = a; return 0; } // both-note {{read}} \
1981+
// both-note {{in call}}
1982+
case 3: { E e; e = static_cast<E&&>(a); return 0; } // both-note {{read}} \
1983+
// both-note {{in call}}
1984+
}
1985+
}
1986+
constexpr int test1 = f(e1, 0);
1987+
constexpr int test2 = f(e2, 0); // both-error {{constant expression}} \
1988+
// both-note {{in call}}
1989+
constexpr int test3 = f(e3, 0);
1990+
constexpr int test4 = f(e1, 1);
1991+
constexpr int test5 = f(e2, 1); // both-error {{constant expression}} \
1992+
// both-note {{in call}}
1993+
constexpr int test6 = f(e3, 1);
1994+
constexpr int test7 = f(e1, 2);
1995+
constexpr int test8 = f(e2, 2); // both-error {{constant expression}} \
1996+
// both-note {{in call}}
1997+
constexpr int test9 = f(e3, 2);
1998+
constexpr int testa = f(e1, 3);
1999+
constexpr int testb = f(e2, 3); // both-error {{constant expression}} \
2000+
// both-note {{in call}}
2001+
constexpr int testc = f(e3, 3);
2002+
}

0 commit comments

Comments
 (0)