Skip to content

Commit

Permalink
For P0784R7: Add support for dynamic allocation with new / delete during
Browse files Browse the repository at this point in the history
constant evaluation.

llvm-svn: 373036
  • Loading branch information
zygoloid committed Sep 27, 2019
1 parent c336557 commit da1b434
Show file tree
Hide file tree
Showing 10 changed files with 901 additions and 73 deletions.
46 changes: 45 additions & 1 deletion clang/include/clang/AST/APValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,34 @@ class TypeInfoLValue {

void print(llvm::raw_ostream &Out, const PrintingPolicy &Policy) const;
};

/// Symbolic representation of a dynamic allocation.
class DynamicAllocLValue {
unsigned Index;

public:
DynamicAllocLValue() : Index(0) {}
explicit DynamicAllocLValue(unsigned Index) : Index(Index + 1) {}
unsigned getIndex() { return Index - 1; }

explicit operator bool() const { return Index != 0; }

void *getOpaqueValue() {
return reinterpret_cast<void *>(static_cast<uintptr_t>(Index)
<< NumLowBitsAvailable);
}
static DynamicAllocLValue getFromOpaqueValue(void *Value) {
DynamicAllocLValue V;
V.Index = reinterpret_cast<uintptr_t>(Value) >> NumLowBitsAvailable;
return V;
}

static unsigned getMaxIndex() {
return (std::numeric_limits<unsigned>::max() >> NumLowBitsAvailable) - 1;
}

static constexpr int NumLowBitsAvailable = 3;
};
}

namespace llvm {
Expand All @@ -67,6 +95,17 @@ template<> struct PointerLikeTypeTraits<clang::TypeInfoLValue> {
// to include Type.h.
static constexpr int NumLowBitsAvailable = 3;
};

template<> struct PointerLikeTypeTraits<clang::DynamicAllocLValue> {
static void *getAsVoidPointer(clang::DynamicAllocLValue V) {
return V.getOpaqueValue();
}
static clang::DynamicAllocLValue getFromVoidPointer(void *P) {
return clang::DynamicAllocLValue::getFromOpaqueValue(P);
}
static constexpr int NumLowBitsAvailable =
clang::DynamicAllocLValue::NumLowBitsAvailable;
};
}

