tbaeder updated this revision to Diff 484212.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D135750/new/
https://reviews.llvm.org/D135750
Files:
clang/lib/AST/Interp/ByteCodeExprGen.cpp
clang/lib/AST/Interp/ByteCodeStmtGen.cpp
clang/lib/AST/Interp/Context.cpp
clang/lib/AST/Interp/Descriptor.cpp
clang/lib/AST/Interp/Descriptor.h
clang/lib/AST/Interp/EvalEmitter.cpp
clang/lib/AST/Interp/Interp.h
clang/lib/AST/Interp/InterpBlock.h
clang/lib/AST/Interp/InterpFrame.cpp
clang/lib/AST/Interp/InterpFrame.h
clang/lib/AST/Interp/Pointer.cpp
clang/lib/AST/Interp/Pointer.h
clang/lib/AST/Interp/Program.cpp
clang/lib/AST/Interp/Program.h
clang/test/AST/Interp/cxx20.cpp
clang/test/AST/Interp/literals.cpp
clang/test/AST/Interp/loops.cpp
Index: clang/test/AST/Interp/loops.cpp
===================================================================
--- clang/test/AST/Interp/loops.cpp
+++ clang/test/AST/Interp/loops.cpp
@@ -5,6 +5,7 @@
// ref-no-diagnostics
// expected-no-diagnostics
+// expected-cpp20-no-diagnostics
namespace WhileLoop {
constexpr int f() {
@@ -165,8 +166,6 @@
static_assert(f5(true) == 8, "");
static_assert(f5(false) == 5, "");
- /// FIXME: This should be accepted in C++20 but is currently being rejected
- /// because the variable declaration doesn't have an initializier.
#if __cplusplus >= 202002L
constexpr int f6() {
int i;
@@ -176,7 +175,7 @@
} while (true);
return i;
}
- static_assert(f6() == 5, ""); // expected-cpp20-error {{not an integral constant}}
+ static_assert(f6() == 5, "");
#endif
#if 0
Index: clang/test/AST/Interp/literals.cpp
===================================================================
--- clang/test/AST/Interp/literals.cpp
+++ clang/test/AST/Interp/literals.cpp
@@ -407,8 +407,7 @@
return 1;
}
static_assert(uninit(), ""); // ref-error {{not an integral constant expression}} \
- // ref-note {{in call to 'uninit()'}} \
- // expected-error {{not an integral constant expression}}
+ // ref-note {{in call to 'uninit()'}}
constexpr int OverFlow() { // ref-error {{never produces a constant expression}}
int a = INT_MAX;
Index: clang/test/AST/Interp/cxx20.cpp
===================================================================
--- clang/test/AST/Interp/cxx20.cpp
+++ clang/test/AST/Interp/cxx20.cpp
@@ -56,33 +56,51 @@
}
static_assert(pointerAssign2() == 12, "");
-
constexpr int unInitLocal() {
int a;
- return a; // ref-note{{read of uninitialized object}}
+ return a; // ref-note {{read of uninitialized object}} \
+ // expected-note {{read of object outside its lifetime}}
+ // FIXME: ^^^ Wrong diagnostic.
}
-static_assert(unInitLocal() == 0, ""); // expected-error {{not an integral constant expression}} \
- // ref-error {{not an integral constant expression}} \
- // ref-note {{in call to 'unInitLocal()'}}
-
-/// TODO: The example above is correctly rejected by the new constexpr
-/// interpreter, but for the wrong reasons. We don't reject it because
-/// it is an uninitialized read, we reject it simply because
-/// the local variable does not have an initializer.
-///
-/// The code below should be accepted but is also being rejected
-/// right now.
-#if 0
+static_assert(unInitLocal() == 0, ""); // ref-error {{not an integral constant expression}} \
+ // ref-note {{in call to 'unInitLocal()'}} \
+ // expected-error {{not an integral constant expression}} \
+ // expected-note {{in call to 'unInitLocal()'}} \
+
constexpr int initializedLocal() {
int a;
- int b;
-
a = 20;
return a;
}
static_assert(initializedLocal() == 20);
-/// Similar here, but the uninitialized local is passed as a function parameter.
+constexpr int initializedLocal2() {
+ int a[2];
+ return *a; // expected-note {{read of object outside its lifetime}} \
+ // ref-note {{read of uninitialized object is not allowed in a constant expression}}
+}
+static_assert(initializedLocal2() == 20); // expected-error {{not an integral constant expression}} \
+ // expected-note {{in call to}} \
+ // ref-error {{not an integral constant expression}} \
+ // ref-note {{in call to}}
+
+
+struct Int { int a; };
+constexpr int initializedLocal3() {
+ Int i;
+ return i.a; // expected-note {{read of object outside its lifetime}} \
+ // ref-note {{read of uninitialized object is not allowed in a constant expression}}
+}
+static_assert(initializedLocal3() == 20); // expected-error {{not an integral constant expression}} \
+ // expected-note {{in call to}} \
+ // ref-error {{not an integral constant expression}} \
+ // ref-note {{in call to}}
+
+
+
+#if 0
+// FIXME: This code should be rejected because we pass an uninitialized value
+// as a function parameter.
constexpr int inc(int a) { return a + 1; }
constexpr int f() {
int i;
Index: clang/lib/AST/Interp/Program.h
===================================================================
--- clang/lib/AST/Interp/Program.h
+++ clang/lib/AST/Interp/Program.h
@@ -114,14 +114,15 @@
/// Creates a descriptor for a primitive type.
Descriptor *createDescriptor(const DeclTy &D, PrimType Type,
- bool IsConst = false,
- bool IsTemporary = false,
+ Descriptor::MetadataSize MDSize = {0},
+ bool IsConst = false, bool IsTemporary = false,
bool IsMutable = false) {
- return allocateDescriptor(D, Type, IsConst, IsTemporary, IsMutable);
+ return allocateDescriptor(D, Type, MDSize, IsConst, IsTemporary, IsMutable);
}
/// Creates a descriptor for a composite type.
Descriptor *createDescriptor(const DeclTy &D, const Type *Ty,
+ Descriptor::MetadataSize MDSize = {0},
bool IsConst = false, bool IsTemporary = false,
bool IsMutable = false,
const Expr *Init = nullptr);
Index: clang/lib/AST/Interp/Program.cpp
===================================================================
--- clang/lib/AST/Interp/Program.cpp
+++ clang/lib/AST/Interp/Program.cpp
@@ -53,10 +53,11 @@
}
// Create a descriptor for the string.
- Descriptor *Desc = allocateDescriptor(S, CharType, S->getLength() + 1,
- /*isConst=*/true,
- /*isTemporary=*/false,
- /*isMutable=*/false);
+ Descriptor *Desc =
+ allocateDescriptor(S, CharType, std::nullopt, S->getLength() + 1,
+ /*isConst=*/true,
+ /*isTemporary=*/false,
+ /*isMutable=*/false);
// Allocate storage for the string.
// The byte length does not include the null terminator.
@@ -184,9 +185,10 @@
const bool IsConst = Ty.isConstQualified();
const bool IsTemporary = D.dyn_cast<const Expr *>();
if (auto T = Ctx.classify(Ty)) {
- Desc = createDescriptor(D, *T, IsConst, IsTemporary);
+ Desc = createDescriptor(D, *T, std::nullopt, IsConst, IsTemporary);
} else {
- Desc = createDescriptor(D, Ty.getTypePtr(), IsConst, IsTemporary);
+ Desc = createDescriptor(D, Ty.getTypePtr(), std::nullopt, IsConst,
+ IsTemporary);
}
if (!Desc)
return {};
@@ -236,7 +238,7 @@
auto GetBaseDesc = [this](const RecordDecl *BD, Record *BR) -> Descriptor * {
if (!BR)
return nullptr;
- return allocateDescriptor(BD, BR, /*isConst=*/false,
+ return allocateDescriptor(BD, BR, std::nullopt, /*isConst=*/false,
/*isTemporary=*/false,
/*isMutable=*/false);
};
@@ -286,10 +288,10 @@
const bool IsMutable = FD->isMutable();
Descriptor *Desc;
if (std::optional<PrimType> T = Ctx.classify(FT)) {
- Desc = createDescriptor(FD, *T, IsConst, /*isTemporary=*/false,
- IsMutable);
+ Desc = createDescriptor(FD, *T, std::nullopt, IsConst,
+ /*isTemporary=*/false, IsMutable);
} else {
- Desc = createDescriptor(FD, FT.getTypePtr(), IsConst,
+ Desc = createDescriptor(FD, FT.getTypePtr(), std::nullopt, IsConst,
/*isTemporary=*/false, IsMutable);
}
if (!Desc)
@@ -305,12 +307,14 @@
}
Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
+ Descriptor::MetadataSize MDSize,
bool IsConst, bool IsTemporary,
bool IsMutable, const Expr *Init) {
// Classes and structures.
if (auto *RT = Ty->getAs<RecordType>()) {
if (auto *Record = getOrCreateRecord(RT->getDecl()))
- return allocateDescriptor(D, Record, IsConst, IsTemporary, IsMutable);
+ return allocateDescriptor(D, Record, MDSize, IsConst, IsTemporary,
+ IsMutable);
}
// Arrays.
@@ -325,21 +329,21 @@
if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems) {
return {};
}
- return allocateDescriptor(D, *T, NumElems, IsConst, IsTemporary,
+ return allocateDescriptor(D, *T, MDSize, NumElems, IsConst, IsTemporary,
IsMutable);
} else {
// Arrays of composites. In this case, the array is a list of pointers,
// followed by the actual elements.
- Descriptor *ElemDesc =
- createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary);
+ Descriptor *ElemDesc = createDescriptor(
+ D, ElemTy.getTypePtr(), std::nullopt, IsConst, IsTemporary);
if (!ElemDesc)
return nullptr;
InterpSize ElemSize =
ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems)
return {};
- return allocateDescriptor(D, ElemDesc, NumElems, IsConst, IsTemporary,
- IsMutable);
+ return allocateDescriptor(D, ElemDesc, MDSize, NumElems, IsConst,
+ IsTemporary, IsMutable);
}
}
@@ -350,8 +354,8 @@
return allocateDescriptor(D, *T, IsTemporary,
Descriptor::UnknownSize{});
} else {
- Descriptor *Desc =
- createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary);
+ Descriptor *Desc = createDescriptor(D, ElemTy.getTypePtr(), MDSize,
+ IsConst, IsTemporary);
if (!Desc)
return nullptr;
return allocateDescriptor(D, Desc, IsTemporary,
@@ -363,13 +367,15 @@
// Atomic types.
if (auto *AT = Ty->getAs<AtomicType>()) {
const Type *InnerTy = AT->getValueType().getTypePtr();
- return createDescriptor(D, InnerTy, IsConst, IsTemporary, IsMutable);
+ return createDescriptor(D, InnerTy, MDSize, IsConst, IsTemporary,
+ IsMutable);
}
// Complex types - represented as arrays of elements.
if (auto *CT = Ty->getAs<ComplexType>()) {
PrimType ElemTy = *Ctx.classify(CT->getElementType());
- return allocateDescriptor(D, ElemTy, 2, IsConst, IsTemporary, IsMutable);
+ return allocateDescriptor(D, ElemTy, MDSize, 2, IsConst, IsTemporary,
+ IsMutable);
}
return nullptr;
Index: clang/lib/AST/Interp/Pointer.h
===================================================================
--- clang/lib/AST/Interp/Pointer.h
+++ clang/lib/AST/Interp/Pointer.h
@@ -33,6 +33,31 @@
///
/// This object can be allocated into interpreter stack frames. If pointing to
/// a live block, it is a link in the chain of pointers pointing to the block.
+///
+/// In the simplest form, a Pointer has a Block* (the pointee) and both Base
+/// and Offset are 0, which means it will point to raw data.
+///
+/// The Base field is used to access metadata about the data. For primitive
+/// arrays, the Base is followed by an InitMap. In a variety of cases, the
+/// Base is preceded by an InlineDescriptor, which is used to track the
+/// initialization state, among other things.
+///
+/// The Offset field is used to access the actual data. In other words, the
+/// data the pointer decribes can be found at
+/// Pointee->rawData() + Pointer.Offset.
+///
+///
+/// Pointee Offset
+/// â â
+/// â â
+/// â¼ â¼
+/// âââââââââ¬âââââââââââââ¬ââââââââââ¬âââââââââââââââââââââââââââââ
+/// â Block â InlineDesc â InitMap â Actual Data â
+/// âââââââââ´âââââââââââââ´ââââââââââ´âââââââââââââââââââââââââââââ
+/// â²
+/// â
+/// â
+/// Base
class Pointer {
private:
static constexpr unsigned PastEndMark = (unsigned)-1;
@@ -41,6 +66,7 @@
public:
Pointer() {}
Pointer(Block *B);
+ Pointer(Block *B, unsigned BaseAndOffset);
Pointer(const Pointer &P);
Pointer(Pointer &&P);
~Pointer();
@@ -276,12 +302,12 @@
/// Dereferences the pointer, if it's live.
template <typename T> T &deref() const {
assert(isLive() && "Invalid pointer");
- return *reinterpret_cast<T *>(Pointee->data() + Offset);
+ return *reinterpret_cast<T *>(Pointee->rawData() + Offset);
}
/// Dereferences a primitive element.
template <typename T> T &elem(unsigned I) const {
- return reinterpret_cast<T *>(Pointee->data())[I];
+ return reinterpret_cast<T *>(Pointee->rawData())[I];
}
/// Initializes a field.
@@ -318,12 +344,13 @@
/// Returns a descriptor at a given offset.
InlineDescriptor *getDescriptor(unsigned Offset) const {
assert(Offset != 0 && "Not a nested pointer");
- return reinterpret_cast<InlineDescriptor *>(Pointee->data() + Offset) - 1;
+ return reinterpret_cast<InlineDescriptor *>(Pointee->rawData() + Offset) -
+ 1;
}
/// Returns a reference to the pointer which stores the initialization map.
InitMap *&getInitMap() const {
- return *reinterpret_cast<InitMap **>(Pointee->data() + Base);
+ return *reinterpret_cast<InitMap **>(Pointee->rawData() + Base);
}
/// The block the pointer is pointing to.
Index: clang/lib/AST/Interp/Pointer.cpp
===================================================================
--- clang/lib/AST/Interp/Pointer.cpp
+++ clang/lib/AST/Interp/Pointer.cpp
@@ -16,6 +16,9 @@
Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {}
+Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset)
+ : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
+
Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {}
Pointer::Pointer(Pointer &&P)
Index: clang/lib/AST/Interp/InterpFrame.h
===================================================================
--- clang/lib/AST/Interp/InterpFrame.h
+++ clang/lib/AST/Interp/InterpFrame.h
@@ -33,7 +33,7 @@
/// Creates a new frame for a method call.
InterpFrame(InterpState &S, const Function *Func, InterpFrame *Caller,
- CodePtr RetPC, Pointer &&This);
+ CodePtr RetPC);
/// Creates a new frame with the values that make sense.
/// I.e., the caller is the current frame of S,
@@ -76,10 +76,11 @@
/// Mutates a local variable.
template <typename T> void setLocal(unsigned Offset, const T &Value) {
localRef<T>(Offset) = Value;
+ localInlineDesc(Offset)->IsInitialized = true;
}
/// Returns a pointer to a local variables.
- Pointer getLocalPointer(unsigned Offset);
+ Pointer getLocalPointer(unsigned Offset) const;
/// Returns the value of an argument.
template <typename T> const T &getParam(unsigned Offset) const {
@@ -128,7 +129,7 @@
/// Returns an offset to a local.
template <typename T> T &localRef(unsigned Offset) const {
- return *reinterpret_cast<T *>(Locals.get() + Offset);
+ return getLocalPointer(Offset).deref<T>();
}
/// Returns a pointer to a local's block.
@@ -136,6 +137,11 @@
return Locals.get() + Offset - sizeof(Block);
}
+ // Returns the inline descriptor of the local.
+ InlineDescriptor *localInlineDesc(unsigned Offset) const {
+ return reinterpret_cast<InlineDescriptor *>(Locals.get() + Offset);
+ }
+
private:
/// Reference to the interpreter state.
InterpState &S;
Index: clang/lib/AST/Interp/InterpFrame.cpp
===================================================================
--- clang/lib/AST/Interp/InterpFrame.cpp
+++ clang/lib/AST/Interp/InterpFrame.cpp
@@ -20,8 +20,8 @@
using namespace clang::interp;
InterpFrame::InterpFrame(InterpState &S, const Function *Func,
- InterpFrame *Caller, CodePtr RetPC, Pointer &&This)
- : Caller(Caller), S(S), Func(Func), This(std::move(This)), RetPC(RetPC),
+ InterpFrame *Caller, CodePtr RetPC)
+ : Caller(Caller), S(S), Func(Func), RetPC(RetPC),
ArgSize(Func ? Func->getArgSize() : 0),
Args(static_cast<char *>(S.Stk.top())), FrameOffset(S.Stk.size()) {
if (Func) {
@@ -31,6 +31,10 @@
for (auto &Local : Scope.locals()) {
Block *B = new (localBlock(Local.Offset)) Block(Local.Desc);
B->invokeCtor();
+ InlineDescriptor *ID = localInlineDesc(Local.Offset);
+ ID->Desc = Local.Desc;
+ ID->IsActive = true;
+ ID->Offset = Local.Offset;
}
}
}
@@ -38,11 +42,7 @@
}
InterpFrame::InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC)
- : Caller(S.Current), S(S), Func(Func), RetPC(RetPC),
- ArgSize(Func ? Func->getArgSize() : 0),
- Args(static_cast<char *>(S.Stk.top())), FrameOffset(S.Stk.size()) {
- assert(Func);
-
+ : InterpFrame(S, Func, S.Current, RetPC) {
// As per our calling convention, the this pointer is
// part of the ArgSize.
// If the function has RVO, the RVO pointer is first.
@@ -58,16 +58,6 @@
else
This = stackRef<Pointer>(0);
}
-
- if (unsigned FrameSize = Func->getFrameSize()) {
- Locals = std::make_unique<char[]>(FrameSize);
- for (auto &Scope : Func->scopes()) {
- for (auto &Local : Scope.locals()) {
- Block *B = new (localBlock(Local.Offset)) Block(Local.Desc);
- B->invokeCtor();
- }
- }
- }
}
InterpFrame::~InterpFrame() {
@@ -186,10 +176,10 @@
return Func->getDecl();
}
-Pointer InterpFrame::getLocalPointer(unsigned Offset) {
+Pointer InterpFrame::getLocalPointer(unsigned Offset) const {
assert(Offset < Func->getFrameSize() && "Invalid local offset.");
- return Pointer(
- reinterpret_cast<Block *>(Locals.get() + Offset - sizeof(Block)));
+ return Pointer(reinterpret_cast<Block *>(localBlock(Offset)),
+ sizeof(InlineDescriptor));
}
Pointer InterpFrame::getParamPointer(unsigned Off) {
Index: clang/lib/AST/Interp/InterpBlock.h
===================================================================
--- clang/lib/AST/Interp/InterpBlock.h
+++ clang/lib/AST/Interp/InterpBlock.h
@@ -31,7 +31,21 @@
/// A memory block, either on the stack or in the heap.
///
-/// The storage described by the block immediately follows it in memory.
+/// The storage described by the block is immediately followed by
+/// optional metadata, which is followed by the actual data.
+///
+/// Block* rawData() data()
+/// â â â
+/// â â â
+/// â¼ â¼ â¼
+/// âââââââââââââââââ¬ââââââââââââââââââââââââââ¬ââââââââââââââââââ
+/// â Block â Metadata â Data â
+/// â sizeof(Block) â Desc->getMetadataSize() â Desc->getSize() â
+/// âââââââââââââââââ´ââââââââââââââââââââââââââ´ââââââââââââââââââ
+///
+/// Desc->getAllocSize() describes the size after the Block, i.e.
+/// the data size and the metadata size.
+///
class Block final {
public:
// Creates a new block.
@@ -59,7 +73,24 @@
std::optional<unsigned> getDeclID() const { return DeclID; }
/// Returns a pointer to the stored data.
- char *data() { return reinterpret_cast<char *>(this + 1); }
+ /// You are allowed to read Desc->getSize() bytes from this address.
+ char *data() {
+ // rawData might contain metadata as well.
+ size_t DataOffset = Desc->getMetadataSize();
+ return rawData() + DataOffset;
+ }
+ const char *data() const {
+ // rawData might contain metadata as well.
+ size_t DataOffset = Desc->getMetadataSize();
+ return rawData() + DataOffset;
+ }
+
+ /// Returns a pointer to the raw data, including metadata.
+ /// You are allowed to read Desc->getAllocSize() bytes from this address.
+ char *rawData() { return reinterpret_cast<char *>(this) + sizeof(Block); }
+ const char *rawData() const {
+ return reinterpret_cast<const char *>(this) + sizeof(Block);
+ }
/// Returns a view over the data.
template <typename T>
@@ -67,7 +98,7 @@
/// Invokes the constructor.
void invokeCtor() {
- std::memset(data(), 0, getSize());
+ std::memset(rawData(), 0, Desc->getAllocSize());
if (Desc->CtorFn)
Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
/*isActive=*/true, Desc);
Index: clang/lib/AST/Interp/Interp.h
===================================================================
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -569,7 +569,10 @@
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
- S.Stk.push<T>(S.Current->getLocal<T>(I));
+ const Pointer &Ptr = S.Current->getLocalPointer(I);
+ if (!CheckLoad(S, OpPC, Ptr))
+ return false;
+ S.Stk.push<T>(Ptr.deref<T>());
return true;
}
@@ -912,6 +915,8 @@
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
+ if (!Ptr.isRoot())
+ Ptr.initialize();
Ptr.deref<T>() = Value;
return true;
}
@@ -922,6 +927,8 @@
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
+ if (!Ptr.isRoot())
+ Ptr.initialize();
Ptr.deref<T>() = Value;
return true;
}
@@ -932,6 +939,8 @@
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
+ if (!Ptr.isRoot())
+ Ptr.initialize();
if (auto *FD = Ptr.getField()) {
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
} else {
@@ -946,6 +955,8 @@
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
+ if (!Ptr.isRoot())
+ Ptr.initialize();
if (auto *FD = Ptr.getField()) {
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
} else {
Index: clang/lib/AST/Interp/EvalEmitter.cpp
===================================================================
--- clang/lib/AST/Interp/EvalEmitter.cpp
+++ clang/lib/AST/Interp/EvalEmitter.cpp
@@ -23,7 +23,8 @@
InterpStack &Stk, APValue &Result)
: Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), Result(Result) {
// Create a dummy frame for the interpreter which does not have locals.
- S.Current = new InterpFrame(S, nullptr, nullptr, CodePtr(), Pointer());
+ S.Current =
+ new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr());
}
llvm::Expected<bool> EvalEmitter::interpretExpr(const Expr *E) {
Index: clang/lib/AST/Interp/Descriptor.h
===================================================================
--- clang/lib/AST/Interp/Descriptor.h
+++ clang/lib/AST/Interp/Descriptor.h
@@ -47,6 +47,34 @@
/// Object size as used by the interpreter.
using InterpSize = unsigned;
+/// Inline descriptor embedded in structures and arrays.
+///
+/// Such descriptors precede all composite array elements and structure fields.
+/// If the base of a pointer is not zero, the base points to the end of this
+/// structure. The offset field is used to traverse the pointer chain up
+/// to the root structure which allocated the object.
+struct InlineDescriptor {
+ /// Offset inside the structure/array.
+ unsigned Offset;
+
+ /// Flag indicating if the storage is constant or not.
+ /// Relevant for primitive fields.
+ unsigned IsConst : 1;
+ /// For primitive fields, it indicates if the field was initialized.
+ /// Primitive fields in static storage are always initialized.
+ /// Arrays are always initialized, even though their elements might not be.
+ /// Base classes are initialized after the constructor is invoked.
+ unsigned IsInitialized : 1;
+ /// Flag indicating if the field is an embedded base class.
+ unsigned IsBase : 1;
+ /// Flag indicating if the field is the active member of a union.
+ unsigned IsActive : 1;
+ /// Flag indicating if the field is mutable (if in a record).
+ unsigned IsMutable : 1;
+
+ Descriptor *Desc;
+};
+
/// Describes a memory block created by an allocation site.
struct Descriptor final {
private:
@@ -56,6 +84,8 @@
const InterpSize ElemSize;
/// Size of the storage, in host bytes.
const InterpSize Size;
+ // Size of the metadata.
+ const InterpSize MDSize;
/// Size of the allocation (storage + metadata), in host bytes.
const InterpSize AllocSize;
@@ -66,6 +96,9 @@
/// Token to denote structures of unknown size.
struct UnknownSize {};
+ using MetadataSize = llvm::Optional<InterpSize>;
+ static constexpr MetadataSize InlineDescMD = sizeof(InlineDescriptor);
+
/// Pointer to the record, if block contains records.
Record *const ElemRecord = nullptr;
/// Descriptor of the array element.
@@ -85,26 +118,26 @@
const BlockMoveFn MoveFn = nullptr;
/// Allocates a descriptor for a primitive.
- Descriptor(const DeclTy &D, PrimType Type, bool IsConst, bool IsTemporary,
- bool IsMutable);
+ Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, bool IsConst,
+ bool IsTemporary, bool IsMutable);
/// Allocates a descriptor for an array of primitives.
- Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, bool IsConst,
- bool IsTemporary, bool IsMutable);
+ Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, size_t NumElems,
+ bool IsConst, bool IsTemporary, bool IsMutable);
/// Allocates a descriptor for an array of primitives of unknown size.
Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize);
/// Allocates a descriptor for an array of composites.
- Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems, bool IsConst,
- bool IsTemporary, bool IsMutable);
+ Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD,
+ unsigned NumElems, bool IsConst, bool IsTemporary, bool IsMutable);
/// Allocates a descriptor for an array of composites of unknown size.
Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, UnknownSize);
/// Allocates a descriptor for a record.
- Descriptor(const DeclTy &D, Record *R, bool IsConst, bool IsTemporary,
- bool IsMutable);
+ Descriptor(const DeclTy &D, Record *R, MetadataSize MD, bool IsConst,
+ bool IsTemporary, bool IsMutable);
QualType getType() const;
SourceLocation getLocation() const;
@@ -134,6 +167,8 @@
unsigned getAllocSize() const { return AllocSize; }
/// returns the size of an element when the structure is viewed as an array.
unsigned getElemSize() const { return ElemSize; }
+ /// Returns the size of the metadata.
+ unsigned getMetadataSize() const { return MDSize; }
/// Returns the number of elements stored in the block.
unsigned getNumElems() const {
@@ -154,34 +189,6 @@
bool isArray() const { return IsArray; }
};
-/// Inline descriptor embedded in structures and arrays.
-///
-/// Such descriptors precede all composite array elements and structure fields.
-/// If the base of a pointer is not zero, the base points to the end of this
-/// structure. The offset field is used to traverse the pointer chain up
-/// to the root structure which allocated the object.
-struct InlineDescriptor {
- /// Offset inside the structure/array.
- unsigned Offset;
-
- /// Flag indicating if the storage is constant or not.
- /// Relevant for primitive fields.
- unsigned IsConst : 1;
- /// For primitive fields, it indicates if the field was initialized.
- /// Primitive fields in static storage are always initialized.
- /// Arrays are always initialized, even though their elements might not be.
- /// Base classes are initialized after the constructor is invoked.
- unsigned IsInitialized : 1;
- /// Flag indicating if the field is an embedded base class.
- unsigned IsBase : 1;
- /// Flag indicating if the field is the active member of a union.
- unsigned IsActive : 1;
- /// Flag indicating if the field is mutable (if in a record).
- unsigned IsMutable : 1;
-
- Descriptor *Desc;
-};
-
/// Bitfield tracking the initialisation status of elements of primitive arrays.
/// A pointer to this is embedded at the end of all primitive arrays.
/// If the map was not yet created and nothing was initialized, the pointer to
Index: clang/lib/AST/Interp/Descriptor.cpp
===================================================================
--- clang/lib/AST/Interp/Descriptor.cpp
+++ clang/lib/AST/Interp/Descriptor.cpp
@@ -191,19 +191,22 @@
COMPOSITE_TYPE_SWITCH(Type, return moveArrayTy<T>, return nullptr);
}
-Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsConst,
- bool IsTemporary, bool IsMutable)
- : Source(D), ElemSize(primSize(Type)), Size(ElemSize), AllocSize(Size),
- IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
- CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)),
- MoveFn(getMovePrim(Type)) {
+Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
+ bool IsConst, bool IsTemporary, bool IsMutable)
+ : Source(D), ElemSize(primSize(Type)), Size(ElemSize),
+ MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), IsConst(IsConst),
+ IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(getCtorPrim(Type)),
+ DtorFn(getDtorPrim(Type)), MoveFn(getMovePrim(Type)) {
+ assert(AllocSize >= Size);
assert(Source && "Missing source");
}
-Descriptor::Descriptor(const DeclTy &D, PrimType Type, size_t NumElems,
- bool IsConst, bool IsTemporary, bool IsMutable)
+Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
+ size_t NumElems, bool IsConst, bool IsTemporary,
+ bool IsMutable)
: Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
- AllocSize(align(Size) + sizeof(InitMap *)), IsConst(IsConst),
+ MDSize(MD.value_or(0)),
+ AllocSize(align(Size) + sizeof(InitMap *) + MDSize), IsConst(IsConst),
IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true),
CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
MoveFn(getMoveArrayPrim(Type)) {
@@ -212,39 +215,42 @@
Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary,
UnknownSize)
- : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark),
+ : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), MDSize(0),
AllocSize(alignof(void *)), IsConst(true), IsMutable(false),
IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)),
DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) {
assert(Source && "Missing source");
}
-Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems,
- bool IsConst, bool IsTemporary, bool IsMutable)
+Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD,
+ unsigned NumElems, bool IsConst, bool IsTemporary,
+ bool IsMutable)
: Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
- Size(ElemSize * NumElems),
- AllocSize(std::max<size_t>(alignof(void *), Size)), ElemDesc(Elem),
- IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
- IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc),
- MoveFn(moveArrayDesc) {
+ Size(ElemSize * NumElems), MDSize(MD.value_or(0)),
+ AllocSize(std::max<size_t>(alignof(void *), Size) + MDSize),
+ ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable),
+ IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc),
+ DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
assert(Source && "Missing source");
}
Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary,
UnknownSize)
: Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
- Size(UnknownSizeMark), AllocSize(alignof(void *)), ElemDesc(Elem),
- IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
- CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
+ Size(UnknownSizeMark), MDSize(0), AllocSize(alignof(void *)),
+ ElemDesc(Elem), IsConst(true), IsMutable(false), IsTemporary(IsTemporary),
+ IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc),
+ MoveFn(moveArrayDesc) {
assert(Source && "Missing source");
}
-Descriptor::Descriptor(const DeclTy &D, Record *R, bool IsConst,
- bool IsTemporary, bool IsMutable)
+Descriptor::Descriptor(const DeclTy &D, Record *R, MetadataSize MD,
+ bool IsConst, bool IsTemporary, bool IsMutable)
: Source(D), ElemSize(std::max<size_t>(alignof(void *), R->getFullSize())),
- Size(ElemSize), AllocSize(Size), ElemRecord(R), IsConst(IsConst),
- IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(ctorRecord),
- DtorFn(dtorRecord), MoveFn(moveRecord) {
+ Size(ElemSize), MDSize(MD.value_or(0)), AllocSize(Size + MDSize),
+ ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable),
+ IsTemporary(IsTemporary), CtorFn(ctorRecord), DtorFn(dtorRecord),
+ MoveFn(moveRecord) {
assert(Source && "Missing source");
}
Index: clang/lib/AST/Interp/Context.cpp
===================================================================
--- clang/lib/AST/Interp/Context.cpp
+++ clang/lib/AST/Interp/Context.cpp
@@ -125,7 +125,7 @@
bool Context::Run(State &Parent, Function *Func, APValue &Result) {
InterpState State(Parent, *P, Stk, *this);
- State.Current = new InterpFrame(State, Func, nullptr, {}, {});
+ State.Current = new InterpFrame(State, Func, /*Caller=*/nullptr, {});
if (Interpret(State, Result))
return true;
Stk.clear();
Index: clang/lib/AST/Interp/ByteCodeStmtGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -402,24 +402,26 @@
if (std::optional<PrimType> T = this->classify(VD->getType())) {
const Expr *Init = VD->getInit();
- if (!Init)
- return false;
-
unsigned Offset =
this->allocateLocalPrimitive(VD, *T, VD->getType().isConstQualified());
// Compile the initializer in its own scope.
- {
+ if (Init) {
ExprScope<Emitter> Scope(this);
if (!this->visit(Init))
return false;
+
+ return this->emitSetLocal(*T, Offset, VD);
}
- // Set the value.
- return this->emitSetLocal(*T, Offset, VD);
+ return true;
}
// Composite types - allocate storage and initialize it.
- if (std::optional<unsigned> Offset = this->allocateLocal(VD))
+ if (std::optional<unsigned> Offset = this->allocateLocal(VD)) {
+ if (!VD->getInit())
+ return true;
+
return this->visitLocalInitializer(VD->getInit(), *Offset);
+ }
return this->bail(VD);
}
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -802,7 +802,11 @@
PrimType Ty,
bool IsConst,
bool IsExtended) {
- Descriptor *D = P.createDescriptor(Src, Ty, IsConst, Src.is<const Expr *>());
+ // FIXME: There are cases where Src.is<Expr*>() is wrong, e.g.
+ // (int){12} in C. Consider using Expr::isTemporaryObject() instead
+ // or isa<MaterializeTemporaryExpr>().
+ Descriptor *D = P.createDescriptor(Src, Ty, Descriptor::InlineDescMD, IsConst,
+ Src.is<const Expr *>());
Scope::Local Local = this->createLocal(D);
if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>()))
Locals.insert({VD, Local});
@@ -831,7 +835,8 @@
}
Descriptor *D = P.createDescriptor(
- Src, Ty.getTypePtr(), Ty.isConstQualified(), IsTemporary, false, Init);
+ Src, Ty.getTypePtr(), Descriptor::InlineDescMD, Ty.isConstQualified(),
+ IsTemporary, /*IsMutable=*/false, Init);
if (!D)
return {};
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits