summaryrefslogtreecommitdiff
path: root/clang/lib/AST/ByteCode
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/AST/ByteCode')
-rw-r--r--clang/lib/AST/ByteCode/Compiler.cpp190
-rw-r--r--clang/lib/AST/ByteCode/Compiler.h4
-rw-r--r--clang/lib/AST/ByteCode/Descriptor.cpp2
-rw-r--r--clang/lib/AST/ByteCode/Descriptor.h4
-rw-r--r--clang/lib/AST/ByteCode/Disasm.cpp1
-rw-r--r--clang/lib/AST/ByteCode/Interp.cpp22
-rw-r--r--clang/lib/AST/ByteCode/Interp.h209
-rw-r--r--clang/lib/AST/ByteCode/InterpBlock.h5
-rw-r--r--clang/lib/AST/ByteCode/InterpBuiltin.cpp3
-rw-r--r--clang/lib/AST/ByteCode/InterpFrame.cpp5
-rw-r--r--clang/lib/AST/ByteCode/Opcodes.td28
-rw-r--r--clang/lib/AST/ByteCode/Pointer.cpp11
-rw-r--r--clang/lib/AST/ByteCode/Pointer.h5
-rw-r--r--clang/lib/AST/ByteCode/State.h1
14 files changed, 364 insertions, 126 deletions
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index afa3b7ea7de7..ea473730350b 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -25,6 +25,34 @@ using APSInt = llvm::APSInt;
namespace clang {
namespace interp {
+static bool refersToUnion(const Expr *E) {
+ for (;;) {
+ if (const auto *ME = dyn_cast<MemberExpr>(E)) {
+ if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
+ FD && FD->getParent()->isUnion())
+ return true;
+ E = ME->getBase();
+ continue;
+ }
+
+ if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
+ E = ASE->getBase()->IgnoreImplicit();
+ continue;
+ }
+
+ if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E);
+ ICE && (ICE->getCastKind() == CK_NoOp ||
+ ICE->getCastKind() == CK_DerivedToBase ||
+ ICE->getCastKind() == CK_UncheckedDerivedToBase)) {
+ E = ICE->getSubExpr();
+ continue;
+ }
+
+ break;
+ }
+ return false;
+}
+
static std::optional<bool> getBoolValue(const Expr *E) {
if (const auto *CE = dyn_cast_if_present<ConstantExpr>(E);
CE && CE->hasAPValueResult() &&
@@ -880,22 +908,11 @@ bool Compiler<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
return this->VisitPointerArithBinOp(BO);
}
- // Assignments require us to evalute the RHS first.
- if (BO->getOpcode() == BO_Assign) {
-
- if (!visit(RHS) || !visit(LHS))
- return false;
-
- // We don't support assignments in C.
- if (!Ctx.getLangOpts().CPlusPlus && !this->emitInvalid(BO))
- return false;
+ if (BO->getOpcode() == BO_Assign)
+ return this->visitAssignment(LHS, RHS, BO);
- if (!this->emitFlip(*LT, *RT, BO))
- return false;
- } else {
- if (!visit(LHS) || !visit(RHS))
- return false;
- }
+ if (!visit(LHS) || !visit(RHS))
+ return false;
// For languages such as C, cast the result of one
// of our comparision opcodes to T (which is usually int).
@@ -946,22 +963,6 @@ bool Compiler<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
if (BO->getType()->isFloatingType())
return Discard(this->emitDivf(getFPOptions(BO), BO));
return Discard(this->emitDiv(*T, BO));
- case BO_Assign:
- if (DiscardResult)
- return LHS->refersToBitField() ? this->emitStoreBitFieldPop(*T, BO)
- : this->emitStorePop(*T, BO);
- if (LHS->refersToBitField()) {
- if (!this->emitStoreBitField(*T, BO))
- return false;
- } else {
- if (!this->emitStore(*T, BO))
- return false;
- }
- // Assignments aren't necessarily lvalues in C.
- // Load from them in that case.
- if (!BO->isLValue())
- return this->emitLoadPop(*T, BO);
- return true;
case BO_And:
return Discard(this->emitBitAnd(*T, BO));
case BO_Or:
@@ -1790,19 +1791,26 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
return this->delegate(Inits[0]);
auto initPrimitiveField = [=](const Record::Field *FieldToInit,
- const Expr *Init, PrimType T) -> bool {
+ const Expr *Init, PrimType T,
+ bool Activate = false) -> bool {
InitStackScope<Emitter> ISS(this, isa<CXXDefaultInitExpr>(Init));
InitLinkScope<Emitter> ILS(this, InitLink::Field(FieldToInit->Offset));
if (!this->visit(Init))
return false;
- if (FieldToInit->isBitField())
+ bool BitField = FieldToInit->isBitField();
+ if (BitField && Activate)
+ return this->emitInitBitFieldActivate(T, FieldToInit, E);
+ if (BitField)
return this->emitInitBitField(T, FieldToInit, E);
+ if (Activate)
+ return this->emitInitFieldActivate(T, FieldToInit->Offset, E);
return this->emitInitField(T, FieldToInit->Offset, E);
};
auto initCompositeField = [=](const Record::Field *FieldToInit,
- const Expr *Init) -> bool {
+ const Expr *Init,
+ bool Activate = false) -> bool {
InitStackScope<Emitter> ISS(this, isa<CXXDefaultInitExpr>(Init));
InitLinkScope<Emitter> ILS(this, InitLink::Field(FieldToInit->Offset));
@@ -1810,6 +1818,10 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
// on the stack and recurse into visitInitializer().
if (!this->emitGetPtrField(FieldToInit->Offset, Init))
return false;
+
+ if (Activate && !this->emitActivate(E))
+ return false;
+
if (!this->visitInitializer(Init))
return false;
return this->emitPopPtr(E);
@@ -1829,10 +1841,10 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
const Record::Field *FieldToInit = R->getField(FToInit);
if (std::optional<PrimType> T = classify(Init)) {
- if (!initPrimitiveField(FieldToInit, Init, *T))
+ if (!initPrimitiveField(FieldToInit, Init, *T, /*Activate=*/true))
return false;
} else {
- if (!initCompositeField(FieldToInit, Init))
+ if (!initCompositeField(FieldToInit, Init, /*Activate=*/true))
return false;
}
}
@@ -2023,7 +2035,8 @@ bool Compiler<Emitter>::visitArrayElemInit(unsigned ElemIndex, const Expr *Init,
template <class Emitter>
bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args,
- const FunctionDecl *FuncDecl) {
+ const FunctionDecl *FuncDecl,
+ bool Activate) {
assert(VarScope->getKind() == ScopeKind::Call);
llvm::BitVector NonNullArgs = collectNonNullArgs(FuncDecl, Args);
@@ -2046,6 +2059,11 @@ bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args,
return false;
}
+ if (ArgIndex == 1 && Activate) {
+ if (!this->emitActivate(Arg))
+ return false;
+ }
+
if (FuncDecl && NonNullArgs[ArgIndex]) {
PrimType ArgT = classify(Arg).value_or(PT_Ptr);
if (ArgT == PT_Ptr) {
@@ -4227,10 +4245,13 @@ bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R,
PrimType T = classifyPrim(D->getType());
if (!this->visitZeroInitializer(T, QT, E))
return false;
+ if (R->isUnion()) {
+ if (!this->emitInitFieldActivate(T, Field.Offset, E))
+ return false;
+ break;
+ }
if (!this->emitInitField(T, Field.Offset, E))
return false;
- if (R->isUnion())
- break;
continue;
}
@@ -4256,13 +4277,15 @@ bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R,
} else
return false;
- if (!this->emitFinishInitPop(E))
- return false;
-
// C++11 [dcl.init]p5: If T is a (possibly cv-qualified) union type, the
// object's first non-static named data member is zero-initialized
- if (R->isUnion())
+ if (R->isUnion()) {
+ if (!this->emitFinishInitActivatePop(E))
+ return false;
break;
+ }
+ if (!this->emitFinishInitPop(E))
+ return false;
}
for (const Record::Base &B : R->bases()) {
@@ -4326,6 +4349,59 @@ bool Compiler<Emitter>::visitZeroArrayInitializer(QualType T, const Expr *E) {
}
template <class Emitter>
+bool Compiler<Emitter>::visitAssignment(const Expr *LHS, const Expr *RHS,
+ const Expr *E) {
+ if (!classify(E->getType()))
+ return false;
+
+ if (!this->visit(RHS))
+ return false;
+ if (!this->visit(LHS))
+ return false;
+
+ // We don't support assignments in C.
+ if (!Ctx.getLangOpts().CPlusPlus && !this->emitInvalid(E))
+ return false;
+
+ PrimType RHT = classifyPrim(RHS);
+ bool Activates = refersToUnion(LHS);
+ bool BitField = LHS->refersToBitField();
+
+ if (!this->emitFlip(PT_Ptr, RHT, E))
+ return false;
+
+ if (DiscardResult) {
+ if (BitField && Activates)
+ return this->emitStoreBitFieldActivatePop(RHT, E);
+ if (BitField)
+ return this->emitStoreBitFieldPop(RHT, E);
+ if (Activates)
+ return this->emitStoreActivatePop(RHT, E);
+ // Otherwise, regular non-activating store.
+ return this->emitStorePop(RHT, E);
+ }
+
+ auto maybeLoad = [&](bool Result) -> bool {
+ if (!Result)
+ return false;
+ // Assignments aren't necessarily lvalues in C.
+ // Load from them in that case.
+ if (!E->isLValue())
+ return this->emitLoadPop(RHT, E);
+ return true;
+ };
+
+ if (BitField && Activates)
+ return maybeLoad(this->emitStoreBitFieldActivate(RHT, E));
+ if (BitField)
+ return maybeLoad(this->emitStoreBitField(RHT, E));
+ if (Activates)
+ return maybeLoad(this->emitStoreActivate(RHT, E));
+ // Otherwise, regular non-activating store.
+ return maybeLoad(this->emitStore(RHT, E));
+}
+
+template <class Emitter>
template <typename T>
bool Compiler<Emitter>::emitConst(T Value, PrimType Ty, const Expr *E) {
switch (Ty) {
@@ -5067,7 +5143,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
return false;
}
- if (!this->visitCallArgs(Args, FuncDecl))
+ if (!this->visitCallArgs(Args, FuncDecl, IsAssignmentOperatorCall))
return false;
// Undo the argument reversal we did earlier.
@@ -5851,7 +5927,8 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
assert(!ReturnType);
auto emitFieldInitializer = [&](const Record::Field *F, unsigned FieldOffset,
- const Expr *InitExpr) -> bool {
+ const Expr *InitExpr,
+ bool Activate = false) -> bool {
// We don't know what to do with these, so just return false.
if (InitExpr->getType().isNull())
return false;
@@ -5860,8 +5937,13 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
if (!this->visit(InitExpr))
return false;
- if (F->isBitField())
+ bool BitField = F->isBitField();
+ if (BitField && Activate)
+ return this->emitInitThisBitFieldActivate(*T, F, FieldOffset, InitExpr);
+ if (BitField)
return this->emitInitThisBitField(*T, F, FieldOffset, InitExpr);
+ if (Activate)
+ return this->emitInitThisFieldActivate(*T, FieldOffset, InitExpr);
return this->emitInitThisField(*T, FieldOffset, InitExpr);
}
// Non-primitive case. Get a pointer to the field-to-initialize
@@ -5870,6 +5952,9 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
if (!this->emitGetPtrThisField(FieldOffset, InitExpr))
return false;
+ if (Activate && !this->emitActivate(InitExpr))
+ return false;
+
if (!this->visitInitializer(InitExpr))
return false;
@@ -5880,8 +5965,9 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
const Record *R = this->getRecord(RD);
if (!R)
return false;
+ bool IsUnion = R->isUnion();
- if (R->isUnion() && Ctor->isCopyOrMoveConstructor()) {
+ if (IsUnion && Ctor->isCopyOrMoveConstructor()) {
if (R->getNumFields() == 0)
return this->emitRetVoid(Ctor);
// union copy and move ctors are special.
@@ -5908,7 +5994,7 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
if (const FieldDecl *Member = Init->getMember()) {
const Record::Field *F = R->getField(Member);
- if (!emitFieldInitializer(F, F->Offset, InitExpr))
+ if (!emitFieldInitializer(F, F->Offset, InitExpr, IsUnion))
return false;
} else if (const Type *Base = Init->getBaseClass()) {
const auto *BaseDecl = Base->getAsCXXRecordDecl();
@@ -5928,11 +6014,15 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
return false;
}
+ if (IsUnion && !this->emitActivate(InitExpr))
+ return false;
+
if (!this->visitInitializer(InitExpr))
return false;
if (!this->emitFinishInitPop(InitExpr))
return false;
} else if (const IndirectFieldDecl *IFD = Init->getIndirectMember()) {
+
assert(IFD->getChainingSize() >= 2);
unsigned NestedFieldOffset = 0;
@@ -5944,12 +6034,14 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
NestedField = FieldRecord->getField(FD);
assert(NestedField);
+ IsUnion = IsUnion || FieldRecord->isUnion();
NestedFieldOffset += NestedField->Offset;
}
assert(NestedField);
- if (!emitFieldInitializer(NestedField, NestedFieldOffset, InitExpr))
+ if (!emitFieldInitializer(NestedField, NestedFieldOffset, InitExpr,
+ IsUnion))
return false;
// Mark all chain links as initialized.
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 05ba7460b343..debee6726853 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -307,7 +307,8 @@ protected:
const Expr *E);
bool visitArrayElemInit(unsigned ElemIndex, const Expr *Init,
std::optional<PrimType> InitT);
- bool visitCallArgs(ArrayRef<const Expr *> Args, const FunctionDecl *FuncDecl);
+ bool visitCallArgs(ArrayRef<const Expr *> Args, const FunctionDecl *FuncDecl,
+ bool Activate);
/// Creates a local primitive value.
unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsConst,
@@ -342,6 +343,7 @@ private:
bool visitZeroInitializer(PrimType T, QualType QT, const Expr *E);
bool visitZeroRecordInitializer(const Record *R, const Expr *E);
bool visitZeroArrayInitializer(QualType T, const Expr *E);
+ bool visitAssignment(const Expr *LHS, const Expr *RHS, const Expr *E);
/// Emits an APSInt constant.
bool emitConst(const llvm::APSInt &Value, PrimType Ty, const Expr *E);
diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp
index c89eca9bef44..5b9f44518fcc 100644
--- a/clang/lib/AST/ByteCode/Descriptor.cpp
+++ b/clang/lib/AST/ByteCode/Descriptor.cpp
@@ -162,6 +162,8 @@ static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
Desc->IsConst = IsConst || D->IsConst;
Desc->IsFieldMutable = IsMutable || D->IsMutable;
Desc->IsVolatile = IsVolatile || D->IsVolatile;
+ // True if this field is const AND the parent is mutable.
+ Desc->IsConstInMutable = Desc->IsConst && IsMutable;
if (auto Fn = D->CtorFn)
Fn(B, Ptr + FieldOffset, Desc->IsConst, Desc->IsFieldMutable,
diff --git a/clang/lib/AST/ByteCode/Descriptor.h b/clang/lib/AST/ByteCode/Descriptor.h
index 4591eabb69bb..0227e4c0c7e3 100644
--- a/clang/lib/AST/ByteCode/Descriptor.h
+++ b/clang/lib/AST/ByteCode/Descriptor.h
@@ -101,6 +101,10 @@ struct InlineDescriptor {
/// Flag indicating if the field is mutable (if in a record).
LLVM_PREFERRED_TYPE(bool)
unsigned IsFieldMutable : 1;
+ /// Flag indicating if this field is a const field nested in
+ /// a mutable parent field.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned IsConstInMutable : 1;
/// Flag indicating if the field is an element of a composite array.
LLVM_PREFERRED_TYPE(bool)
unsigned IsArrayElement : 1;
diff --git a/clang/lib/AST/ByteCode/Disasm.cpp b/clang/lib/AST/ByteCode/Disasm.cpp
index f64501f4a31e..74399d177b5a 100644
--- a/clang/lib/AST/ByteCode/Disasm.cpp
+++ b/clang/lib/AST/ByteCode/Disasm.cpp
@@ -445,6 +445,7 @@ LLVM_DUMP_METHOD void InlineDescriptor::dump(llvm::raw_ostream &OS) const {
OS << "InUnion: " << InUnion << "\n";
OS << "IsFieldMutable: " << IsFieldMutable << "\n";
OS << "IsArrayElement: " << IsArrayElement << "\n";
+ OS << "IsConstInMutable: " << IsConstInMutable << '\n';
OS << "Desc: ";
if (Desc)
Desc->dump(OS);
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 457de2bed37d..df5e3be83d74 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -326,7 +326,6 @@ bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return true;
assert(Ptr.inUnion());
- assert(Ptr.isField() && Ptr.getField());
Pointer U = Ptr.getBase();
Pointer C = Ptr;
@@ -567,7 +566,10 @@ bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
assert(Ptr.isLive() && "Pointer is not live");
- if (!Ptr.isConst() || Ptr.isMutable())
+ if (!Ptr.isConst())
+ return true;
+
+ if (Ptr.isMutable() && !Ptr.isConstInMutable())
return true;
if (!Ptr.isBlockPointer())
@@ -575,7 +577,7 @@ bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
// The This pointer is writable in constructors and destructors,
// even if isConst() returns true.
- if (llvm::find(S.InitializingBlocks, Ptr.block()))
+ if (llvm::is_contained(S.InitializingBlocks, Ptr.block()))
return true;
const QualType Ty = Ptr.getType();
@@ -805,6 +807,8 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return false;
if (!CheckRange(S, OpPC, Ptr, AK_Assign))
return false;
+ if (!CheckActive(S, OpPC, Ptr, AK_Assign))
+ return false;
if (!CheckGlobal(S, OpPC, Ptr))
return false;
if (!CheckConst(S, OpPC, Ptr))
@@ -814,7 +818,7 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return true;
}
-bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+static bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
return false;
if (!Ptr.isDummy()) {
@@ -936,7 +940,7 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
return false;
}
-bool CheckCallDepth(InterpState &S, CodePtr OpPC) {
+static bool CheckCallDepth(InterpState &S, CodePtr OpPC) {
if ((S.Current->getDepth() + 1) > S.getLangOpts().ConstexprCallDepth) {
S.FFDiag(S.Current->getSource(OpPC),
diag::note_constexpr_depth_limit_exceeded)
@@ -1091,8 +1095,8 @@ bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return false;
}
-bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F,
- const CallExpr *CE, unsigned ArgSize) {
+static bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F,
+ const CallExpr *CE, unsigned ArgSize) {
auto Args = ArrayRef(CE->getArgs(), CE->getNumArgs());
auto NonNullArgs = collectNonNullArgs(F->getDecl(), Args);
unsigned Offset = 0;
@@ -1500,7 +1504,6 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
if (!CheckInvoke(S, OpPC, ThisPtr))
return cleanup();
if (!Func->isConstructor() && !Func->isDestructor() &&
- !Func->isCopyOrMoveOperator() &&
!CheckActive(S, OpPC, ThisPtr, AK_MemberCall))
return false;
}
@@ -1773,6 +1776,9 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
std::optional<uint64_t> ArraySize) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
+ if (Ptr.inUnion() && Ptr.getBase().getRecord()->isUnion())
+ Ptr.activate();
+
// Similar to CheckStore(), but with the additional CheckTemporary() call and
// the AccessKinds are different.
if (!CheckTemporary(S, OpPC, Ptr, AK_Construct))
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 9d17f96c97c8..ce0ebdd8321b 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -98,26 +98,12 @@ bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
/// Checks if a value can be stored in a block.
bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
-/// Checks if a method can be invoked on an object.
-bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
-
/// Checks if a value can be initialized.
bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
-/// Checks if a method can be called.
-bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F);
-
-/// Checks if calling the currently active function would exceed
-/// the allowed call depth.
-bool CheckCallDepth(InterpState &S, CodePtr OpPC);
-
/// Checks the 'this' pointer.
bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This);
-/// Checks if all the arguments annotated as 'nonnull' are in fact not null.
-bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F,
- const CallExpr *CE, unsigned ArgSize);
-
/// Checks if dynamic memory allocation is available in the current
/// language mode.
bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr OpPC);
@@ -1592,6 +1578,21 @@ bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
if (!CheckThis(S, OpPC, This))
return false;
const Pointer &Field = This.atField(I);
+ assert(Field.canBeInitialized());
+ Field.deref<T>() = S.Stk.pop<T>();
+ Field.initialize();
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitThisFieldActivate(InterpState &S, CodePtr OpPC, uint32_t I) {
+ if (S.checkingPotentialConstantExpression() && S.Current->getDepth() == 0)
+ return false;
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ const Pointer &Field = This.atField(I);
+ assert(Field.canBeInitialized());
Field.deref<T>() = S.Stk.pop<T>();
Field.activate();
Field.initialize();
@@ -1610,12 +1611,31 @@ bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F,
if (!CheckThis(S, OpPC, This))
return false;
const Pointer &Field = This.atField(FieldOffset);
+ assert(Field.canBeInitialized());
const auto &Value = S.Stk.pop<T>();
Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue());
Field.initialize();
return true;
}
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitThisBitFieldActivate(InterpState &S, CodePtr OpPC,
+ const Record::Field *F, uint32_t FieldOffset) {
+ assert(F->isBitField());
+ if (S.checkingPotentialConstantExpression() && S.Current->getDepth() == 0)
+ return false;
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ const Pointer &Field = This.atField(FieldOffset);
+ assert(Field.canBeInitialized());
+ const auto &Value = S.Stk.pop<T>();
+ Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue());
+ Field.initialize();
+ Field.activate();
+ return true;
+}
+
/// 1) Pops the value from the stack
/// 2) Peeks a pointer from the stack
/// 3) Pushes the value to field I of the pointer on the stack
@@ -1627,6 +1647,18 @@ bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) {
return false;
const Pointer &Field = Ptr.atField(I);
Field.deref<T>() = Value;
+ Field.initialize();
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitFieldActivate(InterpState &S, CodePtr OpPC, uint32_t I) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+ if (!CheckRange(S, OpPC, Ptr, CSK_Field))
+ return false;
+ const Pointer &Field = Ptr.atField(I);
+ Field.deref<T>() = Value;
Field.activate();
Field.initialize();
return true;
@@ -1653,6 +1685,32 @@ bool InitBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
} else {
Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue());
}
+ Field.initialize();
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitBitFieldActivate(InterpState &S, CodePtr OpPC,
+ const Record::Field *F) {
+ assert(F->isBitField());
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Field = S.Stk.peek<Pointer>().atField(F->Offset);
+
+ if constexpr (needsAlloc<T>()) {
+ T Result = S.allocAP<T>(Value.bitWidth());
+ if (T::isSigned())
+ Result.copy(Value.toAPSInt()
+ .trunc(F->Decl->getBitWidthValue())
+ .sextOrTrunc(Value.bitWidth()));
+ else
+ Result.copy(Value.toAPSInt()
+ .trunc(F->Decl->getBitWidthValue())
+ .zextOrTrunc(Value.bitWidth()));
+
+ Field.deref<T>() = Result;
+ } else {
+ Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue());
+ }
Field.activate();
Field.initialize();
return true;
@@ -1695,32 +1753,6 @@ inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
return true;
}
-inline bool GetPtrActiveField(InterpState &S, CodePtr OpPC, uint32_t Off) {
- const Pointer &Ptr = S.Stk.pop<Pointer>();
- if (!CheckNull(S, OpPC, Ptr, CSK_Field))
- return false;
- if (!CheckRange(S, OpPC, Ptr, CSK_Field))
- return false;
- Pointer Field = Ptr.atField(Off);
- Ptr.deactivate();
- Field.activate();
- S.Stk.push<Pointer>(std::move(Field));
- return true;
-}
-
-inline bool GetPtrActiveThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
- if (S.checkingPotentialConstantExpression())
- return false;
- const Pointer &This = S.Current->getThis();
- if (!CheckThis(S, OpPC, This))
- return false;
- Pointer Field = This.atField(Off);
- This.deactivate();
- Field.activate();
- S.Stk.push<Pointer>(std::move(Field));
- return true;
-}
-
inline bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off,
bool NullOK, const Type *TargetType) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
@@ -1816,6 +1848,20 @@ inline bool GetPtrThisBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
inline bool FinishInitPop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (Ptr.canBeInitialized())
+ Ptr.initialize();
+ return true;
+}
+
+inline bool FinishInit(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+ if (Ptr.canBeInitialized())
+ Ptr.initialize();
+ return true;
+}
+
+inline bool FinishInitActivate(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
if (Ptr.canBeInitialized()) {
Ptr.initialize();
Ptr.activate();
@@ -1823,8 +1869,8 @@ inline bool FinishInitPop(InterpState &S, CodePtr OpPC) {
return true;
}
-inline bool FinishInit(InterpState &S, CodePtr OpPC) {
- const Pointer &Ptr = S.Stk.peek<Pointer>();
+inline bool FinishInitActivatePop(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
if (Ptr.canBeInitialized()) {
Ptr.initialize();
Ptr.activate();
@@ -1902,10 +1948,8 @@ bool Store(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
- if (Ptr.canBeInitialized()) {
+ if (Ptr.canBeInitialized())
Ptr.initialize();
- Ptr.activate();
- }
Ptr.deref<T>() = Value;
return true;
}
@@ -1916,10 +1960,46 @@ bool StorePop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
+ if (Ptr.canBeInitialized())
+ Ptr.initialize();
+ Ptr.deref<T>() = Value;
+ return true;
+}
+
+static inline bool Activate(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+ if (Ptr.canBeInitialized())
+ Ptr.activate();
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool StoreActivate(InterpState &S, CodePtr OpPC) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+
+ if (Ptr.canBeInitialized()) {
+ Ptr.initialize();
+ Ptr.activate();
+ }
+
+ if (!CheckStore(S, OpPC, Ptr))
+ return false;
+ Ptr.deref<T>() = Value;
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool StoreActivatePop(InterpState &S, CodePtr OpPC) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
if (Ptr.canBeInitialized()) {
Ptr.initialize();
Ptr.activate();
}
+ if (!CheckStore(S, OpPC, Ptr))
+ return false;
Ptr.deref<T>() = Value;
return true;
}
@@ -1930,10 +2010,8 @@ bool StoreBitField(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
- if (Ptr.canBeInitialized()) {
+ if (Ptr.canBeInitialized())
Ptr.initialize();
- Ptr.activate();
- }
if (const auto *FD = Ptr.getField())
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue());
else
@@ -1947,10 +2025,43 @@ bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
+ if (Ptr.canBeInitialized())
+ Ptr.initialize();
+ if (const auto *FD = Ptr.getField())
+ Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue());
+ else
+ Ptr.deref<T>() = Value;
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool StoreBitFieldActivate(InterpState &S, CodePtr OpPC) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+ if (Ptr.canBeInitialized()) {
+ Ptr.initialize();
+ Ptr.activate();
+ }
+ if (!CheckStore(S, OpPC, Ptr))
+ return false;
+ if (const auto *FD = Ptr.getField())
+ Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue());
+ else
+ Ptr.deref<T>() = Value;
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool StoreBitFieldActivatePop(InterpState &S, CodePtr OpPC) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
if (Ptr.canBeInitialized()) {
Ptr.initialize();
Ptr.activate();
}
+ if (!CheckStore(S, OpPC, Ptr))
+ return false;
if (const auto *FD = Ptr.getField())
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue());
else
@@ -1964,7 +2075,6 @@ bool Init(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (!CheckInit(S, OpPC, Ptr))
return false;
- Ptr.activate();
Ptr.initialize();
new (&Ptr.deref<T>()) T(Value);
return true;
@@ -1976,7 +2086,6 @@ bool InitPop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckInit(S, OpPC, Ptr))
return false;
- Ptr.activate();
Ptr.initialize();
new (&Ptr.deref<T>()) T(Value);
return true;
diff --git a/clang/lib/AST/ByteCode/InterpBlock.h b/clang/lib/AST/ByteCode/InterpBlock.h
index 7798b6f886a8..51622238e275 100644
--- a/clang/lib/AST/ByteCode/InterpBlock.h
+++ b/clang/lib/AST/ByteCode/InterpBlock.h
@@ -14,11 +14,6 @@
#define LLVM_CLANG_AST_INTERP_BLOCK_H
#include "Descriptor.h"
-#include "clang/AST/ComparisonCategories.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/AST/Expr.h"
-#include "llvm/ADT/PointerUnion.h"
#include "llvm/Support/raw_ostream.h"
namespace clang {
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index de0b97fd93c7..462b9a11e0a5 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -7,7 +7,6 @@
//===----------------------------------------------------------------------===//
#include "../ExprConstShared.h"
#include "Boolean.h"
-#include "Compiler.h"
#include "EvalEmitter.h"
#include "Interp.h"
#include "InterpBuiltinBitCast.h"
@@ -2906,6 +2905,8 @@ static bool copyRecord(InterpState &S, CodePtr OpPC, const Pointer &Src,
if (!copyField(F, /*Activate=*/true))
return false;
} else {
+ if (!CheckMutable(S, OpPC, Src.atField(F.Offset)))
+ return false;
Pointer DestField = Dest.atField(F.Offset);
zeroAll(DestField);
}
diff --git a/clang/lib/AST/ByteCode/InterpFrame.cpp b/clang/lib/AST/ByteCode/InterpFrame.cpp
index a5a4bd25fe71..d62a4f6275b5 100644
--- a/clang/lib/AST/ByteCode/InterpFrame.cpp
+++ b/clang/lib/AST/ByteCode/InterpFrame.cpp
@@ -128,6 +128,11 @@ static bool shouldSkipInBacktrace(const Function *F) {
if (FD->getDeclName().getCXXOverloadedOperator() == OO_New ||
FD->getDeclName().getCXXOverloadedOperator() == OO_Array_New)
return true;
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
+ MD && MD->getParent()->isAnonymousStructOrUnion())
+ return true;
+
return false;
}
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index 57e01f7bd9da..804853d29512 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -315,10 +315,6 @@ def GetPtrGlobal : OffsetOpcode;
// [Pointer] -> [Pointer]
def GetPtrField : OffsetOpcode;
def GetPtrFieldPop : OffsetOpcode;
-// [Pointer] -> [Pointer]
-def GetPtrActiveField : OffsetOpcode;
-// [] -> [Pointer]
-def GetPtrActiveThisField : OffsetOpcode;
// [] -> [Pointer]
def GetPtrThisField : OffsetOpcode;
// [Pointer] -> [Pointer]
@@ -330,9 +326,10 @@ def GetMemberPtrBasePop : Opcode {
let Args = [ArgSint32];
}
-
def FinishInitPop : Opcode;
-def FinishInit : Opcode;
+def FinishInit : Opcode;
+def FinishInitActivate : Opcode;
+def FinishInitActivatePop : Opcode;
def FinishInitGlobal : Opcode;
def GetPtrDerivedPop : Opcode { let Args = [ArgUint32, ArgBool, ArgTypePtr]; }
@@ -460,16 +457,24 @@ def SetThisField : AccessOpcode;
// [Value] -> []
def InitThisField : AccessOpcode;
+def InitThisFieldActivate : AccessOpcode;
// [Value] -> []
def InitThisBitField : Opcode {
let Types = [AluTypeClass];
let Args = [ArgRecordField, ArgUint32];
let HasGroup = 1;
}
+def InitThisBitFieldActivate : Opcode {
+ let Types = [AluTypeClass];
+ let Args = [ArgRecordField, ArgUint32];
+ let HasGroup = 1;
+}
// [Pointer, Value] -> []
def InitField : AccessOpcode;
+def InitFieldActivate : AccessOpcode;
// [Pointer, Value] -> []
def InitBitField : BitFieldOpcode;
+def InitBitFieldActivate : BitFieldOpcode;
//===----------------------------------------------------------------------===//
// Pointer access
@@ -495,15 +500,16 @@ class StoreBitFieldOpcode : Opcode {
let HasGroup = 1;
}
-// [Pointer, Value] -> [Pointer]
def Store : StoreOpcode {}
-// [Pointer, Value] -> []
def StorePop : StoreOpcode {}
-
-// [Pointer, Value] -> [Pointer]
+def StoreActivatePop : StoreOpcode {}
+def StoreActivate : StoreOpcode {}
def StoreBitField : StoreBitFieldOpcode {}
-// [Pointer, Value] -> []
def StoreBitFieldPop : StoreBitFieldOpcode {}
+def StoreBitFieldActivate : StoreBitFieldOpcode {}
+def StoreBitFieldActivatePop : StoreBitFieldOpcode {}
+
+def Activate : Opcode {}
// [Pointer, Value] -> []
def Init : StoreOpcode {}
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp
index 159b4238a6a0..2f9ecf98e558 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -502,8 +502,17 @@ void Pointer::activate() const {
if (!getInlineDesc()->InUnion)
return;
- auto activate = [](Pointer &P) -> void {
+ std::function<void(Pointer &)> activate;
+ activate = [&activate](Pointer &P) -> void {
P.getInlineDesc()->IsActive = true;
+ if (const Record *R = P.getRecord(); R && !R->isUnion()) {
+ for (const Record::Field &F : R->fields()) {
+ Pointer FieldPtr = P.atField(F.Offset);
+ if (!FieldPtr.getInlineDesc()->IsActive)
+ activate(FieldPtr);
+ }
+ // FIXME: Bases?
+ }
};
std::function<void(Pointer &)> deactivate;
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index e6a64e6658f0..da74013cf83a 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -576,6 +576,11 @@ public:
return true;
return isRoot() ? getDeclDesc()->IsConst : getInlineDesc()->IsConst;
}
+ bool isConstInMutable() const {
+ if (!isBlockPointer())
+ return false;
+ return isRoot() ? false : getInlineDesc()->IsConstInMutable;
+ }
/// Checks if an object or a subfield is volatile.
bool isVolatile() const {
diff --git a/clang/lib/AST/ByteCode/State.h b/clang/lib/AST/ByteCode/State.h
index 9a81fa6b7d22..6fc33222ac95 100644
--- a/clang/lib/AST/ByteCode/State.h
+++ b/clang/lib/AST/ByteCode/State.h
@@ -35,6 +35,7 @@ enum AccessKinds {
AK_Construct,
AK_Destroy,
AK_IsWithinLifetime,
+ AK_Dereference
};
/// The order of this enum is important for diagnostics.