namespace clang {
Expand Down Expand Up @@ -97,13 +136,15 @@ class APValue {
};

class LValueBase {
typedef llvm::PointerUnion<const ValueDecl *, const Expr *, TypeInfoLValue>
typedef llvm::PointerUnion<const ValueDecl *, const Expr *, TypeInfoLValue,
DynamicAllocLValue>
PtrTy;

public:
LValueBase() : Local{} {}
LValueBase(const ValueDecl *P, unsigned I = 0, unsigned V = 0);
LValueBase(const Expr *P, unsigned I = 0, unsigned V = 0);
static LValueBase getDynamicAlloc(DynamicAllocLValue LV, QualType Type);
static LValueBase getTypeInfo(TypeInfoLValue LV, QualType TypeInfo);

template <class T>
Expand All @@ -124,6 +165,7 @@ class APValue {
unsigned getCallIndex() const;
unsigned getVersion() const;
QualType getTypeInfoType() const;
QualType getDynamicAllocType() const;

friend bool operator==(const LValueBase &LHS, const LValueBase &RHS);
friend bool operator!=(const LValueBase &LHS, const LValueBase &RHS) {
Expand All @@ -140,6 +182,8 @@ class APValue {
LocalState Local;
/// The type std::type_info, if this is a TypeInfoLValue.
void *TypeInfoType;
/// The QualType, if this is a DynamicAllocLValue.
void *DynamicAllocType;
};
};

Expand Down
44 changes: 44 additions & 0 deletions clang/include/clang/Basic/DiagnosticASTKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ def note_constexpr_nonliteral : Note<
def note_constexpr_non_global : Note<
"%select{pointer|reference}0 to %select{|subobject of }1"
"%select{temporary|%3}2 is not a constant expression">;
def note_constexpr_dynamic_alloc : Note<
"%select{pointer|reference}0 to %select{|subobject of }1"
"heap-allocated object is not a constant expression">;
def note_constexpr_uninitialized : Note<
"%select{|sub}0object of type %1 is not initialized">;
def note_constexpr_subobject_declared_here : Note<
Expand Down Expand Up @@ -100,6 +103,7 @@ def note_constexpr_typeid_polymorphic : Note<
def note_constexpr_void_comparison : Note<
"comparison between unequal pointers to void has unspecified result">;
def note_constexpr_temporary_here : Note<"temporary created here">;
def note_constexpr_dynamic_alloc_here : Note<"heap allocation performed here">;
def note_constexpr_conditional_never_const : Note<
"both arms of conditional operator are unable to produce a "
"constant expression">;
Expand All @@ -109,6 +113,8 @@ def note_constexpr_call_limit_exceeded : Note<
"constexpr evaluation hit maximum call limit">;
def note_constexpr_step_limit_exceeded : Note<
"constexpr evaluation hit maximum step limit; possible infinite loop?">;
def note_constexpr_heap_alloc_limit_exceeded : Note<
"constexpr evaluation hit maximum heap allocation limit">;
def note_constexpr_this : Note<
"%select{|implicit }0use of 'this' pointer is only allowed within the "
"evaluation of a call to a 'constexpr' member function">;
Expand Down Expand Up @@ -174,6 +180,10 @@ def note_constexpr_access_unreadable_object : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
"member call on|dynamic_cast of|typeid applied to}0 object '%1' "
"whose value is not known">;
def note_constexpr_access_deleted_object : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
"member call on|dynamic_cast of|typeid applied to}0 heap allocated "
"object that has been deleted">;
def note_constexpr_modify_global : Note<
"a constant expression cannot modify an object that is visible outside "
"that expression">;
Expand All @@ -193,6 +203,8 @@ def note_constexpr_baa_insufficient_alignment : Note<
def note_constexpr_baa_value_insufficient_alignment : Note<
"value of the aligned pointer (%0) is not a multiple of the asserted %1 "
"%plural{1:byte|:bytes}1">;
def note_constexpr_destroy_out_of_lifetime : Note<
"destroying object '%0' whose lifetime has already ended">;
def note_constexpr_unsupported_destruction : Note<
"non-trivial destruction of type %0 in a constant expression is not supported">;
def note_constexpr_unsupported_unsized_array : Note<
Expand Down Expand Up @@ -234,6 +246,38 @@ def note_constexpr_bit_cast_invalid_subtype : Note<
def note_constexpr_bit_cast_indet_dest : Note<
"indeterminate value can only initialize an object of type 'unsigned char'"
"%select{, 'char',|}1 or 'std::byte'; %0 is invalid">;
def note_constexpr_new : Note<
"dynamic memory allocation is not permitted in constant expressions "
"until C++20">;
def note_constexpr_new_non_replaceable : Note<
"call to %select{placement|class-specific}0 %1">;
def note_constexpr_new_placement : Note<
"this placement new expression is not yet supported in constant expressions">;
def note_constexpr_new_negative : Note<
"cannot allocate array; evaluated array bound %0 is negative">;
def note_constexpr_new_too_large : Note<
"cannot allocate array; evaluated array bound %0 is too large">;
def note_constexpr_new_too_small : Note<
"cannot allocate array; evaluated array bound %0 is too small to hold "
"%1 explicitly initialized elements">;
def note_constexpr_delete_not_heap_alloc : Note<
"delete of pointer '%0' that does not point to a heap-allocated object">;
def note_constexpr_double_delete : Note<
"delete of pointer that has already been deleted">;
def note_constexpr_double_destroy : Note<
"destruction of object that is already being destroyed">;
def note_constexpr_new_delete_mismatch : Note<
"%select{non-|}0array delete used to delete pointer to "
"%select{|non-}0array object of type %1">;
def note_constexpr_delete_subobject : Note<
"delete of pointer%select{ to subobject|}1 '%0' "
"%select{|that does not point to complete object}1">;
def note_constexpr_delete_base_nonvirt_dtor : Note<
"delete of object with dynamic type %1 through pointer to "
"base class type %0 with non-virtual destructor">;
def note_constexpr_memory_leak : Note<
"allocation performed here was not deallocated"
"%plural{0:|: (along with %0 other memory leak%s0)}0">;
def err_experimental_clang_interp_failed : Error<
"the experimental clang interpreter failed to evaluate an expression">;

Expand Down
34 changes: 30 additions & 4 deletions clang/lib/AST/APValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ APValue::LValueBase::LValueBase(const ValueDecl *P, unsigned I, unsigned V)
APValue::LValueBase::LValueBase(const Expr *P, unsigned I, unsigned V)
: Ptr(P), Local{I, V} {}

APValue::LValueBase APValue::LValueBase::getDynamicAlloc(DynamicAllocLValue LV,
QualType Type) {
LValueBase Base;
Base.Ptr = LV;
Base.DynamicAllocType = Type.getAsOpaquePtr();
return Base;
}

APValue::LValueBase APValue::LValueBase::getTypeInfo(TypeInfoLValue LV,
QualType TypeInfo) {
LValueBase Base;
Expand All @@ -51,18 +59,24 @@ APValue::LValueBase APValue::LValueBase::getTypeInfo(TypeInfoLValue LV,
}

unsigned APValue::LValueBase::getCallIndex() const {
return is<TypeInfoLValue>() ? 0 : Local.CallIndex;
return (is<TypeInfoLValue>() || is<DynamicAllocLValue>()) ? 0
: Local.CallIndex;
}

unsigned APValue::LValueBase::getVersion() const {
return is<TypeInfoLValue>() ? 0 : Local.Version;
return (is<TypeInfoLValue>() || is<DynamicAllocLValue>()) ? 0 : Local.Version;
}

QualType APValue::LValueBase::getTypeInfoType() const {
assert(is<TypeInfoLValue>() && "not a type_info lvalue");
return QualType::getFromOpaquePtr(TypeInfoType);
}

QualType APValue::LValueBase::getDynamicAllocType() const {
assert(is<DynamicAllocLValue>() && "not a dynamic allocation lvalue");
return QualType::getFromOpaquePtr(DynamicAllocType);
}

namespace clang {
bool operator==(const APValue::LValueBase &LHS,
const APValue::LValueBase &RHS) {
Expand Down Expand Up @@ -111,7 +125,7 @@ llvm::DenseMapInfo<clang::APValue::LValueBase>::getTombstoneKey() {

namespace clang {
llvm::hash_code hash_value(const APValue::LValueBase &Base) {
if (Base.is<TypeInfoLValue>())
if (Base.is<TypeInfoLValue>() || Base.is<DynamicAllocLValue>())
return llvm::hash_value(Base.getOpaqueValue());
return llvm::hash_combine(Base.getOpaqueValue(), Base.getCallIndex(),
Base.getVersion());
Expand Down Expand Up @@ -528,13 +542,18 @@ void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx,
S = CharUnits::One();
}
Out << '&';
} else if (!IsReference)
} else if (!IsReference) {
Out << '&';
}

if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>())
Out << *VD;
else if (TypeInfoLValue TI = Base.dyn_cast<TypeInfoLValue>()) {
TI.print(Out, Ctx.getPrintingPolicy());
} else if (DynamicAllocLValue DA = Base.dyn_cast<DynamicAllocLValue>()) {
Out << "{*new "
<< Base.getDynamicAllocType().stream(Ctx.getPrintingPolicy()) << "#"
<< DA.getIndex() << "}";
} else {
assert(Base.get<const Expr *>() != nullptr &&
"Expecting non-null Expr");
Expand Down Expand Up @@ -563,10 +582,17 @@ void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx,
} else if (TypeInfoLValue TI = Base.dyn_cast<TypeInfoLValue>()) {
TI.print(Out, Ctx.getPrintingPolicy());
ElemTy = Base.getTypeInfoType();
} else if (DynamicAllocLValue DA = Base.dyn_cast<DynamicAllocLValue>()) {
Out << "{*new "
<< Base.getDynamicAllocType().stream(Ctx.getPrintingPolicy()) << "#"
<< DA.getIndex() << "}";
ElemTy = Base.getDynamicAllocType();
} else {
const Expr *E = Base.get<const Expr*>();
assert(E != nullptr && "Expecting non-null Expr");
E->printPretty(Out, nullptr, Ctx.getPrintingPolicy());
// FIXME: This is wrong if E is a MaterializeTemporaryExpr with an lvalue
// adjustment.
ElemTy = E->getType();
}

Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/ExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,

if (ArraySize) {
if (Expr *SizeExpr = *ArraySize) {
if (SizeExpr->isValueDependent())
ExprBits.ValueDependent = true;
if (SizeExpr->isInstantiationDependent())
ExprBits.InstantiationDependent = true;
if (SizeExpr->containsUnexpandedParameterPack())
Expand All @@ -134,6 +136,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
}

if (Initializer) {
if (Initializer->isValueDependent())
ExprBits.ValueDependent = true;
if (Initializer->isInstantiationDependent())
ExprBits.InstantiationDependent = true;
if (Initializer->containsUnexpandedParameterPack())
Expand All @@ -143,6 +147,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
}

for (unsigned I = 0; I != PlacementArgs.size(); ++I) {
if (PlacementArgs[I]->isValueDependent())
ExprBits.ValueDependent = true;
if (PlacementArgs[I]->isInstantiationDependent())
ExprBits.InstantiationDependent = true;
if (PlacementArgs[I]->containsUnexpandedParameterPack())
Expand Down
Loading

0 comments on commit da1b434

Please sign in to comment.