summaryrefslogtreecommitdiff
path: root/lib/AST
diff options
context:
space:
mode:
Diffstat (limited to 'lib/AST')
-rw-r--r--lib/AST/APValue.cpp38
-rw-r--r--lib/AST/ASTContext.cpp757
-rw-r--r--lib/AST/ASTDiagnostic.cpp2
-rw-r--r--lib/AST/ASTImporter.cpp542
-rw-r--r--lib/AST/ASTStructuralEquivalence.cpp90
-rw-r--r--lib/AST/ASTTypeTraits.cpp16
-rw-r--r--lib/AST/CXXInheritance.cpp2
-rw-r--r--lib/AST/Comment.cpp15
-rw-r--r--lib/AST/CommentLexer.cpp7
-rw-r--r--lib/AST/CommentParser.cpp6
-rw-r--r--lib/AST/CommentSema.cpp8
-rw-r--r--lib/AST/Decl.cpp75
-rw-r--r--lib/AST/DeclBase.cpp15
-rw-r--r--lib/AST/DeclCXX.cpp84
-rw-r--r--lib/AST/DeclPrinter.cpp15
-rw-r--r--lib/AST/DeclTemplate.cpp69
-rw-r--r--lib/AST/Expr.cpp140
-rw-r--r--lib/AST/ExprCXX.cpp176
-rw-r--r--lib/AST/ExprClassification.cpp5
-rw-r--r--lib/AST/ExprConstant.cpp2474
-rw-r--r--lib/AST/ExternalASTMerger.cpp124
-rw-r--r--lib/AST/FormatString.cpp4
-rw-r--r--lib/AST/FormatStringParsing.h13
-rw-r--r--lib/AST/InheritViz.cpp4
-rw-r--r--lib/AST/Interp/Block.cpp87
-rw-r--r--lib/AST/Interp/Block.h140
-rw-r--r--lib/AST/Interp/Boolean.h148
-rw-r--r--lib/AST/Interp/ByteCodeEmitter.cpp175
-rw-r--r--lib/AST/Interp/ByteCodeEmitter.h112
-rw-r--r--lib/AST/Interp/ByteCodeExprGen.cpp580
-rw-r--r--lib/AST/Interp/ByteCodeExprGen.h331
-rw-r--r--lib/AST/Interp/ByteCodeGenError.cpp14
-rw-r--r--lib/AST/Interp/ByteCodeGenError.h46
-rw-r--r--lib/AST/Interp/ByteCodeStmtGen.cpp265
-rw-r--r--lib/AST/Interp/ByteCodeStmtGen.h89
-rw-r--r--lib/AST/Interp/Context.cpp148
-rw-r--r--lib/AST/Interp/Context.h100
-rw-r--r--lib/AST/Interp/Descriptor.cpp292
-rw-r--r--lib/AST/Interp/Descriptor.h220
-rw-r--r--lib/AST/Interp/Disasm.cpp69
-rw-r--r--lib/AST/Interp/EvalEmitter.cpp253
-rw-r--r--lib/AST/Interp/EvalEmitter.h129
-rw-r--r--lib/AST/Interp/Frame.cpp14
-rw-r--r--lib/AST/Interp/Frame.h45
-rw-r--r--lib/AST/Interp/Function.cpp48
-rw-r--r--lib/AST/Interp/Function.h163
-rw-r--r--lib/AST/Interp/Integral.h269
-rw-r--r--lib/AST/Interp/Interp.cpp417
-rw-r--r--lib/AST/Interp/Interp.h960
-rw-r--r--lib/AST/Interp/InterpFrame.cpp193
-rw-r--r--lib/AST/Interp/InterpFrame.h153
-rw-r--r--lib/AST/Interp/InterpStack.cpp78
-rw-r--r--lib/AST/Interp/InterpStack.h113
-rw-r--r--lib/AST/Interp/InterpState.cpp74
-rw-r--r--lib/AST/Interp/InterpState.h112
-rw-r--r--lib/AST/Interp/Opcode.h30
-rw-r--r--lib/AST/Interp/Opcodes.td422
-rw-r--r--lib/AST/Interp/Pointer.cpp193
-rw-r--r--lib/AST/Interp/Pointer.h353
-rw-r--r--lib/AST/Interp/PrimType.cpp23
-rw-r--r--lib/AST/Interp/PrimType.h115
-rw-r--r--lib/AST/Interp/Program.cpp364
-rw-r--r--lib/AST/Interp/Program.h220
-rw-r--r--lib/AST/Interp/Record.cpp46
-rw-r--r--lib/AST/Interp/Record.h121
-rw-r--r--lib/AST/Interp/Source.cpp39
-rw-r--r--lib/AST/Interp/Source.h118
-rw-r--r--lib/AST/Interp/State.cpp158
-rw-r--r--lib/AST/Interp/State.h133
-rw-r--r--lib/AST/ItaniumCXXABI.cpp72
-rw-r--r--lib/AST/ItaniumMangle.cpp154
-rw-r--r--lib/AST/JSONNodeDumper.cpp49
-rw-r--r--lib/AST/Mangle.cpp16
-rw-r--r--lib/AST/MicrosoftCXXABI.cpp4
-rw-r--r--lib/AST/MicrosoftMangle.cpp32
-rw-r--r--lib/AST/NSAPI.cpp14
-rw-r--r--lib/AST/OpenMPClause.cpp71
-rw-r--r--lib/AST/PrintfFormatString.cpp20
-rw-r--r--lib/AST/RawCommentList.cpp64
-rw-r--r--lib/AST/Stmt.cpp11
-rw-r--r--lib/AST/StmtOpenMP.cpp246
-rw-r--r--lib/AST/StmtPrinter.cpp46
-rw-r--r--lib/AST/StmtProfile.cpp37
-rw-r--r--lib/AST/TemplateBase.cpp2
-rw-r--r--lib/AST/TextNodeDumper.cpp17
-rw-r--r--lib/AST/Type.cpp113
-rw-r--r--lib/AST/TypeLoc.cpp3
-rw-r--r--lib/AST/TypePrinter.cpp11
-rw-r--r--lib/AST/VTTBuilder.cpp12
-rw-r--r--lib/AST/VTableBuilder.cpp12
90 files changed, 12343 insertions, 1516 deletions
diff --git a/lib/AST/APValue.cpp b/lib/AST/APValue.cpp
index 1993bba9bd1a1..50f8d05dacb4f 100644
--- a/lib/AST/APValue.cpp
+++ b/lib/AST/APValue.cpp
@@ -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;
@@ -51,11 +59,12 @@ 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 {
@@ -63,6 +72,11 @@ QualType APValue::LValueBase::getTypeInfoType() const {
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) {
@@ -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());
@@ -479,7 +493,7 @@ void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx,
return;
case APValue::Vector: {
Out << '{';
- QualType ElemTy = Ty->getAs<VectorType>()->getElementType();
+ QualType ElemTy = Ty->castAs<VectorType>()->getElementType();
getVectorElt(0).printPretty(Out, Ctx, ElemTy);
for (unsigned i = 1; i != getVectorLength(); ++i) {
Out << ", ";
@@ -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");
@@ -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();
}
@@ -626,7 +652,7 @@ void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx,
}
case APValue::Struct: {
Out << '{';
- const RecordDecl *RD = Ty->getAs<RecordType>()->getDecl();
+ const RecordDecl *RD = Ty->castAs<RecordType>()->getDecl();
bool First = true;
if (unsigned N = getStructNumBases()) {
const CXXRecordDecl *CD = cast<CXXRecordDecl>(RD);
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 0d69eb90abaf6..cda51ec755a8d 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -12,6 +12,7 @@
#include "clang/AST/ASTContext.h"
#include "CXXABI.h"
+#include "Interp/Context.h"
#include "clang/AST/APValue.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/ASTTypeTraits.h"
@@ -98,62 +99,60 @@ enum FloatingRank {
Float16Rank, HalfRank, FloatRank, DoubleRank, LongDoubleRank, Float128Rank
};
-RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const {
+/// \returns location that is relevant when searching for Doc comments related
+/// to \p D.
+static SourceLocation getDeclLocForCommentSearch(const Decl *D,
+ SourceManager &SourceMgr) {
assert(D);
- // If we already tried to load comments but there are none,
- // we won't find anything.
- if (CommentsLoaded && Comments.getComments().empty())
- return nullptr;
-
// User can not attach documentation to implicit declarations.
if (D->isImplicit())
- return nullptr;
+ return {};
// User can not attach documentation to implicit instantiations.
if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
if (FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
- return nullptr;
+ return {};
}
if (const auto *VD = dyn_cast<VarDecl>(D)) {
if (VD->isStaticDataMember() &&
VD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
- return nullptr;
+ return {};
}
if (const auto *CRD = dyn_cast<CXXRecordDecl>(D)) {
if (CRD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
- return nullptr;
+ return {};
}
if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
TemplateSpecializationKind TSK = CTSD->getSpecializationKind();
if (TSK == TSK_ImplicitInstantiation ||
TSK == TSK_Undeclared)
- return nullptr;
+ return {};
}
if (const auto *ED = dyn_cast<EnumDecl>(D)) {
if (ED->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
- return nullptr;
+ return {};
}
if (const auto *TD = dyn_cast<TagDecl>(D)) {
// When tag declaration (but not definition!) is part of the
// decl-specifier-seq of some other declaration, it doesn't get comment
if (TD->isEmbeddedInDeclarator() && !TD->isCompleteDefinition())
- return nullptr;
+ return {};
}
// TODO: handle comments for function parameters properly.
if (isa<ParmVarDecl>(D))
- return nullptr;
+ return {};
// TODO: we could look up template parameter documentation in the template
// documentation.
if (isa<TemplateTypeParmDecl>(D) ||
isa<NonTypeTemplateParmDecl>(D) ||
isa<TemplateTemplateParmDecl>(D))
- return nullptr;
+ return {};
// Find declaration location.
// For Objective-C declarations we generally don't expect to have multiple
@@ -161,20 +160,19 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const {
// location".
// For all other declarations multiple declarators are used quite frequently,
// so we use the location of the identifier as the "declaration location".
- SourceLocation DeclLoc;
if (isa<ObjCMethodDecl>(D) || isa<ObjCContainerDecl>(D) ||
isa<ObjCPropertyDecl>(D) ||
isa<RedeclarableTemplateDecl>(D) ||
isa<ClassTemplateSpecializationDecl>(D))
- DeclLoc = D->getBeginLoc();
+ return D->getBeginLoc();
else {
- DeclLoc = D->getLocation();
+ const SourceLocation DeclLoc = D->getLocation();
if (DeclLoc.isMacroID()) {
if (isa<TypedefDecl>(D)) {
// If location of the typedef name is in a macro, it is because being
// declared via a macro. Try using declaration's starting location as
// the "declaration location".
- DeclLoc = D->getBeginLoc();
+ return D->getBeginLoc();
} else if (const auto *TD = dyn_cast<TagDecl>(D)) {
// If location of the tag decl is inside a macro, but the spelling of
// the tag name comes from a macro argument, it looks like a special
@@ -183,102 +181,73 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const {
// attach the comment to the tag decl.
if (SourceMgr.isMacroArgExpansion(DeclLoc) &&
TD->isCompleteDefinition())
- DeclLoc = SourceMgr.getExpansionLoc(DeclLoc);
+ return SourceMgr.getExpansionLoc(DeclLoc);
}
}
+ return DeclLoc;
}
+ return {};
+}
+
+RawComment *ASTContext::getRawCommentForDeclNoCacheImpl(
+ const Decl *D, const SourceLocation RepresentativeLocForDecl,
+ const std::map<unsigned, RawComment *> &CommentsInTheFile) const {
// If the declaration doesn't map directly to a location in a file, we
// can't find the comment.
- if (DeclLoc.isInvalid() || !DeclLoc.isFileID())
+ if (RepresentativeLocForDecl.isInvalid() ||
+ !RepresentativeLocForDecl.isFileID())
return nullptr;
- if (!CommentsLoaded && ExternalSource) {
- ExternalSource->ReadComments();
-
-#ifndef NDEBUG
- ArrayRef<RawComment *> RawComments = Comments.getComments();
- assert(std::is_sorted(RawComments.begin(), RawComments.end(),
- BeforeThanCompare<RawComment>(SourceMgr)));
-#endif
-
- CommentsLoaded = true;
- }
-
- ArrayRef<RawComment *> RawComments = Comments.getComments();
// If there are no comments anywhere, we won't find anything.
- if (RawComments.empty())
+ if (CommentsInTheFile.empty())
return nullptr;
- // Find the comment that occurs just after this declaration.
- ArrayRef<RawComment *>::iterator Comment;
- {
- // When searching for comments during parsing, the comment we are looking
- // for is usually among the last two comments we parsed -- check them
- // first.
- RawComment CommentAtDeclLoc(
- SourceMgr, SourceRange(DeclLoc), LangOpts.CommentOpts, false);
- BeforeThanCompare<RawComment> Compare(SourceMgr);
- ArrayRef<RawComment *>::iterator MaybeBeforeDecl = RawComments.end() - 1;
- bool Found = Compare(*MaybeBeforeDecl, &CommentAtDeclLoc);
- if (!Found && RawComments.size() >= 2) {
- MaybeBeforeDecl--;
- Found = Compare(*MaybeBeforeDecl, &CommentAtDeclLoc);
- }
-
- if (Found) {
- Comment = MaybeBeforeDecl + 1;
- assert(Comment ==
- llvm::lower_bound(RawComments, &CommentAtDeclLoc, Compare));
- } else {
- // Slow path.
- Comment = llvm::lower_bound(RawComments, &CommentAtDeclLoc, Compare);
- }
- }
-
// Decompose the location for the declaration and find the beginning of the
// file buffer.
- std::pair<FileID, unsigned> DeclLocDecomp = SourceMgr.getDecomposedLoc(DeclLoc);
+ const std::pair<FileID, unsigned> DeclLocDecomp =
+ SourceMgr.getDecomposedLoc(RepresentativeLocForDecl);
+
+ // Slow path.
+ auto OffsetCommentBehindDecl =
+ CommentsInTheFile.lower_bound(DeclLocDecomp.second);
// First check whether we have a trailing comment.
- if (Comment != RawComments.end() &&
- ((*Comment)->isDocumentation() || LangOpts.CommentOpts.ParseAllComments)
- && (*Comment)->isTrailingComment() &&
- (isa<FieldDecl>(D) || isa<EnumConstantDecl>(D) || isa<VarDecl>(D) ||
- isa<ObjCMethodDecl>(D) || isa<ObjCPropertyDecl>(D))) {
- std::pair<FileID, unsigned> CommentBeginDecomp
- = SourceMgr.getDecomposedLoc((*Comment)->getSourceRange().getBegin());
- // Check that Doxygen trailing comment comes after the declaration, starts
- // on the same line and in the same file as the declaration.
- if (DeclLocDecomp.first == CommentBeginDecomp.first &&
- SourceMgr.getLineNumber(DeclLocDecomp.first, DeclLocDecomp.second)
- == SourceMgr.getLineNumber(CommentBeginDecomp.first,
- CommentBeginDecomp.second)) {
- (**Comment).setAttached();
- return *Comment;
+ if (OffsetCommentBehindDecl != CommentsInTheFile.end()) {
+ RawComment *CommentBehindDecl = OffsetCommentBehindDecl->second;
+ if ((CommentBehindDecl->isDocumentation() ||
+ LangOpts.CommentOpts.ParseAllComments) &&
+ CommentBehindDecl->isTrailingComment() &&
+ (isa<FieldDecl>(D) || isa<EnumConstantDecl>(D) || isa<VarDecl>(D) ||
+ isa<ObjCMethodDecl>(D) || isa<ObjCPropertyDecl>(D))) {
+
+ // Check that Doxygen trailing comment comes after the declaration, starts
+ // on the same line and in the same file as the declaration.
+ if (SourceMgr.getLineNumber(DeclLocDecomp.first, DeclLocDecomp.second) ==
+ Comments.getCommentBeginLine(CommentBehindDecl, DeclLocDecomp.first,
+ OffsetCommentBehindDecl->first)) {
+ return CommentBehindDecl;
+ }
}
}
// The comment just after the declaration was not a trailing comment.
// Let's look at the previous comment.
- if (Comment == RawComments.begin())
+ if (OffsetCommentBehindDecl == CommentsInTheFile.begin())
return nullptr;
- --Comment;
+
+ auto OffsetCommentBeforeDecl = --OffsetCommentBehindDecl;
+ RawComment *CommentBeforeDecl = OffsetCommentBeforeDecl->second;
// Check that we actually have a non-member Doxygen comment.
- if (!((*Comment)->isDocumentation() ||
+ if (!(CommentBeforeDecl->isDocumentation() ||
LangOpts.CommentOpts.ParseAllComments) ||
- (*Comment)->isTrailingComment())
+ CommentBeforeDecl->isTrailingComment())
return nullptr;
// Decompose the end of the comment.
- std::pair<FileID, unsigned> CommentEndDecomp
- = SourceMgr.getDecomposedLoc((*Comment)->getSourceRange().getEnd());
-
- // If the comment and the declaration aren't in the same file, then they
- // aren't related.
- if (DeclLocDecomp.first != CommentEndDecomp.first)
- return nullptr;
+ const unsigned CommentEndOffset =
+ Comments.getCommentEndOffset(CommentBeforeDecl);
// Get the corresponding buffer.
bool Invalid = false;
@@ -288,26 +257,49 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const {
return nullptr;
// Extract text between the comment and declaration.
- StringRef Text(Buffer + CommentEndDecomp.second,
- DeclLocDecomp.second - CommentEndDecomp.second);
+ StringRef Text(Buffer + CommentEndOffset,
+ DeclLocDecomp.second - CommentEndOffset);
// There should be no other declarations or preprocessor directives between
// comment and declaration.
if (Text.find_first_of(";{}#@") != StringRef::npos)
return nullptr;
- (**Comment).setAttached();
- return *Comment;
+ return CommentBeforeDecl;
+}
+
+RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const {
+ const SourceLocation DeclLoc = getDeclLocForCommentSearch(D, SourceMgr);
+
+ // If the declaration doesn't map directly to a location in a file, we
+ // can't find the comment.
+ if (DeclLoc.isInvalid() || !DeclLoc.isFileID())
+ return nullptr;
+
+ if (ExternalSource && !CommentsLoaded) {
+ ExternalSource->ReadComments();
+ CommentsLoaded = true;
+ }
+
+ if (Comments.empty())
+ return nullptr;
+
+ const FileID File = SourceMgr.getDecomposedLoc(DeclLoc).first;
+ const auto CommentsInThisFile = Comments.getCommentsInFile(File);
+ if (!CommentsInThisFile || CommentsInThisFile->empty())
+ return nullptr;
+
+ return getRawCommentForDeclNoCacheImpl(D, DeclLoc, *CommentsInThisFile);
}
/// If we have a 'templated' declaration for a template, adjust 'D' to
/// refer to the actual template.
/// If we have an implicit instantiation, adjust 'D' to refer to template.
-static const Decl *adjustDeclToTemplate(const Decl *D) {
- if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+static const Decl &adjustDeclToTemplate(const Decl &D) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(&D)) {
// Is this function declaration part of a function template?
if (const FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate())
- return FTD;
+ return *FTD;
// Nothing to do if function is not an implicit instantiation.
if (FD->getTemplateSpecializationKind() != TSK_ImplicitInstantiation)
@@ -315,28 +307,28 @@ static const Decl *adjustDeclToTemplate(const Decl *D) {
// Function is an implicit instantiation of a function template?
if (const FunctionTemplateDecl *FTD = FD->getPrimaryTemplate())
- return FTD;
+ return *FTD;
// Function is instantiated from a member definition of a class template?
if (const FunctionDecl *MemberDecl =
FD->getInstantiatedFromMemberFunction())
- return MemberDecl;
+ return *MemberDecl;
return D;
}
- if (const auto *VD = dyn_cast<VarDecl>(D)) {
+ if (const auto *VD = dyn_cast<VarDecl>(&D)) {
// Static data member is instantiated from a member definition of a class
// template?
if (VD->isStaticDataMember())
if (const VarDecl *MemberDecl = VD->getInstantiatedFromStaticDataMember())
- return MemberDecl;
+ return *MemberDecl;
return D;
}
- if (const auto *CRD = dyn_cast<CXXRecordDecl>(D)) {
+ if (const auto *CRD = dyn_cast<CXXRecordDecl>(&D)) {
// Is this class declaration part of a class template?
if (const ClassTemplateDecl *CTD = CRD->getDescribedClassTemplate())
- return CTD;
+ return *CTD;
// Class is an implicit instantiation of a class template or partial
// specialization?
@@ -346,23 +338,23 @@ static const Decl *adjustDeclToTemplate(const Decl *D) {
llvm::PointerUnion<ClassTemplateDecl *,
ClassTemplatePartialSpecializationDecl *>
PU = CTSD->getSpecializedTemplateOrPartial();
- return PU.is<ClassTemplateDecl*>() ?
- static_cast<const Decl*>(PU.get<ClassTemplateDecl *>()) :
- static_cast<const Decl*>(
- PU.get<ClassTemplatePartialSpecializationDecl *>());
+ return PU.is<ClassTemplateDecl *>()
+ ? *static_cast<const Decl *>(PU.get<ClassTemplateDecl *>())
+ : *static_cast<const Decl *>(
+ PU.get<ClassTemplatePartialSpecializationDecl *>());
}
// Class is instantiated from a member definition of a class template?
if (const MemberSpecializationInfo *Info =
- CRD->getMemberSpecializationInfo())
- return Info->getInstantiatedFrom();
+ CRD->getMemberSpecializationInfo())
+ return *Info->getInstantiatedFrom();
return D;
}
- if (const auto *ED = dyn_cast<EnumDecl>(D)) {
+ if (const auto *ED = dyn_cast<EnumDecl>(&D)) {
// Enum is instantiated from a member definition of a class template?
if (const EnumDecl *MemberDecl = ED->getInstantiatedFromMemberEnum())
- return MemberDecl;
+ return *MemberDecl;
return D;
}
@@ -373,72 +365,81 @@ static const Decl *adjustDeclToTemplate(const Decl *D) {
const RawComment *ASTContext::getRawCommentForAnyRedecl(
const Decl *D,
const Decl **OriginalDecl) const {
- D = adjustDeclToTemplate(D);
+ if (!D) {
+ if (OriginalDecl)
+ OriginalDecl = nullptr;
+ return nullptr;
+ }
- // Check whether we have cached a comment for this declaration already.
+ D = &adjustDeclToTemplate(*D);
+
+ // Any comment directly attached to D?
{
- llvm::DenseMap<const Decl *, RawCommentAndCacheFlags>::iterator Pos =
- RedeclComments.find(D);
- if (Pos != RedeclComments.end()) {
- const RawCommentAndCacheFlags &Raw = Pos->second;
- if (Raw.getKind() != RawCommentAndCacheFlags::NoCommentInDecl) {
- if (OriginalDecl)
- *OriginalDecl = Raw.getOriginalDecl();
- return Raw.getRaw();
- }
+ auto DeclComment = DeclRawComments.find(D);
+ if (DeclComment != DeclRawComments.end()) {
+ if (OriginalDecl)
+ *OriginalDecl = D;
+ return DeclComment->second;
}
}
- // Search for comments attached to declarations in the redeclaration chain.
- const RawComment *RC = nullptr;
- const Decl *OriginalDeclForRC = nullptr;
- for (auto I : D->redecls()) {
- llvm::DenseMap<const Decl *, RawCommentAndCacheFlags>::iterator Pos =
- RedeclComments.find(I);
- if (Pos != RedeclComments.end()) {
- const RawCommentAndCacheFlags &Raw = Pos->second;
- if (Raw.getKind() != RawCommentAndCacheFlags::NoCommentInDecl) {
- RC = Raw.getRaw();
- OriginalDeclForRC = Raw.getOriginalDecl();
- break;
- }
- } else {
- RC = getRawCommentForDeclNoCache(I);
- OriginalDeclForRC = I;
- RawCommentAndCacheFlags Raw;
- if (RC) {
- // Call order swapped to work around ICE in VS2015 RTM (Release Win32)
- // https://connect.microsoft.com/VisualStudio/feedback/details/1741530
- Raw.setKind(RawCommentAndCacheFlags::FromDecl);
- Raw.setRaw(RC);
- } else
- Raw.setKind(RawCommentAndCacheFlags::NoCommentInDecl);
- Raw.setOriginalDecl(I);
- RedeclComments[I] = Raw;
- if (RC)
- break;
+ // Any comment attached to any redeclaration of D?
+ const Decl *CanonicalD = D->getCanonicalDecl();
+ if (!CanonicalD)
+ return nullptr;
+
+ {
+ auto RedeclComment = RedeclChainComments.find(CanonicalD);
+ if (RedeclComment != RedeclChainComments.end()) {
+ if (OriginalDecl)
+ *OriginalDecl = RedeclComment->second;
+ auto CommentAtRedecl = DeclRawComments.find(RedeclComment->second);
+ assert(CommentAtRedecl != DeclRawComments.end() &&
+ "This decl is supposed to have comment attached.");
+ return CommentAtRedecl->second;
}
}
- // If we found a comment, it should be a documentation comment.
- assert(!RC || RC->isDocumentation() || LangOpts.CommentOpts.ParseAllComments);
+ // Any redeclarations of D that we haven't checked for comments yet?
+ // We can't use DenseMap::iterator directly since it'd get invalid.
+ auto LastCheckedRedecl = [this, CanonicalD]() -> const Decl * {
+ auto LookupRes = CommentlessRedeclChains.find(CanonicalD);
+ if (LookupRes != CommentlessRedeclChains.end())
+ return LookupRes->second;
+ return nullptr;
+ }();
+
+ for (const auto Redecl : D->redecls()) {
+ assert(Redecl);
+ // Skip all redeclarations that have been checked previously.
+ if (LastCheckedRedecl) {
+ if (LastCheckedRedecl == Redecl) {
+ LastCheckedRedecl = nullptr;
+ }
+ continue;
+ }
+ const RawComment *RedeclComment = getRawCommentForDeclNoCache(Redecl);
+ if (RedeclComment) {
+ cacheRawCommentForDecl(*Redecl, *RedeclComment);
+ if (OriginalDecl)
+ *OriginalDecl = Redecl;
+ return RedeclComment;
+ }
+ CommentlessRedeclChains[CanonicalD] = Redecl;
+ }
if (OriginalDecl)
- *OriginalDecl = OriginalDeclForRC;
-
- // Update cache for every declaration in the redeclaration chain.
- RawCommentAndCacheFlags Raw;
- Raw.setRaw(RC);
- Raw.setKind(RawCommentAndCacheFlags::FromRedecl);
- Raw.setOriginalDecl(OriginalDeclForRC);
-
- for (auto I : D->redecls()) {
- RawCommentAndCacheFlags &R = RedeclComments[I];
- if (R.getKind() == RawCommentAndCacheFlags::NoCommentInDecl)
- R = Raw;
- }
+ *OriginalDecl = nullptr;
+ return nullptr;
+}
- return RC;
+void ASTContext::cacheRawCommentForDecl(const Decl &OriginalD,
+ const RawComment &Comment) const {
+ assert(Comment.isDocumentation() || LangOpts.CommentOpts.ParseAllComments);
+ DeclRawComments.try_emplace(&OriginalD, &Comment);
+ const Decl *const CanonicalDecl = OriginalD.getCanonicalDecl();
+ RedeclChainComments.try_emplace(CanonicalDecl, &OriginalD);
+ CommentlessRedeclChains.erase(CanonicalDecl);
}
static void addRedeclaredMethods(const ObjCMethodDecl *ObjCMethod,
@@ -458,6 +459,52 @@ static void addRedeclaredMethods(const ObjCMethodDecl *ObjCMethod,
}
}
+void ASTContext::attachCommentsToJustParsedDecls(ArrayRef<Decl *> Decls,
+ const Preprocessor *PP) {
+ if (Comments.empty() || Decls.empty())
+ return;
+
+ // See if there are any new comments that are not attached to a decl.
+ // The location doesn't have to be precise - we care only about the file.
+ const FileID File =
+ SourceMgr.getDecomposedLoc((*Decls.begin())->getLocation()).first;
+ auto CommentsInThisFile = Comments.getCommentsInFile(File);
+ if (!CommentsInThisFile || CommentsInThisFile->empty() ||
+ CommentsInThisFile->rbegin()->second->isAttached())
+ return;
+
+ // There is at least one comment not attached to a decl.
+ // Maybe it should be attached to one of Decls?
+ //
+ // Note that this way we pick up not only comments that precede the
+ // declaration, but also comments that *follow* the declaration -- thanks to
+ // the lookahead in the lexer: we've consumed the semicolon and looked
+ // ahead through comments.
+
+ for (const Decl *D : Decls) {
+ assert(D);
+ if (D->isInvalidDecl())
+ continue;
+
+ D = &adjustDeclToTemplate(*D);
+
+ const SourceLocation DeclLoc = getDeclLocForCommentSearch(D, SourceMgr);
+
+ if (DeclLoc.isInvalid() || !DeclLoc.isFileID())
+ continue;
+
+ if (DeclRawComments.count(D) > 0)
+ continue;
+
+ if (RawComment *const DocComment =
+ getRawCommentForDeclNoCacheImpl(D, DeclLoc, *CommentsInThisFile)) {
+ cacheRawCommentForDecl(*D, *DocComment);
+ comments::FullComment *FC = DocComment->parse(*this, PP, D);
+ ParsedComments[D->getCanonicalDecl()] = FC;
+ }
+ }
+}
+
comments::FullComment *ASTContext::cloneFullComment(comments::FullComment *FC,
const Decl *D) const {
auto *ThisDeclInfo = new (*this) comments::DeclInfo;
@@ -481,9 +528,9 @@ comments::FullComment *ASTContext::getLocalCommentForDeclUncached(const Decl *D)
comments::FullComment *ASTContext::getCommentForDecl(
const Decl *D,
const Preprocessor *PP) const {
- if (D->isInvalidDecl())
+ if (!D || D->isInvalidDecl())
return nullptr;
- D = adjustDeclToTemplate(D);
+ D = &adjustDeclToTemplate(*D);
const Decl *Canonical = D->getCanonicalDecl();
llvm::DenseMap<const Decl *, comments::FullComment *>::iterator Pos =
@@ -498,7 +545,7 @@ comments::FullComment *ASTContext::getCommentForDecl(
return Pos->second;
}
- const Decl *OriginalDecl;
+ const Decl *OriginalDecl = nullptr;
const RawComment *RC = getRawCommentForAnyRedecl(D, &OriginalDecl);
if (!RC) {
@@ -577,7 +624,7 @@ comments::FullComment *ASTContext::getCommentForDecl(
// should parse the comment in context of that other Decl. This is important
// because comments can contain references to parameter names which can be
// different across redeclarations.
- if (D != OriginalDecl)
+ if (D != OriginalDecl && OriginalDecl)
return getCommentForDecl(OriginalDecl, PP);
comments::FullComment *FC = RC->parse(*this, PP, D);
@@ -691,7 +738,7 @@ ASTContext::getCanonicalTemplateTemplateParmDecl(
cast<TemplateTemplateParmDecl>(*P)));
}
- assert(!TTP->getRequiresClause() &&
+ assert(!TTP->getTemplateParameters()->getRequiresClause() &&
"Unexpected requires-clause on template template-parameter");
Expr *const CanonRequiresClause = nullptr;
@@ -737,6 +784,13 @@ CXXABI *ASTContext::createCXXABI(const TargetInfo &T) {
llvm_unreachable("Invalid CXXABI type!");
}
+interp::Context &ASTContext::getInterpContext() {
+ if (!InterpContext) {
+ InterpContext.reset(new interp::Context(*this));
+ }
+ return *InterpContext.get();
+}
+
static const LangASMap *getAddressSpaceMap(const TargetInfo &T,
const LangOptions &LOpts) {
if (LOpts.FakeAddressSpaceMap) {
@@ -775,7 +829,8 @@ static bool isAddrSpaceMapManglingEnabled(const TargetInfo &TI,
ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
IdentifierTable &idents, SelectorTable &sels,
Builtin::Context &builtins)
- : FunctionProtoTypes(this_()), TemplateSpecializationTypes(this_()),
+ : ConstantArrayTypes(this_()), FunctionProtoTypes(this_()),
+ TemplateSpecializationTypes(this_()),
DependentTemplateSpecializationTypes(this_()),
SubstTemplateTemplateParmPacks(this_()), SourceMgr(SM), LangOpts(LOpts),
SanitizerBL(new SanitizerBlacklist(LangOpts.SanitizerBlacklistFiles, SM)),
@@ -923,7 +978,7 @@ void ASTContext::PrintStats() const {
unsigned counts[] = {
#define TYPE(Name, Parent) 0,
#define ABSTRACT_TYPE(Name, Parent)
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
0 // Extra
};
@@ -943,7 +998,7 @@ void ASTContext::PrintStats() const {
TotalBytes += counts[Idx] * sizeof(Name##Type); \
++Idx;
#define ABSTRACT_TYPE(Name, Parent)
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
llvm::errs() << "Total bytes = " << TotalBytes << "\n";
@@ -1298,6 +1353,12 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target,
#include "clang/Basic/OpenCLExtensionTypes.def"
}
+ if (Target.hasAArch64SVETypes()) {
+#define SVE_TYPE(Name, Id, SingletonId) \
+ InitBuiltinType(SingletonId, BuiltinType::Id);
+#include "clang/Basic/AArch64SVEACLETypes.def"
+ }
+
// Builtin type for __objc_yes and __objc_no
ObjCBuiltinBoolTy = (Target.useSignedCharForObjCBool() ?
SignedCharTy : BoolTy);
@@ -1515,10 +1576,9 @@ void ASTContext::addedLocalImportDecl(ImportDecl *Import) {
/// getFloatTypeSemantics - Return the APFloat 'semantics' for the specified
/// scalar floating point type.
const llvm::fltSemantics &ASTContext::getFloatTypeSemantics(QualType T) const {
- const auto *BT = T->getAs<BuiltinType>();
- assert(BT && "Not a floating point type!");
- switch (BT->getKind()) {
- default: llvm_unreachable("Not a floating point type!");
+ switch (T->castAs<BuiltinType>()->getKind()) {
+ default:
+ llvm_unreachable("Not a floating point type!");
case BuiltinType::Float16:
case BuiltinType::Half:
return Target->getHalfFormat();
@@ -1749,7 +1809,7 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
case Type::Class: \
assert(!T->isDependentType() && "should not see dependent types here"); \
return getTypeInfo(cast<Class##Type>(T)->desugar().getTypePtr());
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
llvm_unreachable("Should not see dependent types");
case Type::FunctionNoProto:
@@ -1968,6 +2028,25 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
Width = Target->getPointerWidth(AS);
Align = Target->getPointerAlign(AS);
break;
+ // The SVE types are effectively target-specific. The length of an
+ // SVE_VECTOR_TYPE is only known at runtime, but it is always a multiple
+ // of 128 bits. There is one predicate bit for each vector byte, so the
+ // length of an SVE_PREDICATE_TYPE is always a multiple of 16 bits.
+ //
+ // Because the length is only known at runtime, we use a dummy value
+ // of 0 for the static length. The alignment values are those defined
+ // by the Procedure Call Standard for the Arm Architecture.
+#define SVE_VECTOR_TYPE(Name, Id, SingletonId, ElKind, ElBits, IsSigned, IsFP)\
+ case BuiltinType::Id: \
+ Width = 0; \
+ Align = 128; \
+ break;
+#define SVE_PREDICATE_TYPE(Name, Id, SingletonId, ElKind) \
+ case BuiltinType::Id: \
+ Width = 0; \
+ Align = 16; \
+ break;
+#include "clang/Basic/AArch64SVEACLETypes.def"
}
break;
case Type::ObjCObjectPointer:
@@ -2364,7 +2443,7 @@ structHasUniqueObjectRepresentations(const ASTContext &Context,
// have tail padding, so just make sure there isn't an error.
if (!isStructEmpty(Base.getType())) {
llvm::Optional<int64_t> Size = structHasUniqueObjectRepresentations(
- Context, Base.getType()->getAs<RecordType>()->getDecl());
+ Context, Base.getType()->castAs<RecordType>()->getDecl());
if (!Size)
return llvm::None;
Bases.emplace_back(Base.getType(), Size.getValue());
@@ -2455,7 +2534,7 @@ bool ASTContext::hasUniqueObjectRepresentations(QualType Ty) const {
}
if (Ty->isRecordType()) {
- const RecordDecl *Record = Ty->getAs<RecordType>()->getDecl();
+ const RecordDecl *Record = Ty->castAs<RecordType>()->getDecl();
if (Record->isInvalidDecl())
return false;
@@ -2790,7 +2869,7 @@ QualType ASTContext::getFunctionTypeWithExceptionSpec(
// Anything else must be a function type. Rebuild it with the new exception
// specification.
- const auto *Proto = Orig->getAs<FunctionProtoType>();
+ const auto *Proto = Orig->castAs<FunctionProtoType>();
return getFunctionType(
Proto->getReturnType(), Proto->getParamTypes(),
Proto->getExtProtoInfo().withExceptionSpec(ESI));
@@ -3087,31 +3166,38 @@ QualType ASTContext::getMemberPointerType(QualType T, const Type *Cls) const {
/// array of the specified element type.
QualType ASTContext::getConstantArrayType(QualType EltTy,
const llvm::APInt &ArySizeIn,
+ const Expr *SizeExpr,
ArrayType::ArraySizeModifier ASM,
unsigned IndexTypeQuals) const {
assert((EltTy->isDependentType() ||
EltTy->isIncompleteType() || EltTy->isConstantSizeType()) &&
"Constant array of VLAs is illegal!");
+ // We only need the size as part of the type if it's instantiation-dependent.
+ if (SizeExpr && !SizeExpr->isInstantiationDependent())
+ SizeExpr = nullptr;
+
// Convert the array size into a canonical width matching the pointer size for
// the target.
llvm::APInt ArySize(ArySizeIn);
ArySize = ArySize.zextOrTrunc(Target->getMaxPointerWidth());
llvm::FoldingSetNodeID ID;
- ConstantArrayType::Profile(ID, EltTy, ArySize, ASM, IndexTypeQuals);
+ ConstantArrayType::Profile(ID, *this, EltTy, ArySize, SizeExpr, ASM,
+ IndexTypeQuals);
void *InsertPos = nullptr;
if (ConstantArrayType *ATP =
ConstantArrayTypes.FindNodeOrInsertPos(ID, InsertPos))
return QualType(ATP, 0);
- // If the element type isn't canonical or has qualifiers, this won't
- // be a canonical type either, so fill in the canonical type field.
+ // If the element type isn't canonical or has qualifiers, or the array bound
+ // is instantiation-dependent, this won't be a canonical type either, so fill
+ // in the canonical type field.
QualType Canon;
- if (!EltTy.isCanonical() || EltTy.hasLocalQualifiers()) {
+ if (!EltTy.isCanonical() || EltTy.hasLocalQualifiers() || SizeExpr) {
SplitQualType canonSplit = getCanonicalType(EltTy).split();
- Canon = getConstantArrayType(QualType(canonSplit.Ty, 0), ArySize,
+ Canon = getConstantArrayType(QualType(canonSplit.Ty, 0), ArySize, nullptr,
ASM, IndexTypeQuals);
Canon = getQualifiedType(Canon, canonSplit.Quals);
@@ -3121,8 +3207,11 @@ QualType ASTContext::getConstantArrayType(QualType EltTy,
assert(!NewIP && "Shouldn't be in the map!"); (void)NewIP;
}
- auto *New = new (*this,TypeAlignment)
- ConstantArrayType(EltTy, Canon, ArySize, ASM, IndexTypeQuals);
+ void *Mem = Allocate(
+ ConstantArrayType::totalSizeToAlloc<const Expr *>(SizeExpr ? 1 : 0),
+ TypeAlignment);
+ auto *New = new (Mem)
+ ConstantArrayType(EltTy, Canon, ArySize, SizeExpr, ASM, IndexTypeQuals);
ConstantArrayTypes.InsertNode(New, InsertPos);
Types.push_back(New);
return QualType(New, 0);
@@ -3143,7 +3232,7 @@ QualType ASTContext::getVariableArrayDecayedType(QualType type) const {
#define TYPE(Class, Base)
#define ABSTRACT_TYPE(Class, Base)
#define NON_CANONICAL_TYPE(Class, Base) case Type::Class:
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
llvm_unreachable("didn't desugar past all non-canonical types?");
// These types should never be variably-modified.
@@ -3219,6 +3308,7 @@ QualType ASTContext::getVariableArrayDecayedType(QualType type) const {
result = getConstantArrayType(
getVariableArrayDecayedType(cat->getElementType()),
cat->getSize(),
+ cat->getSizeExpr(),
cat->getSizeModifier(),
cat->getIndexTypeCVRQualifiers());
break;
@@ -4624,8 +4714,7 @@ ASTContext::applyObjCProtocolQualifiers(QualType type,
QualType
ASTContext::getObjCTypeParamType(const ObjCTypeParamDecl *Decl,
- ArrayRef<ObjCProtocolDecl *> protocols,
- QualType Canonical) const {
+ ArrayRef<ObjCProtocolDecl *> protocols) const {
// Look in the folding set for an existing type.
llvm::FoldingSetNodeID ID;
ObjCTypeParamType::Profile(ID, Decl, protocols);
@@ -4634,16 +4723,14 @@ ASTContext::getObjCTypeParamType(const ObjCTypeParamDecl *Decl,
ObjCTypeParamTypes.FindNodeOrInsertPos(ID, InsertPos))
return QualType(TypeParam, 0);
- if (Canonical.isNull()) {
- // We canonicalize to the underlying type.
- Canonical = getCanonicalType(Decl->getUnderlyingType());
- if (!protocols.empty()) {
- // Apply the protocol qualifers.
- bool hasError;
- Canonical = getCanonicalType(applyObjCProtocolQualifiers(
- Canonical, protocols, hasError, true /*allowOnPointerType*/));
- assert(!hasError && "Error when apply protocol qualifier to bound type");
- }
+ // We canonicalize to the underlying type.
+ QualType Canonical = getCanonicalType(Decl->getUnderlyingType());
+ if (!protocols.empty()) {
+ // Apply the protocol qualifers.
+ bool hasError;
+ Canonical = getCanonicalType(applyObjCProtocolQualifiers(
+ Canonical, protocols, hasError, true /*allowOnPointerType*/));
+ assert(!hasError && "Error when apply protocol qualifier to bound type");
}
unsigned size = sizeof(ObjCTypeParamType);
@@ -5114,7 +5201,7 @@ QualType ASTContext::getUnqualifiedArrayType(QualType type,
if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) {
return getConstantArrayType(unqualElementType, CAT->getSize(),
- CAT->getSizeModifier(), 0);
+ CAT->getSizeExpr(), CAT->getSizeModifier(), 0);
}
if (const auto *IAT = dyn_cast<IncompleteArrayType>(AT)) {
@@ -5487,6 +5574,7 @@ const ArrayType *ASTContext::getAsArrayType(QualType T) const {
if (const auto *CAT = dyn_cast<ConstantArrayType>(ATy))
return cast<ArrayType>(getConstantArrayType(NewEltTy, CAT->getSize(),
+ CAT->getSizeExpr(),
CAT->getSizeModifier(),
CAT->getIndexTypeCVRQualifiers()));
if (const auto *IAT = dyn_cast<IncompleteArrayType>(ATy))
@@ -5599,8 +5687,7 @@ static FloatingRank getFloatingRank(QualType T) {
if (const auto *CT = T->getAs<ComplexType>())
return getFloatingRank(CT->getElementType());
- assert(T->getAs<BuiltinType>() && "getFloatingRank(): not a floating type");
- switch (T->getAs<BuiltinType>()->getKind()) {
+ switch (T->castAs<BuiltinType>()->getKind()) {
default: llvm_unreachable("getFloatingRank(): not a floating type");
case BuiltinType::Float16: return Float16Rank;
case BuiltinType::Half: return HalfRank;
@@ -5977,12 +6064,10 @@ QualType ASTContext::getObjCSuperType() const {
}
void ASTContext::setCFConstantStringType(QualType T) {
- const auto *TD = T->getAs<TypedefType>();
- assert(TD && "Invalid CFConstantStringType");
+ const auto *TD = T->castAs<TypedefType>();
CFConstantStringTypeDecl = cast<TypedefDecl>(TD->getDecl());
const auto *TagType =
- CFConstantStringTypeDecl->getUnderlyingType()->getAs<RecordType>();
- assert(TagType && "Invalid CFConstantStringType");
+ CFConstantStringTypeDecl->getUnderlyingType()->castAs<RecordType>();
CFConstantStringTagDecl = TagType->getDecl();
}
@@ -6238,14 +6323,14 @@ std::string ASTContext::getObjCEncodingForBlock(const BlockExpr *Expr) const {
const BlockDecl *Decl = Expr->getBlockDecl();
QualType BlockTy =
- Expr->getType()->getAs<BlockPointerType>()->getPointeeType();
+ Expr->getType()->castAs<BlockPointerType>()->getPointeeType();
+ QualType BlockReturnTy = BlockTy->castAs<FunctionType>()->getReturnType();
// Encode result type.
if (getLangOpts().EncodeExtendedBlockSig)
- getObjCEncodingForMethodParameter(
- Decl::OBJC_TQ_None, BlockTy->getAs<FunctionType>()->getReturnType(), S,
- true /*Extended*/);
+ getObjCEncodingForMethodParameter(Decl::OBJC_TQ_None, BlockReturnTy, S,
+ true /*Extended*/);
else
- getObjCEncodingForType(BlockTy->getAs<FunctionType>()->getReturnType(), S);
+ getObjCEncodingForType(BlockReturnTy, S);
// Compute size of all parameters.
// Start with computing size of a pointer in number of bytes.
// FIXME: There might(should) be a better way of doing this computation!
@@ -6556,8 +6641,9 @@ void ASTContext::getObjCEncodingForPropertyType(QualType T,
/*Field=*/nullptr);
}
-static char getObjCEncodingForPrimitiveKind(const ASTContext *C,
- BuiltinType::Kind kind) {
+static char getObjCEncodingForPrimitiveType(const ASTContext *C,
+ const BuiltinType *BT) {
+ BuiltinType::Kind kind = BT->getKind();
switch (kind) {
case BuiltinType::Void: return 'v';
case BuiltinType::Bool: return 'B';
@@ -6617,6 +6703,17 @@ static char getObjCEncodingForPrimitiveKind(const ASTContext *C,
// FIXME: potentially need @encodes for these!
return ' ';
+#define SVE_TYPE(Name, Id, SingletonId) \
+ case BuiltinType::Id:
+#include "clang/Basic/AArch64SVEACLETypes.def"
+ {
+ DiagnosticsEngine &Diags = C->getDiagnostics();
+ unsigned DiagID = Diags.getCustomDiagID(
+ DiagnosticsEngine::Error, "cannot yet @encode type %0");
+ Diags.Report(DiagID) << BT->getName(C->getPrintingPolicy());
+ return ' ';
+ }
+
case BuiltinType::ObjCId:
case BuiltinType::ObjCClass:
case BuiltinType::ObjCSel:
@@ -6653,7 +6750,7 @@ static char ObjCEncodingForEnumType(const ASTContext *C, const EnumType *ET) {
// The encoding of a fixed enum type matches its fixed underlying type.
const auto *BT = Enum->getIntegerType()->castAs<BuiltinType>();
- return getObjCEncodingForPrimitiveKind(C, BT->getKind());
+ return getObjCEncodingForPrimitiveType(C, BT);
}
static void EncodeBitField(const ASTContext *Ctx, std::string& S,
@@ -6693,7 +6790,7 @@ static void EncodeBitField(const ASTContext *Ctx, std::string& S,
S += ObjCEncodingForEnumType(Ctx, ET);
else {
const auto *BT = T->castAs<BuiltinType>();
- S += getObjCEncodingForPrimitiveKind(Ctx, BT->getKind());
+ S += getObjCEncodingForPrimitiveType(Ctx, BT);
}
}
S += llvm::utostr(FD->getBitWidthValue(*Ctx));
@@ -6711,7 +6808,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S,
if (FD && FD->isBitField())
return EncodeBitField(this, S, T, FD);
if (const auto *BT = dyn_cast<BuiltinType>(CT))
- S += getObjCEncodingForPrimitiveKind(this, BT->getKind());
+ S += getObjCEncodingForPrimitiveType(this, BT);
else
S += ObjCEncodingForEnumType(this, cast<EnumType>(CT));
return;
@@ -6760,8 +6857,8 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S,
}
} else if (Options.IsOutermostType()) {
QualType P = PointeeTy;
- while (P->getAs<PointerType>())
- P = P->getAs<PointerType>()->getPointeeType();
+ while (auto PT = P->getAs<PointerType>())
+ P = PT->getPointeeType();
if (P.isConstQualified()) {
isReadOnly = true;
S += 'r';
@@ -7033,7 +7130,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S,
case Type::KIND:
#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(KIND, BASE) \
case Type::KIND:
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
llvm_unreachable("@encode for dependent type!");
}
llvm_unreachable("bad type kind!");
@@ -7384,7 +7481,7 @@ static TypedefDecl *CreatePowerABIBuiltinVaListDecl(const ASTContext *Context) {
llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 1);
QualType VaListTagArrayType
= Context->getConstantArrayType(VaListTagTypedefType,
- Size, ArrayType::Normal, 0);
+ Size, nullptr, ArrayType::Normal, 0);
return Context->buildImplicitTypedef(VaListTagArrayType, "__builtin_va_list");
}
@@ -7437,16 +7534,16 @@ CreateX86_64ABIBuiltinVaListDecl(const ASTContext *Context) {
// typedef struct __va_list_tag __builtin_va_list[1];
llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 1);
- QualType VaListTagArrayType =
- Context->getConstantArrayType(VaListTagType, Size, ArrayType::Normal, 0);
+ QualType VaListTagArrayType = Context->getConstantArrayType(
+ VaListTagType, Size, nullptr, ArrayType::Normal, 0);
return Context->buildImplicitTypedef(VaListTagArrayType, "__builtin_va_list");
}
static TypedefDecl *CreatePNaClABIBuiltinVaListDecl(const ASTContext *Context) {
// typedef int __builtin_va_list[4];
llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 4);
- QualType IntArrayType =
- Context->getConstantArrayType(Context->IntTy, Size, ArrayType::Normal, 0);
+ QualType IntArrayType = Context->getConstantArrayType(
+ Context->IntTy, Size, nullptr, ArrayType::Normal, 0);
return Context->buildImplicitTypedef(IntArrayType, "__builtin_va_list");
}
@@ -7540,8 +7637,8 @@ CreateSystemZBuiltinVaListDecl(const ASTContext *Context) {
// typedef __va_list_tag __builtin_va_list[1];
llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 1);
- QualType VaListTagArrayType =
- Context->getConstantArrayType(VaListTagType, Size, ArrayType::Normal, 0);
+ QualType VaListTagArrayType = Context->getConstantArrayType(
+ VaListTagType, Size, nullptr, ArrayType::Normal, 0);
return Context->buildImplicitTypedef(VaListTagArrayType, "__builtin_va_list");
}
@@ -7816,7 +7913,7 @@ Qualifiers::GC ASTContext::getObjCGCAttrKind(QualType Ty) const {
if (Ty->isObjCObjectPointerType() || Ty->isBlockPointerType())
return Qualifiers::Strong;
else if (Ty->isPointerType())
- return getObjCGCAttrKind(Ty->getAs<PointerType>()->getPointeeType());
+ return getObjCGCAttrKind(Ty->castAs<PointerType>()->getPointeeType());
} else {
// It's not valid to set GC attributes on anything that isn't a
// pointer.
@@ -7853,8 +7950,8 @@ bool ASTContext::areCompatibleVectorTypes(QualType FirstVec,
// Treat Neon vector types and most AltiVec vector types as if they are the
// equivalent GCC vector types.
- const auto *First = FirstVec->getAs<VectorType>();
- const auto *Second = SecondVec->getAs<VectorType>();
+ const auto *First = FirstVec->castAs<VectorType>();
+ const auto *Second = SecondVec->castAs<VectorType>();
if (First->getNumElements() == Second->getNumElements() &&
hasSameType(First->getElementType(), Second->getElementType()) &&
First->getVectorKind() != VectorType::AltiVecPixel &&
@@ -7866,6 +7963,28 @@ bool ASTContext::areCompatibleVectorTypes(QualType FirstVec,
return false;
}
+bool ASTContext::hasDirectOwnershipQualifier(QualType Ty) const {
+ while (true) {
+ // __strong id
+ if (const AttributedType *Attr = dyn_cast<AttributedType>(Ty)) {
+ if (Attr->getAttrKind() == attr::ObjCOwnership)
+ return true;
+
+ Ty = Attr->getModifiedType();
+
+ // X *__strong (...)
+ } else if (const ParenType *Paren = dyn_cast<ParenType>(Ty)) {
+ Ty = Paren->getInnerType();
+
+ // We do not want to look through typedefs, typeof(expr),
+ // typeof(type), or any other way that the type is somehow
+ // abstracted.
+ } else {
+ return false;
+ }
+ }
+}
+
//===----------------------------------------------------------------------===//
// ObjCQualifiedIdTypesAreCompatible - Compatibility testing for qualified id's.
//===----------------------------------------------------------------------===//
@@ -7885,15 +8004,11 @@ ASTContext::ProtocolCompatibleWithProtocol(ObjCProtocolDecl *lProto,
/// ObjCQualifiedClassTypesAreCompatible - compare Class<pr,...> and
/// Class<pr1, ...>.
-bool ASTContext::ObjCQualifiedClassTypesAreCompatible(QualType lhs,
- QualType rhs) {
- const auto *lhsQID = lhs->getAs<ObjCObjectPointerType>();
- const auto *rhsOPT = rhs->getAs<ObjCObjectPointerType>();
- assert((lhsQID && rhsOPT) && "ObjCQualifiedClassTypesAreCompatible");
-
- for (auto *lhsProto : lhsQID->quals()) {
+bool ASTContext::ObjCQualifiedClassTypesAreCompatible(
+ const ObjCObjectPointerType *lhs, const ObjCObjectPointerType *rhs) {
+ for (auto *lhsProto : lhs->quals()) {
bool match = false;
- for (auto *rhsProto : rhsOPT->quals()) {
+ for (auto *rhsProto : rhs->quals()) {
if (ProtocolCompatibleWithProtocol(lhsProto, rhsProto)) {
match = true;
break;
@@ -7907,26 +8022,24 @@ bool ASTContext::ObjCQualifiedClassTypesAreCompatible(QualType lhs,
/// ObjCQualifiedIdTypesAreCompatible - We know that one of lhs/rhs is an
/// ObjCQualifiedIDType.
-bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs,
- bool compare) {
- // Allow id<P..> and an 'id' or void* type in all cases.
- if (lhs->isVoidPointerType() ||
- lhs->isObjCIdType() || lhs->isObjCClassType())
- return true;
- else if (rhs->isVoidPointerType() ||
- rhs->isObjCIdType() || rhs->isObjCClassType())
+bool ASTContext::ObjCQualifiedIdTypesAreCompatible(
+ const ObjCObjectPointerType *lhs, const ObjCObjectPointerType *rhs,
+ bool compare) {
+ // Allow id<P..> and an 'id' in all cases.
+ if (lhs->isObjCIdType() || rhs->isObjCIdType())
return true;
- if (const ObjCObjectPointerType *lhsQID = lhs->getAsObjCQualifiedIdType()) {
- const auto *rhsOPT = rhs->getAs<ObjCObjectPointerType>();
-
- if (!rhsOPT) return false;
+ // Don't allow id<P..> to convert to Class or Class<P..> in either direction.
+ if (lhs->isObjCClassType() || lhs->isObjCQualifiedClassType() ||
+ rhs->isObjCClassType() || rhs->isObjCQualifiedClassType())
+ return false;
- if (rhsOPT->qual_empty()) {
+ if (lhs->isObjCQualifiedIdType()) {
+ if (rhs->qual_empty()) {
// If the RHS is a unqualified interface pointer "NSString*",
// make sure we check the class hierarchy.
- if (ObjCInterfaceDecl *rhsID = rhsOPT->getInterfaceDecl()) {
- for (auto *I : lhsQID->quals()) {
+ if (ObjCInterfaceDecl *rhsID = rhs->getInterfaceDecl()) {
+ for (auto *I : lhs->quals()) {
// when comparing an id<P> on lhs with a static type on rhs,
// see if static class implements all of id's protocols, directly or
// through its super class and categories.
@@ -7938,13 +8051,13 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs,
return true;
}
// Both the right and left sides have qualifiers.
- for (auto *lhsProto : lhsQID->quals()) {
+ for (auto *lhsProto : lhs->quals()) {
bool match = false;
// when comparing an id<P> on lhs with a static type on rhs,
// see if static class implements all of id's protocols, directly or
// through its super class and categories.
- for (auto *rhsProto : rhsOPT->quals()) {
+ for (auto *rhsProto : rhs->quals()) {
if (ProtocolCompatibleWithProtocol(lhsProto, rhsProto) ||
(compare && ProtocolCompatibleWithProtocol(rhsProto, lhsProto))) {
match = true;
@@ -7953,8 +8066,8 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs,
}
// If the RHS is a qualified interface pointer "NSString<P>*",
// make sure we check the class hierarchy.
- if (ObjCInterfaceDecl *rhsID = rhsOPT->getInterfaceDecl()) {
- for (auto *I : lhsQID->quals()) {
+ if (ObjCInterfaceDecl *rhsID = rhs->getInterfaceDecl()) {
+ for (auto *I : lhs->quals()) {
// when comparing an id<P> on lhs with a static type on rhs,
// see if static class implements all of id's protocols, directly or
// through its super class and categories.
@@ -7971,13 +8084,11 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs,
return true;
}
- const ObjCObjectPointerType *rhsQID = rhs->getAsObjCQualifiedIdType();
- assert(rhsQID && "One of the LHS/RHS should be id<x>");
+ assert(rhs->isObjCQualifiedIdType() && "One of the LHS/RHS should be id<x>");
- if (const ObjCObjectPointerType *lhsOPT =
- lhs->getAsObjCInterfacePointerType()) {
+ if (lhs->getInterfaceType()) {
// If both the right and left sides have qualifiers.
- for (auto *lhsProto : lhsOPT->quals()) {
+ for (auto *lhsProto : lhs->quals()) {
bool match = false;
// when comparing an id<P> on rhs with a static type on lhs,
@@ -7985,7 +8096,7 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs,
// through its super class and categories.
// First, lhs protocols in the qualifier list must be found, direct
// or indirect in rhs's qualifier list or it is a mismatch.
- for (auto *rhsProto : rhsQID->quals()) {
+ for (auto *rhsProto : rhs->quals()) {
if (ProtocolCompatibleWithProtocol(lhsProto, rhsProto) ||
(compare && ProtocolCompatibleWithProtocol(rhsProto, lhsProto))) {
match = true;
@@ -7998,17 +8109,17 @@ bool ASTContext::ObjCQualifiedIdTypesAreCompatible(QualType lhs, QualType rhs,
// Static class's protocols, or its super class or category protocols
// must be found, direct or indirect in rhs's qualifier list or it is a mismatch.
- if (ObjCInterfaceDecl *lhsID = lhsOPT->getInterfaceDecl()) {
+ if (ObjCInterfaceDecl *lhsID = lhs->getInterfaceDecl()) {
llvm::SmallPtrSet<ObjCProtocolDecl *, 8> LHSInheritedProtocols;
CollectInheritedProtocols(lhsID, LHSInheritedProtocols);
// This is rather dubious but matches gcc's behavior. If lhs has
// no type qualifier and its class has no static protocol(s)
// assume that it is mismatch.
- if (LHSInheritedProtocols.empty() && lhsOPT->qual_empty())
+ if (LHSInheritedProtocols.empty() && lhs->qual_empty())
return false;
for (auto *lhsProto : LHSInheritedProtocols) {
bool match = false;
- for (auto *rhsProto : rhsQID->quals()) {
+ for (auto *rhsProto : rhs->quals()) {
if (ProtocolCompatibleWithProtocol(lhsProto, rhsProto) ||
(compare && ProtocolCompatibleWithProtocol(rhsProto, lhsProto))) {
match = true;
@@ -8032,9 +8143,8 @@ bool ASTContext::canAssignObjCInterfaces(const ObjCObjectPointerType *LHSOPT,
const ObjCObjectType* LHS = LHSOPT->getObjectType();
const ObjCObjectType* RHS = RHSOPT->getObjectType();
- // If either type represents the built-in 'id' or 'Class' types, return true.
- if (LHS->isObjCUnqualifiedIdOrClass() ||
- RHS->isObjCUnqualifiedIdOrClass())
+ // If either type represents the built-in 'id' type, return true.
+ if (LHS->isObjCUnqualifiedId() || RHS->isObjCUnqualifiedId())
return true;
// Function object that propagates a successful result or handles
@@ -8052,15 +8162,20 @@ bool ASTContext::canAssignObjCInterfaces(const ObjCObjectPointerType *LHSOPT,
LHSOPT->stripObjCKindOfTypeAndQuals(*this));
};
+ // Casts from or to id<P> are allowed when the other side has compatible
+ // protocols.
if (LHS->isObjCQualifiedId() || RHS->isObjCQualifiedId()) {
- return finish(ObjCQualifiedIdTypesAreCompatible(QualType(LHSOPT,0),
- QualType(RHSOPT,0),
- false));
+ return finish(ObjCQualifiedIdTypesAreCompatible(LHSOPT, RHSOPT, false));
}
+ // Verify protocol compatibility for casts from Class<P1> to Class<P2>.
if (LHS->isObjCQualifiedClass() && RHS->isObjCQualifiedClass()) {
- return finish(ObjCQualifiedClassTypesAreCompatible(QualType(LHSOPT,0),
- QualType(RHSOPT,0)));
+ return finish(ObjCQualifiedClassTypesAreCompatible(LHSOPT, RHSOPT));
+ }
+
+ // Casts from Class to Class<Foo>, or vice-versa, are allowed.
+ if (LHS->isObjCClass() && RHS->isObjCClass()) {
+ return true;
}
// If we have 2 user-defined types, fall into that path.
@@ -8108,9 +8223,9 @@ bool ASTContext::canAssignObjCInterfacesInBlockPointer(
}
if (LHSOPT->isObjCQualifiedIdType() || RHSOPT->isObjCQualifiedIdType())
- return finish(ObjCQualifiedIdTypesAreCompatible(QualType(LHSOPT,0),
- QualType(RHSOPT,0),
- false));
+ return finish(ObjCQualifiedIdTypesAreCompatible(
+ (BlockReturnType ? LHSOPT : RHSOPT),
+ (BlockReturnType ? RHSOPT : LHSOPT), false));
const ObjCInterfaceType* LHS = LHSOPT->getInterfaceType();
const ObjCInterfaceType* RHS = RHSOPT->getInterfaceType();
@@ -8834,7 +8949,7 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS,
#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) case Type::Class:
#define NON_CANONICAL_TYPE(Class, Base) case Type::Class:
#define DEPENDENT_TYPE(Class, Base) case Type::Class:
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
llvm_unreachable("Non-canonical and dependent types shouldn't get here");
case Type::Auto:
@@ -8854,8 +8969,8 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS,
case Type::Pointer:
{
// Merge two pointer types, while trying to preserve typedef info
- QualType LHSPointee = LHS->getAs<PointerType>()->getPointeeType();
- QualType RHSPointee = RHS->getAs<PointerType>()->getPointeeType();
+ QualType LHSPointee = LHS->castAs<PointerType>()->getPointeeType();
+ QualType RHSPointee = RHS->castAs<PointerType>()->getPointeeType();
if (Unqualified) {
LHSPointee = LHSPointee.getUnqualifiedType();
RHSPointee = RHSPointee.getUnqualifiedType();
@@ -8873,8 +8988,8 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS,
case Type::BlockPointer:
{
// Merge two block pointer types, while trying to preserve typedef info
- QualType LHSPointee = LHS->getAs<BlockPointerType>()->getPointeeType();
- QualType RHSPointee = RHS->getAs<BlockPointerType>()->getPointeeType();
+ QualType LHSPointee = LHS->castAs<BlockPointerType>()->getPointeeType();
+ QualType RHSPointee = RHS->castAs<BlockPointerType>()->getPointeeType();
if (Unqualified) {
LHSPointee = LHSPointee.getUnqualifiedType();
RHSPointee = RHSPointee.getUnqualifiedType();
@@ -8906,8 +9021,8 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS,
case Type::Atomic:
{
// Merge two pointer types, while trying to preserve typedef info
- QualType LHSValue = LHS->getAs<AtomicType>()->getValueType();
- QualType RHSValue = RHS->getAs<AtomicType>()->getValueType();
+ QualType LHSValue = LHS->castAs<AtomicType>()->getValueType();
+ QualType RHSValue = RHS->castAs<AtomicType>()->getValueType();
if (Unqualified) {
LHSValue = LHSValue.getUnqualifiedType();
RHSValue = RHSValue.getUnqualifiedType();
@@ -8975,10 +9090,14 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS,
return LHS;
if (RCAT && getCanonicalType(RHSElem) == getCanonicalType(ResultType))
return RHS;
- if (LCAT) return getConstantArrayType(ResultType, LCAT->getSize(),
- ArrayType::ArraySizeModifier(), 0);
- if (RCAT) return getConstantArrayType(ResultType, RCAT->getSize(),
- ArrayType::ArraySizeModifier(), 0);
+ if (LCAT)
+ return getConstantArrayType(ResultType, LCAT->getSize(),
+ LCAT->getSizeExpr(),
+ ArrayType::ArraySizeModifier(), 0);
+ if (RCAT)
+ return getConstantArrayType(ResultType, RCAT->getSize(),
+ RCAT->getSizeExpr(),
+ ArrayType::ArraySizeModifier(), 0);
if (LVAT && getCanonicalType(LHSElem) == getCanonicalType(ResultType))
return LHS;
if (RVAT && getCanonicalType(RHSElem) == getCanonicalType(ResultType))
@@ -9013,34 +9132,30 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS,
return {};
case Type::Vector:
// FIXME: The merged type should be an ExtVector!
- if (areCompatVectorTypes(LHSCan->getAs<VectorType>(),
- RHSCan->getAs<VectorType>()))
+ if (areCompatVectorTypes(LHSCan->castAs<VectorType>(),
+ RHSCan->castAs<VectorType>()))
return LHS;
return {};
case Type::ObjCObject: {
// Check if the types are assignment compatible.
// FIXME: This should be type compatibility, e.g. whether
// "LHS x; RHS x;" at global scope is legal.
- const auto *LHSIface = LHS->getAs<ObjCObjectType>();
- const auto *RHSIface = RHS->getAs<ObjCObjectType>();
- if (canAssignObjCInterfaces(LHSIface, RHSIface))
+ if (canAssignObjCInterfaces(LHS->castAs<ObjCObjectType>(),
+ RHS->castAs<ObjCObjectType>()))
return LHS;
-
return {};
}
case Type::ObjCObjectPointer:
if (OfBlockPointer) {
if (canAssignObjCInterfacesInBlockPointer(
- LHS->getAs<ObjCObjectPointerType>(),
- RHS->getAs<ObjCObjectPointerType>(),
- BlockReturnType))
+ LHS->castAs<ObjCObjectPointerType>(),
+ RHS->castAs<ObjCObjectPointerType>(), BlockReturnType))
return LHS;
return {};
}
- if (canAssignObjCInterfaces(LHS->getAs<ObjCObjectPointerType>(),
- RHS->getAs<ObjCObjectPointerType>()))
+ if (canAssignObjCInterfaces(LHS->castAs<ObjCObjectPointerType>(),
+ RHS->castAs<ObjCObjectPointerType>()))
return LHS;
-
return {};
case Type::Pipe:
assert(LHS != RHS &&
@@ -9125,7 +9240,7 @@ QualType ASTContext::mergeObjCGCQualifiers(QualType LHS, QualType RHS) {
if (ResReturnType == NewReturnType || ResReturnType == OldReturnType) {
// id foo(); ... __strong id foo(); or: __strong id foo(); ... id foo();
// In either case, use OldReturnType to build the new function type.
- const auto *F = LHS->getAs<FunctionType>();
+ const auto *F = LHS->castAs<FunctionType>();
if (const auto *FPT = cast<FunctionProtoType>(F)) {
FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();
EPI.ExtInfo = getFunctionExtInfo(LHS);
@@ -9166,8 +9281,8 @@ QualType ASTContext::mergeObjCGCQualifiers(QualType LHS, QualType RHS) {
}
if (LHSCan->isObjCObjectPointerType() && RHSCan->isObjCObjectPointerType()) {
- QualType LHSBaseQT = LHS->getAs<ObjCObjectPointerType>()->getPointeeType();
- QualType RHSBaseQT = RHS->getAs<ObjCObjectPointerType>()->getPointeeType();
+ QualType LHSBaseQT = LHS->castAs<ObjCObjectPointerType>()->getPointeeType();
+ QualType RHSBaseQT = RHS->castAs<ObjCObjectPointerType>()->getPointeeType();
QualType ResQT = mergeObjCGCQualifiers(LHSBaseQT, RHSBaseQT);
if (ResQT == LHSBaseQT)
return LHS;
@@ -9203,9 +9318,7 @@ QualType ASTContext::getCorrespondingUnsignedType(QualType T) const {
if (const auto *ETy = T->getAs<EnumType>())
T = ETy->getDecl()->getIntegerType();
- const auto *BTy = T->getAs<BuiltinType>();
- assert(BTy && "Unexpected signed integer or fixed point type");
- switch (BTy->getKind()) {
+ switch (T->castAs<BuiltinType>()->getKind()) {
case BuiltinType::Char_S:
case BuiltinType::SChar:
return UnsignedCharTy;
@@ -9860,7 +9973,7 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
return !D->getDeclContext()->isDependentContext();
else if (isa<OMPAllocateDecl>(D))
return !D->getDeclContext()->isDependentContext();
- else if (isa<OMPDeclareReductionDecl>(D))
+ else if (isa<OMPDeclareReductionDecl>(D) || isa<OMPDeclareMapperDecl>(D))
return !D->getDeclContext()->isDependentContext();
else if (isa<ImportDecl>(D))
return true;
@@ -9963,7 +10076,7 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
return false;
// Variables that have destruction with side-effects are required.
- if (VD->getType().isDestructedType())
+ if (VD->needsDestruction(*this))
return true;
// Variables that have initialization with side-effects are required.
@@ -10035,7 +10148,7 @@ CallingConv ASTContext::getDefaultCallingConvention(bool IsVariadic,
break;
}
}
- return Target->getDefaultCallingConv(TargetInfo::CCMT_Unknown);
+ return Target->getDefaultCallingConv();
}
bool ASTContext::isNearlyEmpty(const CXXRecordDecl *RD) const {
@@ -10153,6 +10266,16 @@ ASTContext::getManglingNumberContext(const DeclContext *DC) {
return *MCtx;
}
+MangleNumberingContext &
+ASTContext::getManglingNumberContext(NeedExtraManglingDecl_t, const Decl *D) {
+ assert(LangOpts.CPlusPlus); // We don't need mangling numbers for plain C.
+ std::unique_ptr<MangleNumberingContext> &MCtx =
+ ExtraMangleNumberingContexts[D];
+ if (!MCtx)
+ MCtx = createMangleNumberingContext();
+ return *MCtx;
+}
+
std::unique_ptr<MangleNumberingContext>
ASTContext::createMangleNumberingContext() const {
return ABI->createMangleNumberingContext();
@@ -10226,7 +10349,7 @@ QualType ASTContext::getStringLiteralArrayType(QualType EltTy,
// Get an array type for the string, according to C99 6.4.5. This includes
// the null terminator character.
- return getConstantArrayType(EltTy, llvm::APInt(32, Length + 1),
+ return getConstantArrayType(EltTy, llvm::APInt(32, Length + 1), nullptr,
ArrayType::Normal, /*IndexTypeQuals*/ 0);
}
@@ -10389,7 +10512,7 @@ ASTContext::getParents(const ast_type_traits::DynTypedNode &Node) {
if (!Parents)
// We build the parent map for the traversal scope (usually whole TU), as
// hasAncestor can escape any subtree.
- Parents = llvm::make_unique<ParentMap>(*this);
+ Parents = std::make_unique<ParentMap>(*this);
return Parents->getParents(Node);
}
@@ -10446,8 +10569,7 @@ QualType ASTContext::getCorrespondingSaturatedType(QualType Ty) const {
if (Ty->isSaturatedFixedPointType()) return Ty;
- const auto &BT = Ty->getAs<BuiltinType>();
- switch (BT->getKind()) {
+ switch (Ty->castAs<BuiltinType>()->getKind()) {
default:
llvm_unreachable("Not a fixed point type!");
case BuiltinType::ShortAccum:
@@ -10499,9 +10621,8 @@ clang::LazyGenerationalUpdatePtr<
unsigned char ASTContext::getFixedPointScale(QualType Ty) const {
assert(Ty->isFixedPointType());
- const auto *BT = Ty->getAs<BuiltinType>();
const TargetInfo &Target = getTargetInfo();
- switch (BT->getKind()) {
+ switch (Ty->castAs<BuiltinType>()->getKind()) {
default:
llvm_unreachable("Not a fixed point type!");
case BuiltinType::ShortAccum:
@@ -10546,9 +10667,8 @@ unsigned char ASTContext::getFixedPointScale(QualType Ty) const {
unsigned char ASTContext::getFixedPointIBits(QualType Ty) const {
assert(Ty->isFixedPointType());
- const auto *BT = Ty->getAs<BuiltinType>();
const TargetInfo &Target = getTargetInfo();
- switch (BT->getKind()) {
+ switch (Ty->castAs<BuiltinType>()->getKind()) {
default:
llvm_unreachable("Not a fixed point type!");
case BuiltinType::ShortAccum:
@@ -10613,9 +10733,8 @@ APFixedPoint ASTContext::getFixedPointMin(QualType Ty) const {
QualType ASTContext::getCorrespondingSignedFixedPointType(QualType Ty) const {
assert(Ty->isUnsignedFixedPointType() &&
"Expected unsigned fixed point type");
- const auto *BTy = Ty->getAs<BuiltinType>();
- switch (BTy->getKind()) {
+ switch (Ty->castAs<BuiltinType>()->getKind()) {
case BuiltinType::UShortAccum:
return ShortAccumTy;
case BuiltinType::UAccum:
diff --git a/lib/AST/ASTDiagnostic.cpp b/lib/AST/ASTDiagnostic.cpp
index 15df865852941..30985441031de 100644
--- a/lib/AST/ASTDiagnostic.cpp
+++ b/lib/AST/ASTDiagnostic.cpp
@@ -154,7 +154,7 @@ Underlying = CTy->desugar(); \
} \
break; \
}
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
}
// If it wasn't sugared, we're done.
diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp
index 9d5dd84161dec..54acca7dc62cc 100644
--- a/lib/AST/ASTImporter.cpp
+++ b/lib/AST/ASTImporter.cpp
@@ -80,6 +80,7 @@ namespace clang {
using ExpectedExpr = llvm::Expected<Expr *>;
using ExpectedDecl = llvm::Expected<Decl *>;
using ExpectedSLoc = llvm::Expected<SourceLocation>;
+ using ExpectedName = llvm::Expected<DeclarationName>;
std::string ImportError::toString() const {
// FIXME: Improve error texts.
@@ -426,6 +427,9 @@ namespace clang {
Error ImportFunctionDeclBody(FunctionDecl *FromFD, FunctionDecl *ToFD);
+ Error ImportDefaultArgOfParmVarDecl(const ParmVarDecl *FromParam,
+ ParmVarDecl *ToParam);
+
template <typename T>
bool hasSameVisibilityContext(T *Found, T *From);
@@ -635,7 +639,8 @@ namespace clang {
return ImportArrayChecked(InContainer.begin(), InContainer.end(), Obegin);
}
- void ImportOverrides(CXXMethodDecl *ToMethod, CXXMethodDecl *FromMethod);
+ Error ImportOverriddenMethods(CXXMethodDecl *ToMethod,
+ CXXMethodDecl *FromMethod);
Expected<FunctionDecl *> FindFunctionTemplateSpecialization(
FunctionDecl *FromFD);
@@ -927,6 +932,27 @@ Expected<LambdaCapture> ASTNodeImporter::import(const LambdaCapture &From) {
EllipsisLoc);
}
+template <typename T>
+bool ASTNodeImporter::hasSameVisibilityContext(T *Found, T *From) {
+ if (From->hasExternalFormalLinkage())
+ return Found->hasExternalFormalLinkage();
+ if (Importer.GetFromTU(Found) != From->getTranslationUnitDecl())
+ return false;
+ if (From->isInAnonymousNamespace())
+ return Found->isInAnonymousNamespace();
+ else
+ return !Found->isInAnonymousNamespace() &&
+ !Found->hasExternalFormalLinkage();
+}
+
+template <>
+bool ASTNodeImporter::hasSameVisibilityContext(TypedefNameDecl *Found,
+ TypedefNameDecl *From) {
+ if (From->isInAnonymousNamespace() && Found->isInAnonymousNamespace())
+ return Importer.GetFromTU(Found) == From->getTranslationUnitDecl();
+ return From->isInAnonymousNamespace() == Found->isInAnonymousNamespace();
+}
+
} // namespace clang
//----------------------------------------------------------------------------
@@ -959,6 +985,10 @@ ExpectedType ASTNodeImporter::VisitBuiltinType(const BuiltinType *T) {
case BuiltinType::Id: \
return Importer.getToContext().Id##Ty;
#include "clang/Basic/OpenCLExtensionTypes.def"
+#define SVE_TYPE(Name, Id, SingletonId) \
+ case BuiltinType::Id: \
+ return Importer.getToContext().SingletonId;
+#include "clang/Basic/AArch64SVEACLETypes.def"
#define SHARED_SINGLETON_TYPE(Expansion)
#define BUILTIN_TYPE(Id, SingletonId) \
case BuiltinType::Id: return Importer.getToContext().SingletonId;
@@ -1068,14 +1098,16 @@ ASTNodeImporter::VisitMemberPointerType(const MemberPointerType *T) {
ExpectedType
ASTNodeImporter::VisitConstantArrayType(const ConstantArrayType *T) {
- ExpectedType ToElementTypeOrErr = import(T->getElementType());
- if (!ToElementTypeOrErr)
- return ToElementTypeOrErr.takeError();
+ QualType ToElementType;
+ const Expr *ToSizeExpr;
+ if (auto Imp = importSeq(T->getElementType(), T->getSizeExpr()))
+ std::tie(ToElementType, ToSizeExpr) = *Imp;
+ else
+ return Imp.takeError();
- return Importer.getToContext().getConstantArrayType(*ToElementTypeOrErr,
- T->getSize(),
- T->getSizeModifier(),
- T->getIndexTypeCVRQualifiers());
+ return Importer.getToContext().getConstantArrayType(
+ ToElementType, T->getSize(), ToSizeExpr, T->getSizeModifier(),
+ T->getIndexTypeCVRQualifiers());
}
ExpectedType
@@ -1645,7 +1677,6 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) {
bool AccumulateChildErrors = isa<TagDecl>(FromDC);
Error ChildErrors = Error::success();
- llvm::SmallVector<Decl *, 8> ImportedDecls;
for (auto *From : FromDC->decls()) {
ExpectedDecl ImportedOrErr = import(From);
if (!ImportedOrErr) {
@@ -1657,6 +1688,59 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) {
}
}
+ // We reorder declarations in RecordDecls because they may have another order
+ // in the "to" context than they have in the "from" context. This may happen
+ // e.g when we import a class like this:
+ // struct declToImport {
+ // int a = c + b;
+ // int b = 1;
+ // int c = 2;
+ // };
+ // During the import of `a` we import first the dependencies in sequence,
+ // thus the order would be `c`, `b`, `a`. We will get the normal order by
+ // first removing the already imported members and then adding them in the
+ // order as they apper in the "from" context.
+ //
+ // Keeping field order is vital because it determines structure layout.
+ //
+ // Here and below, we cannot call field_begin() method and its callers on
+ // ToDC if it has an external storage. Calling field_begin() will
+ // automatically load all the fields by calling
+ // LoadFieldsFromExternalStorage(). LoadFieldsFromExternalStorage() would
+ // call ASTImporter::Import(). This is because the ExternalASTSource
+ // interface in LLDB is implemented by the means of the ASTImporter. However,
+ // calling an import at this point would result in an uncontrolled import, we
+ // must avoid that.
+ const auto *FromRD = dyn_cast<RecordDecl>(FromDC);
+ if (!FromRD)
+ return ChildErrors;
+
+ auto ToDCOrErr = Importer.ImportContext(FromDC);
+ if (!ToDCOrErr) {
+ consumeError(std::move(ChildErrors));
+ return ToDCOrErr.takeError();
+ }
+
+ DeclContext *ToDC = *ToDCOrErr;
+ // Remove all declarations, which may be in wrong order in the
+ // lexical DeclContext and then add them in the proper order.
+ for (auto *D : FromRD->decls()) {
+ if (isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D) || isa<FriendDecl>(D)) {
+ assert(D && "DC contains a null decl");
+ Decl *ToD = Importer.GetAlreadyImportedOrNull(D);
+ // Remove only the decls which we successfully imported.
+ if (ToD) {
+ assert(ToDC == ToD->getLexicalDeclContext() && ToDC->containsDecl(ToD));
+ // Remove the decl from its wrong place in the linked list.
+ ToDC->removeDecl(ToD);
+ // Add the decl to the end of the linked list.
+ // This time it will be at the proper place because the enclosing for
+ // loop iterates in the original (good) order of the decls.
+ ToDC->addDeclInternal(ToD);
+ }
+ }
+ }
+
return ChildErrors;
}
@@ -1752,71 +1836,10 @@ Error ASTNodeImporter::ImportDefinition(
struct CXXRecordDecl::DefinitionData &ToData = ToCXX->data();
struct CXXRecordDecl::DefinitionData &FromData = FromCXX->data();
- ToData.UserDeclaredConstructor = FromData.UserDeclaredConstructor;
- ToData.UserDeclaredSpecialMembers = FromData.UserDeclaredSpecialMembers;
- ToData.Aggregate = FromData.Aggregate;
- ToData.PlainOldData = FromData.PlainOldData;
- ToData.Empty = FromData.Empty;
- ToData.Polymorphic = FromData.Polymorphic;
- ToData.Abstract = FromData.Abstract;
- ToData.IsStandardLayout = FromData.IsStandardLayout;
- ToData.IsCXX11StandardLayout = FromData.IsCXX11StandardLayout;
- ToData.HasBasesWithFields = FromData.HasBasesWithFields;
- ToData.HasBasesWithNonStaticDataMembers =
- FromData.HasBasesWithNonStaticDataMembers;
- ToData.HasPrivateFields = FromData.HasPrivateFields;
- ToData.HasProtectedFields = FromData.HasProtectedFields;
- ToData.HasPublicFields = FromData.HasPublicFields;
- ToData.HasMutableFields = FromData.HasMutableFields;
- ToData.HasVariantMembers = FromData.HasVariantMembers;
- ToData.HasOnlyCMembers = FromData.HasOnlyCMembers;
- ToData.HasInClassInitializer = FromData.HasInClassInitializer;
- ToData.HasUninitializedReferenceMember
- = FromData.HasUninitializedReferenceMember;
- ToData.HasUninitializedFields = FromData.HasUninitializedFields;
- ToData.HasInheritedConstructor = FromData.HasInheritedConstructor;
- ToData.HasInheritedAssignment = FromData.HasInheritedAssignment;
- ToData.NeedOverloadResolutionForCopyConstructor
- = FromData.NeedOverloadResolutionForCopyConstructor;
- ToData.NeedOverloadResolutionForMoveConstructor
- = FromData.NeedOverloadResolutionForMoveConstructor;
- ToData.NeedOverloadResolutionForMoveAssignment
- = FromData.NeedOverloadResolutionForMoveAssignment;
- ToData.NeedOverloadResolutionForDestructor
- = FromData.NeedOverloadResolutionForDestructor;
- ToData.DefaultedCopyConstructorIsDeleted
- = FromData.DefaultedCopyConstructorIsDeleted;
- ToData.DefaultedMoveConstructorIsDeleted
- = FromData.DefaultedMoveConstructorIsDeleted;
- ToData.DefaultedMoveAssignmentIsDeleted
- = FromData.DefaultedMoveAssignmentIsDeleted;
- ToData.DefaultedDestructorIsDeleted = FromData.DefaultedDestructorIsDeleted;
- ToData.HasTrivialSpecialMembers = FromData.HasTrivialSpecialMembers;
- ToData.HasIrrelevantDestructor = FromData.HasIrrelevantDestructor;
- ToData.HasConstexprNonCopyMoveConstructor
- = FromData.HasConstexprNonCopyMoveConstructor;
- ToData.HasDefaultedDefaultConstructor
- = FromData.HasDefaultedDefaultConstructor;
- ToData.DefaultedDefaultConstructorIsConstexpr
- = FromData.DefaultedDefaultConstructorIsConstexpr;
- ToData.HasConstexprDefaultConstructor
- = FromData.HasConstexprDefaultConstructor;
- ToData.HasNonLiteralTypeFieldsOrBases
- = FromData.HasNonLiteralTypeFieldsOrBases;
- // ComputedVisibleConversions not imported.
- ToData.UserProvidedDefaultConstructor
- = FromData.UserProvidedDefaultConstructor;
- ToData.DeclaredSpecialMembers = FromData.DeclaredSpecialMembers;
- ToData.ImplicitCopyConstructorCanHaveConstParamForVBase
- = FromData.ImplicitCopyConstructorCanHaveConstParamForVBase;
- ToData.ImplicitCopyConstructorCanHaveConstParamForNonVBase
- = FromData.ImplicitCopyConstructorCanHaveConstParamForNonVBase;
- ToData.ImplicitCopyAssignmentHasConstParam
- = FromData.ImplicitCopyAssignmentHasConstParam;
- ToData.HasDeclaredCopyConstructorWithConstParam
- = FromData.HasDeclaredCopyConstructorWithConstParam;
- ToData.HasDeclaredCopyAssignmentWithConstParam
- = FromData.HasDeclaredCopyAssignmentWithConstParam;
+
+ #define FIELD(Name, Width, Merge) \
+ ToData.Name = FromData.Name;
+ #include "clang/AST/CXXRecordDeclDefinitionBits.def"
// Copy over the data stored in RecordDeclBits
ToCXX->setArgPassingRestrictions(FromCXX->getArgPassingRestrictions());
@@ -2188,11 +2211,13 @@ ExpectedDecl ASTNodeImporter::VisitNamespaceDecl(NamespaceDecl *D) {
}
if (!ConflictingDecls.empty()) {
- Name = Importer.HandleNameConflict(Name, DC, Decl::IDNS_Namespace,
- ConflictingDecls.data(),
- ConflictingDecls.size());
- if (!Name)
- return make_error<ImportError>(ImportError::NameConflict);
+ ExpectedName NameOrErr = Importer.HandleNameConflict(
+ Name, DC, Decl::IDNS_Namespace, ConflictingDecls.data(),
+ ConflictingDecls.size());
+ if (NameOrErr)
+ Name = NameOrErr.get();
+ else
+ return NameOrErr.takeError();
}
}
@@ -2281,6 +2306,9 @@ ASTNodeImporter::VisitTypedefNameDecl(TypedefNameDecl *D, bool IsAlias) {
// If this typedef is not in block scope, determine whether we've
// seen a typedef with the same name (that we can merge with) or any
// other entity by that name (which name lookup could conflict with).
+ // Note: Repeated typedefs are not valid in C99:
+ // 'typedef int T; typedef int T;' is invalid
+ // We do not care about this now.
if (!DC->isFunctionOrMethod()) {
SmallVector<NamedDecl *, 4> ConflictingDecls;
unsigned IDNS = Decl::IDNS_Ordinary;
@@ -2289,6 +2317,9 @@ ASTNodeImporter::VisitTypedefNameDecl(TypedefNameDecl *D, bool IsAlias) {
if (!FoundDecl->isInIdentifierNamespace(IDNS))
continue;
if (auto *FoundTypedef = dyn_cast<TypedefNameDecl>(FoundDecl)) {
+ if (!hasSameVisibilityContext(FoundTypedef, D))
+ continue;
+
QualType FromUT = D->getUnderlyingType();
QualType FoundUT = FoundTypedef->getUnderlyingType();
if (Importer.IsStructurallyEquivalent(FromUT, FoundUT)) {
@@ -2296,21 +2327,21 @@ ASTNodeImporter::VisitTypedefNameDecl(TypedefNameDecl *D, bool IsAlias) {
// already have a complete underlying type then return with that.
if (!FromUT->isIncompleteType() && !FoundUT->isIncompleteType())
return Importer.MapImported(D, FoundTypedef);
+ // FIXME Handle redecl chain. When you do that make consistent changes
+ // in ASTImporterLookupTable too.
+ } else {
+ ConflictingDecls.push_back(FoundDecl);
}
- // FIXME Handle redecl chain. When you do that make consistent changes
- // in ASTImporterLookupTable too.
- break;
}
-
- ConflictingDecls.push_back(FoundDecl);
}
if (!ConflictingDecls.empty()) {
- Name = Importer.HandleNameConflict(Name, DC, IDNS,
- ConflictingDecls.data(),
- ConflictingDecls.size());
- if (!Name)
- return make_error<ImportError>(ImportError::NameConflict);
+ ExpectedName NameOrErr = Importer.HandleNameConflict(
+ Name, DC, IDNS, ConflictingDecls.data(), ConflictingDecls.size());
+ if (NameOrErr)
+ Name = NameOrErr.get();
+ else
+ return NameOrErr.takeError();
}
}
@@ -2383,11 +2414,12 @@ ASTNodeImporter::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
}
if (!ConflictingDecls.empty()) {
- Name = Importer.HandleNameConflict(Name, DC, IDNS,
- ConflictingDecls.data(),
- ConflictingDecls.size());
- if (!Name)
- return make_error<ImportError>(ImportError::NameConflict);
+ ExpectedName NameOrErr = Importer.HandleNameConflict(
+ Name, DC, IDNS, ConflictingDecls.data(), ConflictingDecls.size());
+ if (NameOrErr)
+ Name = NameOrErr.get();
+ else
+ return NameOrErr.takeError();
}
}
@@ -2491,17 +2523,18 @@ ExpectedDecl ASTNodeImporter::VisitEnumDecl(EnumDecl *D) {
continue;
if (IsStructuralMatch(D, FoundEnum))
return Importer.MapImported(D, FoundEnum);
+ ConflictingDecls.push_back(FoundDecl);
}
-
- ConflictingDecls.push_back(FoundDecl);
}
if (!ConflictingDecls.empty()) {
- Name = Importer.HandleNameConflict(SearchName, DC, IDNS,
- ConflictingDecls.data(),
- ConflictingDecls.size());
- if (!Name)
- return make_error<ImportError>(ImportError::NameConflict);
+ ExpectedName NameOrErr = Importer.HandleNameConflict(
+ SearchName, DC, IDNS, ConflictingDecls.data(),
+ ConflictingDecls.size());
+ if (NameOrErr)
+ Name = NameOrErr.get();
+ else
+ return NameOrErr.takeError();
}
}
@@ -2546,10 +2579,10 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
}
// Import the major distinguishing characteristics of this record.
- DeclContext *DC, *LexicalDC;
+ DeclContext *DC = nullptr, *LexicalDC = nullptr;
DeclarationName Name;
SourceLocation Loc;
- NamedDecl *ToD;
+ NamedDecl *ToD = nullptr;
if (Error Err = ImportDeclParts(D, DC, LexicalDC, Name, ToD, Loc))
return std::move(Err);
if (ToD)
@@ -2568,7 +2601,7 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
// We may already have a record of the same name; try to find and match it.
RecordDecl *PrevDecl = nullptr;
- if (!DC->isFunctionOrMethod()) {
+ if (!DC->isFunctionOrMethod() && !D->isLambda()) {
SmallVector<NamedDecl *, 4> ConflictingDecls;
auto FoundDecls =
Importer.findDeclsInToCtx(DC, SearchName);
@@ -2626,17 +2659,18 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
PrevDecl = FoundRecord->getMostRecentDecl();
break;
}
- }
-
- ConflictingDecls.push_back(FoundDecl);
+ ConflictingDecls.push_back(FoundDecl);
+ } // kind is RecordDecl
} // for
if (!ConflictingDecls.empty() && SearchName) {
- Name = Importer.HandleNameConflict(SearchName, DC, IDNS,
- ConflictingDecls.data(),
- ConflictingDecls.size());
- if (!Name)
- return make_error<ImportError>(ImportError::NameConflict);
+ ExpectedName NameOrErr = Importer.HandleNameConflict(
+ SearchName, DC, IDNS, ConflictingDecls.data(),
+ ConflictingDecls.size());
+ if (NameOrErr)
+ Name = NameOrErr.get();
+ else
+ return NameOrErr.takeError();
}
}
@@ -2660,7 +2694,8 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
ExpectedDecl CDeclOrErr = import(DCXX->getLambdaContextDecl());
if (!CDeclOrErr)
return CDeclOrErr.takeError();
- D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), *CDeclOrErr);
+ D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), *CDeclOrErr,
+ DCXX->hasKnownLambdaInternalLinkage());
} else if (DCXX->isInjectedClassName()) {
// We have to be careful to do a similar dance to the one in
// Sema::ActOnStartCXXMemberDeclarations
@@ -2795,17 +2830,17 @@ ExpectedDecl ASTNodeImporter::VisitEnumConstantDecl(EnumConstantDecl *D) {
if (auto *FoundEnumConstant = dyn_cast<EnumConstantDecl>(FoundDecl)) {
if (IsStructuralMatch(D, FoundEnumConstant))
return Importer.MapImported(D, FoundEnumConstant);
+ ConflictingDecls.push_back(FoundDecl);
}
-
- ConflictingDecls.push_back(FoundDecl);
}
if (!ConflictingDecls.empty()) {
- Name = Importer.HandleNameConflict(Name, DC, IDNS,
- ConflictingDecls.data(),
- ConflictingDecls.size());
- if (!Name)
- return make_error<ImportError>(ImportError::NameConflict);
+ ExpectedName NameOrErr = Importer.HandleNameConflict(
+ Name, DC, IDNS, ConflictingDecls.data(), ConflictingDecls.size());
+ if (NameOrErr)
+ Name = NameOrErr.get();
+ else
+ return NameOrErr.takeError();
}
}
@@ -2956,19 +2991,6 @@ Error ASTNodeImporter::ImportFunctionDeclBody(FunctionDecl *FromFD,
return Error::success();
}
-template <typename T>
-bool ASTNodeImporter::hasSameVisibilityContext(T *Found, T *From) {
- if (From->hasExternalFormalLinkage())
- return Found->hasExternalFormalLinkage();
- if (Importer.GetFromTU(Found) != From->getTranslationUnitDecl())
- return false;
- if (From->isInAnonymousNamespace())
- return Found->isInAnonymousNamespace();
- else
- return !Found->isInAnonymousNamespace() &&
- !Found->hasExternalFormalLinkage();
-}
-
ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
SmallVector<Decl *, 2> Redecls = getCanonicalForwardRedeclChain(D);
@@ -3043,17 +3065,17 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
<< Name << D->getType() << FoundFunction->getType();
Importer.ToDiag(FoundFunction->getLocation(), diag::note_odr_value_here)
<< FoundFunction->getType();
+ ConflictingDecls.push_back(FoundDecl);
}
-
- ConflictingDecls.push_back(FoundDecl);
}
if (!ConflictingDecls.empty()) {
- Name = Importer.HandleNameConflict(Name, DC, IDNS,
- ConflictingDecls.data(),
- ConflictingDecls.size());
- if (!Name)
- return make_error<ImportError>(ImportError::NameConflict);
+ ExpectedName NameOrErr = Importer.HandleNameConflict(
+ Name, DC, IDNS, ConflictingDecls.data(), ConflictingDecls.size());
+ if (NameOrErr)
+ Name = NameOrErr.get();
+ else
+ return NameOrErr.takeError();
}
}
@@ -3066,9 +3088,19 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
if (FoundByLookup) {
if (isa<CXXMethodDecl>(FoundByLookup)) {
if (D->getLexicalDeclContext() == D->getDeclContext()) {
- if (!D->doesThisDeclarationHaveABody())
+ if (!D->doesThisDeclarationHaveABody()) {
+ if (FunctionTemplateDecl *DescribedD =
+ D->getDescribedFunctionTemplate()) {
+ // Handle a "templated" function together with its described
+ // template. This avoids need for a similar check at import of the
+ // described template.
+ assert(FoundByLookup->getDescribedFunctionTemplate() &&
+ "Templated function mapped to non-templated?");
+ Importer.MapImported(DescribedD,
+ FoundByLookup->getDescribedFunctionTemplate());
+ }
return Importer.MapImported(D, FoundByLookup);
- else {
+ } else {
// Let's continue and build up the redecl chain in this case.
// FIXME Merge the functions into one decl.
}
@@ -3154,7 +3186,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
if (GetImportedOrCreateDecl<CXXDestructorDecl>(
ToFunction, D, Importer.getToContext(), cast<CXXRecordDecl>(DC),
ToInnerLocStart, NameInfo, T, TInfo, D->isInlineSpecified(),
- D->isImplicit()))
+ D->isImplicit(), D->getConstexprKind()))
return ToFunction;
CXXDestructorDecl *ToDtor = cast<CXXDestructorDecl>(ToFunction);
@@ -3203,29 +3235,15 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
// decl and its redeclarations may be required.
}
- // Import Ctor initializers.
- if (auto *FromConstructor = dyn_cast<CXXConstructorDecl>(D)) {
- if (unsigned NumInitializers = FromConstructor->getNumCtorInitializers()) {
- SmallVector<CXXCtorInitializer *, 4> CtorInitializers(NumInitializers);
- // Import first, then allocate memory and copy if there was no error.
- if (Error Err = ImportContainerChecked(
- FromConstructor->inits(), CtorInitializers))
- return std::move(Err);
- auto **Memory =
- new (Importer.getToContext()) CXXCtorInitializer *[NumInitializers];
- std::copy(CtorInitializers.begin(), CtorInitializers.end(), Memory);
- auto *ToCtor = cast<CXXConstructorDecl>(ToFunction);
- ToCtor->setCtorInitializers(Memory);
- ToCtor->setNumCtorInitializers(NumInitializers);
- }
- }
-
ToFunction->setQualifierInfo(ToQualifierLoc);
ToFunction->setAccess(D->getAccess());
ToFunction->setLexicalDeclContext(LexicalDC);
ToFunction->setVirtualAsWritten(D->isVirtualAsWritten());
ToFunction->setTrivial(D->isTrivial());
ToFunction->setPure(D->isPure());
+ ToFunction->setDefaulted(D->isDefaulted());
+ ToFunction->setExplicitlyDefaulted(D->isExplicitlyDefaulted());
+ ToFunction->setDeletedAsWritten(D->isDeletedAsWritten());
ToFunction->setRangeEnd(ToEndLoc);
// Set the parameters.
@@ -3260,6 +3278,23 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
return ToFTOrErr.takeError();
}
+ // Import Ctor initializers.
+ if (auto *FromConstructor = dyn_cast<CXXConstructorDecl>(D)) {
+ if (unsigned NumInitializers = FromConstructor->getNumCtorInitializers()) {
+ SmallVector<CXXCtorInitializer *, 4> CtorInitializers(NumInitializers);
+ // Import first, then allocate memory and copy if there was no error.
+ if (Error Err = ImportContainerChecked(
+ FromConstructor->inits(), CtorInitializers))
+ return std::move(Err);
+ auto **Memory =
+ new (Importer.getToContext()) CXXCtorInitializer *[NumInitializers];
+ std::copy(CtorInitializers.begin(), CtorInitializers.end(), Memory);
+ auto *ToCtor = cast<CXXConstructorDecl>(ToFunction);
+ ToCtor->setCtorInitializers(Memory);
+ ToCtor->setNumCtorInitializers(NumInitializers);
+ }
+ }
+
if (D->doesThisDeclarationHaveABody()) {
Error Err = ImportFunctionDeclBody(D, ToFunction);
@@ -3292,7 +3327,9 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
}
if (auto *FromCXXMethod = dyn_cast<CXXMethodDecl>(D))
- ImportOverrides(cast<CXXMethodDecl>(ToFunction), FromCXXMethod);
+ if (Error Err = ImportOverriddenMethods(cast<CXXMethodDecl>(ToFunction),
+ FromCXXMethod))
+ return std::move(Err);
// Import the rest of the chain. I.e. import all subsequent declarations.
for (++RedeclIt; RedeclIt != Redecls.end(); ++RedeclIt) {
@@ -3686,17 +3723,17 @@ ExpectedDecl ASTNodeImporter::VisitVarDecl(VarDecl *D) {
<< Name << D->getType() << FoundVar->getType();
Importer.ToDiag(FoundVar->getLocation(), diag::note_odr_value_here)
<< FoundVar->getType();
+ ConflictingDecls.push_back(FoundDecl);
}
-
- ConflictingDecls.push_back(FoundDecl);
}
if (!ConflictingDecls.empty()) {
- Name = Importer.HandleNameConflict(Name, DC, IDNS,
- ConflictingDecls.data(),
- ConflictingDecls.size());
- if (!Name)
- return make_error<ImportError>(ImportError::NameConflict);
+ ExpectedName NameOrErr = Importer.HandleNameConflict(
+ Name, DC, IDNS, ConflictingDecls.data(), ConflictingDecls.size());
+ if (NameOrErr)
+ Name = NameOrErr.get();
+ else
+ return NameOrErr.takeError();
}
}
@@ -3772,6 +3809,28 @@ ExpectedDecl ASTNodeImporter::VisitImplicitParamDecl(ImplicitParamDecl *D) {
return ToParm;
}
+Error ASTNodeImporter::ImportDefaultArgOfParmVarDecl(
+ const ParmVarDecl *FromParam, ParmVarDecl *ToParam) {
+ ToParam->setHasInheritedDefaultArg(FromParam->hasInheritedDefaultArg());
+ ToParam->setKNRPromoted(FromParam->isKNRPromoted());
+
+ if (FromParam->hasUninstantiatedDefaultArg()) {
+ if (auto ToDefArgOrErr = import(FromParam->getUninstantiatedDefaultArg()))
+ ToParam->setUninstantiatedDefaultArg(*ToDefArgOrErr);
+ else
+ return ToDefArgOrErr.takeError();
+ } else if (FromParam->hasUnparsedDefaultArg()) {
+ ToParam->setUnparsedDefaultArg();
+ } else if (FromParam->hasDefaultArg()) {
+ if (auto ToDefArgOrErr = import(FromParam->getDefaultArg()))
+ ToParam->setDefaultArg(*ToDefArgOrErr);
+ else
+ return ToDefArgOrErr.takeError();
+ }
+
+ return Error::success();
+}
+
ExpectedDecl ASTNodeImporter::VisitParmVarDecl(ParmVarDecl *D) {
// Parameters are created in the translation unit's context, then moved
// into the function declaration's context afterward.
@@ -3798,23 +3857,11 @@ ExpectedDecl ASTNodeImporter::VisitParmVarDecl(ParmVarDecl *D) {
/*DefaultArg*/ nullptr))
return ToParm;
- // Set the default argument.
- ToParm->setHasInheritedDefaultArg(D->hasInheritedDefaultArg());
- ToParm->setKNRPromoted(D->isKNRPromoted());
-
- if (D->hasUninstantiatedDefaultArg()) {
- if (auto ToDefArgOrErr = import(D->getUninstantiatedDefaultArg()))
- ToParm->setUninstantiatedDefaultArg(*ToDefArgOrErr);
- else
- return ToDefArgOrErr.takeError();
- } else if (D->hasUnparsedDefaultArg()) {
- ToParm->setUnparsedDefaultArg();
- } else if (D->hasDefaultArg()) {
- if (auto ToDefArgOrErr = import(D->getDefaultArg()))
- ToParm->setDefaultArg(*ToDefArgOrErr);
- else
- return ToDefArgOrErr.takeError();
- }
+ // Set the default argument. It should be no problem if it was already done.
+ // Do not import the default expression before GetImportedOrCreateDecl call
+ // to avoid possible infinite import loop because circular dependency.
+ if (Error Err = ImportDefaultArgOfParmVarDecl(D, ToParm))
+ return std::move(Err);
if (D->isObjCMethodParameter()) {
ToParm->setObjCMethodScopeInfo(D->getFunctionScopeIndex());
@@ -5016,25 +5063,27 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
if (IsStructuralMatch(D, FoundTemplate)) {
ClassTemplateDecl *TemplateWithDef =
getTemplateDefinition(FoundTemplate);
- if (D->isThisDeclarationADefinition() && TemplateWithDef) {
+ if (D->isThisDeclarationADefinition() && TemplateWithDef)
return Importer.MapImported(D, TemplateWithDef);
- }
- FoundByLookup = FoundTemplate;
- break;
+ if (!FoundByLookup)
+ FoundByLookup = FoundTemplate;
+ // Search in all matches because there may be multiple decl chains,
+ // see ASTTests test ImportExistingFriendClassTemplateDef.
+ continue;
}
+ ConflictingDecls.push_back(FoundDecl);
}
-
- ConflictingDecls.push_back(FoundDecl);
}
if (!ConflictingDecls.empty()) {
- Name = Importer.HandleNameConflict(Name, DC, Decl::IDNS_Ordinary,
- ConflictingDecls.data(),
- ConflictingDecls.size());
+ ExpectedName NameOrErr = Importer.HandleNameConflict(
+ Name, DC, Decl::IDNS_Ordinary, ConflictingDecls.data(),
+ ConflictingDecls.size());
+ if (NameOrErr)
+ Name = NameOrErr.get();
+ else
+ return NameOrErr.takeError();
}
-
- if (!Name)
- return make_error<ImportError>(ImportError::NameConflict);
}
CXXRecordDecl *FromTemplated = D->getTemplatedDecl();
@@ -5307,22 +5356,20 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateDecl(VarTemplateDecl *D) {
FoundTemplate->getTemplatedDecl());
return Importer.MapImported(D, FoundTemplate);
}
+ ConflictingDecls.push_back(FoundDecl);
}
-
- ConflictingDecls.push_back(FoundDecl);
}
if (!ConflictingDecls.empty()) {
- Name = Importer.HandleNameConflict(Name, DC, Decl::IDNS_Ordinary,
- ConflictingDecls.data(),
- ConflictingDecls.size());
+ ExpectedName NameOrErr = Importer.HandleNameConflict(
+ Name, DC, Decl::IDNS_Ordinary, ConflictingDecls.data(),
+ ConflictingDecls.size());
+ if (NameOrErr)
+ Name = NameOrErr.get();
+ else
+ return NameOrErr.takeError();
}
- if (!Name)
- // FIXME: Is it possible to get other error than name conflict?
- // (Put this `if` into the previous `if`?)
- return make_error<ImportError>(ImportError::NameConflict);
-
VarDecl *DTemplated = D->getTemplatedDecl();
// Import the type.
@@ -5533,17 +5580,16 @@ ASTNodeImporter::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
continue;
if (auto *FoundTemplate = dyn_cast<FunctionTemplateDecl>(FoundDecl)) {
- if (FoundTemplate->hasExternalFormalLinkage() &&
- D->hasExternalFormalLinkage()) {
- if (IsStructuralMatch(D, FoundTemplate)) {
- FunctionTemplateDecl *TemplateWithDef =
- getTemplateDefinition(FoundTemplate);
- if (D->isThisDeclarationADefinition() && TemplateWithDef) {
- return Importer.MapImported(D, TemplateWithDef);
- }
- FoundByLookup = FoundTemplate;
- break;
- }
+ if (!hasSameVisibilityContext(FoundTemplate, D))
+ continue;
+ if (IsStructuralMatch(D, FoundTemplate)) {
+ FunctionTemplateDecl *TemplateWithDef =
+ getTemplateDefinition(FoundTemplate);
+ if (D->isThisDeclarationADefinition() && TemplateWithDef)
+ return Importer.MapImported(D, TemplateWithDef);
+
+ FoundByLookup = FoundTemplate;
+ break;
// TODO: handle conflicting names
}
}
@@ -6868,8 +6914,23 @@ ExpectedStmt ASTNodeImporter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
if (!UsedContextOrErr)
return UsedContextOrErr.takeError();
- return CXXDefaultArgExpr::Create(
- Importer.getToContext(), *ToUsedLocOrErr, *ToParamOrErr, *UsedContextOrErr);
+ // Import the default arg if it was not imported yet.
+ // This is needed because it can happen that during the import of the
+ // default expression (from VisitParmVarDecl) the same ParmVarDecl is
+ // encountered here. The default argument for a ParmVarDecl is set in the
+ // ParmVarDecl only after it is imported (set in VisitParmVarDecl if not here,
+ // see VisitParmVarDecl).
+ ParmVarDecl *ToParam = *ToParamOrErr;
+ if (!ToParam->getDefaultArg()) {
+ Optional<ParmVarDecl *> FromParam = Importer.getImportedFromDecl(ToParam);
+ assert(FromParam && "ParmVarDecl was not imported?");
+
+ if (Error Err = ImportDefaultArgOfParmVarDecl(*FromParam, ToParam))
+ return std::move(Err);
+ }
+
+ return CXXDefaultArgExpr::Create(Importer.getToContext(), *ToUsedLocOrErr,
+ *ToParamOrErr, *UsedContextOrErr);
}
ExpectedStmt
@@ -7701,15 +7762,18 @@ ExpectedStmt ASTNodeImporter::VisitCXXTypeidExpr(CXXTypeidExpr *E) {
*ToTypeOrErr, *ToExprOperandOrErr, *ToSourceRangeOrErr);
}
-void ASTNodeImporter::ImportOverrides(CXXMethodDecl *ToMethod,
- CXXMethodDecl *FromMethod) {
+Error ASTNodeImporter::ImportOverriddenMethods(CXXMethodDecl *ToMethod,
+ CXXMethodDecl *FromMethod) {
+ Error ImportErrors = Error::success();
for (auto *FromOverriddenMethod : FromMethod->overridden_methods()) {
if (auto ImportedOrErr = import(FromOverriddenMethod))
ToMethod->getCanonicalDecl()->addOverriddenMethod(cast<CXXMethodDecl>(
(*ImportedOrErr)->getCanonicalDecl()));
else
- consumeError(ImportedOrErr.takeError());
+ ImportErrors =
+ joinErrors(std::move(ImportErrors), ImportedOrErr.takeError());
}
+ return ImportErrors;
}
ASTImporter::ASTImporter(ASTContext &ToContext, FileManager &ToFileManager,
@@ -7718,7 +7782,7 @@ ASTImporter::ASTImporter(ASTContext &ToContext, FileManager &ToFileManager,
std::shared_ptr<ASTImporterSharedState> SharedState)
: SharedState(SharedState), ToContext(ToContext), FromContext(FromContext),
ToFileManager(ToFileManager), FromFileManager(FromFileManager),
- Minimal(MinimalImport) {
+ Minimal(MinimalImport), ODRHandling(ODRHandlingType::Conservative) {
// Create a default state without the lookup table: LLDB case.
if (!SharedState) {
@@ -8390,13 +8454,13 @@ Expected<FileID> ASTImporter::Import(FileID FromID, bool IsBuiltin) {
// disk again
// FIXME: We definitely want to re-use the existing MemoryBuffer, rather
// than mmap the files several times.
- const FileEntry *Entry =
+ auto Entry =
ToFileManager.getFile(Cache->OrigEntry->getName());
// FIXME: The filename may be a virtual name that does probably not
// point to a valid file and we get no Entry here. In this case try with
// the memory buffer below.
if (Entry)
- ToID = ToSM.createFileID(Entry, *ToIncludeLoc,
+ ToID = ToSM.createFileID(*Entry, *ToIncludeLoc,
FromSLoc.getFile().getFileCharacteristic());
}
}
@@ -8404,8 +8468,9 @@ Expected<FileID> ASTImporter::Import(FileID FromID, bool IsBuiltin) {
if (ToID.isInvalid() || IsBuiltin) {
// FIXME: We want to re-use the existing MemoryBuffer!
bool Invalid = true;
- const llvm::MemoryBuffer *FromBuf = Cache->getBuffer(
- FromContext.getDiagnostics(), FromSM, SourceLocation{}, &Invalid);
+ const llvm::MemoryBuffer *FromBuf =
+ Cache->getBuffer(FromContext.getDiagnostics(),
+ FromSM.getFileManager(), SourceLocation{}, &Invalid);
if (!FromBuf || Invalid)
// FIXME: Use a new error kind?
return llvm::make_error<ImportError>(ImportError::Unknown);
@@ -8421,6 +8486,10 @@ Expected<FileID> ASTImporter::Import(FileID FromID, bool IsBuiltin) {
assert(ToID.isValid() && "Unexpected invalid fileID was created.");
ImportedFileIDs[FromID] = ToID;
+
+ if (FileIDImportHandler)
+ FileIDImportHandler(ToID, FromID);
+
return ToID;
}
@@ -8640,12 +8709,17 @@ Expected<Selector> ASTImporter::Import(Selector FromSel) {
return ToContext.Selectors.getSelector(FromSel.getNumArgs(), Idents.data());
}
-DeclarationName ASTImporter::HandleNameConflict(DeclarationName Name,
- DeclContext *DC,
- unsigned IDNS,
- NamedDecl **Decls,
- unsigned NumDecls) {
- return Name;
+Expected<DeclarationName> ASTImporter::HandleNameConflict(DeclarationName Name,
+ DeclContext *DC,
+ unsigned IDNS,
+ NamedDecl **Decls,
+ unsigned NumDecls) {
+ if (ODRHandling == ODRHandlingType::Conservative)
+ // Report error at any name conflict.
+ return make_error<ImportError>(ImportError::NameConflict);
+ else
+ // Allow to create the new Decl with the same name.
+ return Name;
}
DiagnosticBuilder ASTImporter::ToDiag(SourceLocation Loc, unsigned DiagID) {
diff --git a/lib/AST/ASTStructuralEquivalence.cpp b/lib/AST/ASTStructuralEquivalence.cpp
index 912db3c130c51..db48405055cda 100644
--- a/lib/AST/ASTStructuralEquivalence.cpp
+++ b/lib/AST/ASTStructuralEquivalence.cpp
@@ -235,12 +235,21 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
const TemplateName &N1,
const TemplateName &N2) {
- if (N1.getKind() != N2.getKind())
+ TemplateDecl *TemplateDeclN1 = N1.getAsTemplateDecl();
+ TemplateDecl *TemplateDeclN2 = N2.getAsTemplateDecl();
+ if (TemplateDeclN1 && TemplateDeclN2) {
+ if (!IsStructurallyEquivalent(Context, TemplateDeclN1, TemplateDeclN2))
+ return false;
+ // If the kind is different we compare only the template decl.
+ if (N1.getKind() != N2.getKind())
+ return true;
+ } else if (TemplateDeclN1 || TemplateDeclN2)
+ return false;
+ else if (N1.getKind() != N2.getKind())
return false;
+
+ // Check for special case incompatibilities.
switch (N1.getKind()) {
- case TemplateName::Template:
- return IsStructurallyEquivalent(Context, N1.getAsTemplateDecl(),
- N2.getAsTemplateDecl());
case TemplateName::OverloadedTemplate: {
OverloadedTemplateStorage *OS1 = N1.getAsOverloadedTemplate(),
@@ -259,14 +268,6 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return TN1->getDeclName() == TN2->getDeclName();
}
- case TemplateName::QualifiedTemplate: {
- QualifiedTemplateName *QN1 = N1.getAsQualifiedTemplateName(),
- *QN2 = N2.getAsQualifiedTemplateName();
- return IsStructurallyEquivalent(Context, QN1->getDecl(), QN2->getDecl()) &&
- IsStructurallyEquivalent(Context, QN1->getQualifier(),
- QN2->getQualifier());
- }
-
case TemplateName::DependentTemplate: {
DependentTemplateName *DN1 = N1.getAsDependentTemplateName(),
*DN2 = N2.getAsDependentTemplateName();
@@ -281,15 +282,6 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return false;
}
- case TemplateName::SubstTemplateTemplateParm: {
- SubstTemplateTemplateParmStorage *TS1 = N1.getAsSubstTemplateTemplateParm(),
- *TS2 = N2.getAsSubstTemplateTemplateParm();
- return IsStructurallyEquivalent(Context, TS1->getParameter(),
- TS2->getParameter()) &&
- IsStructurallyEquivalent(Context, TS1->getReplacement(),
- TS2->getReplacement());
- }
-
case TemplateName::SubstTemplateTemplateParmPack: {
SubstTemplateTemplateParmPackStorage
*P1 = N1.getAsSubstTemplateTemplateParmPack(),
@@ -299,8 +291,16 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
IsStructurallyEquivalent(Context, P1->getParameterPack(),
P2->getParameterPack());
}
+
+ case TemplateName::Template:
+ case TemplateName::QualifiedTemplate:
+ case TemplateName::SubstTemplateTemplateParm:
+ // It is sufficient to check value of getAsTemplateDecl.
+ break;
+
}
- return false;
+
+ return true;
}
/// Determine whether two template arguments are equivalent.
@@ -1574,20 +1574,24 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
Decl *D1, Decl *D2) {
// FIXME: Check for known structural equivalences via a callback of some sort.
+ D1 = D1->getCanonicalDecl();
+ D2 = D2->getCanonicalDecl();
+ std::pair<Decl *, Decl *> P{D1, D2};
+
// Check whether we already know that these two declarations are not
// structurally equivalent.
- if (Context.NonEquivalentDecls.count(
- std::make_pair(D1->getCanonicalDecl(), D2->getCanonicalDecl())))
+ if (Context.NonEquivalentDecls.count(P))
return false;
- // Determine whether we've already produced a tentative equivalence for D1.
- Decl *&EquivToD1 = Context.TentativeEquivalences[D1->getCanonicalDecl()];
- if (EquivToD1)
- return EquivToD1 == D2->getCanonicalDecl();
+ // Check if a check for these declarations is already pending.
+ // If yes D1 and D2 will be checked later (from DeclsToCheck),
+ // or these are already checked (and equivalent).
+ bool Inserted = Context.VisitedDecls.insert(P).second;
+ if (!Inserted)
+ return true;
+
+ Context.DeclsToCheck.push(P);
- // Produce a tentative equivalence D1 <-> D2, which will be checked later.
- EquivToD1 = D2->getCanonicalDecl();
- Context.DeclsToCheck.push_back(D1->getCanonicalDecl());
return true;
}
@@ -1703,11 +1707,13 @@ bool StructuralEquivalenceContext::IsEquivalent(Decl *D1, Decl *D2) {
// Ensure that the implementation functions (all static functions in this TU)
// never call the public ASTStructuralEquivalence::IsEquivalent() functions,
// because that will wreak havoc the internal state (DeclsToCheck and
- // TentativeEquivalences members) and can cause faulty behaviour. For
- // instance, some leaf declarations can be stated and cached as inequivalent
- // as a side effect of one inequivalent element in the DeclsToCheck list.
+ // VisitedDecls members) and can cause faulty behaviour.
+ // In other words: Do not start a graph search from a new node with the
+ // internal data of another search in progress.
+ // FIXME: Better encapsulation and separation of internal and public
+ // functionality.
assert(DeclsToCheck.empty());
- assert(TentativeEquivalences.empty());
+ assert(VisitedDecls.empty());
if (!::IsStructurallyEquivalent(*this, D1, D2))
return false;
@@ -1717,7 +1723,7 @@ bool StructuralEquivalenceContext::IsEquivalent(Decl *D1, Decl *D2) {
bool StructuralEquivalenceContext::IsEquivalent(QualType T1, QualType T2) {
assert(DeclsToCheck.empty());
- assert(TentativeEquivalences.empty());
+ assert(VisitedDecls.empty());
if (!::IsStructurallyEquivalent(*this, T1, T2))
return false;
@@ -1876,11 +1882,11 @@ bool StructuralEquivalenceContext::CheckKindSpecificEquivalence(
bool StructuralEquivalenceContext::Finish() {
while (!DeclsToCheck.empty()) {
// Check the next declaration.
- Decl *D1 = DeclsToCheck.front();
- DeclsToCheck.pop_front();
+ std::pair<Decl *, Decl *> P = DeclsToCheck.front();
+ DeclsToCheck.pop();
- Decl *D2 = TentativeEquivalences[D1];
- assert(D2 && "Unrecorded tentative equivalence?");
+ Decl *D1 = P.first;
+ Decl *D2 = P.second;
bool Equivalent =
CheckCommonEquivalence(D1, D2) && CheckKindSpecificEquivalence(D1, D2);
@@ -1888,8 +1894,8 @@ bool StructuralEquivalenceContext::Finish() {
if (!Equivalent) {
// Note that these two declarations are not equivalent (and we already
// know about it).
- NonEquivalentDecls.insert(
- std::make_pair(D1->getCanonicalDecl(), D2->getCanonicalDecl()));
+ NonEquivalentDecls.insert(P);
+
return true;
}
}
diff --git a/lib/AST/ASTTypeTraits.cpp b/lib/AST/ASTTypeTraits.cpp
index ba1581bd3f6b6..6b7f6ec51086f 100644
--- a/lib/AST/ASTTypeTraits.cpp
+++ b/lib/AST/ASTTypeTraits.cpp
@@ -15,6 +15,7 @@
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/NestedNameSpecifier.h"
namespace clang {
namespace ast_type_traits {
@@ -36,7 +37,7 @@ const ASTNodeKind::KindInfo ASTNodeKind::AllKindInfo[] = {
#include "clang/AST/StmtNodes.inc"
{ NKI_None, "Type" },
#define TYPE(DERIVED, BASE) { NKI_##BASE, #DERIVED "Type" },
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
{ NKI_None, "OMPClause" },
#define OPENMP_CLAUSE(TextualSpelling, Class) {NKI_OMPClause, #Class},
#include "clang/Basic/OpenMPKinds.def"
@@ -103,7 +104,7 @@ ASTNodeKind ASTNodeKind::getFromNode(const Type &T) {
#define TYPE(Class, Base) \
case Type::Class: return ASTNodeKind(NKI_##Class##Type);
#define ABSTRACT_TYPE(Class, Base)
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
}
llvm_unreachable("invalid type kind");
}
@@ -115,6 +116,8 @@ ASTNodeKind ASTNodeKind::getFromNode(const OMPClause &C) {
#include "clang/Basic/OpenMPKinds.def"
case OMPC_threadprivate:
case OMPC_uniform:
+ case OMPC_device_type:
+ case OMPC_match:
case OMPC_unknown:
llvm_unreachable("unexpected OpenMP clause kind");
}
@@ -129,9 +132,12 @@ void DynTypedNode::print(llvm::raw_ostream &OS,
TN->print(OS, PP);
else if (const NestedNameSpecifier *NNS = get<NestedNameSpecifier>())
NNS->print(OS, PP);
- else if (const NestedNameSpecifierLoc *NNSL = get<NestedNameSpecifierLoc>())
- NNSL->getNestedNameSpecifier()->print(OS, PP);
- else if (const QualType *QT = get<QualType>())
+ else if (const NestedNameSpecifierLoc *NNSL = get<NestedNameSpecifierLoc>()) {
+ if (const NestedNameSpecifier *NNS = NNSL->getNestedNameSpecifier())
+ NNS->print(OS, PP);
+ else
+ OS << "(empty NestedNameSpecifierLoc)";
+ } else if (const QualType *QT = get<QualType>())
QT->print(OS, PP);
else if (const TypeLoc *TL = get<TypeLoc>())
TL->getType().print(OS, PP);
diff --git a/lib/AST/CXXInheritance.cpp b/lib/AST/CXXInheritance.cpp
index ecf451b175afb..a3a3794b2edd4 100644
--- a/lib/AST/CXXInheritance.cpp
+++ b/lib/AST/CXXInheritance.cpp
@@ -44,7 +44,7 @@ void CXXBasePaths::ComputeDeclsFound() {
Decls.insert(Path->Decls.front());
NumDeclsFound = Decls.size();
- DeclsFound = llvm::make_unique<NamedDecl *[]>(NumDeclsFound);
+ DeclsFound = std::make_unique<NamedDecl *[]>(NumDeclsFound);
std::copy(Decls.begin(), Decls.end(), DeclsFound.get());
}
diff --git a/lib/AST/Comment.cpp b/lib/AST/Comment.cpp
index 25339c7901e30..23dc7ba93591e 100644
--- a/lib/AST/Comment.cpp
+++ b/lib/AST/Comment.cpp
@@ -13,10 +13,25 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/CharInfo.h"
#include "llvm/Support/ErrorHandling.h"
+#include <type_traits>
namespace clang {
namespace comments {
+// Check that no comment class has a non-trival destructor. They are allocated
+// with a BumpPtrAllocator and therefore their destructor is not executed.
+#define ABSTRACT_COMMENT(COMMENT)
+#define COMMENT(CLASS, PARENT) \
+ static_assert(std::is_trivially_destructible<CLASS>::value, \
+ #CLASS " should be trivially destructible!");
+#include "clang/AST/CommentNodes.inc"
+#undef COMMENT
+#undef ABSTRACT_COMMENT
+
+// DeclInfo is also allocated with a BumpPtrAllocator.
+static_assert(std::is_trivially_destructible<DeclInfo>::value,
+ "DeclInfo should be trivially destructible!");
+
const char *Comment::getCommentKindName() const {
switch (getCommentKind()) {
case NoCommentKind: return "NoCommentKind";
diff --git a/lib/AST/CommentLexer.cpp b/lib/AST/CommentLexer.cpp
index 19485f6018c02..c1ea3eab075e2 100644
--- a/lib/AST/CommentLexer.cpp
+++ b/lib/AST/CommentLexer.cpp
@@ -850,17 +850,14 @@ again:
}
StringRef Lexer::getSpelling(const Token &Tok,
- const SourceManager &SourceMgr,
- bool *Invalid) const {
+ const SourceManager &SourceMgr) const {
SourceLocation Loc = Tok.getLocation();
std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedLoc(Loc);
bool InvalidTemp = false;
StringRef File = SourceMgr.getBufferData(LocInfo.first, &InvalidTemp);
- if (InvalidTemp) {
- *Invalid = true;
+ if (InvalidTemp)
return StringRef();
- }
const char *Begin = File.data() + LocInfo.second;
return StringRef(Begin, Tok.getLength());
diff --git a/lib/AST/CommentParser.cpp b/lib/AST/CommentParser.cpp
index c7f8aa7e16a0a..29983b0a16c3c 100644
--- a/lib/AST/CommentParser.cpp
+++ b/lib/AST/CommentParser.cpp
@@ -422,6 +422,12 @@ InlineCommandComment *Parser::parseInlineCommand() {
IC = S.actOnInlineCommand(CommandTok.getLocation(),
CommandTok.getEndLocation(),
CommandTok.getCommandID());
+
+ Diag(CommandTok.getEndLocation().getLocWithOffset(1),
+ diag::warn_doc_inline_contents_no_argument)
+ << CommandTok.is(tok::at_command)
+ << Traits.getCommandInfo(CommandTok.getCommandID())->Name
+ << SourceRange(CommandTok.getLocation(), CommandTok.getEndLocation());
}
Retokenizer.putBackLeftoverTokens();
diff --git a/lib/AST/CommentSema.cpp b/lib/AST/CommentSema.cpp
index 067b3ae4222e3..69d61dc55162b 100644
--- a/lib/AST/CommentSema.cpp
+++ b/lib/AST/CommentSema.cpp
@@ -588,6 +588,8 @@ void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
if (isObjCPropertyDecl())
return;
if (isFunctionDecl() || isFunctionOrBlockPointerVarLikeDecl()) {
+ assert(!ThisDeclInfo->ReturnType.isNull() &&
+ "should have a valid return type");
if (ThisDeclInfo->ReturnType->isVoidType()) {
unsigned DiagKind;
switch (ThisDeclInfo->CommentDecl->getKind()) {
@@ -873,6 +875,12 @@ bool Sema::isFunctionOrBlockPointerVarLikeDecl() {
// can be ignored.
if (QT->getAs<TypedefType>())
return false;
+ if (const auto *P = QT->getAs<PointerType>())
+ if (P->getPointeeType()->getAs<TypedefType>())
+ return false;
+ if (const auto *P = QT->getAs<BlockPointerType>())
+ if (P->getPointeeType()->getAs<TypedefType>())
+ return false;
return QT->isFunctionPointerType() || QT->isBlockPointerType();
}
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 21cf9da18a8b2..80235d8496d22 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -1385,7 +1385,8 @@ LinkageInfo LinkageComputer::computeLVForDecl(const NamedDecl *D,
case Decl::CXXRecord: {
const auto *Record = cast<CXXRecordDecl>(D);
if (Record->isLambda()) {
- if (!Record->getLambdaManglingNumber()) {
+ if (Record->hasKnownLambdaInternalLinkage() ||
+ !Record->getLambdaManglingNumber()) {
// This lambda has no mangling number, so it's internal.
return getInternalLinkageFor(D);
}
@@ -1402,7 +1403,8 @@ LinkageInfo LinkageComputer::computeLVForDecl(const NamedDecl *D,
// };
const CXXRecordDecl *OuterMostLambda =
getOutermostEnclosingLambda(Record);
- if (!OuterMostLambda->getLambdaManglingNumber())
+ if (OuterMostLambda->hasKnownLambdaInternalLinkage() ||
+ !OuterMostLambda->getLambdaManglingNumber())
return getInternalLinkageFor(D);
return getLVForClosure(
@@ -1558,6 +1560,24 @@ void NamedDecl::printQualifiedName(raw_ostream &OS) const {
void NamedDecl::printQualifiedName(raw_ostream &OS,
const PrintingPolicy &P) const {
+ if (getDeclContext()->isFunctionOrMethod()) {
+ // We do not print '(anonymous)' for function parameters without name.
+ printName(OS);
+ return;
+ }
+ printNestedNameSpecifier(OS, P);
+ if (getDeclName() || isa<DecompositionDecl>(this))
+ OS << *this;
+ else
+ OS << "(anonymous)";
+}
+
+void NamedDecl::printNestedNameSpecifier(raw_ostream &OS) const {
+ printNestedNameSpecifier(OS, getASTContext().getPrintingPolicy());
+}
+
+void NamedDecl::printNestedNameSpecifier(raw_ostream &OS,
+ const PrintingPolicy &P) const {
const DeclContext *Ctx = getDeclContext();
// For ObjC methods and properties, look through categories and use the
@@ -1571,10 +1591,8 @@ void NamedDecl::printQualifiedName(raw_ostream &OS,
Ctx = ID;
}
- if (Ctx->isFunctionOrMethod()) {
- printName(OS);
+ if (Ctx->isFunctionOrMethod())
return;
- }
using ContextsTy = SmallVector<const DeclContext *, 8>;
ContextsTy Contexts;
@@ -1644,11 +1662,6 @@ void NamedDecl::printQualifiedName(raw_ostream &OS,
}
OS << "::";
}
-
- if (getDeclName() || isa<DecompositionDecl>(this))
- OS << *this;
- else
- OS << "(anonymous)";
}
void NamedDecl::getNameForDiagnostic(raw_ostream &OS,
@@ -2220,6 +2233,22 @@ Stmt **VarDecl::getInitAddress() {
return Init.getAddrOfPtr1();
}
+VarDecl *VarDecl::getInitializingDeclaration() {
+ VarDecl *Def = nullptr;
+ for (auto I : redecls()) {
+ if (I->hasInit())
+ return I;
+
+ if (I->isThisDeclarationADefinition()) {
+ if (isStaticDataMember())
+ return I;
+ else
+ Def = I;
+ }
+ }
+ return Def;
+}
+
bool VarDecl::isOutOfLine() const {
if (Decl::isOutOfLine())
return true;
@@ -2565,6 +2594,18 @@ bool VarDecl::isNoDestroy(const ASTContext &Ctx) const {
!hasAttr<AlwaysDestroyAttr>()));
}
+QualType::DestructionKind
+VarDecl::needsDestruction(const ASTContext &Ctx) const {
+ if (EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>())
+ if (Eval->HasConstantDestruction)
+ return QualType::DK_none;
+
+ if (isNoDestroy(Ctx))
+ return QualType::DK_none;
+
+ return getType().isDestructedType();
+}
+
MemberSpecializationInfo *VarDecl::getMemberSpecializationInfo() const {
if (isStaticDataMember())
// FIXME: Remove ?
@@ -2950,8 +2991,7 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction(bool *IsAligned) const
Ty = Ty->getPointeeType();
if (Ty.getCVRQualifiers() != Qualifiers::Const)
return false;
- const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
- if (RD && isNamed(RD, "nothrow_t") && RD->isInStdNamespace())
+ if (Ty->isNothrowT())
Consume();
}
@@ -3235,6 +3275,9 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const {
return true;
}
+ if (Context.getLangOpts().CPlusPlus)
+ return false;
+
if (Context.getLangOpts().GNUInline || hasAttr<GNUInlineAttr>()) {
// With GNU inlining, a declaration with 'inline' but not 'extern', forces
// an externally visible definition.
@@ -3263,9 +3306,6 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const {
return FoundBody;
}
- if (Context.getLangOpts().CPlusPlus)
- return false;
-
// C99 6.7.4p6:
// [...] If all of the file scope declarations for a function in a
// translation unit include the inline function specifier without extern,
@@ -3332,7 +3372,8 @@ SourceRange FunctionDecl::getExceptionSpecSourceRange() const {
/// an externally visible symbol, but "extern inline" will not create an
/// externally visible symbol.
bool FunctionDecl::isInlineDefinitionExternallyVisible() const {
- assert((doesThisDeclarationHaveABody() || willHaveBody()) &&
+ assert((doesThisDeclarationHaveABody() || willHaveBody() ||
+ hasAttr<AliasAttr>()) &&
"Must be a function definition");
assert(isInlined() && "Function must be inline");
ASTContext &Context = getASTContext();
@@ -3344,6 +3385,8 @@ bool FunctionDecl::isInlineDefinitionExternallyVisible() const {
// If it's not the case that both 'inline' and 'extern' are
// specified on the definition, then this inline definition is
// externally visible.
+ if (Context.getLangOpts().CPlusPlus)
+ return false;
if (!(isInlineSpecified() && getStorageClass() == SC_Extern))
return true;
diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp
index fd80e1532eb5d..77a3a4c679a1a 100644
--- a/lib/AST/DeclBase.cpp
+++ b/lib/AST/DeclBase.cpp
@@ -12,6 +12,7 @@
#include "clang/AST/DeclBase.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTLambda.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/Attr.h"
#include "clang/AST/AttrIterator.h"
@@ -99,7 +100,7 @@ void *Decl::operator new(std::size_t Size, const ASTContext &Ctx,
// Ensure required alignment of the resulting object by adding extra
// padding at the start if required.
size_t ExtraAlign =
- llvm::OffsetToAlignment(sizeof(Module *), alignof(Decl));
+ llvm::offsetToAlignment(sizeof(Module *), llvm::Align(alignof(Decl)));
auto *Buffer = reinterpret_cast<char *>(
::operator new(ExtraAlign + sizeof(Module *) + Size + Extra, Ctx));
Buffer += ExtraAlign;
@@ -958,11 +959,11 @@ const FunctionType *Decl::getFunctionType(bool BlocksToo) const {
return nullptr;
if (Ty->isFunctionPointerType())
- Ty = Ty->getAs<PointerType>()->getPointeeType();
+ Ty = Ty->castAs<PointerType>()->getPointeeType();
else if (Ty->isFunctionReferenceType())
- Ty = Ty->getAs<ReferenceType>()->getPointeeType();
+ Ty = Ty->castAs<ReferenceType>()->getPointeeType();
else if (BlocksToo && Ty->isBlockPointerType())
- Ty = Ty->getAs<BlockPointerType>()->getPointeeType();
+ Ty = Ty->castAs<BlockPointerType>()->getPointeeType();
return Ty->getAs<FunctionType>();
}
@@ -1043,6 +1044,12 @@ DeclContext *DeclContext::getLookupParent() {
getLexicalParent()->getRedeclContext()->isRecord())
return getLexicalParent();
+ // A lookup within the call operator of a lambda never looks in the lambda
+ // class; instead, skip to the context in which that closure type is
+ // declared.
+ if (isLambdaCallOperator(this))
+ return getParent()->getParent();
+
return getParent();
}
diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp
index 59710a55498f2..12ec44fa02791 100644
--- a/lib/AST/DeclCXX.cpp
+++ b/lib/AST/DeclCXX.cpp
@@ -95,14 +95,16 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
HasDefaultedDefaultConstructor(false),
DefaultedDefaultConstructorIsConstexpr(true),
HasConstexprDefaultConstructor(false),
- HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false),
+ DefaultedDestructorIsConstexpr(true),
+ HasNonLiteralTypeFieldsOrBases(false),
UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0),
ImplicitCopyConstructorCanHaveConstParamForVBase(true),
ImplicitCopyConstructorCanHaveConstParamForNonVBase(true),
ImplicitCopyAssignmentHasConstParam(true),
HasDeclaredCopyConstructorWithConstParam(false),
HasDeclaredCopyAssignmentWithConstParam(false), IsLambda(false),
- IsParsingBaseSpecifiers(false), HasODRHash(false), Definition(D) {}
+ IsParsingBaseSpecifiers(false), ComputedVisibleConversions(false),
+ HasODRHash(false), Definition(D) {}
CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const {
return Bases.get(Definition->getASTContext().getExternalSource());
@@ -217,7 +219,7 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
if (BaseType->isDependentType())
continue;
auto *BaseClassDecl =
- cast<CXXRecordDecl>(BaseType->getAs<RecordType>()->getDecl());
+ cast<CXXRecordDecl>(BaseType->castAs<RecordType>()->getDecl());
// C++2a [class]p7:
// A standard-layout class is a class that:
@@ -325,10 +327,12 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
data().IsStandardLayout = false;
data().IsCXX11StandardLayout = false;
- // C++11 [dcl.constexpr]p4:
- // In the definition of a constexpr constructor [...]
- // -- the class shall not have any virtual base classes
+ // C++20 [dcl.constexpr]p3:
+ // In the definition of a constexpr function [...]
+ // -- if the function is a constructor or destructor,
+ // its class shall not have any virtual base classes
data().DefaultedDefaultConstructorIsConstexpr = false;
+ data().DefaultedDestructorIsConstexpr = false;
// C++1z [class.copy]p8:
// The implicitly-declared copy constructor for a class X will have
@@ -520,6 +524,19 @@ void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
data().NeedOverloadResolutionForMoveConstructor = true;
data().NeedOverloadResolutionForDestructor = true;
}
+
+ // C++2a [dcl.constexpr]p4:
+ // The definition of a constexpr destructor [shall] satisfy the
+ // following requirement:
+ // -- for every subobject of class type or (possibly multi-dimensional)
+ // array thereof, that class type shall have a constexpr destructor
+ if (!Subobj->hasConstexprDestructor())
+ data().DefaultedDestructorIsConstexpr = false;
+}
+
+bool CXXRecordDecl::hasConstexprDestructor() const {
+ auto *Dtor = getDestructor();
+ return Dtor ? Dtor->isConstexpr() : defaultedDestructorIsConstexpr();
}
bool CXXRecordDecl::hasAnyDependentBases() const {
@@ -1263,7 +1280,8 @@ void CXXRecordDecl::addedMember(Decl *D) {
} else {
// Base element type of field is a non-class type.
if (!T->isLiteralType(Context) ||
- (!Field->hasInClassInitializer() && !isUnion()))
+ (!Field->hasInClassInitializer() && !isUnion() &&
+ !Context.getLangOpts().CPlusPlus2a))
data().DefaultedDefaultConstructorIsConstexpr = false;
// C++11 [class.copy]p23:
@@ -1382,17 +1400,29 @@ static bool allLookupResultsAreTheSame(const DeclContext::lookup_result &R) {
}
#endif
-CXXMethodDecl* CXXRecordDecl::getLambdaCallOperator() const {
- if (!isLambda()) return nullptr;
+static NamedDecl* getLambdaCallOperatorHelper(const CXXRecordDecl &RD) {
+ if (!RD.isLambda()) return nullptr;
DeclarationName Name =
- getASTContext().DeclarationNames.getCXXOperatorName(OO_Call);
- DeclContext::lookup_result Calls = lookup(Name);
+ RD.getASTContext().DeclarationNames.getCXXOperatorName(OO_Call);
+ DeclContext::lookup_result Calls = RD.lookup(Name);
assert(!Calls.empty() && "Missing lambda call operator!");
assert(allLookupResultsAreTheSame(Calls) &&
"More than one lambda call operator!");
+ return Calls.front();
+}
+
+FunctionTemplateDecl* CXXRecordDecl::getDependentLambdaCallOperator() const {
+ NamedDecl *CallOp = getLambdaCallOperatorHelper(*this);
+ return dyn_cast_or_null<FunctionTemplateDecl>(CallOp);
+}
+
+CXXMethodDecl *CXXRecordDecl::getLambdaCallOperator() const {
+ NamedDecl *CallOp = getLambdaCallOperatorHelper(*this);
+
+ if (CallOp == nullptr)
+ return nullptr;
- NamedDecl *CallOp = Calls.front();
if (const auto *CallOpTmpl = dyn_cast<FunctionTemplateDecl>(CallOp))
return cast<CXXMethodDecl>(CallOpTmpl->getTemplatedDecl());
@@ -1880,7 +1910,7 @@ bool CXXRecordDecl::mayBeAbstract() const {
for (const auto &B : bases()) {
const auto *BaseDecl =
- cast<CXXRecordDecl>(B.getType()->getAs<RecordType>()->getDecl());
+ cast<CXXRecordDecl>(B.getType()->castAs<RecordType>()->getDecl());
if (BaseDecl->isAbstract())
return true;
}
@@ -2067,10 +2097,15 @@ CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base,
if (DevirtualizedMethod->hasAttr<FinalAttr>())
return DevirtualizedMethod;
- // Similarly, if the class itself is marked 'final' it can't be overridden
- // and we can therefore devirtualize the member function call.
+ // Similarly, if the class itself or its destructor is marked 'final',
+ // the class can't be derived from and we can therefore devirtualize the
+ // member function call.
if (BestDynamicDecl->hasAttr<FinalAttr>())
return DevirtualizedMethod;
+ if (const auto *dtor = BestDynamicDecl->getDestructor()) {
+ if (dtor->hasAttr<FinalAttr>())
+ return DevirtualizedMethod;
+ }
if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()))
@@ -2532,7 +2567,7 @@ bool CXXConstructorDecl::isConvertingConstructor(bool AllowExplicit) const {
return false;
return (getNumParams() == 0 &&
- getType()->getAs<FunctionProtoType>()->isVariadic()) ||
+ getType()->castAs<FunctionProtoType>()->isVariadic()) ||
(getNumParams() == 1) ||
(getNumParams() > 1 &&
(getParamDecl(1)->hasDefaultArg() ||
@@ -2565,20 +2600,19 @@ CXXDestructorDecl *
CXXDestructorDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
return new (C, ID)
CXXDestructorDecl(C, nullptr, SourceLocation(), DeclarationNameInfo(),
- QualType(), nullptr, false, false);
+ QualType(), nullptr, false, false, CSK_unspecified);
}
-CXXDestructorDecl *
-CXXDestructorDecl::Create(ASTContext &C, CXXRecordDecl *RD,
- SourceLocation StartLoc,
- const DeclarationNameInfo &NameInfo,
- QualType T, TypeSourceInfo *TInfo,
- bool isInline, bool isImplicitlyDeclared) {
+CXXDestructorDecl *CXXDestructorDecl::Create(
+ ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
+ const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
+ bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind) {
assert(NameInfo.getName().getNameKind()
== DeclarationName::CXXDestructorName &&
"Name must refer to a destructor");
- return new (C, RD) CXXDestructorDecl(C, RD, StartLoc, NameInfo, T, TInfo,
- isInline, isImplicitlyDeclared);
+ return new (C, RD)
+ CXXDestructorDecl(C, RD, StartLoc, NameInfo, T, TInfo, isInline,
+ isImplicitlyDeclared, ConstexprKind);
}
void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) {
diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp
index f5c69944034a5..608b0b44072b7 100644
--- a/lib/AST/DeclPrinter.cpp
+++ b/lib/AST/DeclPrinter.cpp
@@ -1001,12 +1001,19 @@ void DeclPrinter::VisitCXXRecordDecl(CXXRecordDecl *D) {
void DeclPrinter::VisitLinkageSpecDecl(LinkageSpecDecl *D) {
const char *l;
- if (D->getLanguage() == LinkageSpecDecl::lang_c)
+ switch (D->getLanguage()) {
+ case LinkageSpecDecl::lang_c:
l = "C";
- else {
- assert(D->getLanguage() == LinkageSpecDecl::lang_cxx &&
- "unknown language in linkage specification");
+ break;
+ case LinkageSpecDecl::lang_cxx_14:
+ l = "C++14";
+ break;
+ case LinkageSpecDecl::lang_cxx_11:
+ l = "C++11";
+ break;
+ case LinkageSpecDecl::lang_cxx:
l = "C++";
+ break;
}
Out << "extern \"" << l << "\" ";
diff --git a/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp
index 40c39c845db63..7e013c6c54d8f 100644
--- a/lib/AST/DeclTemplate.cpp
+++ b/lib/AST/DeclTemplate.cpp
@@ -70,6 +70,8 @@ TemplateParameterList::TemplateParameterList(SourceLocation TemplateLoc,
}
if (RequiresClause) {
*getTrailingObjects<Expr *>() = RequiresClause;
+ if (RequiresClause->containsUnexpandedParameterPack())
+ ContainsUnexpandedParameterPack = true;
}
}
@@ -136,6 +138,18 @@ static void AdoptTemplateParameterList(TemplateParameterList *Params,
}
}
+void TemplateParameterList::
+getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
+ // TODO: Concepts: Collect immediately-introduced constraints.
+ if (HasRequiresClause)
+ AC.push_back(getRequiresClause());
+}
+
+bool TemplateParameterList::hasAssociatedConstraints() const {
+ // TODO: Concepts: Regard immediately-introduced constraints.
+ return HasRequiresClause;
+}
+
namespace clang {
void *allocateDefaultArgStorageChain(const ASTContext &C) {
@@ -145,6 +159,28 @@ void *allocateDefaultArgStorageChain(const ASTContext &C) {
} // namespace clang
//===----------------------------------------------------------------------===//
+// TemplateDecl Implementation
+//===----------------------------------------------------------------------===//
+
+TemplateDecl::TemplateDecl(Kind DK, DeclContext *DC, SourceLocation L,
+ DeclarationName Name, TemplateParameterList *Params,
+ NamedDecl *Decl)
+ : NamedDecl(DK, DC, L, Name), TemplatedDecl(Decl), TemplateParams(Params) {}
+
+void TemplateDecl::anchor() {}
+
+void TemplateDecl::
+getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
+ // TODO: Concepts: Append function trailing requires clause.
+ TemplateParams->getAssociatedConstraints(AC);
+}
+
+bool TemplateDecl::hasAssociatedConstraints() const {
+ // TODO: Concepts: Regard function trailing requires clause.
+ return TemplateParams->hasAssociatedConstraints();
+}
+
+//===----------------------------------------------------------------------===//
// RedeclarableTemplateDecl Implementation
//===----------------------------------------------------------------------===//
@@ -344,19 +380,10 @@ ClassTemplateDecl *ClassTemplateDecl::Create(ASTContext &C,
SourceLocation L,
DeclarationName Name,
TemplateParameterList *Params,
- NamedDecl *Decl,
- Expr *AssociatedConstraints) {
+ NamedDecl *Decl) {
AdoptTemplateParameterList(Params, cast<DeclContext>(Decl));
- if (!AssociatedConstraints) {
- return new (C, DC) ClassTemplateDecl(C, DC, L, Name, Params, Decl);
- }
-
- auto *const CTDI = new (C) ConstrainedTemplateDeclInfo;
- auto *const New =
- new (C, DC) ClassTemplateDecl(CTDI, C, DC, L, Name, Params, Decl);
- New->setAssociatedConstraints(AssociatedConstraints);
- return New;
+ return new (C, DC) ClassTemplateDecl(C, DC, L, Name, Params, Decl);
}
ClassTemplateDecl *ClassTemplateDecl::CreateDeserialized(ASTContext &C,
@@ -510,20 +537,24 @@ SourceRange TemplateTypeParmDecl::getSourceRange() const {
if (hasDefaultArgument() && !defaultArgumentWasInherited())
return SourceRange(getBeginLoc(),
getDefaultArgumentInfo()->getTypeLoc().getEndLoc());
- else
- return TypeDecl::getSourceRange();
+ // TypeDecl::getSourceRange returns a range containing name location, which is
+ // wrong for unnamed template parameters. e.g:
+ // it will return <[[typename>]] instead of <[[typename]]>
+ else if (getDeclName().isEmpty())
+ return SourceRange(getBeginLoc());
+ return TypeDecl::getSourceRange();
}
unsigned TemplateTypeParmDecl::getDepth() const {
- return getTypeForDecl()->getAs<TemplateTypeParmType>()->getDepth();
+ return getTypeForDecl()->castAs<TemplateTypeParmType>()->getDepth();
}
unsigned TemplateTypeParmDecl::getIndex() const {
- return getTypeForDecl()->getAs<TemplateTypeParmType>()->getIndex();
+ return getTypeForDecl()->castAs<TemplateTypeParmType>()->getIndex();
}
bool TemplateTypeParmDecl::isParameterPack() const {
- return getTypeForDecl()->getAs<TemplateTypeParmType>()->isParameterPack();
+ return getTypeForDecl()->castAs<TemplateTypeParmType>()->isParameterPack();
}
//===----------------------------------------------------------------------===//
@@ -704,12 +735,6 @@ FunctionTemplateSpecializationInfo *FunctionTemplateSpecializationInfo::Create(
}
//===----------------------------------------------------------------------===//
-// TemplateDecl Implementation
-//===----------------------------------------------------------------------===//
-
-void TemplateDecl::anchor() {}
-
-//===----------------------------------------------------------------------===//
// ClassTemplateSpecializationDecl Implementation
//===----------------------------------------------------------------------===//
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index 6ef77b8aee684..3438c3aadc6b8 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -85,8 +85,8 @@ const Expr *Expr::skipRValueSubobjectAdjustments(
CE->getCastKind() == CK_UncheckedDerivedToBase) &&
E->getType()->isRecordType()) {
E = CE->getSubExpr();
- CXXRecordDecl *Derived
- = cast<CXXRecordDecl>(E->getType()->getAs<RecordType>()->getDecl());
+ auto *Derived =
+ cast<CXXRecordDecl>(E->getType()->castAs<RecordType>()->getDecl());
Adjustments.push_back(SubobjectAdjustment(CE, Derived));
continue;
}
@@ -185,6 +185,12 @@ bool Expr::isKnownToHaveBooleanValue() const {
return CO->getTrueExpr()->isKnownToHaveBooleanValue() &&
CO->getFalseExpr()->isKnownToHaveBooleanValue();
+ if (isa<ObjCBoolLiteralExpr>(E))
+ return true;
+
+ if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E))
+ return OVE->getSourceExpr()->isKnownToHaveBooleanValue();
+
return false;
}
@@ -2563,13 +2569,31 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc,
case CXXTemporaryObjectExprClass:
case CXXConstructExprClass: {
if (const CXXRecordDecl *Type = getType()->getAsCXXRecordDecl()) {
- if (Type->hasAttr<WarnUnusedAttr>()) {
+ const auto *WarnURAttr = Type->getAttr<WarnUnusedResultAttr>();
+ if (Type->hasAttr<WarnUnusedAttr>() ||
+ (WarnURAttr && WarnURAttr->IsCXX11NoDiscard())) {
WarnE = this;
Loc = getBeginLoc();
R1 = getSourceRange();
return true;
}
}
+
+ const auto *CE = cast<CXXConstructExpr>(this);
+ if (const CXXConstructorDecl *Ctor = CE->getConstructor()) {
+ const auto *WarnURAttr = Ctor->getAttr<WarnUnusedResultAttr>();
+ if (WarnURAttr && WarnURAttr->IsCXX11NoDiscard()) {
+ WarnE = this;
+ Loc = getBeginLoc();
+ R1 = getSourceRange();
+
+ if (unsigned NumArgs = CE->getNumArgs())
+ R2 = SourceRange(CE->getArg(0)->getBeginLoc(),
+ CE->getArg(NumArgs - 1)->getEndLoc());
+ return true;
+ }
+ }
+
return false;
}
@@ -3181,7 +3205,7 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef,
if (ILE->getType()->isRecordType()) {
unsigned ElementNo = 0;
- RecordDecl *RD = ILE->getType()->getAs<RecordType>()->getDecl();
+ RecordDecl *RD = ILE->getType()->castAs<RecordType>()->getDecl();
for (const auto *Field : RD->fields()) {
// If this is a union, skip all the fields that aren't being initialized.
if (RD->isUnion() && ILE->getInitializedFieldInUnion() != Field)
@@ -3379,6 +3403,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
case CXXUuidofExprClass:
case OpaqueValueExprClass:
case SourceLocExprClass:
+ case ConceptSpecializationExprClass:
// These never have a side-effect.
return false;
@@ -3448,6 +3473,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
case ArrayInitLoopExprClass:
case ParenListExprClass:
case CXXPseudoDestructorExprClass:
+ case CXXRewrittenBinaryOperatorClass:
case CXXStdInitializerListExprClass:
case SubstNonTypeTemplateParmExprClass:
case MaterializeTemporaryExprClass:
@@ -3897,6 +3923,112 @@ bool Expr::refersToGlobalRegisterVar() const {
return false;
}
+bool Expr::isSameComparisonOperand(const Expr* E1, const Expr* E2) {
+ E1 = E1->IgnoreParens();
+ E2 = E2->IgnoreParens();
+
+ if (E1->getStmtClass() != E2->getStmtClass())
+ return false;
+
+ switch (E1->getStmtClass()) {
+ default:
+ return false;
+ case CXXThisExprClass:
+ return true;
+ case DeclRefExprClass: {
+ // DeclRefExpr without an ImplicitCastExpr can happen for integral
+ // template parameters.
+ const auto *DRE1 = cast<DeclRefExpr>(E1);
+ const auto *DRE2 = cast<DeclRefExpr>(E2);
+ return DRE1->isRValue() && DRE2->isRValue() &&
+ DRE1->getDecl() == DRE2->getDecl();
+ }
+ case ImplicitCastExprClass: {
+ // Peel off implicit casts.
+ while (true) {
+ const auto *ICE1 = dyn_cast<ImplicitCastExpr>(E1);
+ const auto *ICE2 = dyn_cast<ImplicitCastExpr>(E2);
+ if (!ICE1 || !ICE2)
+ return false;
+ if (ICE1->getCastKind() != ICE2->getCastKind())
+ return false;
+ E1 = ICE1->getSubExpr()->IgnoreParens();
+ E2 = ICE2->getSubExpr()->IgnoreParens();
+ // The final cast must be one of these types.
+ if (ICE1->getCastKind() == CK_LValueToRValue ||
+ ICE1->getCastKind() == CK_ArrayToPointerDecay ||
+ ICE1->getCastKind() == CK_FunctionToPointerDecay) {
+ break;
+ }
+ }
+
+ const auto *DRE1 = dyn_cast<DeclRefExpr>(E1);
+ const auto *DRE2 = dyn_cast<DeclRefExpr>(E2);
+ if (DRE1 && DRE2)
+ return declaresSameEntity(DRE1->getDecl(), DRE2->getDecl());
+
+ const auto *Ivar1 = dyn_cast<ObjCIvarRefExpr>(E1);
+ const auto *Ivar2 = dyn_cast<ObjCIvarRefExpr>(E2);
+ if (Ivar1 && Ivar2) {
+ return Ivar1->isFreeIvar() && Ivar2->isFreeIvar() &&
+ declaresSameEntity(Ivar1->getDecl(), Ivar2->getDecl());
+ }
+
+ const auto *Array1 = dyn_cast<ArraySubscriptExpr>(E1);
+ const auto *Array2 = dyn_cast<ArraySubscriptExpr>(E2);
+ if (Array1 && Array2) {
+ if (!isSameComparisonOperand(Array1->getBase(), Array2->getBase()))
+ return false;
+
+ auto Idx1 = Array1->getIdx();
+ auto Idx2 = Array2->getIdx();
+ const auto Integer1 = dyn_cast<IntegerLiteral>(Idx1);
+ const auto Integer2 = dyn_cast<IntegerLiteral>(Idx2);
+ if (Integer1 && Integer2) {
+ if (!llvm::APInt::isSameValue(Integer1->getValue(),
+ Integer2->getValue()))
+ return false;
+ } else {
+ if (!isSameComparisonOperand(Idx1, Idx2))
+ return false;
+ }
+
+ return true;
+ }
+
+ // Walk the MemberExpr chain.
+ while (isa<MemberExpr>(E1) && isa<MemberExpr>(E2)) {
+ const auto *ME1 = cast<MemberExpr>(E1);
+ const auto *ME2 = cast<MemberExpr>(E2);
+ if (!declaresSameEntity(ME1->getMemberDecl(), ME2->getMemberDecl()))
+ return false;
+ if (const auto *D = dyn_cast<VarDecl>(ME1->getMemberDecl()))
+ if (D->isStaticDataMember())
+ return true;
+ E1 = ME1->getBase()->IgnoreParenImpCasts();
+ E2 = ME2->getBase()->IgnoreParenImpCasts();
+ }
+
+ if (isa<CXXThisExpr>(E1) && isa<CXXThisExpr>(E2))
+ return true;
+
+ // A static member variable can end the MemberExpr chain with either
+ // a MemberExpr or a DeclRefExpr.
+ auto getAnyDecl = [](const Expr *E) -> const ValueDecl * {
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
+ return DRE->getDecl();
+ if (const auto *ME = dyn_cast<MemberExpr>(E))
+ return ME->getMemberDecl();
+ return nullptr;
+ };
+
+ const ValueDecl *VD1 = getAnyDecl(E1);
+ const ValueDecl *VD2 = getAnyDecl(E2);
+ return declaresSameEntity(VD1, VD2);
+ }
+ }
+}
+
/// isArrow - Return true if the base expression is a pointer to vector,
/// return false if the base expression is a vector.
bool ExtVectorElementExpr::isArrow() const {
diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp
index b30f785ba8f54..904928bdf2861 100644
--- a/lib/AST/ExprCXX.cpp
+++ b/lib/AST/ExprCXX.cpp
@@ -58,6 +58,76 @@ bool CXXOperatorCallExpr::isInfixBinaryOp() const {
}
}
+CXXRewrittenBinaryOperator::DecomposedForm
+CXXRewrittenBinaryOperator::getDecomposedForm() const {
+ DecomposedForm Result = {};
+ const Expr *E = getSemanticForm()->IgnoreImplicit();
+
+ // Remove an outer '!' if it exists (only happens for a '!=' rewrite).
+ bool SkippedNot = false;
+ if (auto *NotEq = dyn_cast<UnaryOperator>(E)) {
+ assert(NotEq->getOpcode() == UO_LNot);
+ E = NotEq->getSubExpr()->IgnoreImplicit();
+ SkippedNot = true;
+ }
+
+ // Decompose the outer binary operator.
+ if (auto *BO = dyn_cast<BinaryOperator>(E)) {
+ assert(!SkippedNot || BO->getOpcode() == BO_EQ);
+ Result.Opcode = SkippedNot ? BO_NE : BO->getOpcode();
+ Result.LHS = BO->getLHS();
+ Result.RHS = BO->getRHS();
+ Result.InnerBinOp = BO;
+ } else if (auto *BO = dyn_cast<CXXOperatorCallExpr>(E)) {
+ assert(!SkippedNot || BO->getOperator() == OO_EqualEqual);
+ assert(BO->isInfixBinaryOp());
+ switch (BO->getOperator()) {
+ case OO_Less: Result.Opcode = BO_LT; break;
+ case OO_LessEqual: Result.Opcode = BO_LE; break;
+ case OO_Greater: Result.Opcode = BO_GT; break;
+ case OO_GreaterEqual: Result.Opcode = BO_GE; break;
+ case OO_Spaceship: Result.Opcode = BO_Cmp; break;
+ case OO_EqualEqual: Result.Opcode = SkippedNot ? BO_NE : BO_EQ; break;
+ default: llvm_unreachable("unexpected binop in rewritten operator expr");
+ }
+ Result.LHS = BO->getArg(0);
+ Result.RHS = BO->getArg(1);
+ Result.InnerBinOp = BO;
+ } else {
+ llvm_unreachable("unexpected rewritten operator form");
+ }
+
+ // Put the operands in the right order for == and !=, and canonicalize the
+ // <=> subexpression onto the LHS for all other forms.
+ if (isReversed())
+ std::swap(Result.LHS, Result.RHS);
+
+ // If this isn't a spaceship rewrite, we're done.
+ if (Result.Opcode == BO_EQ || Result.Opcode == BO_NE)
+ return Result;
+
+ // Otherwise, we expect a <=> to now be on the LHS.
+ E = Result.LHS->IgnoreImplicit();
+ if (auto *BO = dyn_cast<BinaryOperator>(E)) {
+ assert(BO->getOpcode() == BO_Cmp);
+ Result.LHS = BO->getLHS();
+ Result.RHS = BO->getRHS();
+ Result.InnerBinOp = BO;
+ } else if (auto *BO = dyn_cast<CXXOperatorCallExpr>(E)) {
+ assert(BO->getOperator() == OO_Spaceship);
+ Result.LHS = BO->getArg(0);
+ Result.RHS = BO->getArg(1);
+ Result.InnerBinOp = BO;
+ } else {
+ llvm_unreachable("unexpected rewritten operator form");
+ }
+
+ // Put the comparison operands in the right order.
+ if (isReversed())
+ std::swap(Result.LHS, Result.RHS);
+ return Result;
+}
+
bool CXXTypeidExpr::isPotentiallyEvaluated() const {
if (isTypeOperand())
return false;
@@ -124,6 +194,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())
@@ -134,6 +206,8 @@ CXXNewExpr::CXXNewExpr(bool IsGlobalNew, FunctionDecl *OperatorNew,
}
if (Initializer) {
+ if (Initializer->isValueDependent())
+ ExprBits.ValueDependent = true;
if (Initializer->isInstantiationDependent())
ExprBits.InstantiationDependent = true;
if (Initializer->containsUnexpandedParameterPack())
@@ -143,6 +217,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())
@@ -245,7 +321,7 @@ QualType CXXDeleteExpr::getDestroyedType() const {
if (ArgType->isDependentType() && !ArgType->isPointerType())
return QualType();
- return ArgType->getAs<PointerType>()->getPointeeType();
+ return ArgType->castAs<PointerType>()->getPointeeType();
}
// CXXPseudoDestructorExpr
@@ -651,6 +727,13 @@ Expr *CXXMemberCallExpr::getImplicitObjectArgument() const {
return nullptr;
}
+QualType CXXMemberCallExpr::getObjectType() const {
+ QualType Ty = getImplicitObjectArgument()->getType();
+ if (Ty->isPointerType())
+ Ty = Ty->getPointeeType();
+ return Ty;
+}
+
CXXMethodDecl *CXXMemberCallExpr::getMethodDecl() const {
if (const auto *MemExpr = dyn_cast<MemberExpr>(getCallee()->IgnoreParens()))
return cast<CXXMethodDecl>(MemExpr->getMemberDecl());
@@ -1205,6 +1288,11 @@ CXXMethodDecl *LambdaExpr::getCallOperator() const {
return Record->getLambdaCallOperator();
}
+FunctionTemplateDecl *LambdaExpr::getDependentCallOperator() const {
+ CXXRecordDecl *Record = getLambdaClass();
+ return Record->getDependentLambdaCallOperator();
+}
+
TemplateParameterList *LambdaExpr::getTemplateParameterList() const {
CXXRecordDecl *Record = getLambdaClass();
return Record->getGenericLambdaTemplateParameterList();
@@ -1494,11 +1582,8 @@ CXXRecordDecl *UnresolvedMemberExpr::getNamingClass() {
// Otherwise the naming class must have been the base class.
else {
QualType BaseType = getBaseType().getNonReferenceType();
- if (isArrow()) {
- const auto *PT = BaseType->getAs<PointerType>();
- assert(PT && "base of arrow member access is not pointer");
- BaseType = PT->getPointeeType();
- }
+ if (isArrow())
+ BaseType = BaseType->castAs<PointerType>()->getPointeeType();
Record = BaseType->getAsCXXRecordDecl();
assert(Record && "base of member expression does not name record");
@@ -1665,3 +1750,82 @@ CUDAKernelCallExpr *CUDAKernelCallExpr::CreateEmpty(const ASTContext &Ctx,
alignof(CUDAKernelCallExpr));
return new (Mem) CUDAKernelCallExpr(NumArgs, Empty);
}
+
+ConceptSpecializationExpr::ConceptSpecializationExpr(ASTContext &C,
+ NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
+ SourceLocation ConceptNameLoc, NamedDecl *FoundDecl,
+ ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten,
+ ArrayRef<TemplateArgument> ConvertedArgs, Optional<bool> IsSatisfied)
+ : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary,
+ /*TypeDependent=*/false,
+ // All the flags below are set in setTemplateArguments.
+ /*ValueDependent=*/!IsSatisfied.hasValue(),
+ /*InstantiationDependent=*/false,
+ /*ContainsUnexpandedParameterPacks=*/false),
+ NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc),
+ ConceptNameLoc(ConceptNameLoc), FoundDecl(FoundDecl),
+ NamedConcept(NamedConcept, IsSatisfied ? *IsSatisfied : true),
+ NumTemplateArgs(ConvertedArgs.size()) {
+
+ setTemplateArguments(ArgsAsWritten, ConvertedArgs);
+}
+
+ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty,
+ unsigned NumTemplateArgs)
+ : Expr(ConceptSpecializationExprClass, Empty),
+ NumTemplateArgs(NumTemplateArgs) { }
+
+void ConceptSpecializationExpr::setTemplateArguments(
+ const ASTTemplateArgumentListInfo *ArgsAsWritten,
+ ArrayRef<TemplateArgument> Converted) {
+ assert(Converted.size() == NumTemplateArgs);
+ assert(!this->ArgsAsWritten && "setTemplateArguments can only be used once");
+ this->ArgsAsWritten = ArgsAsWritten;
+ std::uninitialized_copy(Converted.begin(), Converted.end(),
+ getTrailingObjects<TemplateArgument>());
+ bool IsInstantiationDependent = false;
+ bool ContainsUnexpandedParameterPack = false;
+ for (const TemplateArgumentLoc& LocInfo : ArgsAsWritten->arguments()) {
+ if (LocInfo.getArgument().isInstantiationDependent())
+ IsInstantiationDependent = true;
+ if (LocInfo.getArgument().containsUnexpandedParameterPack())
+ ContainsUnexpandedParameterPack = true;
+ if (ContainsUnexpandedParameterPack && IsInstantiationDependent)
+ break;
+ }
+
+ // Currently guaranteed by the fact concepts can only be at namespace-scope.
+ assert(!NestedNameSpec ||
+ (!NestedNameSpec.getNestedNameSpecifier()->isInstantiationDependent() &&
+ !NestedNameSpec.getNestedNameSpecifier()
+ ->containsUnexpandedParameterPack()));
+ setInstantiationDependent(IsInstantiationDependent);
+ setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack);
+ assert((!isValueDependent() || isInstantiationDependent()) &&
+ "should not be value-dependent");
+}
+
+ConceptSpecializationExpr *
+ConceptSpecializationExpr::Create(ASTContext &C, NestedNameSpecifierLoc NNS,
+ SourceLocation TemplateKWLoc,
+ SourceLocation ConceptNameLoc,
+ NamedDecl *FoundDecl,
+ ConceptDecl *NamedConcept,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten,
+ ArrayRef<TemplateArgument> ConvertedArgs,
+ Optional<bool> IsSatisfied) {
+ void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(
+ ConvertedArgs.size()));
+ return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc,
+ ConceptNameLoc, FoundDecl,
+ NamedConcept, ArgsAsWritten,
+ ConvertedArgs, IsSatisfied);
+}
+
+ConceptSpecializationExpr *
+ConceptSpecializationExpr::Create(ASTContext &C, EmptyShell Empty,
+ unsigned NumTemplateArgs) {
+ void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>(
+ NumTemplateArgs));
+ return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs);
+}
diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp
index c61ee703aca8e..9dbf6fe9e0f06 100644
--- a/lib/AST/ExprClassification.cpp
+++ b/lib/AST/ExprClassification.cpp
@@ -192,6 +192,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
case Expr::NoInitExprClass:
case Expr::DesignatedInitUpdateExprClass:
case Expr::SourceLocExprClass:
+ case Expr::ConceptSpecializationExprClass:
return Cl::CL_PRValue;
case Expr::ConstantExprClass:
@@ -306,6 +307,10 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
case Expr::CUDAKernelCallExprClass:
return ClassifyUnnamed(Ctx, cast<CallExpr>(E)->getCallReturnType(Ctx));
+ case Expr::CXXRewrittenBinaryOperatorClass:
+ return ClassifyInternal(
+ Ctx, cast<CXXRewrittenBinaryOperator>(E)->getSemanticForm());
+
// __builtin_choose_expr is equivalent to the chosen expression.
case Expr::ChooseExprClass:
return ClassifyInternal(Ctx, cast<ChooseExpr>(E)->getChosenSubExpr());
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index f01b42e7ff761..42c746e602858 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -32,15 +32,21 @@
//
//===----------------------------------------------------------------------===//
+#include <cstring>
+#include <functional>
+#include "Interp/Context.h"
+#include "Interp/Frame.h"
+#include "Interp/State.h"
#include "clang/AST/APValue.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/ASTLambda.h"
+#include "clang/AST/CXXInheritance.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/CurrentSourceLocExprScope.h"
-#include "clang/AST/CXXInheritance.h"
#include "clang/AST/Expr.h"
#include "clang/AST/OSLog.h"
+#include "clang/AST/OptionalDiagnostic.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TypeLoc.h"
@@ -51,8 +57,6 @@
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/raw_ostream.h"
-#include <cstring>
-#include <functional>
#define DEBUG_TYPE "exprconstant"
@@ -62,12 +66,10 @@ using llvm::APSInt;
using llvm::APFloat;
using llvm::Optional;
-static bool IsGlobalLValue(APValue::LValueBase B);
-
namespace {
struct LValue;
- struct CallStackFrame;
- struct EvalInfo;
+ class CallStackFrame;
+ class EvalInfo;
using SourceLocExprScopeGuard =
CurrentSourceLocExprScope::SourceLocExprScopeGuard;
@@ -94,6 +96,9 @@ namespace {
if (B.is<TypeInfoLValue>())
return B.getTypeInfoType();
+ if (B.is<DynamicAllocLValue>())
+ return B.getDynamicAllocType();
+
const Expr *Base = B.get<const Expr*>();
// For a materialized temporary, the type of the temporary we materialized
@@ -130,6 +135,14 @@ namespace {
return E.getAsBaseOrMember().getInt();
}
+ /// Given an expression, determine the type used to store the result of
+ /// evaluating that expression.
+ static QualType getStorageType(const ASTContext &Ctx, const Expr *E) {
+ if (E->isRValue())
+ return E->getType();
+ return Ctx.getLValueReferenceType(E->getType());
+ }
+
/// Given a CallExpr, try to get the alloc_size attribute. May return null.
static const AllocSizeAttr *getAllocSizeAttr(const CallExpr *CE) {
const FunctionDecl *Callee = CE->getDirectCallee();
@@ -222,12 +235,6 @@ namespace {
return MostDerivedLength;
}
- // The order of this enum is important for diagnostics.
- enum CheckSubobjectKind {
- CSK_Base, CSK_Derived, CSK_Field, CSK_ArrayToPointer, CSK_ArrayIndex,
- CSK_Real, CSK_Imag
- };
-
/// A path from a glvalue to a subobject of that glvalue.
struct SubobjectDesignator {
/// True if the subobject was named in a manner not supported by C++11. Such
@@ -480,7 +487,8 @@ namespace {
};
/// A stack frame in the constexpr call stack.
- struct CallStackFrame {
+ class CallStackFrame : public interp::Frame {
+ public:
EvalInfo &Info;
/// Parent - The caller of this stack frame.
@@ -573,7 +581,26 @@ namespace {
return 0;
}
- APValue &createTemporary(const void *Key, bool IsLifetimeExtended);
+ /// Allocate storage for an object of type T in this stack frame.
+ /// Populates LV with a handle to the created object. Key identifies
+ /// the temporary within the stack frame, and must not be reused without
+ /// bumping the temporary version number.
+ template<typename KeyT>
+ APValue &createTemporary(const KeyT *Key, QualType T,
+ bool IsLifetimeExtended, LValue &LV);
+
+ void describe(llvm::raw_ostream &OS) override;
+
+ Frame *getCaller() const override { return Caller; }
+ SourceLocation getCallLocation() const override { return CallLoc; }
+ const FunctionDecl *getCallee() const override { return Callee; }
+
+ bool isStdFunction() const {
+ for (const DeclContext *DC = Callee; DC; DC = DC->getParent())
+ if (DC->isStdNamespace())
+ return true;
+ return false;
+ }
};
/// Temporarily override 'this'.
@@ -591,71 +618,42 @@ namespace {
CallStackFrame &Frame;
const LValue *OldThis;
};
+}
- /// A partial diagnostic which we might know in advance that we are not going
- /// to emit.
- class OptionalDiagnostic {
- PartialDiagnostic *Diag;
-
- public:
- explicit OptionalDiagnostic(PartialDiagnostic *Diag = nullptr)
- : Diag(Diag) {}
-
- template<typename T>
- OptionalDiagnostic &operator<<(const T &v) {
- if (Diag)
- *Diag << v;
- return *this;
- }
-
- OptionalDiagnostic &operator<<(const APSInt &I) {
- if (Diag) {
- SmallVector<char, 32> Buffer;
- I.toString(Buffer);
- *Diag << StringRef(Buffer.data(), Buffer.size());
- }
- return *this;
- }
-
- OptionalDiagnostic &operator<<(const APFloat &F) {
- if (Diag) {
- // FIXME: Force the precision of the source value down so we don't
- // print digits which are usually useless (we don't really care here if
- // we truncate a digit by accident in edge cases). Ideally,
- // APFloat::toString would automatically print the shortest
- // representation which rounds to the correct value, but it's a bit
- // tricky to implement.
- unsigned precision =
- llvm::APFloat::semanticsPrecision(F.getSemantics());
- precision = (precision * 59 + 195) / 196;
- SmallVector<char, 32> Buffer;
- F.toString(Buffer, precision);
- *Diag << StringRef(Buffer.data(), Buffer.size());
- }
- return *this;
- }
-
- OptionalDiagnostic &operator<<(const APFixedPoint &FX) {
- if (Diag) {
- SmallVector<char, 32> Buffer;
- FX.toString(Buffer);
- *Diag << StringRef(Buffer.data(), Buffer.size());
- }
- return *this;
- }
- };
+static bool HandleDestruction(EvalInfo &Info, const Expr *E,
+ const LValue &This, QualType ThisType);
+static bool HandleDestruction(EvalInfo &Info, SourceLocation Loc,
+ APValue::LValueBase LVBase, APValue &Value,
+ QualType T);
+namespace {
/// A cleanup, and a flag indicating whether it is lifetime-extended.
class Cleanup {
llvm::PointerIntPair<APValue*, 1, bool> Value;
+ APValue::LValueBase Base;
+ QualType T;
public:
- Cleanup(APValue *Val, bool IsLifetimeExtended)
- : Value(Val, IsLifetimeExtended) {}
+ Cleanup(APValue *Val, APValue::LValueBase Base, QualType T,
+ bool IsLifetimeExtended)
+ : Value(Val, IsLifetimeExtended), Base(Base), T(T) {}
bool isLifetimeExtended() const { return Value.getInt(); }
- void endLifetime() {
+ bool endLifetime(EvalInfo &Info, bool RunDestructors) {
+ if (RunDestructors) {
+ SourceLocation Loc;
+ if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>())
+ Loc = VD->getLocation();
+ else if (const Expr *E = Base.dyn_cast<const Expr*>())
+ Loc = E->getExprLoc();
+ return HandleDestruction(Info, Loc, Base, *Value.getPointer(), T);
+ }
*Value.getPointer() = APValue();
+ return true;
+ }
+
+ bool hasSideEffect() {
+ return T.isDestructedType();
}
};
@@ -671,7 +669,13 @@ namespace {
return llvm::hash_combine(Obj.Base, Obj.Path);
}
};
- enum class ConstructionPhase { None, Bases, AfterBases };
+ enum class ConstructionPhase {
+ None,
+ Bases,
+ AfterBases,
+ Destroying,
+ DestroyingBases
+ };
}
namespace llvm {
@@ -693,6 +697,37 @@ template<> struct DenseMapInfo<ObjectUnderConstruction> {
}
namespace {
+ /// A dynamically-allocated heap object.
+ struct DynAlloc {
+ /// The value of this heap-allocated object.
+ APValue Value;
+ /// The allocating expression; used for diagnostics. Either a CXXNewExpr
+ /// or a CallExpr (the latter is for direct calls to operator new inside
+ /// std::allocator<T>::allocate).
+ const Expr *AllocExpr = nullptr;
+
+ enum Kind {
+ New,
+ ArrayNew,
+ StdAllocator
+ };
+
+ /// Get the kind of the allocation. This must match between allocation
+ /// and deallocation.
+ Kind getKind() const {
+ if (auto *NE = dyn_cast<CXXNewExpr>(AllocExpr))
+ return NE->isArray() ? ArrayNew : New;
+ assert(isa<CallExpr>(AllocExpr));
+ return StdAllocator;
+ }
+ };
+
+ struct DynAllocOrder {
+ bool operator()(DynamicAllocLValue L, DynamicAllocLValue R) const {
+ return L.getIndex() < R.getIndex();
+ }
+ };
+
/// EvalInfo - This is a private struct used by the evaluator to capture
/// information about a subexpression as it is folded. It retains information
/// about the AST context, but also maintains information about the folded
@@ -707,7 +742,8 @@ namespace {
/// rules. For example, the RHS of (0 && foo()) is not evaluated. We can
/// evaluate the expression regardless of what the RHS is, but C only allows
/// certain things in certain situations.
- struct EvalInfo {
+ class EvalInfo : public interp::State {
+ public:
ASTContext &Ctx;
/// EvalStatus - Contains information about the evaluation.
@@ -727,6 +763,13 @@ namespace {
/// we will evaluate.
unsigned StepsLeft;
+ /// Force the use of the experimental new constant interpreter, bailing out
+ /// with an error if a feature is not supported.
+ bool ForceNewConstInterp;
+
+ /// Enable the experimental new constant interpreter.
+ bool EnableNewConstInterp;
+
/// BottomFrame - The frame in which evaluation started. This must be
/// initialized after CurrentCall and CallStackDepth.
CallStackFrame BottomFrame;
@@ -739,6 +782,15 @@ namespace {
/// evaluated, if any.
APValue::LValueBase EvaluatingDecl;
+ enum class EvaluatingDeclKind {
+ None,
+ /// We're evaluating the construction of EvaluatingDecl.
+ Ctor,
+ /// We're evaluating the destruction of EvaluatingDecl.
+ Dtor,
+ };
+ EvaluatingDeclKind IsEvaluatingDecl = EvaluatingDeclKind::None;
+
/// EvaluatingDeclValue - This is the value being constructed for the
/// declaration whose initializer is being evaluated, if any.
APValue *EvaluatingDeclValue;
@@ -747,6 +799,14 @@ namespace {
llvm::DenseMap<ObjectUnderConstruction, ConstructionPhase>
ObjectsUnderConstruction;
+ /// Current heap allocations, along with the location where each was
+ /// allocated. We use std::map here because we need stable addresses
+ /// for the stored APValues.
+ std::map<DynamicAllocLValue, DynAlloc, DynAllocOrder> HeapAllocs;
+
+ /// The number of heap allocations performed so far in this evaluation.
+ unsigned NumHeapAllocs = 0;
+
struct EvaluatingConstructorRAII {
EvalInfo &EI;
ObjectUnderConstruction Object;
@@ -768,9 +828,29 @@ namespace {
}
};
+ struct EvaluatingDestructorRAII {
+ EvalInfo &EI;
+ ObjectUnderConstruction Object;
+ bool DidInsert;
+ EvaluatingDestructorRAII(EvalInfo &EI, ObjectUnderConstruction Object)
+ : EI(EI), Object(Object) {
+ DidInsert = EI.ObjectsUnderConstruction
+ .insert({Object, ConstructionPhase::Destroying})
+ .second;
+ }
+ void startedDestroyingBases() {
+ EI.ObjectsUnderConstruction[Object] =
+ ConstructionPhase::DestroyingBases;
+ }
+ ~EvaluatingDestructorRAII() {
+ if (DidInsert)
+ EI.ObjectsUnderConstruction.erase(Object);
+ }
+ };
+
ConstructionPhase
- isEvaluatingConstructor(APValue::LValueBase Base,
- ArrayRef<APValue::LValuePathEntry> Path) {
+ isEvaluatingCtorDtor(APValue::LValueBase Base,
+ ArrayRef<APValue::LValuePathEntry> Path) {
return ObjectsUnderConstruction.lookup({Base, Path});
}
@@ -794,76 +874,74 @@ namespace {
/// constant value.
bool InConstantContext;
+ /// Whether we're checking that an expression is a potential constant
+ /// expression. If so, do not fail on constructs that could become constant
+ /// later on (such as a use of an undefined global).
+ bool CheckingPotentialConstantExpression = false;
+
+ /// Whether we're checking for an expression that has undefined behavior.
+ /// If so, we will produce warnings if we encounter an operation that is
+ /// always undefined.
+ bool CheckingForUndefinedBehavior = false;
+
enum EvaluationMode {
/// Evaluate as a constant expression. Stop if we find that the expression
/// is not a constant expression.
EM_ConstantExpression,
- /// Evaluate as a potential constant expression. Keep going if we hit a
- /// construct that we can't evaluate yet (because we don't yet know the
- /// value of something) but stop if we hit something that could never be
- /// a constant expression.
- EM_PotentialConstantExpression,
+ /// Evaluate as a constant expression. Stop if we find that the expression
+ /// is not a constant expression. Some expressions can be retried in the
+ /// optimizer if we don't constant fold them here, but in an unevaluated
+ /// context we try to fold them immediately since the optimizer never
+ /// gets a chance to look at it.
+ EM_ConstantExpressionUnevaluated,
/// Fold the expression to a constant. Stop if we hit a side-effect that
/// we can't model.
EM_ConstantFold,
- /// Evaluate the expression looking for integer overflow and similar
- /// issues. Don't worry about side-effects, and try to visit all
- /// subexpressions.
- EM_EvaluateForOverflow,
-
/// Evaluate in any way we know how. Don't worry about side-effects that
/// can't be modeled.
EM_IgnoreSideEffects,
-
- /// Evaluate as a constant expression. Stop if we find that the expression
- /// is not a constant expression. Some expressions can be retried in the
- /// optimizer if we don't constant fold them here, but in an unevaluated
- /// context we try to fold them immediately since the optimizer never
- /// gets a chance to look at it.
- EM_ConstantExpressionUnevaluated,
-
- /// Evaluate as a potential constant expression. Keep going if we hit a
- /// construct that we can't evaluate yet (because we don't yet know the
- /// value of something) but stop if we hit something that could never be
- /// a constant expression. Some expressions can be retried in the
- /// optimizer if we don't constant fold them here, but in an unevaluated
- /// context we try to fold them immediately since the optimizer never
- /// gets a chance to look at it.
- EM_PotentialConstantExpressionUnevaluated,
} EvalMode;
/// Are we checking whether the expression is a potential constant
/// expression?
- bool checkingPotentialConstantExpression() const {
- return EvalMode == EM_PotentialConstantExpression ||
- EvalMode == EM_PotentialConstantExpressionUnevaluated;
+ bool checkingPotentialConstantExpression() const override {
+ return CheckingPotentialConstantExpression;
}
/// Are we checking an expression for overflow?
// FIXME: We should check for any kind of undefined or suspicious behavior
// in such constructs, not just overflow.
- bool checkingForOverflow() { return EvalMode == EM_EvaluateForOverflow; }
+ bool checkingForUndefinedBehavior() const override {
+ return CheckingForUndefinedBehavior;
+ }
EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode)
- : Ctx(const_cast<ASTContext &>(C)), EvalStatus(S), CurrentCall(nullptr),
- CallStackDepth(0), NextCallIndex(1),
- StepsLeft(getLangOpts().ConstexprStepLimit),
- BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr),
- EvaluatingDecl((const ValueDecl *)nullptr),
- EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false),
- HasFoldFailureDiagnostic(false),
- InConstantContext(false), EvalMode(Mode) {}
-
- void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
+ : Ctx(const_cast<ASTContext &>(C)), EvalStatus(S), CurrentCall(nullptr),
+ CallStackDepth(0), NextCallIndex(1),
+ StepsLeft(getLangOpts().ConstexprStepLimit),
+ ForceNewConstInterp(getLangOpts().ForceNewConstInterp),
+ EnableNewConstInterp(ForceNewConstInterp ||
+ getLangOpts().EnableNewConstInterp),
+ BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr),
+ EvaluatingDecl((const ValueDecl *)nullptr),
+ EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false),
+ HasFoldFailureDiagnostic(false), InConstantContext(false),
+ EvalMode(Mode) {}
+
+ ~EvalInfo() {
+ discardCleanups();
+ }
+
+ void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value,
+ EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) {
EvaluatingDecl = Base;
+ IsEvaluatingDecl = EDK;
EvaluatingDeclValue = &Value;
}
- const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); }
-
bool CheckCallLimit(SourceLocation Loc) {
// Don't perform any constexpr calls (other than the call we're checking)
// when checking a potential constant expression.
@@ -906,133 +984,124 @@ namespace {
return true;
}
- private:
- /// Add a diagnostic to the diagnostics list.
- PartialDiagnostic &addDiag(SourceLocation Loc, diag::kind DiagId) {
- PartialDiagnostic PD(DiagId, Ctx.getDiagAllocator());
- EvalStatus.Diag->push_back(std::make_pair(Loc, PD));
- return EvalStatus.Diag->back().second;
+ APValue *createHeapAlloc(const Expr *E, QualType T, LValue &LV);
+
+ Optional<DynAlloc*> lookupDynamicAlloc(DynamicAllocLValue DA) {
+ Optional<DynAlloc*> Result;
+ auto It = HeapAllocs.find(DA);
+ if (It != HeapAllocs.end())
+ Result = &It->second;
+ return Result;
}
- /// Add notes containing a call stack to the current point of evaluation.
- void addCallStack(unsigned Limit);
+ /// Information about a stack frame for std::allocator<T>::[de]allocate.
+ struct StdAllocatorCaller {
+ unsigned FrameIndex;
+ QualType ElemType;
+ explicit operator bool() const { return FrameIndex != 0; };
+ };
- private:
- OptionalDiagnostic Diag(SourceLocation Loc, diag::kind DiagId,
- unsigned ExtraNotes, bool IsCCEDiag) {
+ StdAllocatorCaller getStdAllocatorCaller(StringRef FnName) const {
+ for (const CallStackFrame *Call = CurrentCall; Call != &BottomFrame;
+ Call = Call->Caller) {
+ const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Call->Callee);
+ if (!MD)
+ continue;
+ const IdentifierInfo *FnII = MD->getIdentifier();
+ if (!FnII || !FnII->isStr(FnName))
+ continue;
- if (EvalStatus.Diag) {
- // If we have a prior diagnostic, it will be noting that the expression
- // isn't a constant expression. This diagnostic is more important,
- // unless we require this evaluation to produce a constant expression.
- //
- // FIXME: We might want to show both diagnostics to the user in
- // EM_ConstantFold mode.
- if (!EvalStatus.Diag->empty()) {
- switch (EvalMode) {
- case EM_ConstantFold:
- case EM_IgnoreSideEffects:
- case EM_EvaluateForOverflow:
- if (!HasFoldFailureDiagnostic)
- break;
- // We've already failed to fold something. Keep that diagnostic.
- LLVM_FALLTHROUGH;
- case EM_ConstantExpression:
- case EM_PotentialConstantExpression:
- case EM_ConstantExpressionUnevaluated:
- case EM_PotentialConstantExpressionUnevaluated:
- HasActiveDiagnostic = false;
- return OptionalDiagnostic();
- }
- }
+ const auto *CTSD =
+ dyn_cast<ClassTemplateSpecializationDecl>(MD->getParent());
+ if (!CTSD)
+ continue;
- unsigned CallStackNotes = CallStackDepth - 1;
- unsigned Limit = Ctx.getDiagnostics().getConstexprBacktraceLimit();
- if (Limit)
- CallStackNotes = std::min(CallStackNotes, Limit + 1);
- if (checkingPotentialConstantExpression())
- CallStackNotes = 0;
-
- HasActiveDiagnostic = true;
- HasFoldFailureDiagnostic = !IsCCEDiag;
- EvalStatus.Diag->clear();
- EvalStatus.Diag->reserve(1 + ExtraNotes + CallStackNotes);
- addDiag(Loc, DiagId);
- if (!checkingPotentialConstantExpression())
- addCallStack(Limit);
- return OptionalDiagnostic(&(*EvalStatus.Diag)[0].second);
+ const IdentifierInfo *ClassII = CTSD->getIdentifier();
+ const TemplateArgumentList &TAL = CTSD->getTemplateArgs();
+ if (CTSD->isInStdNamespace() && ClassII &&
+ ClassII->isStr("allocator") && TAL.size() >= 1 &&
+ TAL[0].getKind() == TemplateArgument::Type)
+ return {Call->Index, TAL[0].getAsType()};
}
- HasActiveDiagnostic = false;
- return OptionalDiagnostic();
- }
- public:
- // Diagnose that the evaluation could not be folded (FF => FoldFailure)
- OptionalDiagnostic
- FFDiag(SourceLocation Loc,
- diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr,
- unsigned ExtraNotes = 0) {
- return Diag(Loc, DiagId, ExtraNotes, false);
- }
- OptionalDiagnostic FFDiag(const Expr *E, diag::kind DiagId
- = diag::note_invalid_subexpr_in_const_expr,
- unsigned ExtraNotes = 0) {
- if (EvalStatus.Diag)
- return Diag(E->getExprLoc(), DiagId, ExtraNotes, /*IsCCEDiag*/false);
- HasActiveDiagnostic = false;
- return OptionalDiagnostic();
+ return {};
+ }
+
+ void performLifetimeExtension() {
+ // Disable the cleanups for lifetime-extended temporaries.
+ CleanupStack.erase(
+ std::remove_if(CleanupStack.begin(), CleanupStack.end(),
+ [](Cleanup &C) { return C.isLifetimeExtended(); }),
+ CleanupStack.end());
+ }
+
+ /// Throw away any remaining cleanups at the end of evaluation. If any
+ /// cleanups would have had a side-effect, note that as an unmodeled
+ /// side-effect and return false. Otherwise, return true.
+ bool discardCleanups() {
+ for (Cleanup &C : CleanupStack)
+ if (C.hasSideEffect())
+ if (!noteSideEffect())
+ return false;
+ return true;
}
- /// Diagnose that the evaluation does not produce a C++11 core constant
- /// expression.
- ///
- /// FIXME: Stop evaluating if we're in EM_ConstantExpression or
- /// EM_PotentialConstantExpression mode and we produce one of these.
- OptionalDiagnostic CCEDiag(SourceLocation Loc, diag::kind DiagId
- = diag::note_invalid_subexpr_in_const_expr,
- unsigned ExtraNotes = 0) {
- // Don't override a previous diagnostic. Don't bother collecting
- // diagnostics if we're evaluating for overflow.
- if (!EvalStatus.Diag || !EvalStatus.Diag->empty()) {
- HasActiveDiagnostic = false;
- return OptionalDiagnostic();
+ private:
+ interp::Frame *getCurrentFrame() override { return CurrentCall; }
+ const interp::Frame *getBottomFrame() const override { return &BottomFrame; }
+
+ bool hasActiveDiagnostic() override { return HasActiveDiagnostic; }
+ void setActiveDiagnostic(bool Flag) override { HasActiveDiagnostic = Flag; }
+
+ void setFoldFailureDiagnostic(bool Flag) override {
+ HasFoldFailureDiagnostic = Flag;
+ }
+
+ Expr::EvalStatus &getEvalStatus() const override { return EvalStatus; }
+
+ ASTContext &getCtx() const override { return Ctx; }
+
+ // If we have a prior diagnostic, it will be noting that the expression
+ // isn't a constant expression. This diagnostic is more important,
+ // unless we require this evaluation to produce a constant expression.
+ //
+ // FIXME: We might want to show both diagnostics to the user in
+ // EM_ConstantFold mode.
+ bool hasPriorDiagnostic() override {
+ if (!EvalStatus.Diag->empty()) {
+ switch (EvalMode) {
+ case EM_ConstantFold:
+ case EM_IgnoreSideEffects:
+ if (!HasFoldFailureDiagnostic)
+ break;
+ // We've already failed to fold something. Keep that diagnostic.
+ LLVM_FALLTHROUGH;
+ case EM_ConstantExpression:
+ case EM_ConstantExpressionUnevaluated:
+ setActiveDiagnostic(false);
+ return true;
+ }
}
- return Diag(Loc, DiagId, ExtraNotes, true);
- }
- OptionalDiagnostic CCEDiag(const Expr *E, diag::kind DiagId
- = diag::note_invalid_subexpr_in_const_expr,
- unsigned ExtraNotes = 0) {
- return CCEDiag(E->getExprLoc(), DiagId, ExtraNotes);
- }
- /// Add a note to a prior diagnostic.
- OptionalDiagnostic Note(SourceLocation Loc, diag::kind DiagId) {
- if (!HasActiveDiagnostic)
- return OptionalDiagnostic();
- return OptionalDiagnostic(&addDiag(Loc, DiagId));
+ return false;
}
- /// Add a stack of notes to a prior diagnostic.
- void addNotes(ArrayRef<PartialDiagnosticAt> Diags) {
- if (HasActiveDiagnostic) {
- EvalStatus.Diag->insert(EvalStatus.Diag->end(),
- Diags.begin(), Diags.end());
- }
- }
+ unsigned getCallStackDepth() override { return CallStackDepth; }
+ public:
/// Should we continue evaluation after encountering a side-effect that we
/// couldn't model?
bool keepEvaluatingAfterSideEffect() {
switch (EvalMode) {
- case EM_PotentialConstantExpression:
- case EM_PotentialConstantExpressionUnevaluated:
- case EM_EvaluateForOverflow:
case EM_IgnoreSideEffects:
return true;
case EM_ConstantExpression:
case EM_ConstantExpressionUnevaluated:
case EM_ConstantFold:
- return false;
+ // By default, assume any side effect might be valid in some other
+ // evaluation of this expression from a different context.
+ return checkingPotentialConstantExpression() ||
+ checkingForUndefinedBehavior();
}
llvm_unreachable("Missed EvalMode case");
}
@@ -1047,16 +1116,13 @@ namespace {
/// Should we continue evaluation after encountering undefined behavior?
bool keepEvaluatingAfterUndefinedBehavior() {
switch (EvalMode) {
- case EM_EvaluateForOverflow:
case EM_IgnoreSideEffects:
case EM_ConstantFold:
return true;
- case EM_PotentialConstantExpression:
- case EM_PotentialConstantExpressionUnevaluated:
case EM_ConstantExpression:
case EM_ConstantExpressionUnevaluated:
- return false;
+ return checkingForUndefinedBehavior();
}
llvm_unreachable("Missed EvalMode case");
}
@@ -1064,28 +1130,24 @@ namespace {
/// Note that we hit something that was technically undefined behavior, but
/// that we can evaluate past it (such as signed overflow or floating-point
/// division by zero.)
- bool noteUndefinedBehavior() {
+ bool noteUndefinedBehavior() override {
EvalStatus.HasUndefinedBehavior = true;
return keepEvaluatingAfterUndefinedBehavior();
}
/// Should we continue evaluation as much as possible after encountering a
/// construct which can't be reduced to a value?
- bool keepEvaluatingAfterFailure() {
+ bool keepEvaluatingAfterFailure() const override {
if (!StepsLeft)
return false;
switch (EvalMode) {
- case EM_PotentialConstantExpression:
- case EM_PotentialConstantExpressionUnevaluated:
- case EM_EvaluateForOverflow:
- return true;
-
case EM_ConstantExpression:
case EM_ConstantExpressionUnevaluated:
case EM_ConstantFold:
case EM_IgnoreSideEffects:
- return false;
+ return checkingPotentialConstantExpression() ||
+ checkingForUndefinedBehavior();
}
llvm_unreachable("Missed EvalMode case");
}
@@ -1142,9 +1204,7 @@ namespace {
Info.EvalStatus.Diag->empty() &&
!Info.EvalStatus.HasSideEffects),
OldMode(Info.EvalMode) {
- if (Enabled &&
- (Info.EvalMode == EvalInfo::EM_ConstantExpression ||
- Info.EvalMode == EvalInfo::EM_ConstantExpressionUnevaluated))
+ if (Enabled)
Info.EvalMode = EvalInfo::EM_ConstantFold;
}
void keepDiagnostics() { Enabled = false; }
@@ -1163,8 +1223,7 @@ namespace {
EvalInfo::EvaluationMode OldMode;
explicit IgnoreSideEffectsRAII(EvalInfo &Info)
: Info(Info), OldMode(Info.EvalMode) {
- if (!Info.checkingPotentialConstantExpression())
- Info.EvalMode = EvalInfo::EM_IgnoreSideEffects;
+ Info.EvalMode = EvalInfo::EM_IgnoreSideEffects;
}
~IgnoreSideEffectsRAII() { Info.EvalMode = OldMode; }
@@ -1230,29 +1289,45 @@ namespace {
// temporaries created in different iterations of a loop.
Info.CurrentCall->pushTempVersion();
}
+ bool destroy(bool RunDestructors = true) {
+ bool OK = cleanup(Info, RunDestructors, OldStackSize);
+ OldStackSize = -1U;
+ return OK;
+ }
~ScopeRAII() {
+ if (OldStackSize != -1U)
+ destroy(false);
// Body moved to a static method to encourage the compiler to inline away
// instances of this class.
- cleanup(Info, OldStackSize);
Info.CurrentCall->popTempVersion();
}
private:
- static void cleanup(EvalInfo &Info, unsigned OldStackSize) {
- unsigned NewEnd = OldStackSize;
- for (unsigned I = OldStackSize, N = Info.CleanupStack.size();
- I != N; ++I) {
- if (IsFullExpression && Info.CleanupStack[I].isLifetimeExtended()) {
- // Full-expression cleanup of a lifetime-extended temporary: nothing
- // to do, just move this cleanup to the right place in the stack.
- std::swap(Info.CleanupStack[I], Info.CleanupStack[NewEnd]);
- ++NewEnd;
- } else {
- // End the lifetime of the object.
- Info.CleanupStack[I].endLifetime();
+ static bool cleanup(EvalInfo &Info, bool RunDestructors,
+ unsigned OldStackSize) {
+ assert(OldStackSize <= Info.CleanupStack.size() &&
+ "running cleanups out of order?");
+
+ // Run all cleanups for a block scope, and non-lifetime-extended cleanups
+ // for a full-expression scope.
+ bool Success = true;
+ for (unsigned I = Info.CleanupStack.size(); I > OldStackSize; --I) {
+ if (!(IsFullExpression &&
+ Info.CleanupStack[I - 1].isLifetimeExtended())) {
+ if (!Info.CleanupStack[I - 1].endLifetime(Info, RunDestructors)) {
+ Success = false;
+ break;
+ }
}
}
- Info.CleanupStack.erase(Info.CleanupStack.begin() + NewEnd,
- Info.CleanupStack.end());
+
+ // Compact lifetime-extended cleanups.
+ auto NewEnd = Info.CleanupStack.begin() + OldStackSize;
+ if (IsFullExpression)
+ NewEnd =
+ std::remove_if(NewEnd, Info.CleanupStack.end(),
+ [](Cleanup &C) { return !C.isLifetimeExtended(); });
+ Info.CleanupStack.erase(NewEnd, Info.CleanupStack.end());
+ return Success;
}
};
typedef ScopeRAII<false> BlockScopeRAII;
@@ -1312,74 +1387,14 @@ CallStackFrame::~CallStackFrame() {
Info.CurrentCall = Caller;
}
-APValue &CallStackFrame::createTemporary(const void *Key,
- bool IsLifetimeExtended) {
- unsigned Version = Info.CurrentCall->getTempVersion();
- APValue &Result = Temporaries[MapKeyTy(Key, Version)];
- assert(Result.isAbsent() && "temporary created multiple times");
- Info.CleanupStack.push_back(Cleanup(&Result, IsLifetimeExtended));
- return Result;
+static bool isRead(AccessKinds AK) {
+ return AK == AK_Read || AK == AK_ReadObjectRepresentation;
}
-static void describeCall(CallStackFrame *Frame, raw_ostream &Out);
-
-void EvalInfo::addCallStack(unsigned Limit) {
- // Determine which calls to skip, if any.
- unsigned ActiveCalls = CallStackDepth - 1;
- unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart;
- if (Limit && Limit < ActiveCalls) {
- SkipStart = Limit / 2 + Limit % 2;
- SkipEnd = ActiveCalls - Limit / 2;
- }
-
- // Walk the call stack and add the diagnostics.
- unsigned CallIdx = 0;
- for (CallStackFrame *Frame = CurrentCall; Frame != &BottomFrame;
- Frame = Frame->Caller, ++CallIdx) {
- // Skip this call?
- if (CallIdx >= SkipStart && CallIdx < SkipEnd) {
- if (CallIdx == SkipStart) {
- // Note that we're skipping calls.
- addDiag(Frame->CallLoc, diag::note_constexpr_calls_suppressed)
- << unsigned(ActiveCalls - Limit);
- }
- continue;
- }
-
- // Use a different note for an inheriting constructor, because from the
- // user's perspective it's not really a function at all.
- if (auto *CD = dyn_cast_or_null<CXXConstructorDecl>(Frame->Callee)) {
- if (CD->isInheritingConstructor()) {
- addDiag(Frame->CallLoc, diag::note_constexpr_inherited_ctor_call_here)
- << CD->getParent();
- continue;
- }
- }
-
- SmallVector<char, 128> Buffer;
- llvm::raw_svector_ostream Out(Buffer);
- describeCall(Frame, Out);
- addDiag(Frame->CallLoc, diag::note_constexpr_call_here) << Out.str();
- }
-}
-
-/// Kinds of access we can perform on an object, for diagnostics. Note that
-/// we consider a member function call to be a kind of access, even though
-/// it is not formally an access of the object, because it has (largely) the
-/// same set of semantic restrictions.
-enum AccessKinds {
- AK_Read,
- AK_Assign,
- AK_Increment,
- AK_Decrement,
- AK_MemberCall,
- AK_DynamicCast,
- AK_TypeId,
-};
-
static bool isModification(AccessKinds AK) {
switch (AK) {
case AK_Read:
+ case AK_ReadObjectRepresentation:
case AK_MemberCall:
case AK_DynamicCast:
case AK_TypeId:
@@ -1387,14 +1402,20 @@ static bool isModification(AccessKinds AK) {
case AK_Assign:
case AK_Increment:
case AK_Decrement:
+ case AK_Construct:
+ case AK_Destroy:
return true;
}
llvm_unreachable("unknown access kind");
}
+static bool isAnyAccess(AccessKinds AK) {
+ return isRead(AK) || isModification(AK);
+}
+
/// Is this an access per the C++ definition?
static bool isFormalAccess(AccessKinds AK) {
- return AK == AK_Read || isModification(AK);
+ return isAnyAccess(AK) && AK != AK_Construct && AK != AK_Destroy;
}
namespace {
@@ -1490,9 +1511,10 @@ namespace {
IsNullPtr = false;
}
- void setNull(QualType PointerTy, uint64_t TargetVal) {
+ void setNull(ASTContext &Ctx, QualType PointerTy) {
Base = (Expr *)nullptr;
- Offset = CharUnits::fromQuantity(TargetVal);
+ Offset =
+ CharUnits::fromQuantity(Ctx.getTargetNullPointerValue(PointerTy));
InvalidBase = false;
Designator = SubobjectDesignator(PointerTy->getPointeeType());
IsNullPtr = true;
@@ -1502,6 +1524,12 @@ namespace {
set(B, true);
}
+ std::string toString(ASTContext &Ctx, QualType T) const {
+ APValue Printable;
+ moveInto(Printable);
+ return Printable.getAsString(Ctx, T);
+ }
+
private:
// Check that this LValue is not based on a null pointer. If it is, produce
// a diagnostic and mark the designator as invalid.
@@ -1724,15 +1752,6 @@ static bool EvaluateFixedPoint(const Expr *E, APFixedPoint &Result,
// Misc utilities
//===----------------------------------------------------------------------===//
-/// A helper function to create a temporary and set an LValue.
-template <class KeyTy>
-static APValue &createTemporary(const KeyTy *Key, bool IsLifetimeExtended,
- LValue &LV, CallStackFrame &Frame) {
- LV.set({Key, Frame.Info.CurrentCall->Index,
- Frame.Info.CurrentCall->getTempVersion()});
- return Frame.createTemporary(Key, IsLifetimeExtended);
-}
-
/// Negate an APSInt in place, converting it to a signed form if necessary, and
/// preserving its value (by extending by up to one bit as needed).
static void negateAsSigned(APSInt &Int) {
@@ -1743,37 +1762,74 @@ static void negateAsSigned(APSInt &Int) {
Int = -Int;
}
+template<typename KeyT>
+APValue &CallStackFrame::createTemporary(const KeyT *Key, QualType T,
+ bool IsLifetimeExtended, LValue &LV) {
+ unsigned Version = getTempVersion();
+ APValue::LValueBase Base(Key, Index, Version);
+ LV.set(Base);
+ APValue &Result = Temporaries[MapKeyTy(Key, Version)];
+ assert(Result.isAbsent() && "temporary created multiple times");
+
+ // If we're creating a temporary immediately in the operand of a speculative
+ // evaluation, don't register a cleanup to be run outside the speculative
+ // evaluation context, since we won't actually be able to initialize this
+ // object.
+ if (Index <= Info.SpeculativeEvaluationDepth) {
+ if (T.isDestructedType())
+ Info.noteSideEffect();
+ } else {
+ Info.CleanupStack.push_back(Cleanup(&Result, Base, T, IsLifetimeExtended));
+ }
+ return Result;
+}
+
+APValue *EvalInfo::createHeapAlloc(const Expr *E, QualType T, LValue &LV) {
+ if (NumHeapAllocs > DynamicAllocLValue::getMaxIndex()) {
+ FFDiag(E, diag::note_constexpr_heap_alloc_limit_exceeded);
+ return nullptr;
+ }
+
+ DynamicAllocLValue DA(NumHeapAllocs++);
+ LV.set(APValue::LValueBase::getDynamicAlloc(DA, T));
+ auto Result = HeapAllocs.emplace(std::piecewise_construct,
+ std::forward_as_tuple(DA), std::tuple<>());
+ assert(Result.second && "reused a heap alloc index?");
+ Result.first->second.AllocExpr = E;
+ return &Result.first->second.Value;
+}
+
/// Produce a string describing the given constexpr call.
-static void describeCall(CallStackFrame *Frame, raw_ostream &Out) {
+void CallStackFrame::describe(raw_ostream &Out) {
unsigned ArgIndex = 0;
- bool IsMemberCall = isa<CXXMethodDecl>(Frame->Callee) &&
- !isa<CXXConstructorDecl>(Frame->Callee) &&
- cast<CXXMethodDecl>(Frame->Callee)->isInstance();
+ bool IsMemberCall = isa<CXXMethodDecl>(Callee) &&
+ !isa<CXXConstructorDecl>(Callee) &&
+ cast<CXXMethodDecl>(Callee)->isInstance();
if (!IsMemberCall)
- Out << *Frame->Callee << '(';
+ Out << *Callee << '(';
- if (Frame->This && IsMemberCall) {
+ if (This && IsMemberCall) {
APValue Val;
- Frame->This->moveInto(Val);
- Val.printPretty(Out, Frame->Info.Ctx,
- Frame->This->Designator.MostDerivedType);
+ This->moveInto(Val);
+ Val.printPretty(Out, Info.Ctx,
+ This->Designator.MostDerivedType);
// FIXME: Add parens around Val if needed.
- Out << "->" << *Frame->Callee << '(';
+ Out << "->" << *Callee << '(';
IsMemberCall = false;
}
- for (FunctionDecl::param_const_iterator I = Frame->Callee->param_begin(),
- E = Frame->Callee->param_end(); I != E; ++I, ++ArgIndex) {
+ for (FunctionDecl::param_const_iterator I = Callee->param_begin(),
+ E = Callee->param_end(); I != E; ++I, ++ArgIndex) {
if (ArgIndex > (unsigned)IsMemberCall)
Out << ", ";
const ParmVarDecl *Param = *I;
- const APValue &Arg = Frame->Arguments[ArgIndex];
- Arg.printPretty(Out, Frame->Info.Ctx, Param->getType());
+ const APValue &Arg = Arguments[ArgIndex];
+ Arg.printPretty(Out, Info.Ctx, Param->getType());
if (ArgIndex == 0 && IsMemberCall)
- Out << "->" << *Frame->Callee << '(';
+ Out << "->" << *Callee << '(';
}
Out << ')';
@@ -1813,7 +1869,7 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
return isa<FunctionDecl>(D);
}
- if (B.is<TypeInfoLValue>())
+ if (B.is<TypeInfoLValue>() || B.is<DynamicAllocLValue>())
return true;
const Expr *E = B.get<const Expr*>();
@@ -1912,15 +1968,39 @@ static void NoteLValueLocation(EvalInfo &Info, APValue::LValueBase Base) {
Info.Note(VD->getLocation(), diag::note_declared_at);
else if (const Expr *E = Base.dyn_cast<const Expr*>())
Info.Note(E->getExprLoc(), diag::note_constexpr_temporary_here);
+ else if (DynamicAllocLValue DA = Base.dyn_cast<DynamicAllocLValue>()) {
+ // FIXME: Produce a note for dangling pointers too.
+ if (Optional<DynAlloc*> Alloc = Info.lookupDynamicAlloc(DA))
+ Info.Note((*Alloc)->AllocExpr->getExprLoc(),
+ diag::note_constexpr_dynamic_alloc_here);
+ }
// We have no information to show for a typeid(T) object.
}
+enum class CheckEvaluationResultKind {
+ ConstantExpression,
+ FullyInitialized,
+};
+
+/// Materialized temporaries that we've already checked to determine if they're
+/// initializsed by a constant expression.
+using CheckedTemporaries =
+ llvm::SmallPtrSet<const MaterializeTemporaryExpr *, 8>;
+
+static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
+ EvalInfo &Info, SourceLocation DiagLoc,
+ QualType Type, const APValue &Value,
+ Expr::ConstExprUsage Usage,
+ SourceLocation SubobjectLoc,
+ CheckedTemporaries &CheckedTemps);
+
/// Check that this reference or pointer core constant expression is a valid
/// value for an address or reference constant expression. Return true if we
/// can fold this expression, whether or not it's a constant expression.
static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
QualType Type, const LValue &LVal,
- Expr::ConstExprUsage Usage) {
+ Expr::ConstExprUsage Usage,
+ CheckedTemporaries &CheckedTemps) {
bool IsReferenceType = Type->isReferenceType();
APValue::LValueBase Base = LVal.getLValueBase();
@@ -1946,14 +2026,23 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
LVal.getLValueCallIndex() == 0) &&
"have call index for global lvalue");
+ if (Base.is<DynamicAllocLValue>()) {
+ Info.FFDiag(Loc, diag::note_constexpr_dynamic_alloc)
+ << IsReferenceType << !Designator.Entries.empty();
+ NoteLValueLocation(Info, Base);
+ return false;
+ }
+
if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>()) {
if (const VarDecl *Var = dyn_cast<const VarDecl>(VD)) {
// Check if this is a thread-local variable.
if (Var->getTLSKind())
+ // FIXME: Diagnostic!
return false;
// A dllimport variable never acts like a constant.
if (Usage == Expr::EvaluateForCodeGen && Var->hasAttr<DLLImportAttr>())
+ // FIXME: Diagnostic!
return false;
}
if (const auto *FD = dyn_cast<const FunctionDecl>(VD)) {
@@ -1969,6 +2058,25 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
// perform initialization with the address of the thunk.
if (Info.getLangOpts().CPlusPlus && Usage == Expr::EvaluateForCodeGen &&
FD->hasAttr<DLLImportAttr>())
+ // FIXME: Diagnostic!
+ return false;
+ }
+ } else if (const auto *MTE = dyn_cast_or_null<MaterializeTemporaryExpr>(
+ Base.dyn_cast<const Expr *>())) {
+ if (CheckedTemps.insert(MTE).second) {
+ QualType TempType = getType(Base);
+ if (TempType.isDestructedType()) {
+ Info.FFDiag(MTE->getExprLoc(),
+ diag::note_constexpr_unsupported_tempoarary_nontrivial_dtor)
+ << TempType;
+ return false;
+ }
+
+ APValue *V = Info.Ctx.getMaterializedTemporaryValue(MTE, false);
+ assert(V && "evasluation result refers to uninitialised temporary");
+ if (!CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression,
+ Info, MTE->getExprLoc(), TempType, *V,
+ Usage, SourceLocation(), CheckedTemps))
return false;
}
}
@@ -2043,14 +2151,12 @@ static bool CheckLiteralType(EvalInfo &Info, const Expr *E,
return false;
}
-/// Check that this core constant expression value is a valid value for a
-/// constant expression. If not, report an appropriate diagnostic. Does not
-/// check that the expression is of literal type.
-static bool
-CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type,
- const APValue &Value,
- Expr::ConstExprUsage Usage = Expr::EvaluateForCodeGen,
- SourceLocation SubobjectLoc = SourceLocation()) {
+static bool CheckEvaluationResult(CheckEvaluationResultKind CERK,
+ EvalInfo &Info, SourceLocation DiagLoc,
+ QualType Type, const APValue &Value,
+ Expr::ConstExprUsage Usage,
+ SourceLocation SubobjectLoc,
+ CheckedTemporaries &CheckedTemps) {
if (!Value.hasValue()) {
Info.FFDiag(DiagLoc, diag::note_constexpr_uninitialized)
<< true << Type;
@@ -2070,30 +2176,31 @@ CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type,
if (Value.isArray()) {
QualType EltTy = Type->castAsArrayTypeUnsafe()->getElementType();
for (unsigned I = 0, N = Value.getArrayInitializedElts(); I != N; ++I) {
- if (!CheckConstantExpression(Info, DiagLoc, EltTy,
- Value.getArrayInitializedElt(I), Usage,
- SubobjectLoc))
+ if (!CheckEvaluationResult(CERK, Info, DiagLoc, EltTy,
+ Value.getArrayInitializedElt(I), Usage,
+ SubobjectLoc, CheckedTemps))
return false;
}
if (!Value.hasArrayFiller())
return true;
- return CheckConstantExpression(Info, DiagLoc, EltTy, Value.getArrayFiller(),
- Usage, SubobjectLoc);
+ return CheckEvaluationResult(CERK, Info, DiagLoc, EltTy,
+ Value.getArrayFiller(), Usage, SubobjectLoc,
+ CheckedTemps);
}
if (Value.isUnion() && Value.getUnionField()) {
- return CheckConstantExpression(Info, DiagLoc,
- Value.getUnionField()->getType(),
- Value.getUnionValue(), Usage,
- Value.getUnionField()->getLocation());
+ return CheckEvaluationResult(
+ CERK, Info, DiagLoc, Value.getUnionField()->getType(),
+ Value.getUnionValue(), Usage, Value.getUnionField()->getLocation(),
+ CheckedTemps);
}
if (Value.isStruct()) {
RecordDecl *RD = Type->castAs<RecordType>()->getDecl();
if (const CXXRecordDecl *CD = dyn_cast<CXXRecordDecl>(RD)) {
unsigned BaseIndex = 0;
for (const CXXBaseSpecifier &BS : CD->bases()) {
- if (!CheckConstantExpression(Info, DiagLoc, BS.getType(),
- Value.getStructBase(BaseIndex), Usage,
- BS.getBeginLoc()))
+ if (!CheckEvaluationResult(CERK, Info, DiagLoc, BS.getType(),
+ Value.getStructBase(BaseIndex), Usage,
+ BS.getBeginLoc(), CheckedTemps))
return false;
++BaseIndex;
}
@@ -2102,26 +2209,66 @@ CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type,
if (I->isUnnamedBitfield())
continue;
- if (!CheckConstantExpression(Info, DiagLoc, I->getType(),
- Value.getStructField(I->getFieldIndex()),
- Usage, I->getLocation()))
+ if (!CheckEvaluationResult(CERK, Info, DiagLoc, I->getType(),
+ Value.getStructField(I->getFieldIndex()),
+ Usage, I->getLocation(), CheckedTemps))
return false;
}
}
- if (Value.isLValue()) {
+ if (Value.isLValue() &&
+ CERK == CheckEvaluationResultKind::ConstantExpression) {
LValue LVal;
LVal.setFrom(Info.Ctx, Value);
- return CheckLValueConstantExpression(Info, DiagLoc, Type, LVal, Usage);
+ return CheckLValueConstantExpression(Info, DiagLoc, Type, LVal, Usage,
+ CheckedTemps);
}
- if (Value.isMemberPointer())
+ if (Value.isMemberPointer() &&
+ CERK == CheckEvaluationResultKind::ConstantExpression)
return CheckMemberPointerConstantExpression(Info, DiagLoc, Type, Value, Usage);
// Everything else is fine.
return true;
}
+/// Check that this core constant expression value is a valid value for a
+/// constant expression. If not, report an appropriate diagnostic. Does not
+/// check that the expression is of literal type.
+static bool
+CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type,
+ const APValue &Value,
+ Expr::ConstExprUsage Usage = Expr::EvaluateForCodeGen) {
+ CheckedTemporaries CheckedTemps;
+ return CheckEvaluationResult(CheckEvaluationResultKind::ConstantExpression,
+ Info, DiagLoc, Type, Value, Usage,
+ SourceLocation(), CheckedTemps);
+}
+
+/// Check that this evaluated value is fully-initialized and can be loaded by
+/// an lvalue-to-rvalue conversion.
+static bool CheckFullyInitialized(EvalInfo &Info, SourceLocation DiagLoc,
+ QualType Type, const APValue &Value) {
+ CheckedTemporaries CheckedTemps;
+ return CheckEvaluationResult(
+ CheckEvaluationResultKind::FullyInitialized, Info, DiagLoc, Type, Value,
+ Expr::EvaluateForCodeGen, SourceLocation(), CheckedTemps);
+}
+
+/// Enforce C++2a [expr.const]/4.17, which disallows new-expressions unless
+/// "the allocated storage is deallocated within the evaluation".
+static bool CheckMemoryLeaks(EvalInfo &Info) {
+ if (!Info.HeapAllocs.empty()) {
+ // We can still fold to a constant despite a compile-time memory leak,
+ // so long as the heap allocation isn't referenced in the result (we check
+ // that in CheckConstantExpression).
+ Info.CCEDiag(Info.HeapAllocs.begin()->second.AllocExpr,
+ diag::note_constexpr_memory_leak)
+ << unsigned(Info.HeapAllocs.size() - 1);
+ }
+ return true;
+}
+
static bool EvalPointerValueAsBool(const APValue &Value, bool &Result) {
// A null base expression indicates a null pointer. These are always
// evaluatable, and they are false unless the offset is zero.
@@ -2323,7 +2470,7 @@ static bool CheckedIntArithmetic(EvalInfo &Info, const Expr *E,
APSInt Value(Op(LHS.extend(BitWidth), RHS.extend(BitWidth)), false);
Result = Value.trunc(LHS.getBitWidth());
if (Result.extend(BitWidth) != Value) {
- if (Info.checkingForOverflow())
+ if (Info.checkingForUndefinedBehavior())
Info.Ctx.getDiagnostics().Report(E->getExprLoc(),
diag::warn_integer_constant_overflow)
<< Result.toString(10) << E->getType();
@@ -2813,9 +2960,10 @@ static APSInt extractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit,
// FIXME: This is inefficient; we should probably introduce something similar
// to the LLVM ConstantDataArray to make this cheaper.
static void expandStringLiteral(EvalInfo &Info, const StringLiteral *S,
- APValue &Result) {
- const ConstantArrayType *CAT =
- Info.Ctx.getAsConstantArrayType(S->getType());
+ APValue &Result,
+ QualType AllocType = QualType()) {
+ const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(
+ AllocType.isNull() ? S->getType() : AllocType);
assert(CAT && "string literal isn't an array");
QualType CharType = CAT->getElementType();
assert(CharType->isIntegerType() && "unexpected character type");
@@ -2879,8 +3027,8 @@ static bool isReadByLvalueToRvalueConversion(QualType T) {
/// Diagnose an attempt to read from any unreadable field within the specified
/// type, which might be a class type.
-static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E,
- QualType T) {
+static bool diagnoseMutableFields(EvalInfo &Info, const Expr *E, AccessKinds AK,
+ QualType T) {
CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
if (!RD)
return false;
@@ -2895,17 +3043,17 @@ static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E,
// FIXME: Add core issue number for the union case.
if (Field->isMutable() &&
(RD->isUnion() || isReadByLvalueToRvalueConversion(Field->getType()))) {
- Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1) << Field;
+ Info.FFDiag(E, diag::note_constexpr_access_mutable, 1) << AK << Field;
Info.Note(Field->getLocation(), diag::note_declared_at);
return true;
}
- if (diagnoseUnreadableFields(Info, E, Field->getType()))
+ if (diagnoseMutableFields(Info, E, AK, Field->getType()))
return true;
}
for (auto &BaseSpec : RD->bases())
- if (diagnoseUnreadableFields(Info, E, BaseSpec.getType()))
+ if (diagnoseMutableFields(Info, E, AK, BaseSpec.getType()))
return true;
// All mutable fields were empty, and thus not actually read.
@@ -2913,7 +3061,8 @@ static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E,
}
static bool lifetimeStartedInEvaluation(EvalInfo &Info,
- APValue::LValueBase Base) {
+ APValue::LValueBase Base,
+ bool MutableSubobject = false) {
// A temporary we created.
if (Base.getCallIndex())
return true;
@@ -2922,19 +3071,42 @@ static bool lifetimeStartedInEvaluation(EvalInfo &Info,
if (!Evaluating)
return false;
- // The variable whose initializer we're evaluating.
- if (auto *BaseD = Base.dyn_cast<const ValueDecl*>())
- if (declaresSameEntity(Evaluating, BaseD))
- return true;
+ auto *BaseD = Base.dyn_cast<const ValueDecl*>();
- // A temporary lifetime-extended by the variable whose initializer we're
- // evaluating.
- if (auto *BaseE = Base.dyn_cast<const Expr *>())
- if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(BaseE))
- if (declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating))
- return true;
+ switch (Info.IsEvaluatingDecl) {
+ case EvalInfo::EvaluatingDeclKind::None:
+ return false;
- return false;
+ case EvalInfo::EvaluatingDeclKind::Ctor:
+ // The variable whose initializer we're evaluating.
+ if (BaseD)
+ return declaresSameEntity(Evaluating, BaseD);
+
+ // A temporary lifetime-extended by the variable whose initializer we're
+ // evaluating.
+ if (auto *BaseE = Base.dyn_cast<const Expr *>())
+ if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(BaseE))
+ return declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating);
+ return false;
+
+ case EvalInfo::EvaluatingDeclKind::Dtor:
+ // C++2a [expr.const]p6:
+ // [during constant destruction] the lifetime of a and its non-mutable
+ // subobjects (but not its mutable subobjects) [are] considered to start
+ // within e.
+ //
+ // FIXME: We can meaningfully extend this to cover non-const objects, but
+ // we will need special handling: we should be able to access only
+ // subobjects of such objects that are themselves declared const.
+ if (!BaseD ||
+ !(BaseD->getType().isConstQualified() ||
+ BaseD->getType()->isReferenceType()) ||
+ MutableSubobject)
+ return false;
+ return declaresSameEntity(Evaluating, BaseD);
+ }
+
+ llvm_unreachable("unknown evaluating decl kind");
}
namespace {
@@ -2952,13 +3124,13 @@ struct CompleteObject {
CompleteObject(APValue::LValueBase Base, APValue *Value, QualType Type)
: Base(Base), Value(Value), Type(Type) {}
- bool mayReadMutableMembers(EvalInfo &Info) const {
+ bool mayAccessMutableMembers(EvalInfo &Info, AccessKinds AK) const {
// In C++14 onwards, it is permitted to read a mutable member whose
// lifetime began within the evaluation.
// FIXME: Should we also allow this in C++11?
if (!Info.getLangOpts().CPlusPlus14)
return false;
- return lifetimeStartedInEvaluation(Info, Base);
+ return lifetimeStartedInEvaluation(Info, Base, /*MutableSubobject*/true);
}
explicit operator bool() const { return !Type.isNull(); }
@@ -3006,19 +3178,22 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
// Walk the designator's path to find the subobject.
for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) {
// Reading an indeterminate value is undefined, but assigning over one is OK.
- if (O->isAbsent() || (O->isIndeterminate() && handler.AccessKind != AK_Assign)) {
+ if ((O->isAbsent() && !(handler.AccessKind == AK_Construct && I == N)) ||
+ (O->isIndeterminate() && handler.AccessKind != AK_Construct &&
+ handler.AccessKind != AK_Assign &&
+ handler.AccessKind != AK_ReadObjectRepresentation)) {
if (!Info.checkingPotentialConstantExpression())
Info.FFDiag(E, diag::note_constexpr_access_uninit)
<< handler.AccessKind << O->isIndeterminate();
return handler.failed();
}
- // C++ [class.ctor]p5:
+ // C++ [class.ctor]p5, C++ [class.dtor]p5:
// const and volatile semantics are not applied on an object under
- // construction.
+ // {con,de}struction.
if ((ObjType.isConstQualified() || ObjType.isVolatileQualified()) &&
ObjType->isRecordType() &&
- Info.isEvaluatingConstructor(
+ Info.isEvaluatingCtorDtor(
Obj.Base, llvm::makeArrayRef(Sub.Entries.begin(),
Sub.Entries.begin() + I)) !=
ConstructionPhase::None) {
@@ -3061,9 +3236,9 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
// things we need to check: if there are any mutable subobjects, we
// cannot perform this read. (This only happens when performing a trivial
// copy or assignment.)
- if (ObjType->isRecordType() && handler.AccessKind == AK_Read &&
- !Obj.mayReadMutableMembers(Info) &&
- diagnoseUnreadableFields(Info, E, ObjType))
+ if (ObjType->isRecordType() &&
+ !Obj.mayAccessMutableMembers(Info, handler.AccessKind) &&
+ diagnoseMutableFields(Info, E, handler.AccessKind, ObjType))
return handler.failed();
}
@@ -3101,7 +3276,7 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
if (O->getArrayInitializedElts() > Index)
O = &O->getArrayInitializedElt(Index);
- else if (handler.AccessKind != AK_Read) {
+ else if (!isRead(handler.AccessKind)) {
expandArray(*O, Index);
O = &O->getArrayInitializedElt(Index);
} else
@@ -3131,10 +3306,10 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
: O->getComplexFloatReal(), ObjType);
}
} else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {
- if (Field->isMutable() && handler.AccessKind == AK_Read &&
- !Obj.mayReadMutableMembers(Info)) {
- Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1)
- << Field;
+ if (Field->isMutable() &&
+ !Obj.mayAccessMutableMembers(Info, handler.AccessKind)) {
+ Info.FFDiag(E, diag::note_constexpr_access_mutable, 1)
+ << handler.AccessKind << Field;
Info.Note(Field->getLocation(), diag::note_declared_at);
return handler.failed();
}
@@ -3145,9 +3320,18 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
const FieldDecl *UnionField = O->getUnionField();
if (!UnionField ||
UnionField->getCanonicalDecl() != Field->getCanonicalDecl()) {
- Info.FFDiag(E, diag::note_constexpr_access_inactive_union_member)
- << handler.AccessKind << Field << !UnionField << UnionField;
- return handler.failed();
+ if (I == N - 1 && handler.AccessKind == AK_Construct) {
+ // Placement new onto an inactive union member makes it active.
+ O->setUnion(Field, APValue());
+ } else {
+ // FIXME: If O->getUnionValue() is absent, report that there's no
+ // active union member rather than reporting the prior active union
+ // member. We'll need to fix nullptr_t to not use APValue() as its
+ // representation first.
+ Info.FFDiag(E, diag::note_constexpr_access_inactive_union_member)
+ << handler.AccessKind << Field << !UnionField << UnionField;
+ return handler.failed();
+ }
}
O = &O->getUnionValue();
} else
@@ -3171,15 +3355,17 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
namespace {
struct ExtractSubobjectHandler {
EvalInfo &Info;
+ const Expr *E;
APValue &Result;
-
- static const AccessKinds AccessKind = AK_Read;
+ const AccessKinds AccessKind;
typedef bool result_type;
bool failed() { return false; }
bool found(APValue &Subobj, QualType SubobjType) {
Result = Subobj;
- return true;
+ if (AccessKind == AK_ReadObjectRepresentation)
+ return true;
+ return CheckFullyInitialized(Info, E->getExprLoc(), SubobjType, Result);
}
bool found(APSInt &Value, QualType SubobjType) {
Result = APValue(Value);
@@ -3192,14 +3378,13 @@ struct ExtractSubobjectHandler {
};
} // end anonymous namespace
-const AccessKinds ExtractSubobjectHandler::AccessKind;
-
/// Extract the designated sub-object of an rvalue.
static bool extractSubobject(EvalInfo &Info, const Expr *E,
const CompleteObject &Obj,
- const SubobjectDesignator &Sub,
- APValue &Result) {
- ExtractSubobjectHandler Handler = { Info, Result };
+ const SubobjectDesignator &Sub, APValue &Result,
+ AccessKinds AK = AK_Read) {
+ assert(AK == AK_Read || AK == AK_ReadObjectRepresentation);
+ ExtractSubobjectHandler Handler = {Info, E, Result, AK};
return findSubobject(Info, E, Obj, Sub, Handler);
}
@@ -3345,13 +3530,13 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
}
}
- bool IsAccess = isFormalAccess(AK);
+ bool IsAccess = isAnyAccess(AK);
// C++11 DR1311: An lvalue-to-rvalue conversion on a volatile-qualified type
// is not a constant expression (even if the object is non-volatile). We also
// apply this rule to C++98, in order to conform to the expected 'volatile'
// semantics.
- if (IsAccess && LValType.isVolatileQualified()) {
+ if (isFormalAccess(AK) && LValType.isVolatileQualified()) {
if (Info.getLangOpts().CPlusPlus)
Info.FFDiag(E, diag::note_constexpr_access_volatile_type)
<< AK << LValType;
@@ -3386,8 +3571,7 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
// the variable we're reading must be const.
if (!Frame) {
if (Info.getLangOpts().CPlusPlus14 &&
- declaresSameEntity(
- VD, Info.EvaluatingDecl.dyn_cast<const ValueDecl *>())) {
+ lifetimeStartedInEvaluation(Info, LVal.Base)) {
// OK, we can read and modify an object if we're in the process of
// evaluating its initializer, because its lifetime began in this
// evaluation.
@@ -3446,6 +3630,14 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
if (!evaluateVarDeclInit(Info, E, VD, Frame, BaseVal, &LVal))
return CompleteObject();
+ } else if (DynamicAllocLValue DA = LVal.Base.dyn_cast<DynamicAllocLValue>()) {
+ Optional<DynAlloc*> Alloc = Info.lookupDynamicAlloc(DA);
+ if (!Alloc) {
+ Info.FFDiag(E, diag::note_constexpr_access_deleted_object) << AK;
+ return CompleteObject();
+ }
+ return CompleteObject(LVal.Base, &(*Alloc)->Value,
+ LVal.Base.getDynamicAllocType());
} else {
const Expr *Base = LVal.Base.dyn_cast<const Expr*>();
@@ -3469,11 +3661,14 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
// int x = ++r;
// constexpr int k = r;
// Therefore we use the C++14 rules in C++11 too.
- const ValueDecl *VD = Info.EvaluatingDecl.dyn_cast<const ValueDecl*>();
- const ValueDecl *ED = MTE->getExtendingDecl();
+ //
+ // Note that temporaries whose lifetimes began while evaluating a
+ // variable's constructor are not usable while evaluating the
+ // corresponding destructor, not even if they're of const-qualified
+ // types.
if (!(BaseType.isConstQualified() &&
BaseType->isIntegralOrEnumerationType()) &&
- !(VD && VD->getCanonicalDecl() == ED->getCanonicalDecl())) {
+ !lifetimeStartedInEvaluation(Info, LVal.Base)) {
if (!IsAccess)
return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);
Info.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;
@@ -3525,15 +3720,22 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
/// case of a non-class type).
/// \param LVal - The glvalue on which we are attempting to perform this action.
/// \param RVal - The produced value will be placed here.
-static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
- QualType Type,
- const LValue &LVal, APValue &RVal) {
+/// \param WantObjectRepresentation - If true, we're looking for the object
+/// representation rather than the value, and in particular,
+/// there is no requirement that the result be fully initialized.
+static bool
+handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, QualType Type,
+ const LValue &LVal, APValue &RVal,
+ bool WantObjectRepresentation = false) {
if (LVal.Designator.Invalid)
return false;
// Check for special cases where there is no existing APValue to look at.
const Expr *Base = LVal.Base.dyn_cast<const Expr*>();
+ AccessKinds AK =
+ WantObjectRepresentation ? AK_ReadObjectRepresentation : AK_Read;
+
if (Base && !LVal.getLValueCallIndex() && !Type.isVolatileQualified()) {
if (const CompoundLiteralExpr *CLE = dyn_cast<CompoundLiteralExpr>(Base)) {
// In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the
@@ -3547,7 +3749,7 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
if (!Evaluate(Lit, Info, CLE->getInitializer()))
return false;
CompleteObject LitObj(LVal.Base, &Lit, Base->getType());
- return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal);
+ return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal, AK);
} else if (isa<StringLiteral>(Base) || isa<PredefinedExpr>(Base)) {
// Special-case character extraction so we don't have to construct an
// APValue for the whole string.
@@ -3562,7 +3764,7 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
}
if (LVal.Designator.isOnePastTheEnd()) {
if (Info.getLangOpts().CPlusPlus11)
- Info.FFDiag(Conv, diag::note_constexpr_access_past_end) << AK_Read;
+ Info.FFDiag(Conv, diag::note_constexpr_access_past_end) << AK;
else
Info.FFDiag(Conv);
return false;
@@ -3573,8 +3775,8 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
}
}
- CompleteObject Obj = findCompleteObject(Info, Conv, AK_Read, LVal, Type);
- return Obj && extractSubobject(Info, Conv, Obj, LVal.Designator, RVal);
+ CompleteObject Obj = findCompleteObject(Info, Conv, AK, LVal, Type);
+ return Obj && extractSubobject(Info, Conv, Obj, LVal.Designator, RVal, AK);
}
/// Perform an assignment of Val to LVal. Takes ownership of Val.
@@ -3866,7 +4068,7 @@ static bool handleIncDec(EvalInfo &Info, const Expr *E, const LValue &LVal,
/// Build an lvalue for the object argument of a member function call.
static bool EvaluateObjectArgument(EvalInfo &Info, const Expr *Object,
LValue &This) {
- if (Object->getType()->isPointerType())
+ if (Object->getType()->isPointerType() && Object->isRValue())
return EvaluatePointer(Object, This, Info);
if (Object->isGLValue())
@@ -4028,6 +4230,40 @@ static bool HandleBaseToDerivedCast(EvalInfo &Info, const CastExpr *E,
return CastToDerivedClass(Info, E, Result, TargetType, NewEntriesSize);
}
+/// Get the value to use for a default-initialized object of type T.
+static APValue getDefaultInitValue(QualType T) {
+ if (auto *RD = T->getAsCXXRecordDecl()) {
+ if (RD->isUnion())
+ return APValue((const FieldDecl*)nullptr);
+
+ APValue Struct(APValue::UninitStruct(), RD->getNumBases(),
+ std::distance(RD->field_begin(), RD->field_end()));
+
+ unsigned Index = 0;
+ for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(),
+ End = RD->bases_end(); I != End; ++I, ++Index)
+ Struct.getStructBase(Index) = getDefaultInitValue(I->getType());
+
+ for (const auto *I : RD->fields()) {
+ if (I->isUnnamedBitfield())
+ continue;
+ Struct.getStructField(I->getFieldIndex()) =
+ getDefaultInitValue(I->getType());
+ }
+ return Struct;
+ }
+
+ if (auto *AT =
+ dyn_cast_or_null<ConstantArrayType>(T->getAsArrayTypeUnsafe())) {
+ APValue Array(APValue::UninitArray(), 0, AT->getSize().getZExtValue());
+ if (Array.hasArrayFiller())
+ Array.getArrayFiller() = getDefaultInitValue(AT->getElementType());
+ return Array;
+ }
+
+ return APValue::IndeterminateValue();
+}
+
namespace {
enum EvalStmtResult {
/// Evaluation failed.
@@ -4051,14 +4287,13 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
return true;
LValue Result;
- APValue &Val = createTemporary(VD, true, Result, *Info.CurrentCall);
+ APValue &Val =
+ Info.CurrentCall->createTemporary(VD, VD->getType(), true, Result);
const Expr *InitE = VD->getInit();
if (!InitE) {
- Info.FFDiag(VD->getBeginLoc(), diag::note_constexpr_uninitialized)
- << false << VD->getType();
- Val = APValue();
- return false;
+ Val = getDefaultInitValue(VD->getType());
+ return true;
}
if (InitE->isValueDependent())
@@ -4095,7 +4330,9 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
FullExpressionRAII Scope(Info);
if (CondDecl && !EvaluateDecl(Info, CondDecl))
return false;
- return EvaluateAsBooleanCondition(Cond, Result, Info);
+ if (!EvaluateAsBooleanCondition(Cond, Result, Info))
+ return false;
+ return Scope.destroy();
}
namespace {
@@ -4131,7 +4368,12 @@ static EvalStmtResult EvaluateLoopBody(StmtResult &Result, EvalInfo &Info,
const Stmt *Body,
const SwitchCase *Case = nullptr) {
BlockScopeRAII Scope(Info);
- switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, Body, Case)) {
+
+ EvalStmtResult ESR = EvaluateStmt(Result, Info, Body, Case);
+ if (ESR != ESR_Failed && ESR != ESR_CaseNotFound && !Scope.destroy())
+ ESR = ESR_Failed;
+
+ switch (ESR) {
case ESR_Break:
return ESR_Succeeded;
case ESR_Succeeded:
@@ -4153,17 +4395,23 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
// Evaluate the switch condition.
APSInt Value;
{
- FullExpressionRAII Scope(Info);
if (const Stmt *Init = SS->getInit()) {
EvalStmtResult ESR = EvaluateStmt(Result, Info, Init);
- if (ESR != ESR_Succeeded)
+ if (ESR != ESR_Succeeded) {
+ if (ESR != ESR_Failed && !Scope.destroy())
+ ESR = ESR_Failed;
return ESR;
+ }
}
+
+ FullExpressionRAII CondScope(Info);
if (SS->getConditionVariable() &&
!EvaluateDecl(Info, SS->getConditionVariable()))
return ESR_Failed;
if (!EvaluateInteger(SS->getCond(), Value, Info))
return ESR_Failed;
+ if (!CondScope.destroy())
+ return ESR_Failed;
}
// Find the switch case corresponding to the value of the condition.
@@ -4187,10 +4435,14 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
}
if (!Found)
- return ESR_Succeeded;
+ return Scope.destroy() ? ESR_Succeeded : ESR_Failed;
// Search the switch body for the switch case and evaluate it from there.
- switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, SS->getBody(), Found)) {
+ EvalStmtResult ESR = EvaluateStmt(Result, Info, SS->getBody(), Found);
+ if (ESR != ESR_Failed && ESR != ESR_CaseNotFound && !Scope.destroy())
+ return ESR_Failed;
+
+ switch (ESR) {
case ESR_Break:
return ESR_Succeeded;
case ESR_Succeeded:
@@ -4217,10 +4469,6 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
// If we're hunting down a 'case' or 'default' label, recurse through
// substatements until we hit the label.
if (Case) {
- // FIXME: We don't start the lifetime of objects whose initialization we
- // jump over. However, such objects must be of class type with a trivial
- // default constructor that initialize all subobjects, so must be empty,
- // so this almost never matters.
switch (S->getStmtClass()) {
case Stmt::CompoundStmtClass:
// FIXME: Precompute which substatement of a compound statement we
@@ -4246,10 +4494,35 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
// preceded by our switch label.
BlockScopeRAII Scope(Info);
+ // Step into the init statement in case it brings an (uninitialized)
+ // variable into scope.
+ if (const Stmt *Init = IS->getInit()) {
+ EvalStmtResult ESR = EvaluateStmt(Result, Info, Init, Case);
+ if (ESR != ESR_CaseNotFound) {
+ assert(ESR != ESR_Succeeded);
+ return ESR;
+ }
+ }
+
+ // Condition variable must be initialized if it exists.
+ // FIXME: We can skip evaluating the body if there's a condition
+ // variable, as there can't be any case labels within it.
+ // (The same is true for 'for' statements.)
+
EvalStmtResult ESR = EvaluateStmt(Result, Info, IS->getThen(), Case);
- if (ESR != ESR_CaseNotFound || !IS->getElse())
+ if (ESR == ESR_Failed)
return ESR;
- return EvaluateStmt(Result, Info, IS->getElse(), Case);
+ if (ESR != ESR_CaseNotFound)
+ return Scope.destroy() ? ESR : ESR_Failed;
+ if (!IS->getElse())
+ return ESR_CaseNotFound;
+
+ ESR = EvaluateStmt(Result, Info, IS->getElse(), Case);
+ if (ESR == ESR_Failed)
+ return ESR;
+ if (ESR != ESR_CaseNotFound)
+ return Scope.destroy() ? ESR : ESR_Failed;
+ return ESR_CaseNotFound;
}
case Stmt::WhileStmtClass: {
@@ -4262,21 +4535,47 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
case Stmt::ForStmtClass: {
const ForStmt *FS = cast<ForStmt>(S);
+ BlockScopeRAII Scope(Info);
+
+ // Step into the init statement in case it brings an (uninitialized)
+ // variable into scope.
+ if (const Stmt *Init = FS->getInit()) {
+ EvalStmtResult ESR = EvaluateStmt(Result, Info, Init, Case);
+ if (ESR != ESR_CaseNotFound) {
+ assert(ESR != ESR_Succeeded);
+ return ESR;
+ }
+ }
+
EvalStmtResult ESR =
EvaluateLoopBody(Result, Info, FS->getBody(), Case);
if (ESR != ESR_Continue)
return ESR;
if (FS->getInc()) {
FullExpressionRAII IncScope(Info);
- if (!EvaluateIgnoredValue(Info, FS->getInc()))
+ if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy())
return ESR_Failed;
}
break;
}
- case Stmt::DeclStmtClass:
- // FIXME: If the variable has initialization that can't be jumped over,
- // bail out of any immediately-surrounding compound-statement too.
+ case Stmt::DeclStmtClass: {
+ // Start the lifetime of any uninitialized variables we encounter. They
+ // might be used by the selected branch of the switch.
+ const DeclStmt *DS = cast<DeclStmt>(S);
+ for (const auto *D : DS->decls()) {
+ if (const auto *VD = dyn_cast<VarDecl>(D)) {
+ if (VD->hasLocalStorage() && !VD->getInit())
+ if (!EvaluateVarDecl(Info, VD))
+ return ESR_Failed;
+ // FIXME: If the variable has initialization that can't be jumped
+ // over, bail out of any immediately-surrounding compound-statement
+ // too. There can't be any case labels here.
+ }
+ }
+ return ESR_CaseNotFound;
+ }
+
default:
return ESR_CaseNotFound;
}
@@ -4287,8 +4586,10 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
if (const Expr *E = dyn_cast<Expr>(S)) {
// Don't bother evaluating beyond an expression-statement which couldn't
// be evaluated.
+ // FIXME: Do we need the FullExpressionRAII object here?
+ // VisitExprWithCleanups should create one when necessary.
FullExpressionRAII Scope(Info);
- if (!EvaluateIgnoredValue(Info, E))
+ if (!EvaluateIgnoredValue(Info, E) || !Scope.destroy())
return ESR_Failed;
return ESR_Succeeded;
}
@@ -4301,12 +4602,12 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
case Stmt::DeclStmtClass: {
const DeclStmt *DS = cast<DeclStmt>(S);
- for (const auto *DclIt : DS->decls()) {
+ for (const auto *D : DS->decls()) {
// Each declaration initialization is its own full-expression.
- // FIXME: This isn't quite right; if we're performing aggregate
- // initialization, each braced subexpression is its own full-expression.
FullExpressionRAII Scope(Info);
- if (!EvaluateDecl(Info, DclIt) && !Info.noteFailure())
+ if (!EvaluateDecl(Info, D) && !Info.noteFailure())
+ return ESR_Failed;
+ if (!Scope.destroy())
return ESR_Failed;
}
return ESR_Succeeded;
@@ -4320,7 +4621,7 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
? EvaluateInPlace(Result.Value, Info, *Result.Slot, RetExpr)
: Evaluate(Result.Value, Info, RetExpr)))
return ESR_Failed;
- return ESR_Returned;
+ return Scope.destroy() ? ESR_Returned : ESR_Failed;
}
case Stmt::CompoundStmtClass: {
@@ -4331,10 +4632,15 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
EvalStmtResult ESR = EvaluateStmt(Result, Info, BI, Case);
if (ESR == ESR_Succeeded)
Case = nullptr;
- else if (ESR != ESR_CaseNotFound)
+ else if (ESR != ESR_CaseNotFound) {
+ if (ESR != ESR_Failed && !Scope.destroy())
+ return ESR_Failed;
return ESR;
+ }
}
- return Case ? ESR_CaseNotFound : ESR_Succeeded;
+ if (Case)
+ return ESR_CaseNotFound;
+ return Scope.destroy() ? ESR_Succeeded : ESR_Failed;
}
case Stmt::IfStmtClass: {
@@ -4344,8 +4650,11 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
BlockScopeRAII Scope(Info);
if (const Stmt *Init = IS->getInit()) {
EvalStmtResult ESR = EvaluateStmt(Result, Info, Init);
- if (ESR != ESR_Succeeded)
+ if (ESR != ESR_Succeeded) {
+ if (ESR != ESR_Failed && !Scope.destroy())
+ return ESR_Failed;
return ESR;
+ }
}
bool Cond;
if (!EvaluateCond(Info, IS->getConditionVariable(), IS->getCond(), Cond))
@@ -4353,10 +4662,13 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
if (const Stmt *SubStmt = Cond ? IS->getThen() : IS->getElse()) {
EvalStmtResult ESR = EvaluateStmt(Result, Info, SubStmt);
- if (ESR != ESR_Succeeded)
+ if (ESR != ESR_Succeeded) {
+ if (ESR != ESR_Failed && !Scope.destroy())
+ return ESR_Failed;
return ESR;
+ }
}
- return ESR_Succeeded;
+ return Scope.destroy() ? ESR_Succeeded : ESR_Failed;
}
case Stmt::WhileStmtClass: {
@@ -4371,8 +4683,13 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
break;
EvalStmtResult ESR = EvaluateLoopBody(Result, Info, WS->getBody());
- if (ESR != ESR_Continue)
+ if (ESR != ESR_Continue) {
+ if (ESR != ESR_Failed && !Scope.destroy())
+ return ESR_Failed;
return ESR;
+ }
+ if (!Scope.destroy())
+ return ESR_Failed;
}
return ESR_Succeeded;
}
@@ -4387,7 +4704,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
Case = nullptr;
FullExpressionRAII CondScope(Info);
- if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info))
+ if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info) ||
+ !CondScope.destroy())
return ESR_Failed;
} while (Continue);
return ESR_Succeeded;
@@ -4395,14 +4713,17 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
case Stmt::ForStmtClass: {
const ForStmt *FS = cast<ForStmt>(S);
- BlockScopeRAII Scope(Info);
+ BlockScopeRAII ForScope(Info);
if (FS->getInit()) {
EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit());
- if (ESR != ESR_Succeeded)
+ if (ESR != ESR_Succeeded) {
+ if (ESR != ESR_Failed && !ForScope.destroy())
+ return ESR_Failed;
return ESR;
+ }
}
while (true) {
- BlockScopeRAII Scope(Info);
+ BlockScopeRAII IterScope(Info);
bool Continue = true;
if (FS->getCond() && !EvaluateCond(Info, FS->getConditionVariable(),
FS->getCond(), Continue))
@@ -4411,16 +4732,22 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
break;
EvalStmtResult ESR = EvaluateLoopBody(Result, Info, FS->getBody());
- if (ESR != ESR_Continue)
+ if (ESR != ESR_Continue) {
+ if (ESR != ESR_Failed && (!IterScope.destroy() || !ForScope.destroy()))
+ return ESR_Failed;
return ESR;
+ }
if (FS->getInc()) {
FullExpressionRAII IncScope(Info);
- if (!EvaluateIgnoredValue(Info, FS->getInc()))
+ if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy())
return ESR_Failed;
}
+
+ if (!IterScope.destroy())
+ return ESR_Failed;
}
- return ESR_Succeeded;
+ return ForScope.destroy() ? ESR_Succeeded : ESR_Failed;
}
case Stmt::CXXForRangeStmtClass: {
@@ -4430,22 +4757,34 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
// Evaluate the init-statement if present.
if (FS->getInit()) {
EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit());
- if (ESR != ESR_Succeeded)
+ if (ESR != ESR_Succeeded) {
+ if (ESR != ESR_Failed && !Scope.destroy())
+ return ESR_Failed;
return ESR;
+ }
}
// Initialize the __range variable.
EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getRangeStmt());
- if (ESR != ESR_Succeeded)
+ if (ESR != ESR_Succeeded) {
+ if (ESR != ESR_Failed && !Scope.destroy())
+ return ESR_Failed;
return ESR;
+ }
// Create the __begin and __end iterators.
ESR = EvaluateStmt(Result, Info, FS->getBeginStmt());
- if (ESR != ESR_Succeeded)
+ if (ESR != ESR_Succeeded) {
+ if (ESR != ESR_Failed && !Scope.destroy())
+ return ESR_Failed;
return ESR;
+ }
ESR = EvaluateStmt(Result, Info, FS->getEndStmt());
- if (ESR != ESR_Succeeded)
+ if (ESR != ESR_Succeeded) {
+ if (ESR != ESR_Failed && !Scope.destroy())
+ return ESR_Failed;
return ESR;
+ }
while (true) {
// Condition: __begin != __end.
@@ -4461,20 +4800,29 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
// User's variable declaration, initialized by *__begin.
BlockScopeRAII InnerScope(Info);
ESR = EvaluateStmt(Result, Info, FS->getLoopVarStmt());
- if (ESR != ESR_Succeeded)
+ if (ESR != ESR_Succeeded) {
+ if (ESR != ESR_Failed && (!InnerScope.destroy() || !Scope.destroy()))
+ return ESR_Failed;
return ESR;
+ }
// Loop body.
ESR = EvaluateLoopBody(Result, Info, FS->getBody());
- if (ESR != ESR_Continue)
+ if (ESR != ESR_Continue) {
+ if (ESR != ESR_Failed && (!InnerScope.destroy() || !Scope.destroy()))
+ return ESR_Failed;
return ESR;
+ }
// Increment: ++__begin
if (!EvaluateIgnoredValue(Info, FS->getInc()))
return ESR_Failed;
+
+ if (!InnerScope.destroy())
+ return ESR_Failed;
}
- return ESR_Succeeded;
+ return Scope.destroy() ? ESR_Succeeded : ESR_Failed;
}
case Stmt::SwitchStmtClass:
@@ -4649,9 +4997,13 @@ static bool checkDynamicType(EvalInfo &Info, const Expr *E, const LValue &This,
/// Check that the pointee of the 'this' pointer in a member function call is
/// either within its lifetime or in its period of construction or destruction.
-static bool checkNonVirtualMemberCallThisPointer(EvalInfo &Info, const Expr *E,
- const LValue &This) {
- return checkDynamicType(Info, E, This, AK_MemberCall, false);
+static bool
+checkNonVirtualMemberCallThisPointer(EvalInfo &Info, const Expr *E,
+ const LValue &This,
+ const CXXMethodDecl *NamedMember) {
+ return checkDynamicType(
+ Info, E, This,
+ isa<CXXDestructorDecl>(NamedMember) ? AK_Destroy : AK_MemberCall, false);
}
struct DynamicType {
@@ -4699,16 +5051,19 @@ static Optional<DynamicType> ComputeDynamicType(EvalInfo &Info, const Expr *E,
ArrayRef<APValue::LValuePathEntry> Path = This.Designator.Entries;
for (unsigned PathLength = This.Designator.MostDerivedPathLength;
PathLength <= Path.size(); ++PathLength) {
- switch (Info.isEvaluatingConstructor(This.getLValueBase(),
- Path.slice(0, PathLength))) {
+ switch (Info.isEvaluatingCtorDtor(This.getLValueBase(),
+ Path.slice(0, PathLength))) {
case ConstructionPhase::Bases:
- // We're constructing a base class. This is not the dynamic type.
+ case ConstructionPhase::DestroyingBases:
+ // We're constructing or destroying a base class. This is not the dynamic
+ // type.
break;
case ConstructionPhase::None:
case ConstructionPhase::AfterBases:
- // We've finished constructing the base classes, so this is the dynamic
- // type.
+ case ConstructionPhase::Destroying:
+ // We've finished constructing the base classes and not yet started
+ // destroying them again, so this is the dynamic type.
return DynamicType{getBaseClassType(This.Designator, PathLength),
PathLength};
}
@@ -4725,8 +5080,9 @@ static Optional<DynamicType> ComputeDynamicType(EvalInfo &Info, const Expr *E,
static const CXXMethodDecl *HandleVirtualDispatch(
EvalInfo &Info, const Expr *E, LValue &This, const CXXMethodDecl *Found,
llvm::SmallVectorImpl<QualType> &CovariantAdjustmentPath) {
- Optional<DynamicType> DynType =
- ComputeDynamicType(Info, E, This, AK_MemberCall);
+ Optional<DynamicType> DynType = ComputeDynamicType(
+ Info, E, This,
+ isa<CXXDestructorDecl>(Found) ? AK_Destroy : AK_MemberCall);
if (!DynType)
return nullptr;
@@ -4862,8 +5218,7 @@ static bool HandleDynamicCast(EvalInfo &Info, const ExplicitCastExpr *E,
if (!E->isGLValue()) {
// The value of a failed cast to pointer type is the null pointer value
// of the required result type.
- auto TargetVal = Info.Ctx.getTargetNullPointerValue(E->getType());
- Ptr.setNull(E->getType(), TargetVal);
+ Ptr.setNull(Info.Ctx, E->getType());
return true;
}
@@ -4928,39 +5283,6 @@ struct StartLifetimeOfUnionMemberHandler {
static const AccessKinds AccessKind = AK_Assign;
- APValue getDefaultInitValue(QualType SubobjType) {
- if (auto *RD = SubobjType->getAsCXXRecordDecl()) {
- if (RD->isUnion())
- return APValue((const FieldDecl*)nullptr);
-
- APValue Struct(APValue::UninitStruct(), RD->getNumBases(),
- std::distance(RD->field_begin(), RD->field_end()));
-
- unsigned Index = 0;
- for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(),
- End = RD->bases_end(); I != End; ++I, ++Index)
- Struct.getStructBase(Index) = getDefaultInitValue(I->getType());
-
- for (const auto *I : RD->fields()) {
- if (I->isUnnamedBitfield())
- continue;
- Struct.getStructField(I->getFieldIndex()) =
- getDefaultInitValue(I->getType());
- }
- return Struct;
- }
-
- if (auto *AT = dyn_cast_or_null<ConstantArrayType>(
- SubobjType->getAsArrayTypeUnsafe())) {
- APValue Array(APValue::UninitArray(), 0, AT->getSize().getZExtValue());
- if (Array.hasArrayFiller())
- Array.getArrayFiller() = getDefaultInitValue(AT->getElementType());
- return Array;
- }
-
- return APValue::IndeterminateValue();
- }
-
typedef bool result_type;
bool failed() { return false; }
bool found(APValue &Subobj, QualType SubobjType) {
@@ -4973,7 +5295,8 @@ struct StartLifetimeOfUnionMemberHandler {
// * No variant members' lifetimes begin
// * All scalar subobjects whose lifetimes begin have indeterminate values
assert(SubobjType->isUnionType());
- if (!declaresSameEntity(Subobj.getUnionField(), Field))
+ if (!declaresSameEntity(Subobj.getUnionField(), Field) ||
+ !Subobj.getUnionValue().hasValue())
Subobj.setUnion(Field, getDefaultInitValue(Field->getType()));
return true;
}
@@ -5005,7 +5328,9 @@ static bool HandleUnionActiveMemberChange(EvalInfo &Info, const Expr *LHSExpr,
// -- If E is of the form A.B, S(E) contains the elements of S(A)...
if (auto *ME = dyn_cast<MemberExpr>(E)) {
auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
- if (!FD)
+ // Note that we can't implicitly start the lifetime of a reference,
+ // so we don't need to proceed any further if we reach one.
+ if (!FD || FD->getType()->isReferenceType())
break;
// ... and also contains A.B if B names a union member
@@ -5116,18 +5441,18 @@ static bool EvaluateArgs(ArrayRef<const Expr *> Args, ArgVector &ArgValues,
}
}
}
- for (ArrayRef<const Expr*>::iterator I = Args.begin(), E = Args.end();
- I != E; ++I) {
- if (!Evaluate(ArgValues[I - Args.begin()], Info, *I)) {
+ for (unsigned Idx = 0; Idx < Args.size(); Idx++) {
+ if (!Evaluate(ArgValues[Idx], Info, Args[Idx])) {
// If we're checking for a potential constant expression, evaluate all
// initializers even if some of them fail.
if (!Info.noteFailure())
return false;
Success = false;
} else if (!ForbiddenNullArgs.empty() &&
- ForbiddenNullArgs[I - Args.begin()] &&
- ArgValues[I - Args.begin()].isNullPointer()) {
- Info.CCEDiag(*I, diag::note_non_null_attribute_failed);
+ ForbiddenNullArgs[Idx] &&
+ ArgValues[Idx].isLValue() &&
+ ArgValues[Idx].isNullPointer()) {
+ Info.CCEDiag(Args[Idx], diag::note_non_null_attribute_failed);
if (!Info.noteFailure())
return false;
Success = false;
@@ -5166,8 +5491,8 @@ static bool HandleFunctionCall(SourceLocation CallLoc,
LValue RHS;
RHS.setFrom(Info.Ctx, ArgValues[0]);
APValue RHSValue;
- if (!handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(),
- RHS, RHSValue))
+ if (!handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(), RHS,
+ RHSValue, MD->getParent()->isUnion()))
return false;
if (Info.getLangOpts().CPlusPlus2a && MD->isTrivial() &&
!HandleUnionActiveMemberChange(Info, Args[0], *This))
@@ -5230,7 +5555,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
CXXConstructorDecl::init_const_iterator I = Definition->init_begin();
{
FullExpressionRAII InitScope(Info);
- if (!EvaluateInPlace(Result, Info, This, (*I)->getInit()))
+ if (!EvaluateInPlace(Result, Info, This, (*I)->getInit()) ||
+ !InitScope.destroy())
return false;
}
return EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed;
@@ -5251,7 +5577,7 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
RHS.setFrom(Info.Ctx, ArgValues[0]);
return handleLValueToRValueConversion(
Info, E, Definition->getParamDecl(0)->getType().getNonReferenceType(),
- RHS, Result);
+ RHS, Result, Definition->getParent()->isUnion());
}
// Reserve space for the struct members.
@@ -5270,6 +5596,25 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
#ifndef NDEBUG
CXXRecordDecl::base_class_const_iterator BaseIt = RD->bases_begin();
#endif
+ CXXRecordDecl::field_iterator FieldIt = RD->field_begin();
+ auto SkipToField = [&](FieldDecl *FD, bool Indirect) {
+ // We might be initializing the same field again if this is an indirect
+ // field initialization.
+ if (FieldIt == RD->field_end() ||
+ FieldIt->getFieldIndex() > FD->getFieldIndex()) {
+ assert(Indirect && "fields out of order?");
+ return;
+ }
+
+ // Default-initialize any fields with no explicit initializer.
+ for (; !declaresSameEntity(*FieldIt, FD); ++FieldIt) {
+ assert(FieldIt != RD->field_end() && "missing field?");
+ if (!FieldIt->isUnnamedBitfield())
+ Result.getStructField(FieldIt->getFieldIndex()) =
+ getDefaultInitValue(FieldIt->getType());
+ }
+ ++FieldIt;
+ };
for (const auto *I : Definition->inits()) {
LValue Subobject = This;
LValue SubobjectParent = This;
@@ -5298,6 +5643,7 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
Result = APValue(FD);
Value = &Result.getUnionValue();
} else {
+ SkipToField(FD, false);
Value = &Result.getStructField(FD->getFieldIndex());
}
} else if (IndirectFieldDecl *IFD = I->getIndirectMember()) {
@@ -5317,8 +5663,10 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
if (CD->isUnion())
*Value = APValue(FD);
else
- *Value = APValue(APValue::UninitStruct(), CD->getNumBases(),
- std::distance(CD->field_begin(), CD->field_end()));
+ // FIXME: This immediately starts the lifetime of all members of an
+ // anonymous struct. It would be preferable to strictly start member
+ // lifetime in initialization order.
+ *Value = getDefaultInitValue(Info.Ctx.getRecordType(CD));
}
// Store Subobject as its parent before updating it for the last element
// in the chain.
@@ -5328,8 +5676,11 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
return false;
if (CD->isUnion())
Value = &Value->getUnionValue();
- else
+ else {
+ if (C == IndirectFieldChain.front() && !RD->isUnion())
+ SkipToField(FD, true);
Value = &Value->getStructField(FD->getFieldIndex());
+ }
}
} else {
llvm_unreachable("unknown base initializer kind");
@@ -5358,8 +5709,18 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
EvalObj.finishedConstructingBases();
}
+ // Default-initialize any remaining fields.
+ if (!RD->isUnion()) {
+ for (; FieldIt != RD->field_end(); ++FieldIt) {
+ if (!FieldIt->isUnnamedBitfield())
+ Result.getStructField(FieldIt->getFieldIndex()) =
+ getDefaultInitValue(FieldIt->getType());
+ }
+ }
+
return Success &&
- EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed;
+ EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed &&
+ LifetimeExtendedScope.destroy();
}
static bool HandleConstructorCall(const Expr *E, const LValue &This,
@@ -5374,6 +5735,381 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
Info, Result);
}
+static bool HandleDestructionImpl(EvalInfo &Info, SourceLocation CallLoc,
+ const LValue &This, APValue &Value,
+ QualType T) {
+ // Objects can only be destroyed while they're within their lifetimes.
+ // FIXME: We have no representation for whether an object of type nullptr_t
+ // is in its lifetime; it usually doesn't matter. Perhaps we should model it
+ // as indeterminate instead?
+ if (Value.isAbsent() && !T->isNullPtrType()) {
+ APValue Printable;
+ This.moveInto(Printable);
+ Info.FFDiag(CallLoc, diag::note_constexpr_destroy_out_of_lifetime)
+ << Printable.getAsString(Info.Ctx, Info.Ctx.getLValueReferenceType(T));
+ return false;
+ }
+
+ // Invent an expression for location purposes.
+ // FIXME: We shouldn't need to do this.
+ OpaqueValueExpr LocE(CallLoc, Info.Ctx.IntTy, VK_RValue);
+
+ // For arrays, destroy elements right-to-left.
+ if (const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(T)) {
+ uint64_t Size = CAT->getSize().getZExtValue();
+ QualType ElemT = CAT->getElementType();
+
+ LValue ElemLV = This;
+ ElemLV.addArray(Info, &LocE, CAT);
+ if (!HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, Size))
+ return false;
+
+ // Ensure that we have actual array elements available to destroy; the
+ // destructors might mutate the value, so we can't run them on the array
+ // filler.
+ if (Size && Size > Value.getArrayInitializedElts())
+ expandArray(Value, Value.getArraySize() - 1);
+
+ for (; Size != 0; --Size) {
+ APValue &Elem = Value.getArrayInitializedElt(Size - 1);
+ if (!HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, -1) ||
+ !HandleDestructionImpl(Info, CallLoc, ElemLV, Elem, ElemT))
+ return false;
+ }
+
+ // End the lifetime of this array now.
+ Value = APValue();
+ return true;
+ }
+
+ const CXXRecordDecl *RD = T->getAsCXXRecordDecl();
+ if (!RD) {
+ if (T.isDestructedType()) {
+ Info.FFDiag(CallLoc, diag::note_constexpr_unsupported_destruction) << T;
+ return false;
+ }
+
+ Value = APValue();
+ return true;
+ }
+
+ if (RD->getNumVBases()) {
+ Info.FFDiag(CallLoc, diag::note_constexpr_virtual_base) << RD;
+ return false;
+ }
+
+ const CXXDestructorDecl *DD = RD->getDestructor();
+ if (!DD && !RD->hasTrivialDestructor()) {
+ Info.FFDiag(CallLoc);
+ return false;
+ }
+
+ if (!DD || DD->isTrivial() ||
+ (RD->isAnonymousStructOrUnion() && RD->isUnion())) {
+ // A trivial destructor just ends the lifetime of the object. Check for
+ // this case before checking for a body, because we might not bother
+ // building a body for a trivial destructor. Note that it doesn't matter
+ // whether the destructor is constexpr in this case; all trivial
+ // destructors are constexpr.
+ //
+ // If an anonymous union would be destroyed, some enclosing destructor must
+ // have been explicitly defined, and the anonymous union destruction should
+ // have no effect.
+ Value = APValue();
+ return true;
+ }
+
+ if (!Info.CheckCallLimit(CallLoc))
+ return false;
+
+ const FunctionDecl *Definition = nullptr;
+ const Stmt *Body = DD->getBody(Definition);
+
+ if (!CheckConstexprFunction(Info, CallLoc, DD, Definition, Body))
+ return false;
+
+ CallStackFrame Frame(Info, CallLoc, Definition, &This, nullptr);
+
+ // We're now in the period of destruction of this object.
+ unsigned BasesLeft = RD->getNumBases();
+ EvalInfo::EvaluatingDestructorRAII EvalObj(
+ Info,
+ ObjectUnderConstruction{This.getLValueBase(), This.Designator.Entries});
+ if (!EvalObj.DidInsert) {
+ // C++2a [class.dtor]p19:
+ // the behavior is undefined if the destructor is invoked for an object
+ // whose lifetime has ended
+ // (Note that formally the lifetime ends when the period of destruction
+ // begins, even though certain uses of the object remain valid until the
+ // period of destruction ends.)
+ Info.FFDiag(CallLoc, diag::note_constexpr_double_destroy);
+ return false;
+ }
+
+ // FIXME: Creating an APValue just to hold a nonexistent return value is
+ // wasteful.
+ APValue RetVal;
+ StmtResult Ret = {RetVal, nullptr};
+ if (EvaluateStmt(Ret, Info, Definition->getBody()) == ESR_Failed)
+ return false;
+
+ // A union destructor does not implicitly destroy its members.
+ if (RD->isUnion())
+ return true;
+
+ const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
+
+ // We don't have a good way to iterate fields in reverse, so collect all the
+ // fields first and then walk them backwards.
+ SmallVector<FieldDecl*, 16> Fields(RD->field_begin(), RD->field_end());
+ for (const FieldDecl *FD : llvm::reverse(Fields)) {
+ if (FD->isUnnamedBitfield())
+ continue;
+
+ LValue Subobject = This;
+ if (!HandleLValueMember(Info, &LocE, Subobject, FD, &Layout))
+ return false;
+
+ APValue *SubobjectValue = &Value.getStructField(FD->getFieldIndex());
+ if (!HandleDestructionImpl(Info, CallLoc, Subobject, *SubobjectValue,
+ FD->getType()))
+ return false;
+ }
+
+ if (BasesLeft != 0)
+ EvalObj.startedDestroyingBases();
+
+ // Destroy base classes in reverse order.
+ for (const CXXBaseSpecifier &Base : llvm::reverse(RD->bases())) {
+ --BasesLeft;
+
+ QualType BaseType = Base.getType();
+ LValue Subobject = This;
+ if (!HandleLValueDirectBase(Info, &LocE, Subobject, RD,
+ BaseType->getAsCXXRecordDecl(), &Layout))
+ return false;
+
+ APValue *SubobjectValue = &Value.getStructBase(BasesLeft);
+ if (!HandleDestructionImpl(Info, CallLoc, Subobject, *SubobjectValue,
+ BaseType))
+ return false;
+ }
+ assert(BasesLeft == 0 && "NumBases was wrong?");
+
+ // The period of destruction ends now. The object is gone.
+ Value = APValue();
+ return true;
+}
+
+namespace {
+struct DestroyObjectHandler {
+ EvalInfo &Info;
+ const Expr *E;
+ const LValue &This;
+ const AccessKinds AccessKind;
+
+ typedef bool result_type;
+ bool failed() { return false; }
+ bool found(APValue &Subobj, QualType SubobjType) {
+ return HandleDestructionImpl(Info, E->getExprLoc(), This, Subobj,
+ SubobjType);
+ }
+ bool found(APSInt &Value, QualType SubobjType) {
+ Info.FFDiag(E, diag::note_constexpr_destroy_complex_elem);
+ return false;
+ }
+ bool found(APFloat &Value, QualType SubobjType) {
+ Info.FFDiag(E, diag::note_constexpr_destroy_complex_elem);
+ return false;
+ }
+};
+}
+
+/// Perform a destructor or pseudo-destructor call on the given object, which
+/// might in general not be a complete object.
+static bool HandleDestruction(EvalInfo &Info, const Expr *E,
+ const LValue &This, QualType ThisType) {
+ CompleteObject Obj = findCompleteObject(Info, E, AK_Destroy, This, ThisType);
+ DestroyObjectHandler Handler = {Info, E, This, AK_Destroy};
+ return Obj && findSubobject(Info, E, Obj, This.Designator, Handler);
+}
+
+/// Destroy and end the lifetime of the given complete object.
+static bool HandleDestruction(EvalInfo &Info, SourceLocation Loc,
+ APValue::LValueBase LVBase, APValue &Value,
+ QualType T) {
+ // If we've had an unmodeled side-effect, we can't rely on mutable state
+ // (such as the object we're about to destroy) being correct.
+ if (Info.EvalStatus.HasSideEffects)
+ return false;
+
+ LValue LV;
+ LV.set({LVBase});
+ return HandleDestructionImpl(Info, Loc, LV, Value, T);
+}
+
+/// Perform a call to 'perator new' or to `__builtin_operator_new'.
+static bool HandleOperatorNewCall(EvalInfo &Info, const CallExpr *E,
+ LValue &Result) {
+ if (Info.checkingPotentialConstantExpression() ||
+ Info.SpeculativeEvaluationDepth)
+ return false;
+
+ // This is permitted only within a call to std::allocator<T>::allocate.
+ auto Caller = Info.getStdAllocatorCaller("allocate");
+ if (!Caller) {
+ Info.FFDiag(E->getExprLoc(), Info.getLangOpts().CPlusPlus2a
+ ? diag::note_constexpr_new_untyped
+ : diag::note_constexpr_new);
+ return false;
+ }
+
+ QualType ElemType = Caller.ElemType;
+ if (ElemType->isIncompleteType() || ElemType->isFunctionType()) {
+ Info.FFDiag(E->getExprLoc(),
+ diag::note_constexpr_new_not_complete_object_type)
+ << (ElemType->isIncompleteType() ? 0 : 1) << ElemType;
+ return false;
+ }
+
+ APSInt ByteSize;
+ if (!EvaluateInteger(E->getArg(0), ByteSize, Info))
+ return false;
+ bool IsNothrow = false;
+ for (unsigned I = 1, N = E->getNumArgs(); I != N; ++I) {
+ EvaluateIgnoredValue(Info, E->getArg(I));
+ IsNothrow |= E->getType()->isNothrowT();
+ }
+
+ CharUnits ElemSize;
+ if (!HandleSizeof(Info, E->getExprLoc(), ElemType, ElemSize))
+ return false;
+ APInt Size, Remainder;
+ APInt ElemSizeAP(ByteSize.getBitWidth(), ElemSize.getQuantity());
+ APInt::udivrem(ByteSize, ElemSizeAP, Size, Remainder);
+ if (Remainder != 0) {
+ // This likely indicates a bug in the implementation of 'std::allocator'.
+ Info.FFDiag(E->getExprLoc(), diag::note_constexpr_operator_new_bad_size)
+ << ByteSize << APSInt(ElemSizeAP, true) << ElemType;
+ return false;
+ }
+
+ if (ByteSize.getActiveBits() > ConstantArrayType::getMaxSizeBits(Info.Ctx)) {
+ if (IsNothrow) {
+ Result.setNull(Info.Ctx, E->getType());
+ return true;
+ }
+
+ Info.FFDiag(E, diag::note_constexpr_new_too_large) << APSInt(Size, true);
+ return false;
+ }
+
+ QualType AllocType = Info.Ctx.getConstantArrayType(ElemType, Size, nullptr,
+ ArrayType::Normal, 0);
+ APValue *Val = Info.createHeapAlloc(E, AllocType, Result);
+ *Val = APValue(APValue::UninitArray(), 0, Size.getZExtValue());
+ Result.addArray(Info, E, cast<ConstantArrayType>(AllocType));
+ return true;
+}
+
+static bool hasVirtualDestructor(QualType T) {
+ if (CXXRecordDecl *RD = T->getAsCXXRecordDecl())
+ if (CXXDestructorDecl *DD = RD->getDestructor())
+ return DD->isVirtual();
+ return false;
+}
+
+static const FunctionDecl *getVirtualOperatorDelete(QualType T) {
+ if (CXXRecordDecl *RD = T->getAsCXXRecordDecl())
+ if (CXXDestructorDecl *DD = RD->getDestructor())
+ return DD->isVirtual() ? DD->getOperatorDelete() : nullptr;
+ return nullptr;
+}
+
+/// Check that the given object is a suitable pointer to a heap allocation that
+/// still exists and is of the right kind for the purpose of a deletion.
+///
+/// On success, returns the heap allocation to deallocate. On failure, produces
+/// a diagnostic and returns None.
+static Optional<DynAlloc *> CheckDeleteKind(EvalInfo &Info, const Expr *E,
+ const LValue &Pointer,
+ DynAlloc::Kind DeallocKind) {
+ auto PointerAsString = [&] {
+ return Pointer.toString(Info.Ctx, Info.Ctx.VoidPtrTy);
+ };
+
+ DynamicAllocLValue DA = Pointer.Base.dyn_cast<DynamicAllocLValue>();
+ if (!DA) {
+ Info.FFDiag(E, diag::note_constexpr_delete_not_heap_alloc)
+ << PointerAsString();
+ if (Pointer.Base)
+ NoteLValueLocation(Info, Pointer.Base);
+ return None;
+ }
+
+ Optional<DynAlloc *> Alloc = Info.lookupDynamicAlloc(DA);
+ if (!Alloc) {
+ Info.FFDiag(E, diag::note_constexpr_double_delete);
+ return None;
+ }
+
+ QualType AllocType = Pointer.Base.getDynamicAllocType();
+ if (DeallocKind != (*Alloc)->getKind()) {
+ Info.FFDiag(E, diag::note_constexpr_new_delete_mismatch)
+ << DeallocKind << (*Alloc)->getKind() << AllocType;
+ NoteLValueLocation(Info, Pointer.Base);
+ return None;
+ }
+
+ bool Subobject = false;
+ if (DeallocKind == DynAlloc::New) {
+ Subobject = Pointer.Designator.MostDerivedPathLength != 0 ||
+ Pointer.Designator.isOnePastTheEnd();
+ } else {
+ Subobject = Pointer.Designator.Entries.size() != 1 ||
+ Pointer.Designator.Entries[0].getAsArrayIndex() != 0;
+ }
+ if (Subobject) {
+ Info.FFDiag(E, diag::note_constexpr_delete_subobject)
+ << PointerAsString() << Pointer.Designator.isOnePastTheEnd();
+ return None;
+ }
+
+ return Alloc;
+}
+
+// Perform a call to 'operator delete' or '__builtin_operator_delete'.
+bool HandleOperatorDeleteCall(EvalInfo &Info, const CallExpr *E) {
+ if (Info.checkingPotentialConstantExpression() ||
+ Info.SpeculativeEvaluationDepth)
+ return false;
+
+ // This is permitted only within a call to std::allocator<T>::deallocate.
+ if (!Info.getStdAllocatorCaller("deallocate")) {
+ Info.FFDiag(E->getExprLoc());
+ return true;
+ }
+
+ LValue Pointer;
+ if (!EvaluatePointer(E->getArg(0), Pointer, Info))
+ return false;
+ for (unsigned I = 1, N = E->getNumArgs(); I != N; ++I)
+ EvaluateIgnoredValue(Info, E->getArg(I));
+
+ if (Pointer.Designator.Invalid)
+ return false;
+
+ // Deleting a null pointer has no effect.
+ if (Pointer.isNullPointer())
+ return true;
+
+ if (!CheckDeleteKind(Info, E, Pointer, DynAlloc::StdAllocator))
+ return false;
+
+ Info.HeapAllocs.erase(Pointer.Base.get<DynamicAllocLValue>());
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// Generic Evaluation
//===----------------------------------------------------------------------===//
@@ -5706,9 +6442,8 @@ class BufferToAPValueConverter {
QualType RepresentationType = Ty->getDecl()->getIntegerType();
assert(!RepresentationType.isNull() &&
"enum forward decl should be caught by Sema");
- const BuiltinType *AsBuiltin =
- RepresentationType.getCanonicalType()->getAs<BuiltinType>();
- assert(AsBuiltin && "non-integral enum underlying type?");
+ const auto *AsBuiltin =
+ RepresentationType.getCanonicalType()->castAs<BuiltinType>();
// Recurse into the underlying type. Treat std::byte transparently as
// unsigned char.
return visit(AsBuiltin, Offset, /*EnumTy=*/Ty);
@@ -5752,7 +6487,7 @@ class BufferToAPValueConverter {
#define NON_CANONICAL_UNLESS_DEPENDENT(Class, Base) \
case Type::Class: \
llvm_unreachable("either dependent or not canonical!");
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
}
llvm_unreachable("Unhandled Type::TypeClass");
}
@@ -5843,9 +6578,9 @@ static bool handleLValueToRValueBitCast(EvalInfo &Info, APValue &DestValue,
LValue SourceLValue;
APValue SourceRValue;
SourceLValue.setFrom(Info.Ctx, SourceValue);
- if (!handleLValueToRValueConversion(Info, BCE,
- BCE->getSubExpr()->getType().withConst(),
- SourceLValue, SourceRValue))
+ if (!handleLValueToRValueConversion(
+ Info, BCE, BCE->getSubExpr()->getType().withConst(), SourceLValue,
+ SourceRValue, /*WantObjectRepresentation=*/true))
return false;
// Read out SourceValue into a char buffer.
@@ -5984,10 +6719,16 @@ public:
return StmtVisitorTy::Visit(E->getExpr());
}
- // We cannot create any objects for which cleanups are required, so there is
- // nothing to do here; all cleanups must come from unevaluated subexpressions.
- bool VisitExprWithCleanups(const ExprWithCleanups *E)
- { return StmtVisitorTy::Visit(E->getSubExpr()); }
+ bool VisitExprWithCleanups(const ExprWithCleanups *E) {
+ FullExpressionRAII Scope(Info);
+ return StmtVisitorTy::Visit(E->getSubExpr()) && Scope.destroy();
+ }
+
+ // Temporaries are registered when created, so we don't care about
+ // CXXBindTemporaryExpr.
+ bool VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *E) {
+ return StmtVisitorTy::Visit(E->getSubExpr());
+ }
bool VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr *E) {
CCEDiag(E, diag::note_constexpr_invalid_cast) << 0;
@@ -6024,10 +6765,18 @@ public:
}
}
+ bool VisitCXXRewrittenBinaryOperator(const CXXRewrittenBinaryOperator *E) {
+ return StmtVisitorTy::Visit(E->getSemanticForm());
+ }
+
bool VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) {
// Evaluate and cache the common expression. We treat it as a temporary,
// even though it's not quite the same thing.
- if (!Evaluate(Info.CurrentCall->createTemporary(E->getOpaqueValue(), false),
+ LValue CommonLV;
+ if (!Evaluate(Info.CurrentCall->createTemporary(
+ E->getOpaqueValue(),
+ getStorageType(Info.Ctx, E->getOpaqueValue()), false,
+ CommonLV),
Info, E->getCommon()))
return false;
@@ -6047,6 +6796,8 @@ public:
// Always assume __builtin_constant_p(...) ? ... : ... is a potential
// constant expression; we can't check whether it's potentially foldable.
+ // FIXME: We should instead treat __builtin_constant_p as non-constant if
+ // it would return 'false' in this mode.
if (Info.checkingPotentialConstantExpression() && IsBcpCall)
return false;
@@ -6104,11 +6855,21 @@ public:
HasQualifier = ME->hasQualifier();
} else if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(Callee)) {
// Indirect bound member calls ('.*' or '->*').
- Member = dyn_cast_or_null<CXXMethodDecl>(
- HandleMemberPointerAccess(Info, BE, ThisVal, false));
+ const ValueDecl *D =
+ HandleMemberPointerAccess(Info, BE, ThisVal, false);
+ if (!D)
+ return false;
+ Member = dyn_cast<CXXMethodDecl>(D);
if (!Member)
return Error(Callee);
This = &ThisVal;
+ } else if (const auto *PDE = dyn_cast<CXXPseudoDestructorExpr>(Callee)) {
+ if (!Info.getLangOpts().CPlusPlus2a)
+ Info.CCEDiag(PDE, diag::note_constexpr_pseudo_destructor);
+ // FIXME: If pseudo-destructor calls ever start ending the lifetime of
+ // their callee, we should start calling HandleDestruction here.
+ // For now, we just evaluate the object argument and discard it.
+ return EvaluateObjectArgument(Info, PDE->getBase(), ThisVal);
} else
return Error(Callee);
FD = Member;
@@ -6177,6 +6938,17 @@ public:
FD = cast<CXXMethodDecl>(CorrespondingCallOpSpecialization);
} else
FD = LambdaCallOp;
+ } else if (FD->isReplaceableGlobalAllocationFunction()) {
+ if (FD->getDeclName().getCXXOverloadedOperator() == OO_New ||
+ FD->getDeclName().getCXXOverloadedOperator() == OO_Array_New) {
+ LValue Ptr;
+ if (!HandleOperatorNewCall(Info, E, Ptr))
+ return false;
+ Ptr.moveInto(Result);
+ return true;
+ } else {
+ return HandleOperatorDeleteCall(Info, E);
+ }
}
} else
return Error(E);
@@ -6192,11 +6964,20 @@ public:
return false;
} else {
// Check that the 'this' pointer points to an object of the right type.
- if (!checkNonVirtualMemberCallThisPointer(Info, E, *This))
+ // FIXME: If this is an assignment operator call, we may need to change
+ // the active union member before we check this.
+ if (!checkNonVirtualMemberCallThisPointer(Info, E, *This, NamedMember))
return false;
}
}
+ // Destructor calls are different enough that they have their own codepath.
+ if (auto *DD = dyn_cast<CXXDestructorDecl>(FD)) {
+ assert(This && "no 'this' pointer for destructor call");
+ return HandleDestruction(Info, E, *This,
+ Info.Ctx.getRecordType(DD->getParent()));
+ }
+
const FunctionDecl *Definition = nullptr;
Stmt *Body = FD->getBody(Definition);
@@ -6329,14 +7110,14 @@ public:
bool VisitStmtExpr(const StmtExpr *E) {
// We will have checked the full-expressions inside the statement expression
// when they were completed, and don't need to check them again now.
- if (Info.checkingForOverflow())
+ if (Info.checkingForUndefinedBehavior())
return Error(E);
- BlockScopeRAII Scope(Info);
const CompoundStmt *CS = E->getSubStmt();
if (CS->body_empty())
return true;
+ BlockScopeRAII Scope(Info);
for (CompoundStmt::const_body_iterator BI = CS->body_begin(),
BE = CS->body_end();
/**/; ++BI) {
@@ -6347,7 +7128,7 @@ public:
diag::note_constexpr_stmt_expr_unsupported);
return false;
}
- return this->Visit(FinalExpr);
+ return this->Visit(FinalExpr) && Scope.destroy();
}
APValue ReturnValue;
@@ -6440,7 +7221,7 @@ public:
const ValueDecl *MD = E->getMemberDecl();
if (const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl())) {
- assert(BaseTy->getAs<RecordType>()->getDecl()->getCanonicalDecl() ==
+ assert(BaseTy->castAs<RecordType>()->getDecl()->getCanonicalDecl() ==
FD->getParent()->getCanonicalDecl() && "record / field mismatch");
(void)BaseTy;
if (!HandleLValueMember(this->Info, E, Result, FD))
@@ -6696,16 +7477,14 @@ bool LValueExprEvaluator::VisitMaterializeTemporaryExpr(
*Value = APValue();
Result.set(E);
} else {
- Value = &createTemporary(E, E->getStorageDuration() == SD_Automatic, Result,
- *Info.CurrentCall);
+ Value = &Info.CurrentCall->createTemporary(
+ E, E->getType(), E->getStorageDuration() == SD_Automatic, Result);
}
QualType Type = Inner->getType();
// Materialize the temporary itself.
- if (!EvaluateInPlace(*Value, Info, Result, Inner) ||
- (E->getStorageDuration() == SD_Static &&
- !CheckConstantExpression(Info, E->getExprLoc(), Type, *Value))) {
+ if (!EvaluateInPlace(*Value, Info, Result, Inner)) {
*Value = APValue();
return false;
}
@@ -7035,8 +7814,7 @@ public:
return true;
}
bool ZeroInitialization(const Expr *E) {
- auto TargetVal = Info.Ctx.getTargetNullPointerValue(E->getType());
- Result.setNull(E->getType(), TargetVal);
+ Result.setNull(Info.Ctx, E->getType());
return true;
}
@@ -7097,6 +7875,8 @@ public:
return true;
}
+ bool VisitCXXNewExpr(const CXXNewExpr *E);
+
bool VisitSourceLocExpr(const SourceLocExpr *E) {
assert(E->isStringType() && "SourceLocExpr isn't a pointer type?");
APValue LValResult = E->EvaluateInContext(
@@ -7161,12 +7941,22 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
// permitted in constant expressions in C++11. Bitcasts from cv void* are
// also static_casts, but we disallow them as a resolution to DR1312.
if (!E->getType()->isVoidPointerType()) {
- Result.Designator.setInvalid();
- if (SubExpr->getType()->isVoidPointerType())
- CCEDiag(E, diag::note_constexpr_invalid_cast)
- << 3 << SubExpr->getType();
- else
- CCEDiag(E, diag::note_constexpr_invalid_cast) << 2;
+ if (!Result.InvalidBase && !Result.Designator.Invalid &&
+ !Result.IsNullPtr &&
+ Info.Ctx.hasSameUnqualifiedType(Result.Designator.getType(Info.Ctx),
+ E->getType()->getPointeeType()) &&
+ Info.getStdAllocatorCaller("allocate")) {
+ // Inside a call to std::allocator::allocate and friends, we permit
+ // casting from void* back to cv1 T* for a pointer that points to a
+ // cv2 T.
+ } else {
+ Result.Designator.setInvalid();
+ if (SubExpr->getType()->isVoidPointerType())
+ CCEDiag(E, diag::note_constexpr_invalid_cast)
+ << 3 << SubExpr->getType();
+ else
+ CCEDiag(E, diag::note_constexpr_invalid_cast) << 2;
+ }
}
if (E->getCastKind() == CK_AddressSpaceConversion && Result.IsNullPtr)
ZeroInitialization(E);
@@ -7229,8 +8019,8 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
if (!evaluateLValue(SubExpr, Result))
return false;
} else {
- APValue &Value = createTemporary(SubExpr, false, Result,
- *Info.CurrentCall);
+ APValue &Value = Info.CurrentCall->createTemporary(
+ SubExpr, SubExpr->getType(), false, Result);
if (!EvaluateInPlace(Value, Info, Result, SubExpr))
return false;
}
@@ -7403,6 +8193,8 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
return true;
}
+ case Builtin::BI__builtin_operator_new:
+ return HandleOperatorNewCall(Info, E, Result);
case Builtin::BI__builtin_launder:
return evaluatePointer(E->getArg(0), Result);
case Builtin::BIstrchr:
@@ -7638,6 +8430,8 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
while (true) {
APValue Val;
+ // FIXME: Set WantObjectRepresentation to true if we're copying a
+ // char-like type?
if (!handleLValueToRValueConversion(Info, E, T, Src, Val) ||
!handleAssignment(Info, E, Dest, T, Val))
return false;
@@ -7652,10 +8446,208 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
}
default:
- return visitNonBuiltinCallExpr(E);
+ break;
}
+
+ return visitNonBuiltinCallExpr(E);
}
+static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This,
+ APValue &Result, const InitListExpr *ILE,
+ QualType AllocType);
+
+bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
+ if (!Info.getLangOpts().CPlusPlus2a)
+ Info.CCEDiag(E, diag::note_constexpr_new);
+
+ // We cannot speculatively evaluate a delete expression.
+ if (Info.SpeculativeEvaluationDepth)
+ return false;
+
+ FunctionDecl *OperatorNew = E->getOperatorNew();
+
+ bool IsNothrow = false;
+ bool IsPlacement = false;
+ if (OperatorNew->isReservedGlobalPlacementOperator() &&
+ Info.CurrentCall->isStdFunction() && !E->isArray()) {
+ // FIXME Support array placement new.
+ assert(E->getNumPlacementArgs() == 1);
+ if (!EvaluatePointer(E->getPlacementArg(0), Result, Info))
+ return false;
+ if (Result.Designator.Invalid)
+ return false;
+ IsPlacement = true;
+ } else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
+ Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
+ << isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
+ return false;
+ } else if (E->getNumPlacementArgs()) {
+ // The only new-placement list we support is of the form (std::nothrow).
+ //
+ // FIXME: There is no restriction on this, but it's not clear that any
+ // other form makes any sense. We get here for cases such as:
+ //
+ // new (std::align_val_t{N}) X(int)
+ //
+ // (which should presumably be valid only if N is a multiple of
+ // alignof(int), and in any case can't be deallocated unless N is
+ // alignof(X) and X has new-extended alignment).
+ if (E->getNumPlacementArgs() != 1 ||
+ !E->getPlacementArg(0)->getType()->isNothrowT())
+ return Error(E, diag::note_constexpr_new_placement);
+
+ LValue Nothrow;
+ if (!EvaluateLValue(E->getPlacementArg(0), Nothrow, Info))
+ return false;
+ IsNothrow = true;
+ }
+
+ const Expr *Init = E->getInitializer();
+ const InitListExpr *ResizedArrayILE = nullptr;
+
+ QualType AllocType = E->getAllocatedType();
+ if (Optional<const Expr*> ArraySize = E->getArraySize()) {
+ const Expr *Stripped = *ArraySize;
+ for (; auto *ICE = dyn_cast<ImplicitCastExpr>(Stripped);
+ Stripped = ICE->getSubExpr())
+ if (ICE->getCastKind() != CK_NoOp &&
+ ICE->getCastKind() != CK_IntegralCast)
+ break;
+
+ llvm::APSInt ArrayBound;
+ if (!EvaluateInteger(Stripped, ArrayBound, Info))
+ return false;
+
+ // C++ [expr.new]p9:
+ // The expression is erroneous if:
+ // -- [...] its value before converting to size_t [or] applying the
+ // second standard conversion sequence is less than zero
+ if (ArrayBound.isSigned() && ArrayBound.isNegative()) {
+ if (IsNothrow)
+ return ZeroInitialization(E);
+
+ Info.FFDiag(*ArraySize, diag::note_constexpr_new_negative)
+ << ArrayBound << (*ArraySize)->getSourceRange();
+ return false;
+ }
+
+ // -- its value is such that the size of the allocated object would
+ // exceed the implementation-defined limit
+ if (ConstantArrayType::getNumAddressingBits(Info.Ctx, AllocType,
+ ArrayBound) >
+ ConstantArrayType::getMaxSizeBits(Info.Ctx)) {
+ if (IsNothrow)
+ return ZeroInitialization(E);
+
+ Info.FFDiag(*ArraySize, diag::note_constexpr_new_too_large)
+ << ArrayBound << (*ArraySize)->getSourceRange();
+ return false;
+ }
+
+ // -- the new-initializer is a braced-init-list and the number of
+ // array elements for which initializers are provided [...]
+ // exceeds the number of elements to initialize
+ if (Init) {
+ auto *CAT = Info.Ctx.getAsConstantArrayType(Init->getType());
+ assert(CAT && "unexpected type for array initializer");
+
+ unsigned Bits =
+ std::max(CAT->getSize().getBitWidth(), ArrayBound.getBitWidth());
+ llvm::APInt InitBound = CAT->getSize().zextOrSelf(Bits);
+ llvm::APInt AllocBound = ArrayBound.zextOrSelf(Bits);
+ if (InitBound.ugt(AllocBound)) {
+ if (IsNothrow)
+ return ZeroInitialization(E);
+
+ Info.FFDiag(*ArraySize, diag::note_constexpr_new_too_small)
+ << AllocBound.toString(10, /*Signed=*/false)
+ << InitBound.toString(10, /*Signed=*/false)
+ << (*ArraySize)->getSourceRange();
+ return false;
+ }
+
+ // If the sizes differ, we must have an initializer list, and we need
+ // special handling for this case when we initialize.
+ if (InitBound != AllocBound)
+ ResizedArrayILE = cast<InitListExpr>(Init);
+ }
+
+ AllocType = Info.Ctx.getConstantArrayType(AllocType, ArrayBound, nullptr,
+ ArrayType::Normal, 0);
+ } else {
+ assert(!AllocType->isArrayType() &&
+ "array allocation with non-array new");
+ }
+
+ APValue *Val;
+ if (IsPlacement) {
+ AccessKinds AK = AK_Construct;
+ struct FindObjectHandler {
+ EvalInfo &Info;
+ const Expr *E;
+ QualType AllocType;
+ const AccessKinds AccessKind;
+ APValue *Value;
+
+ typedef bool result_type;
+ bool failed() { return false; }
+ bool found(APValue &Subobj, QualType SubobjType) {
+ // FIXME: Reject the cases where [basic.life]p8 would not permit the
+ // old name of the object to be used to name the new object.
+ if (!Info.Ctx.hasSameUnqualifiedType(SubobjType, AllocType)) {
+ Info.FFDiag(E, diag::note_constexpr_placement_new_wrong_type) <<
+ SubobjType << AllocType;
+ return false;
+ }
+ Value = &Subobj;
+ return true;
+ }
+ bool found(APSInt &Value, QualType SubobjType) {
+ Info.FFDiag(E, diag::note_constexpr_construct_complex_elem);
+ return false;
+ }
+ bool found(APFloat &Value, QualType SubobjType) {
+ Info.FFDiag(E, diag::note_constexpr_construct_complex_elem);
+ return false;
+ }
+ } Handler = {Info, E, AllocType, AK, nullptr};
+
+ CompleteObject Obj = findCompleteObject(Info, E, AK, Result, AllocType);
+ if (!Obj || !findSubobject(Info, E, Obj, Result.Designator, Handler))
+ return false;
+
+ Val = Handler.Value;
+
+ // [basic.life]p1:
+ // The lifetime of an object o of type T ends when [...] the storage
+ // which the object occupies is [...] reused by an object that is not
+ // nested within o (6.6.2).
+ *Val = APValue();
+ } else {
+ // Perform the allocation and obtain a pointer to the resulting object.
+ Val = Info.createHeapAlloc(E, AllocType, Result);
+ if (!Val)
+ return false;
+ }
+
+ if (ResizedArrayILE) {
+ if (!EvaluateArrayNewInitList(Info, Result, *Val, ResizedArrayILE,
+ AllocType))
+ return false;
+ } else if (Init) {
+ if (!EvaluateInPlace(*Val, Info, Result, Init))
+ return false;
+ } else {
+ *Val = getDefaultInitValue(AllocType);
+ }
+
+ // Array new returns a pointer to the first element, not a pointer to the
+ // array.
+ if (auto *AT = AllocType->getAsArrayTypeUnsafe())
+ Result.addArray(Info, E, cast<ConstantArrayType>(AT));
+
+ return true;
+}
//===----------------------------------------------------------------------===//
// Member Pointer Evaluation
//===----------------------------------------------------------------------===//
@@ -7779,7 +8771,6 @@ namespace {
bool VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *E);
bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T);
bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E);
-
bool VisitBinCmp(const BinaryOperator *E);
};
}
@@ -8013,15 +9004,11 @@ bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E,
if (Result.hasValue())
return true;
- // We can get here in two different ways:
- // 1) We're performing value-initialization, and should zero-initialize
- // the object, or
- // 2) We're performing default-initialization of an object with a trivial
- // constexpr default constructor, in which case we should start the
- // lifetimes of all the base subobjects (there can be no data member
- // subobjects in this case) per [basic.life]p1.
- // Either way, ZeroInitialization is appropriate.
- return ZeroInitialization(E, T);
+ if (ZeroInit)
+ return ZeroInitialization(E, T);
+
+ Result = getDefaultInitValue(T);
+ return true;
}
const FunctionDecl *Definition = nullptr;
@@ -8121,9 +9108,8 @@ bool RecordExprEvaluator::VisitCXXStdInitializerListExpr(
bool RecordExprEvaluator::VisitLambdaExpr(const LambdaExpr *E) {
const CXXRecordDecl *ClosureClass = E->getLambdaClass();
- if (ClosureClass->isInvalidDecl()) return false;
-
- if (Info.checkingPotentialConstantExpression()) return true;
+ if (ClosureClass->isInvalidDecl())
+ return false;
const size_t NumFields =
std::distance(ClosureClass->field_begin(), ClosureClass->field_end());
@@ -8183,7 +9169,8 @@ public:
/// Visit an expression which constructs the value of this temporary.
bool VisitConstructExpr(const Expr *E) {
- APValue &Value = createTemporary(E, false, Result, *Info.CurrentCall);
+ APValue &Value =
+ Info.CurrentCall->createTemporary(E, E->getType(), false, Result);
return EvaluateInPlace(Value, Info, Result, E);
}
@@ -8383,7 +9370,7 @@ VectorExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
bool
VectorExprEvaluator::ZeroInitialization(const Expr *E) {
- const VectorType *VT = E->getType()->getAs<VectorType>();
+ const auto *VT = E->getType()->castAs<VectorType>();
QualType EltTy = VT->getElementType();
APValue ZeroElement;
if (EltTy->isIntegerType())
@@ -8441,14 +9428,16 @@ namespace {
bool VisitCallExpr(const CallExpr *E) {
return handleCallExpr(E, Result, &This);
}
- bool VisitInitListExpr(const InitListExpr *E);
+ bool VisitInitListExpr(const InitListExpr *E,
+ QualType AllocType = QualType());
bool VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E);
bool VisitCXXConstructExpr(const CXXConstructExpr *E);
bool VisitCXXConstructExpr(const CXXConstructExpr *E,
const LValue &Subobject,
APValue *Value, QualType Type);
- bool VisitStringLiteral(const StringLiteral *E) {
- expandStringLiteral(Info, E, Result);
+ bool VisitStringLiteral(const StringLiteral *E,
+ QualType AllocType = QualType()) {
+ expandStringLiteral(Info, E, Result, AllocType);
return true;
}
};
@@ -8460,6 +9449,15 @@ static bool EvaluateArray(const Expr *E, const LValue &This,
return ArrayExprEvaluator(Info, This, Result).Visit(E);
}
+static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This,
+ APValue &Result, const InitListExpr *ILE,
+ QualType AllocType) {
+ assert(ILE->isRValue() && ILE->getType()->isArrayType() &&
+ "not an array rvalue");
+ return ArrayExprEvaluator(Info, This, Result)
+ .VisitInitListExpr(ILE, AllocType);
+}
+
// Return true iff the given array filler may depend on the element index.
static bool MaybeElementDependentArrayFiller(const Expr *FillerExpr) {
// For now, just whitelist non-class value-initialization and initialization
@@ -8476,15 +9474,23 @@ static bool MaybeElementDependentArrayFiller(const Expr *FillerExpr) {
return true;
}
-bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
- const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(E->getType());
+bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E,
+ QualType AllocType) {
+ const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(
+ AllocType.isNull() ? E->getType() : AllocType);
if (!CAT)
return Error(E);
// C++11 [dcl.init.string]p1: A char array [...] can be initialized by [...]
// an appropriately-typed string literal enclosed in braces.
- if (E->isStringLiteralInit())
- return Visit(E->getInit(0));
+ if (E->isStringLiteralInit()) {
+ auto *SL = dyn_cast<StringLiteral>(E->getInit(0)->IgnoreParens());
+ // FIXME: Support ObjCEncodeExpr here once we support it in
+ // ArrayExprEvaluator generally.
+ if (!SL)
+ return Error(E);
+ return VisitStringLiteral(SL, AllocType);
+ }
bool Success = true;
@@ -8543,8 +9549,12 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
}
bool ArrayExprEvaluator::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E) {
+ LValue CommonLV;
if (E->getCommonExpr() &&
- !Evaluate(Info.CurrentCall->createTemporary(E->getCommonExpr(), false),
+ !Evaluate(Info.CurrentCall->createTemporary(
+ E->getCommonExpr(),
+ getStorageType(Info.Ctx, E->getCommonExpr()), false,
+ CommonLV),
Info, E->getCommonExpr()->getSourceExpr()))
return false;
@@ -8762,6 +9772,7 @@ public:
bool VisitCXXNoexceptExpr(const CXXNoexceptExpr *E);
bool VisitSizeOfPackExpr(const SizeOfPackExpr *E);
bool VisitSourceLocExpr(const SourceLocExpr *E);
+ bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E);
// FIXME: Missing: array subscript of vector, member of vector
};
@@ -8944,7 +9955,7 @@ EvaluateBuiltinClassifyType(QualType T, const LangOptions &LangOpts) {
#define DEPENDENT_TYPE(ID, BASE) case Type::ID:
#define NON_CANONICAL_TYPE(ID, BASE) case Type::ID:
#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(ID, BASE) case Type::ID:
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
case Type::Auto:
case Type::DeducedTemplateSpecialization:
llvm_unreachable("unexpected non-canonical or dependent type");
@@ -9008,6 +10019,9 @@ EvaluateBuiltinClassifyType(QualType T, const LangOptions &LangOpts) {
case BuiltinType::OCLClkEvent:
case BuiltinType::OCLQueue:
case BuiltinType::OCLReserveID:
+#define SVE_TYPE(Name, Id, SingletonId) \
+ case BuiltinType::Id:
+#include "clang/Basic/AArch64SVEACLETypes.def"
return GCCTypeClass::None;
case BuiltinType::Dependent:
@@ -9161,6 +10175,8 @@ static QualType getObjectType(APValue::LValueBase B) {
return E->getType();
} else if (B.is<TypeInfoLValue>()) {
return B.getTypeInfoType();
+ } else if (B.is<DynamicAllocLValue>()) {
+ return B.getDynamicAllocType();
}
return QualType();
@@ -9499,14 +10515,11 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
// size of the referenced object.
switch (Info.EvalMode) {
case EvalInfo::EM_ConstantExpression:
- case EvalInfo::EM_PotentialConstantExpression:
case EvalInfo::EM_ConstantFold:
- case EvalInfo::EM_EvaluateForOverflow:
case EvalInfo::EM_IgnoreSideEffects:
// Leave it to IR generation.
return Error(E);
case EvalInfo::EM_ConstantExpressionUnevaluated:
- case EvalInfo::EM_PotentialConstantExpressionUnevaluated:
// Reduce it to a constant now.
return Success((Type & 2) ? 0 : -1, E);
}
@@ -10834,7 +11847,7 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
Info.CCEDiag(E, diag::note_constexpr_pointer_subtraction_not_same_array);
QualType Type = E->getLHS()->getType();
- QualType ElementType = Type->getAs<PointerType>()->getPointeeType();
+ QualType ElementType = Type->castAs<PointerType>()->getPointeeType();
CharUnits ElementSize;
if (!HandleSizeof(Info, E->getExprLoc(), ElementType, ElementSize))
@@ -11242,6 +12255,12 @@ bool IntExprEvaluator::VisitCXXNoexceptExpr(const CXXNoexceptExpr *E) {
return Success(E->getValue(), E);
}
+bool IntExprEvaluator::VisitConceptSpecializationExpr(
+ const ConceptSpecializationExpr *E) {
+ return Success(E->isSatisfied(), E);
+}
+
+
bool FixedPointExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
switch (E->getOpcode()) {
default:
@@ -11731,9 +12750,9 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
if (!Visit(E->getSubExpr()))
return false;
- QualType To = E->getType()->getAs<ComplexType>()->getElementType();
+ QualType To = E->getType()->castAs<ComplexType>()->getElementType();
QualType From
- = E->getSubExpr()->getType()->getAs<ComplexType>()->getElementType();
+ = E->getSubExpr()->getType()->castAs<ComplexType>()->getElementType();
return HandleFloatToFloatCast(Info, E, From, To, Result.FloatReal) &&
HandleFloatToFloatCast(Info, E, From, To, Result.FloatImag);
@@ -11743,9 +12762,9 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
if (!Visit(E->getSubExpr()))
return false;
- QualType To = E->getType()->getAs<ComplexType>()->getElementType();
+ QualType To = E->getType()->castAs<ComplexType>()->getElementType();
QualType From
- = E->getSubExpr()->getType()->getAs<ComplexType>()->getElementType();
+ = E->getSubExpr()->getType()->castAs<ComplexType>()->getElementType();
Result.makeComplexInt();
return HandleFloatToIntCast(Info, E, From, Result.FloatReal,
To, Result.IntReal) &&
@@ -11767,9 +12786,9 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
if (!Visit(E->getSubExpr()))
return false;
- QualType To = E->getType()->getAs<ComplexType>()->getElementType();
+ QualType To = E->getType()->castAs<ComplexType>()->getElementType();
QualType From
- = E->getSubExpr()->getType()->getAs<ComplexType>()->getElementType();
+ = E->getSubExpr()->getType()->castAs<ComplexType>()->getElementType();
Result.IntReal = HandleIntToIntCast(Info, E, To, From, Result.IntReal);
Result.IntImag = HandleIntToIntCast(Info, E, To, From, Result.IntImag);
@@ -12143,17 +13162,98 @@ public:
bool VisitCallExpr(const CallExpr *E) {
switch (E->getBuiltinCallee()) {
- default:
- return ExprEvaluatorBaseTy::VisitCallExpr(E);
case Builtin::BI__assume:
case Builtin::BI__builtin_assume:
// The argument is not evaluated!
return true;
+
+ case Builtin::BI__builtin_operator_delete:
+ return HandleOperatorDeleteCall(Info, E);
+
+ default:
+ break;
}
+
+ return ExprEvaluatorBaseTy::VisitCallExpr(E);
}
+
+ bool VisitCXXDeleteExpr(const CXXDeleteExpr *E);
};
} // end anonymous namespace
+bool VoidExprEvaluator::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
+ // We cannot speculatively evaluate a delete expression.
+ if (Info.SpeculativeEvaluationDepth)
+ return false;
+
+ FunctionDecl *OperatorDelete = E->getOperatorDelete();
+ if (!OperatorDelete->isReplaceableGlobalAllocationFunction()) {
+ Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
+ << isa<CXXMethodDecl>(OperatorDelete) << OperatorDelete;
+ return false;
+ }
+
+ const Expr *Arg = E->getArgument();
+
+ LValue Pointer;
+ if (!EvaluatePointer(Arg, Pointer, Info))
+ return false;
+ if (Pointer.Designator.Invalid)
+ return false;
+
+ // Deleting a null pointer has no effect.
+ if (Pointer.isNullPointer()) {
+ // This is the only case where we need to produce an extension warning:
+ // the only other way we can succeed is if we find a dynamic allocation,
+ // and we will have warned when we allocated it in that case.
+ if (!Info.getLangOpts().CPlusPlus2a)
+ Info.CCEDiag(E, diag::note_constexpr_new);
+ return true;
+ }
+
+ Optional<DynAlloc *> Alloc = CheckDeleteKind(
+ Info, E, Pointer, E->isArrayForm() ? DynAlloc::ArrayNew : DynAlloc::New);
+ if (!Alloc)
+ return false;
+ QualType AllocType = Pointer.Base.getDynamicAllocType();
+
+ // For the non-array case, the designator must be empty if the static type
+ // does not have a virtual destructor.
+ if (!E->isArrayForm() && Pointer.Designator.Entries.size() != 0 &&
+ !hasVirtualDestructor(Arg->getType()->getPointeeType())) {
+ Info.FFDiag(E, diag::note_constexpr_delete_base_nonvirt_dtor)
+ << Arg->getType()->getPointeeType() << AllocType;
+ return false;
+ }
+
+ // For a class type with a virtual destructor, the selected operator delete
+ // is the one looked up when building the destructor.
+ if (!E->isArrayForm() && !E->isGlobalDelete()) {
+ const FunctionDecl *VirtualDelete = getVirtualOperatorDelete(AllocType);
+ if (VirtualDelete &&
+ !VirtualDelete->isReplaceableGlobalAllocationFunction()) {
+ Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
+ << isa<CXXMethodDecl>(VirtualDelete) << VirtualDelete;
+ return false;
+ }
+ }
+
+ if (!HandleDestruction(Info, E->getExprLoc(), Pointer.getLValueBase(),
+ (*Alloc)->Value, AllocType))
+ return false;
+
+ if (!Info.HeapAllocs.erase(Pointer.Base.dyn_cast<DynamicAllocLValue>())) {
+ // The element was already erased. This means the destructor call also
+ // deleted the object.
+ // FIXME: This probably results in undefined behavior before we get this
+ // far, and should be diagnosed elsewhere first.
+ Info.FFDiag(E, diag::note_constexpr_double_delete);
+ return false;
+ }
+
+ return true;
+}
+
static bool EvaluateVoid(const Expr *E, EvalInfo &Info) {
assert(E->isRValue() && E->getType()->isVoidType());
return VoidExprEvaluator(Info).Visit(E);
@@ -12203,13 +13303,14 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) {
return true;
} else if (T->isArrayType()) {
LValue LV;
- APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall);
+ APValue &Value =
+ Info.CurrentCall->createTemporary(E, T, false, LV);
if (!EvaluateArray(E, LV, Value, Info))
return false;
Result = Value;
} else if (T->isRecordType()) {
LValue LV;
- APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall);
+ APValue &Value = Info.CurrentCall->createTemporary(E, T, false, LV);
if (!EvaluateRecord(E, LV, Value, Info))
return false;
Result = Value;
@@ -12223,7 +13324,7 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) {
QualType Unqual = T.getAtomicUnqualifiedType();
if (Unqual->isArrayType() || Unqual->isRecordType()) {
LValue LV;
- APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall);
+ APValue &Value = Info.CurrentCall->createTemporary(E, Unqual, false, LV);
if (!EvaluateAtomic(E, &LV, Value, Info))
return false;
} else {
@@ -12273,6 +13374,18 @@ static bool EvaluateInPlace(APValue &Result, EvalInfo &Info, const LValue &This,
/// EvaluateAsRValue - Try to evaluate this expression, performing an implicit
/// lvalue-to-rvalue cast if it is an lvalue.
static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) {
+ if (Info.EnableNewConstInterp) {
+ auto &InterpCtx = Info.Ctx.getInterpContext();
+ switch (InterpCtx.evaluateAsRValue(Info, E, Result)) {
+ case interp::InterpResult::Success:
+ return true;
+ case interp::InterpResult::Fail:
+ return false;
+ case interp::InterpResult::Bail:
+ break;
+ }
+ }
+
if (E->getType().isNull())
return false;
@@ -12290,7 +13403,8 @@ static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) {
}
// Check this core constant expression is a constant expression.
- return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result);
+ return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result) &&
+ CheckMemoryLeaks(Info);
}
static bool FastEvaluateAsRValue(const Expr *Exp, Expr::EvalResult &Result,
@@ -12439,10 +13553,12 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx,
EvalInfo Info(Ctx, Result, EvalInfo::EM_ConstantFold);
Info.InConstantContext = InConstantContext;
LValue LV;
- if (!EvaluateLValue(this, LV, Info) || Result.HasSideEffects ||
+ CheckedTemporaries CheckedTemps;
+ if (!EvaluateLValue(this, LV, Info) || !Info.discardCleanups() ||
+ Result.HasSideEffects ||
!CheckLValueConstantExpression(Info, getExprLoc(),
Ctx.getLValueReferenceType(getType()), LV,
- Expr::EvaluateForCodeGen))
+ Expr::EvaluateForCodeGen, CheckedTemps))
return false;
LV.moveInto(Result.Val);
@@ -12458,11 +13574,15 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage,
EvalInfo Info(Ctx, Result, EM);
Info.InConstantContext = true;
- if (!::Evaluate(Result.Val, Info, this))
+ if (!::Evaluate(Result.Val, Info, this) || Result.HasSideEffects)
return false;
- return CheckConstantExpression(Info, getExprLoc(), getType(), Result.Val,
- Usage);
+ if (!Info.discardCleanups())
+ llvm_unreachable("Unhandled cleanup; missing full expression marker?");
+
+ return CheckConstantExpression(Info, getExprLoc(), getStorageType(Ctx, this),
+ Result.Val, Usage) &&
+ CheckMemoryLeaks(Info);
}
bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
@@ -12480,11 +13600,29 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
Expr::EvalStatus EStatus;
EStatus.Diag = &Notes;
- EvalInfo InitInfo(Ctx, EStatus, VD->isConstexpr()
+ EvalInfo Info(Ctx, EStatus, VD->isConstexpr()
? EvalInfo::EM_ConstantExpression
: EvalInfo::EM_ConstantFold);
- InitInfo.setEvaluatingDecl(VD, Value);
- InitInfo.InConstantContext = true;
+ Info.setEvaluatingDecl(VD, Value);
+ Info.InConstantContext = true;
+
+ SourceLocation DeclLoc = VD->getLocation();
+ QualType DeclTy = VD->getType();
+
+ if (Info.EnableNewConstInterp) {
+ auto &InterpCtx = const_cast<ASTContext &>(Ctx).getInterpContext();
+ switch (InterpCtx.evaluateAsInitializer(Info, VD, Value)) {
+ case interp::InterpResult::Fail:
+ // Bail out if an error was encountered.
+ return false;
+ case interp::InterpResult::Success:
+ // Evaluation succeeded and value was set.
+ return CheckConstantExpression(Info, DeclLoc, DeclTy, Value);
+ case interp::InterpResult::Bail:
+ // Evaluate the value again for the tree evaluator to use.
+ break;
+ }
+ }
LValue LVal;
LVal.set(VD);
@@ -12494,20 +13632,62 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
// zero-initialized before any other initialization takes place.
// This behavior is not present in C.
if (Ctx.getLangOpts().CPlusPlus && !VD->hasLocalStorage() &&
- !VD->getType()->isReferenceType()) {
- ImplicitValueInitExpr VIE(VD->getType());
- if (!EvaluateInPlace(Value, InitInfo, LVal, &VIE,
+ !DeclTy->isReferenceType()) {
+ ImplicitValueInitExpr VIE(DeclTy);
+ if (!EvaluateInPlace(Value, Info, LVal, &VIE,
/*AllowNonLiteralTypes=*/true))
return false;
}
- if (!EvaluateInPlace(Value, InitInfo, LVal, this,
+ if (!EvaluateInPlace(Value, Info, LVal, this,
/*AllowNonLiteralTypes=*/true) ||
EStatus.HasSideEffects)
return false;
- return CheckConstantExpression(InitInfo, VD->getLocation(), VD->getType(),
- Value);
+ // At this point, any lifetime-extended temporaries are completely
+ // initialized.
+ Info.performLifetimeExtension();
+
+ if (!Info.discardCleanups())
+ llvm_unreachable("Unhandled cleanup; missing full expression marker?");
+
+ return CheckConstantExpression(Info, DeclLoc, DeclTy, Value) &&
+ CheckMemoryLeaks(Info);
+}
+
+bool VarDecl::evaluateDestruction(
+ SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
+ assert(getEvaluatedValue() && !getEvaluatedValue()->isAbsent() &&
+ "cannot evaluate destruction of non-constant-initialized variable");
+
+ Expr::EvalStatus EStatus;
+ EStatus.Diag = &Notes;
+
+ // Make a copy of the value for the destructor to mutate.
+ APValue DestroyedValue = *getEvaluatedValue();
+
+ EvalInfo Info(getASTContext(), EStatus, EvalInfo::EM_ConstantExpression);
+ Info.setEvaluatingDecl(this, DestroyedValue,
+ EvalInfo::EvaluatingDeclKind::Dtor);
+ Info.InConstantContext = true;
+
+ SourceLocation DeclLoc = getLocation();
+ QualType DeclTy = getType();
+
+ LValue LVal;
+ LVal.set(this);
+
+ // FIXME: Consider storing whether this variable has constant destruction in
+ // the EvaluatedStmt so that CodeGen can query it.
+ if (!HandleDestruction(Info, DeclLoc, LVal.Base, DestroyedValue, DeclTy) ||
+ EStatus.HasSideEffects)
+ return false;
+
+ if (!Info.discardCleanups())
+ llvm_unreachable("Unhandled cleanup; missing full expression marker?");
+
+ ensureEvaluatedStmt()->HasConstantDestruction = true;
+ return true;
}
/// isEvaluatable - Call EvaluateAsRValue to see if this expression can be
@@ -12546,8 +13726,9 @@ APSInt Expr::EvaluateKnownConstIntCheckOverflow(
EvalResult EVResult;
EVResult.Diag = Diag;
- EvalInfo Info(Ctx, EVResult, EvalInfo::EM_EvaluateForOverflow);
+ EvalInfo Info(Ctx, EVResult, EvalInfo::EM_IgnoreSideEffects);
Info.InConstantContext = true;
+ Info.CheckingForUndefinedBehavior = true;
bool Result = ::EvaluateAsRValue(Info, this, EVResult.Val);
(void)Result;
@@ -12564,7 +13745,8 @@ void Expr::EvaluateForOverflow(const ASTContext &Ctx) const {
bool IsConst;
EvalResult EVResult;
if (!FastEvaluateAsRValue(this, EVResult, Ctx, IsConst)) {
- EvalInfo Info(Ctx, EVResult, EvalInfo::EM_EvaluateForOverflow);
+ EvalInfo Info(Ctx, EVResult, EvalInfo::EM_IgnoreSideEffects);
+ Info.CheckingForUndefinedBehavior = true;
(void)::EvaluateAsRValue(Info, this, EVResult.Val);
}
}
@@ -12752,6 +13934,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
case Expr::CXXBoolLiteralExprClass:
case Expr::CXXScalarValueInitExprClass:
case Expr::TypeTraitExprClass:
+ case Expr::ConceptSpecializationExprClass:
case Expr::ArrayTypeTraitExprClass:
case Expr::ExpressionTraitExprClass:
case Expr::CXXNoexceptExprClass:
@@ -12766,6 +13949,9 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
return CheckEvalInICE(E, Ctx);
return ICEDiag(IK_NotICE, E->getBeginLoc());
}
+ case Expr::CXXRewrittenBinaryOperatorClass:
+ return CheckICE(cast<CXXRewrittenBinaryOperator>(E)->getSemanticForm(),
+ Ctx);
case Expr::DeclRefExprClass: {
if (isa<EnumConstantDecl>(cast<DeclRefExpr>(E)->getDecl()))
return NoDiag();
@@ -13111,7 +14297,11 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result,
EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression);
APValue Scratch;
- bool IsConstExpr = ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch);
+ bool IsConstExpr =
+ ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch) &&
+ // FIXME: We don't produce a diagnostic for this, but the callers that
+ // call us on arbitrary full-expressions should generally not care.
+ Info.discardCleanups() && !Status.HasSideEffects;
if (!Diags.empty()) {
IsConstExpr = false;
@@ -13163,7 +14353,8 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
// Build fake call to Callee.
CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr,
ArgValues.data());
- return Evaluate(Value, Info, this) && !Info.EvalStatus.HasSideEffects;
+ return Evaluate(Value, Info, this) && Info.discardCleanups() &&
+ !Info.EvalStatus.HasSideEffects;
}
bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
@@ -13178,9 +14369,21 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
Expr::EvalStatus Status;
Status.Diag = &Diags;
- EvalInfo Info(FD->getASTContext(), Status,
- EvalInfo::EM_PotentialConstantExpression);
+ EvalInfo Info(FD->getASTContext(), Status, EvalInfo::EM_ConstantExpression);
Info.InConstantContext = true;
+ Info.CheckingPotentialConstantExpression = true;
+
+ // The constexpr VM attempts to compile all methods to bytecode here.
+ if (Info.EnableNewConstInterp) {
+ auto &InterpCtx = Info.Ctx.getInterpContext();
+ switch (InterpCtx.isPotentialConstantExpr(Info, FD)) {
+ case interp::InterpResult::Success:
+ case interp::InterpResult::Fail:
+ return Diags.empty();
+ case interp::InterpResult::Bail:
+ break;
+ }
+ }
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
const CXXRecordDecl *RD = MD ? MD->getParent()->getCanonicalDecl() : nullptr;
@@ -13219,8 +14422,9 @@ bool Expr::isPotentialConstantExprUnevaluated(Expr *E,
Status.Diag = &Diags;
EvalInfo Info(FD->getASTContext(), Status,
- EvalInfo::EM_PotentialConstantExpressionUnevaluated);
+ EvalInfo::EM_ConstantExpressionUnevaluated);
Info.InConstantContext = true;
+ Info.CheckingPotentialConstantExpression = true;
// Fabricate a call stack frame to give the arguments a plausible cover story.
ArrayRef<const Expr*> Args;
diff --git a/lib/AST/ExternalASTMerger.cpp b/lib/AST/ExternalASTMerger.cpp
index 61e657da7c919..f678c2dd3b59e 100644
--- a/lib/AST/ExternalASTMerger.cpp
+++ b/lib/AST/ExternalASTMerger.cpp
@@ -101,21 +101,103 @@ private:
ExternalASTMerger &Parent;
ASTImporter Reverse;
const ExternalASTMerger::OriginMap &FromOrigins;
-
+ /// @see ExternalASTMerger::ImporterSource::Temporary
+ bool TemporarySource;
+ /// Map of imported declarations back to the declarations they originated
+ /// from.
+ llvm::DenseMap<Decl *, Decl *> ToOrigin;
+ /// @see ExternalASTMerger::ImporterSource::Merger
+ ExternalASTMerger *SourceMerger;
llvm::raw_ostream &logs() { return Parent.logs(); }
public:
LazyASTImporter(ExternalASTMerger &_Parent, ASTContext &ToContext,
- FileManager &ToFileManager, ASTContext &FromContext,
- FileManager &FromFileManager,
- const ExternalASTMerger::OriginMap &_FromOrigins)
- : ASTImporter(ToContext, ToFileManager, FromContext, FromFileManager,
- /*MinimalImport=*/true),
- Parent(_Parent), Reverse(FromContext, FromFileManager, ToContext,
- ToFileManager, /*MinimalImport=*/true), FromOrigins(_FromOrigins) {}
+ FileManager &ToFileManager,
+ const ExternalASTMerger::ImporterSource &S,
+ std::shared_ptr<ASTImporterSharedState> SharedState)
+ : ASTImporter(ToContext, ToFileManager, S.getASTContext(),
+ S.getFileManager(),
+ /*MinimalImport=*/true, SharedState),
+ Parent(_Parent),
+ Reverse(S.getASTContext(), S.getFileManager(), ToContext, ToFileManager,
+ /*MinimalImport=*/true),
+ FromOrigins(S.getOriginMap()), TemporarySource(S.isTemporary()),
+ SourceMerger(S.getMerger()) {}
+
+ llvm::Expected<Decl *> ImportImpl(Decl *FromD) override {
+ if (!TemporarySource || !SourceMerger)
+ return ASTImporter::ImportImpl(FromD);
+
+ // If we get here, then this source is importing from a temporary ASTContext
+ // that also has another ExternalASTMerger attached. It could be
+ // possible that the current ExternalASTMerger and the temporary ASTContext
+ // share a common ImporterSource, which means that the temporary
+ // AST could contain declarations that were imported from a source
+ // that this ExternalASTMerger can access directly. Instead of importing
+ // such declarations from the temporary ASTContext, they should instead
+ // be directly imported by this ExternalASTMerger from the original
+ // source. This way the ExternalASTMerger can safely do a minimal import
+ // without creating incomplete declarations originated from a temporary
+ // ASTContext. If we would try to complete such declarations later on, we
+ // would fail to do so as their temporary AST could be deleted (which means
+ // that the missing parts of the minimally imported declaration in that
+ // ASTContext were also deleted).
+ //
+ // The following code tracks back any declaration that needs to be
+ // imported from the temporary ASTContext to a persistent ASTContext.
+ // Then the ExternalASTMerger tries to import from the persistent
+ // ASTContext directly by using the associated ASTImporter. If that
+ // succeeds, this ASTImporter just maps the declarations imported by
+ // the other (persistent) ASTImporter to this (temporary) ASTImporter.
+ // The steps can be visualized like this:
+ //
+ // Target AST <--- 3. Indirect import --- Persistent AST
+ // ^ of persistent decl ^
+ // | |
+ // 1. Current import 2. Tracking back to persistent decl
+ // 4. Map persistent decl |
+ // & pretend we imported. |
+ // | |
+ // Temporary AST -------------------------------'
+
+ // First, ask the ExternalASTMerger of the source where the temporary
+ // declaration originated from.
+ Decl *Persistent = SourceMerger->FindOriginalDecl(FromD);
+ // FromD isn't from a persistent AST, so just do a normal import.
+ if (!Persistent)
+ return ASTImporter::ImportImpl(FromD);
+ // Now ask the current ExternalASTMerger to try import the persistent
+ // declaration into the target.
+ ASTContext &PersistentCtx = Persistent->getASTContext();
+ ASTImporter &OtherImporter = Parent.ImporterForOrigin(PersistentCtx);
+ // Check that we never end up in the current Importer again.
+ assert((&PersistentCtx != &getFromContext()) && (&OtherImporter != this) &&
+ "Delegated to same Importer?");
+ auto DeclOrErr = OtherImporter.Import(Persistent);
+ // Errors when importing the persistent decl are treated as if we
+ // had errors with importing the temporary decl.
+ if (!DeclOrErr)
+ return DeclOrErr.takeError();
+ Decl *D = *DeclOrErr;
+ // Tell the current ASTImporter that this has already been imported
+ // to prevent any further queries for the temporary decl.
+ MapImported(FromD, D);
+ return D;
+ }
+
+ /// Implements the ASTImporter interface for tracking back a declaration
+ /// to its original declaration it came from.
+ Decl *GetOriginalDecl(Decl *To) override {
+ auto It = ToOrigin.find(To);
+ if (It != ToOrigin.end())
+ return It->second;
+ return nullptr;
+ }
/// Whenever a DeclContext is imported, ensure that ExternalASTSource's origin
/// map is kept up to date. Also set the appropriate flags.
void Imported(Decl *From, Decl *To) override {
+ ToOrigin[To] = From;
+
if (auto *ToDC = dyn_cast<DeclContext>(To)) {
const bool LoggingEnabled = Parent.LoggingEnabled();
if (LoggingEnabled)
@@ -314,28 +396,40 @@ void ExternalASTMerger::RecordOriginImpl(const DeclContext *ToDC, DCOrigin Origi
ExternalASTMerger::ExternalASTMerger(const ImporterTarget &Target,
llvm::ArrayRef<ImporterSource> Sources) : LogStream(&llvm::nulls()), Target(Target) {
+ SharedState = std::make_shared<ASTImporterSharedState>(
+ *Target.AST.getTranslationUnitDecl());
AddSources(Sources);
}
+Decl *ExternalASTMerger::FindOriginalDecl(Decl *D) {
+ assert(&D->getASTContext() == &Target.AST);
+ for (const auto &I : Importers)
+ if (auto Result = I->GetOriginalDecl(D))
+ return Result;
+ return nullptr;
+}
+
void ExternalASTMerger::AddSources(llvm::ArrayRef<ImporterSource> Sources) {
for (const ImporterSource &S : Sources) {
- assert(&S.AST != &Target.AST);
- Importers.push_back(llvm::make_unique<LazyASTImporter>(
- *this, Target.AST, Target.FM, S.AST, S.FM, S.OM));
+ assert(&S.getASTContext() != &Target.AST);
+ // Check that the associated merger actually imports into the source AST.
+ assert(!S.getMerger() || &S.getMerger()->Target.AST == &S.getASTContext());
+ Importers.push_back(std::make_unique<LazyASTImporter>(
+ *this, Target.AST, Target.FM, S, SharedState));
}
}
void ExternalASTMerger::RemoveSources(llvm::ArrayRef<ImporterSource> Sources) {
if (LoggingEnabled())
for (const ImporterSource &S : Sources)
- logs() << "(ExternalASTMerger*)" << (void*)this
- << " removing source (ASTContext*)" << (void*)&S.AST
+ logs() << "(ExternalASTMerger*)" << (void *)this
+ << " removing source (ASTContext*)" << (void *)&S.getASTContext()
<< "\n";
Importers.erase(
std::remove_if(Importers.begin(), Importers.end(),
[&Sources](std::unique_ptr<ASTImporter> &Importer) -> bool {
for (const ImporterSource &S : Sources) {
- if (&Importer->getFromContext() == &S.AST)
+ if (&Importer->getFromContext() == &S.getASTContext())
return true;
}
return false;
@@ -345,7 +439,7 @@ void ExternalASTMerger::RemoveSources(llvm::ArrayRef<ImporterSource> Sources) {
std::pair<const DeclContext *, DCOrigin> Origin = *OI;
bool Erase = false;
for (const ImporterSource &S : Sources) {
- if (&S.AST == Origin.second.AST) {
+ if (&S.getASTContext() == Origin.second.AST) {
Erase = true;
break;
}
diff --git a/lib/AST/FormatString.cpp b/lib/AST/FormatString.cpp
index 578d5bc567338..fcc0b3b11e259 100644
--- a/lib/AST/FormatString.cpp
+++ b/lib/AST/FormatString.cpp
@@ -359,6 +359,7 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const {
case BuiltinType::SChar:
case BuiltinType::UChar:
case BuiltinType::Char_U:
+ case BuiltinType::Bool:
return Match;
}
return NoMatch;
@@ -386,6 +387,9 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const {
case BuiltinType::SChar:
case BuiltinType::Char_U:
case BuiltinType::UChar:
+ case BuiltinType::Bool:
+ if (T == C.UnsignedShortTy || T == C.ShortTy)
+ return NoMatchTypeConfusion;
return T == C.UnsignedCharTy || T == C.SignedCharTy ? Match
: NoMatch;
case BuiltinType::Short:
diff --git a/lib/AST/FormatStringParsing.h b/lib/AST/FormatStringParsing.h
index 9da829adcb494..764e5d46394d7 100644
--- a/lib/AST/FormatStringParsing.h
+++ b/lib/AST/FormatStringParsing.h
@@ -1,3 +1,16 @@
+//===----- FormatStringParsing.h - Format String Parsing --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This provides some shared functions between printf and scanf format string
+// parsing code.
+//
+//===----------------------------------------------------------------------===//
+
#ifndef LLVM_CLANG_LIB_ANALYSIS_FORMATSTRINGPARSING_H
#define LLVM_CLANG_LIB_ANALYSIS_FORMATSTRINGPARSING_H
diff --git a/lib/AST/InheritViz.cpp b/lib/AST/InheritViz.cpp
index 4b3d5bee5631e..2ed0ce1c79c5b 100644
--- a/lib/AST/InheritViz.cpp
+++ b/lib/AST/InheritViz.cpp
@@ -90,8 +90,8 @@ void InheritanceHierarchyWriter::WriteNode(QualType Type, bool FromVirtual) {
Out << " \"];\n";
// Display the base classes.
- const CXXRecordDecl *Decl
- = static_cast<const CXXRecordDecl *>(Type->getAs<RecordType>()->getDecl());
+ const auto *Decl =
+ static_cast<const CXXRecordDecl *>(Type->castAs<RecordType>()->getDecl());
for (const auto &Base : Decl->bases()) {
QualType CanonBaseType = Context.getCanonicalType(Base.getType());
diff --git a/lib/AST/Interp/Block.cpp b/lib/AST/Interp/Block.cpp
new file mode 100644
index 0000000000000..5fc93eb39f4ea
--- /dev/null
+++ b/lib/AST/Interp/Block.cpp
@@ -0,0 +1,87 @@
+//===--- Block.cpp - Allocated blocks for the interpreter -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the classes describing allocated blocks.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Block.h"
+#include "Pointer.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+
+
+void Block::addPointer(Pointer *P) {
+ if (IsStatic)
+ return;
+ if (Pointers)
+ Pointers->Prev = P;
+ P->Next = Pointers;
+ P->Prev = nullptr;
+ Pointers = P;
+}
+
+void Block::removePointer(Pointer *P) {
+ if (IsStatic)
+ return;
+ if (Pointers == P)
+ Pointers = P->Next;
+ if (P->Prev)
+ P->Prev->Next = P->Next;
+ if (P->Next)
+ P->Next->Prev = P->Prev;
+}
+
+void Block::cleanup() {
+ if (Pointers == nullptr && IsDead)
+ (reinterpret_cast<DeadBlock *>(this + 1) - 1)->free();
+}
+
+void Block::movePointer(Pointer *From, Pointer *To) {
+ if (IsStatic)
+ return;
+ To->Prev = From->Prev;
+ if (To->Prev)
+ To->Prev->Next = To;
+ To->Next = From->Next;
+ if (To->Next)
+ To->Next->Prev = To;
+ if (Pointers == From)
+ Pointers = To;
+
+ From->Prev = nullptr;
+ From->Next = nullptr;
+}
+
+DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
+ : Root(Root), B(Blk->Desc, Blk->IsStatic, Blk->IsExtern, /*isDead=*/true) {
+ // Add the block to the chain of dead blocks.
+ if (Root)
+ Root->Prev = this;
+
+ Next = Root;
+ Prev = nullptr;
+ Root = this;
+
+ // Transfer pointers.
+ B.Pointers = Blk->Pointers;
+ for (Pointer *P = Blk->Pointers; P; P = P->Next)
+ P->Pointee = &B;
+}
+
+void DeadBlock::free() {
+ if (Prev)
+ Prev->Next = Next;
+ if (Next)
+ Next->Prev = Prev;
+ if (Root == this)
+ Root = Next;
+ ::free(this);
+}
diff --git a/lib/AST/Interp/Block.h b/lib/AST/Interp/Block.h
new file mode 100644
index 0000000000000..97fb9a3ca0961
--- /dev/null
+++ b/lib/AST/Interp/Block.h
@@ -0,0 +1,140 @@
+//===--- Block.h - Allocated blocks for the interpreter ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the classes describing allocated blocks.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_BLOCK_H
+#define LLVM_CLANG_AST_INTERP_BLOCK_H
+
+#include "Descriptor.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ComparisonCategories.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace interp {
+class Block;
+class DeadBlock;
+class Context;
+class InterpState;
+class Pointer;
+class Function;
+enum PrimType : unsigned;
+
+/// A memory block, either on the stack or in the heap.
+///
+/// The storage described by the block immediately follows it in memory.
+class Block {
+public:
+ // Creates a new block.
+ Block(const llvm::Optional<unsigned> &DeclID, Descriptor *Desc,
+ bool IsStatic = false, bool IsExtern = false)
+ : DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) {}
+
+ Block(Descriptor *Desc, bool IsStatic = false, bool IsExtern = false)
+ : DeclID((unsigned)-1), IsStatic(IsStatic), IsExtern(IsExtern),
+ Desc(Desc) {}
+
+ /// Returns the block's descriptor.
+ Descriptor *getDescriptor() const { return Desc; }
+ /// Checks if the block has any live pointers.
+ bool hasPointers() const { return Pointers; }
+ /// Checks if the block is extern.
+ bool isExtern() const { return IsExtern; }
+ /// Checks if the block has static storage duration.
+ bool isStatic() const { return IsStatic; }
+ /// Checks if the block is temporary.
+ bool isTemporary() const { return Desc->IsTemporary; }
+ /// Returns the size of the block.
+ InterpSize getSize() const { return Desc->getAllocSize(); }
+ /// Returns the declaration ID.
+ llvm::Optional<unsigned> getDeclID() const { return DeclID; }
+
+ /// Returns a pointer to the stored data.
+ char *data() { return reinterpret_cast<char *>(this + 1); }
+
+ /// Returns a view over the data.
+ template <typename T>
+ T &deref() { return *reinterpret_cast<T *>(data()); }
+
+ /// Invokes the constructor.
+ void invokeCtor() {
+ std::memset(data(), 0, getSize());
+ if (Desc->CtorFn)
+ Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
+ /*isActive=*/true, Desc);
+ }
+
+protected:
+ friend class Pointer;
+ friend class DeadBlock;
+ friend class InterpState;
+
+ Block(Descriptor *Desc, bool IsExtern, bool IsStatic, bool IsDead)
+ : IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true), Desc(Desc) {}
+
+ // Deletes a dead block at the end of its lifetime.
+ void cleanup();
+
+ // Pointer chain management.
+ void addPointer(Pointer *P);
+ void removePointer(Pointer *P);
+ void movePointer(Pointer *From, Pointer *To);
+
+ /// Start of the chain of pointers.
+ Pointer *Pointers = nullptr;
+ /// Unique identifier of the declaration.
+ llvm::Optional<unsigned> DeclID;
+ /// Flag indicating if the block has static storage duration.
+ bool IsStatic = false;
+ /// Flag indicating if the block is an extern.
+ bool IsExtern = false;
+ /// Flag indicating if the pointer is dead.
+ bool IsDead = false;
+ /// Pointer to the stack slot descriptor.
+ Descriptor *Desc;
+};
+
+/// Descriptor for a dead block.
+///
+/// Dead blocks are chained in a double-linked list to deallocate them
+/// whenever pointers become dead.
+class DeadBlock {
+public:
+ /// Copies the block.
+ DeadBlock(DeadBlock *&Root, Block *Blk);
+
+ /// Returns a pointer to the stored data.
+ char *data() { return B.data(); }
+
+private:
+ friend class Block;
+ friend class InterpState;
+
+ void free();
+
+ /// Root pointer of the list.
+ DeadBlock *&Root;
+ /// Previous block in the list.
+ DeadBlock *Prev;
+ /// Next block in the list.
+ DeadBlock *Next;
+
+ /// Actual block storing data and tracking pointers.
+ Block B;
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/Boolean.h b/lib/AST/Interp/Boolean.h
new file mode 100644
index 0000000000000..3e6c8b5da9f05
--- /dev/null
+++ b/lib/AST/Interp/Boolean.h
@@ -0,0 +1,148 @@
+//===--- Boolean.h - Wrapper for boolean types for the VM -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_BOOLEAN_H
+#define LLVM_CLANG_AST_INTERP_BOOLEAN_H
+
+#include <cstddef>
+#include <cstdint>
+#include "Integral.h"
+#include "clang/AST/APValue.h"
+#include "clang/AST/ComparisonCategories.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace interp {
+
+/// Wrapper around boolean types.
+class Boolean {
+ private:
+ /// Underlying boolean.
+ bool V;
+
+ /// Construct a wrapper from a boolean.
+ explicit Boolean(bool V) : V(V) {}
+
+ public:
+ /// Zero-initializes a boolean.
+ Boolean() : V(false) {}
+
+ bool operator<(Boolean RHS) const { return V < RHS.V; }
+ bool operator>(Boolean RHS) const { return V > RHS.V; }
+ bool operator<=(Boolean RHS) const { return V <= RHS.V; }
+ bool operator>=(Boolean RHS) const { return V >= RHS.V; }
+ bool operator==(Boolean RHS) const { return V == RHS.V; }
+ bool operator!=(Boolean RHS) const { return V != RHS.V; }
+
+ bool operator>(unsigned RHS) const { return static_cast<unsigned>(V) > RHS; }
+
+ Boolean operator-() const { return Boolean(V); }
+ Boolean operator~() const { return Boolean(true); }
+
+ explicit operator unsigned() const { return V; }
+ explicit operator int64_t() const { return V; }
+ explicit operator uint64_t() const { return V; }
+
+ APSInt toAPSInt() const {
+ return APSInt(APInt(1, static_cast<uint64_t>(V), false), true);
+ }
+ APSInt toAPSInt(unsigned NumBits) const {
+ return APSInt(toAPSInt().zextOrTrunc(NumBits), true);
+ }
+ APValue toAPValue() const { return APValue(toAPSInt()); }
+
+ Boolean toUnsigned() const { return *this; }
+
+ constexpr static unsigned bitWidth() { return true; }
+ bool isZero() const { return !V; }
+ bool isMin() const { return isZero(); }
+
+ constexpr static bool isMinusOne() { return false; }
+
+ constexpr static bool isSigned() { return false; }
+
+ constexpr static bool isNegative() { return false; }
+ constexpr static bool isPositive() { return !isNegative(); }
+
+ ComparisonCategoryResult compare(const Boolean &RHS) const {
+ return Compare(V, RHS.V);
+ }
+
+ unsigned countLeadingZeros() const { return V ? 0 : 1; }
+
+ Boolean truncate(unsigned TruncBits) const { return *this; }
+
+ void print(llvm::raw_ostream &OS) const { OS << (V ? "true" : "false"); }
+
+ static Boolean min(unsigned NumBits) { return Boolean(false); }
+ static Boolean max(unsigned NumBits) { return Boolean(true); }
+
+ template <typename T>
+ static typename std::enable_if<std::is_integral<T>::value, Boolean>::type
+ from(T Value) {
+ return Boolean(Value != 0);
+ }
+
+ template <unsigned SrcBits, bool SrcSign>
+ static typename std::enable_if<SrcBits != 0, Boolean>::type from(
+ Integral<SrcBits, SrcSign> Value) {
+ return Boolean(!Value.isZero());
+ }
+
+ template <bool SrcSign>
+ static Boolean from(Integral<0, SrcSign> Value) {
+ return Boolean(!Value.isZero());
+ }
+
+ static Boolean zero() { return from(false); }
+
+ template <typename T>
+ static Boolean from(T Value, unsigned NumBits) {
+ return Boolean(Value);
+ }
+
+ static bool inRange(int64_t Value, unsigned NumBits) {
+ return Value == 0 || Value == 1;
+ }
+
+ static bool increment(Boolean A, Boolean *R) {
+ *R = Boolean(true);
+ return false;
+ }
+
+ static bool decrement(Boolean A, Boolean *R) {
+ llvm_unreachable("Cannot decrement booleans");
+ }
+
+ static bool add(Boolean A, Boolean B, unsigned OpBits, Boolean *R) {
+ *R = Boolean(A.V || B.V);
+ return false;
+ }
+
+ static bool sub(Boolean A, Boolean B, unsigned OpBits, Boolean *R) {
+ *R = Boolean(A.V ^ B.V);
+ return false;
+ }
+
+ static bool mul(Boolean A, Boolean B, unsigned OpBits, Boolean *R) {
+ *R = Boolean(A.V && B.V);
+ return false;
+ }
+};
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Boolean &B) {
+ B.print(OS);
+ return OS;
+}
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/ByteCodeEmitter.cpp b/lib/AST/Interp/ByteCodeEmitter.cpp
new file mode 100644
index 0000000000000..7a4569820a1d1
--- /dev/null
+++ b/lib/AST/Interp/ByteCodeEmitter.cpp
@@ -0,0 +1,175 @@
+//===--- ByteCodeEmitter.cpp - Instruction emitter for the VM ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ByteCodeEmitter.h"
+#include "Context.h"
+#include "Opcode.h"
+#include "Program.h"
+#include "clang/AST/DeclCXX.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+using APSInt = llvm::APSInt;
+using Error = llvm::Error;
+
+Expected<Function *> ByteCodeEmitter::compileFunc(const FunctionDecl *F) {
+ // Do not try to compile undefined functions.
+ if (!F->isDefined(F) || (!F->hasBody() && F->willHaveBody()))
+ return nullptr;
+
+ // Set up argument indices.
+ unsigned ParamOffset = 0;
+ SmallVector<PrimType, 8> ParamTypes;
+ llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors;
+
+ // If the return is not a primitive, a pointer to the storage where the value
+ // is initialized in is passed as the first argument.
+ QualType Ty = F->getReturnType();
+ if (!Ty->isVoidType() && !Ctx.classify(Ty)) {
+ ParamTypes.push_back(PT_Ptr);
+ ParamOffset += align(primSize(PT_Ptr));
+ }
+
+ // Assign descriptors to all parameters.
+ // Composite objects are lowered to pointers.
+ for (const ParmVarDecl *PD : F->parameters()) {
+ PrimType Ty;
+ if (llvm::Optional<PrimType> T = Ctx.classify(PD->getType())) {
+ Ty = *T;
+ } else {
+ Ty = PT_Ptr;
+ }
+
+ Descriptor *Desc = P.createDescriptor(PD, Ty);
+ ParamDescriptors.insert({ParamOffset, {Ty, Desc}});
+ Params.insert({PD, ParamOffset});
+ ParamOffset += align(primSize(Ty));
+ ParamTypes.push_back(Ty);
+ }
+
+ // Create a handle over the emitted code.
+ Function *Func = P.createFunction(F, ParamOffset, std::move(ParamTypes),
+ std::move(ParamDescriptors));
+ // Compile the function body.
+ if (!F->isConstexpr() || !visitFunc(F)) {
+ // Return a dummy function if compilation failed.
+ if (BailLocation)
+ return llvm::make_error<ByteCodeGenError>(*BailLocation);
+ else
+ return Func;
+ } else {
+ // Create scopes from descriptors.
+ llvm::SmallVector<Scope, 2> Scopes;
+ for (auto &DS : Descriptors) {
+ Scopes.emplace_back(std::move(DS));
+ }
+
+ // Set the function's code.
+ Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap),
+ std::move(Scopes));
+ return Func;
+ }
+}
+
+Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) {
+ NextLocalOffset += sizeof(Block);
+ unsigned Location = NextLocalOffset;
+ NextLocalOffset += align(D->getAllocSize());
+ return {Location, D};
+}
+
+void ByteCodeEmitter::emitLabel(LabelTy Label) {
+ const size_t Target = Code.size();
+ LabelOffsets.insert({Label, Target});
+ auto It = LabelRelocs.find(Label);
+ if (It != LabelRelocs.end()) {
+ for (unsigned Reloc : It->second) {
+ using namespace llvm::support;
+
+ /// Rewrite the operand of all jumps to this label.
+ void *Location = Code.data() + Reloc - sizeof(int32_t);
+ const int32_t Offset = Target - static_cast<int64_t>(Reloc);
+ endian::write<int32_t, endianness::native, 1>(Location, Offset);
+ }
+ LabelRelocs.erase(It);
+ }
+}
+
+int32_t ByteCodeEmitter::getOffset(LabelTy Label) {
+ // Compute the PC offset which the jump is relative to.
+ const int64_t Position = Code.size() + sizeof(Opcode) + sizeof(int32_t);
+
+ // If target is known, compute jump offset.
+ auto It = LabelOffsets.find(Label);
+ if (It != LabelOffsets.end()) {
+ return It->second - Position;
+ }
+
+ // Otherwise, record relocation and return dummy offset.
+ LabelRelocs[Label].push_back(Position);
+ return 0ull;
+}
+
+bool ByteCodeEmitter::bail(const SourceLocation &Loc) {
+ if (!BailLocation)
+ BailLocation = Loc;
+ return false;
+}
+
+template <typename... Tys>
+bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) {
+ bool Success = true;
+
+ /// Helper to write bytecode and bail out if 32-bit offsets become invalid.
+ auto emit = [this, &Success](const char *Data, size_t Size) {
+ if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
+ Success = false;
+ return;
+ }
+ Code.insert(Code.end(), Data, Data + Size);
+ };
+
+ /// The opcode is followed by arguments. The source info is
+ /// attached to the address after the opcode.
+ emit(reinterpret_cast<const char *>(&Op), sizeof(Opcode));
+ if (SI)
+ SrcMap.emplace_back(Code.size(), SI);
+
+ /// The initializer list forces the expression to be evaluated
+ /// for each argument in the variadic template, in order.
+ (void)std::initializer_list<int>{
+ (emit(reinterpret_cast<const char *>(&Args), sizeof(Args)), 0)...};
+
+ return Success;
+}
+
+bool ByteCodeEmitter::jumpTrue(const LabelTy &Label) {
+ return emitJt(getOffset(Label), SourceInfo{});
+}
+
+bool ByteCodeEmitter::jumpFalse(const LabelTy &Label) {
+ return emitJf(getOffset(Label), SourceInfo{});
+}
+
+bool ByteCodeEmitter::jump(const LabelTy &Label) {
+ return emitJmp(getOffset(Label), SourceInfo{});
+}
+
+bool ByteCodeEmitter::fallthrough(const LabelTy &Label) {
+ emitLabel(Label);
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Opcode emitters
+//===----------------------------------------------------------------------===//
+
+#define GET_LINK_IMPL
+#include "Opcodes.inc"
+#undef GET_LINK_IMPL
diff --git a/lib/AST/Interp/ByteCodeEmitter.h b/lib/AST/Interp/ByteCodeEmitter.h
new file mode 100644
index 0000000000000..03452a350c960
--- /dev/null
+++ b/lib/AST/Interp/ByteCodeEmitter.h
@@ -0,0 +1,112 @@
+//===--- ByteCodeEmitter.h - Instruction emitter for the VM ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the instruction emitters.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_LINKEMITTER_H
+#define LLVM_CLANG_AST_INTERP_LINKEMITTER_H
+
+#include "ByteCodeGenError.h"
+#include "Context.h"
+#include "InterpStack.h"
+#include "InterpState.h"
+#include "PrimType.h"
+#include "Program.h"
+#include "Source.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace interp {
+class Context;
+class SourceInfo;
+enum Opcode : uint32_t;
+
+/// An emitter which links the program to bytecode for later use.
+class ByteCodeEmitter {
+protected:
+ using LabelTy = uint32_t;
+ using AddrTy = uintptr_t;
+ using Local = Scope::Local;
+
+public:
+ /// Compiles the function into the module.
+ llvm::Expected<Function *> compileFunc(const FunctionDecl *F);
+
+protected:
+ ByteCodeEmitter(Context &Ctx, Program &P) : Ctx(Ctx), P(P) {}
+
+ virtual ~ByteCodeEmitter() {}
+
+ /// Define a label.
+ void emitLabel(LabelTy Label);
+ /// Create a label.
+ LabelTy getLabel() { return ++NextLabel; }
+
+ /// Methods implemented by the compiler.
+ virtual bool visitFunc(const FunctionDecl *E) = 0;
+ virtual bool visitExpr(const Expr *E) = 0;
+ virtual bool visitDecl(const VarDecl *E) = 0;
+
+ /// Bails out if a given node cannot be compiled.
+ bool bail(const Stmt *S) { return bail(S->getBeginLoc()); }
+ bool bail(const Decl *D) { return bail(D->getBeginLoc()); }
+ bool bail(const SourceLocation &Loc);
+
+ /// Emits jumps.
+ bool jumpTrue(const LabelTy &Label);
+ bool jumpFalse(const LabelTy &Label);
+ bool jump(const LabelTy &Label);
+ bool fallthrough(const LabelTy &Label);
+
+ /// Callback for local registration.
+ Local createLocal(Descriptor *D);
+
+ /// Parameter indices.
+ llvm::DenseMap<const ParmVarDecl *, unsigned> Params;
+ /// Local descriptors.
+ llvm::SmallVector<SmallVector<Local, 8>, 2> Descriptors;
+
+private:
+ /// Current compilation context.
+ Context &Ctx;
+ /// Program to link to.
+ Program &P;
+ /// Index of the next available label.
+ LabelTy NextLabel = 0;
+ /// Offset of the next local variable.
+ unsigned NextLocalOffset = 0;
+ /// Location of a failure.
+ llvm::Optional<SourceLocation> BailLocation;
+ /// Label information for linker.
+ llvm::DenseMap<LabelTy, unsigned> LabelOffsets;
+ /// Location of label relocations.
+ llvm::DenseMap<LabelTy, llvm::SmallVector<unsigned, 5>> LabelRelocs;
+ /// Program code.
+ std::vector<char> Code;
+ /// Opcode to expression mapping.
+ SourceMap SrcMap;
+
+ /// Returns the offset for a jump or records a relocation.
+ int32_t getOffset(LabelTy Label);
+
+ /// Emits an opcode.
+ template <typename... Tys>
+ bool emitOp(Opcode Op, const Tys &... Args, const SourceInfo &L);
+
+protected:
+#define GET_LINK_PROTO
+#include "Opcodes.inc"
+#undef GET_LINK_PROTO
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/ByteCodeExprGen.cpp b/lib/AST/Interp/ByteCodeExprGen.cpp
new file mode 100644
index 0000000000000..5c8cb4274260f
--- /dev/null
+++ b/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -0,0 +1,580 @@
+//===--- ByteCodeExprGen.cpp - Code generator for expressions ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ByteCodeExprGen.h"
+#include "ByteCodeEmitter.h"
+#include "ByteCodeGenError.h"
+#include "Context.h"
+#include "Function.h"
+#include "PrimType.h"
+#include "Program.h"
+#include "State.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+using APSInt = llvm::APSInt;
+template <typename T> using Expected = llvm::Expected<T>;
+template <typename T> using Optional = llvm::Optional<T>;
+
+namespace clang {
+namespace interp {
+
+/// Scope used to handle temporaries in toplevel variable declarations.
+template <class Emitter> class DeclScope final : public LocalScope<Emitter> {
+public:
+ DeclScope(ByteCodeExprGen<Emitter> *Ctx, const VarDecl *VD)
+ : LocalScope<Emitter>(Ctx), Scope(Ctx->P, VD) {}
+
+ void addExtended(const Scope::Local &Local) override {
+ return this->addLocal(Local);
+ }
+
+private:
+ Program::DeclScope Scope;
+};
+
+/// Scope used to handle initialization methods.
+template <class Emitter> class OptionScope {
+public:
+ using InitFnRef = typename ByteCodeExprGen<Emitter>::InitFnRef;
+ using ChainedInitFnRef = std::function<bool(InitFnRef)>;
+
+ /// Root constructor, compiling or discarding primitives.
+ OptionScope(ByteCodeExprGen<Emitter> *Ctx, bool NewDiscardResult)
+ : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult),
+ OldInitFn(std::move(Ctx->InitFn)) {
+ Ctx->DiscardResult = NewDiscardResult;
+ Ctx->InitFn = llvm::Optional<InitFnRef>{};
+ }
+
+ /// Root constructor, setting up compilation state.
+ OptionScope(ByteCodeExprGen<Emitter> *Ctx, InitFnRef NewInitFn)
+ : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult),
+ OldInitFn(std::move(Ctx->InitFn)) {
+ Ctx->DiscardResult = true;
+ Ctx->InitFn = NewInitFn;
+ }
+
+ /// Extends the chain of initialisation pointers.
+ OptionScope(ByteCodeExprGen<Emitter> *Ctx, ChainedInitFnRef NewInitFn)
+ : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult),
+ OldInitFn(std::move(Ctx->InitFn)) {
+ assert(OldInitFn && "missing initializer");
+ Ctx->InitFn = [this, NewInitFn] { return NewInitFn(*OldInitFn); };
+ }
+
+ ~OptionScope() {
+ Ctx->DiscardResult = OldDiscardResult;
+ Ctx->InitFn = std::move(OldInitFn);
+ }
+
+private:
+ /// Parent context.
+ ByteCodeExprGen<Emitter> *Ctx;
+ /// Old discard flag to restore.
+ bool OldDiscardResult;
+ /// Old pointer emitter to restore.
+ llvm::Optional<InitFnRef> OldInitFn;
+};
+
+} // namespace interp
+} // namespace clang
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
+ auto *SubExpr = CE->getSubExpr();
+ switch (CE->getCastKind()) {
+
+ case CK_LValueToRValue: {
+ return dereference(
+ CE->getSubExpr(), DerefKind::Read,
+ [](PrimType) {
+ // Value loaded - nothing to do here.
+ return true;
+ },
+ [this, CE](PrimType T) {
+ // Pointer on stack - dereference it.
+ if (!this->emitLoadPop(T, CE))
+ return false;
+ return DiscardResult ? this->emitPop(T, CE) : true;
+ });
+ }
+
+ case CK_ArrayToPointerDecay:
+ case CK_AtomicToNonAtomic:
+ case CK_ConstructorConversion:
+ case CK_FunctionToPointerDecay:
+ case CK_NonAtomicToAtomic:
+ case CK_NoOp:
+ case CK_UserDefinedConversion:
+ return this->Visit(SubExpr);
+
+ case CK_ToVoid:
+ return discard(SubExpr);
+
+ default: {
+ // TODO: implement other casts.
+ return this->bail(CE);
+ }
+ }
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitIntegerLiteral(const IntegerLiteral *LE) {
+ if (DiscardResult)
+ return true;
+
+ auto Val = LE->getValue();
+ QualType LitTy = LE->getType();
+ if (Optional<PrimType> T = classify(LitTy))
+ return emitConst(*T, getIntWidth(LitTy), LE->getValue(), LE);
+ return this->bail(LE);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitParenExpr(const ParenExpr *PE) {
+ return this->Visit(PE->getSubExpr());
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
+ const Expr *LHS = BO->getLHS();
+ const Expr *RHS = BO->getRHS();
+
+ // Deal with operations which have composite or void types.
+ switch (BO->getOpcode()) {
+ case BO_Comma:
+ if (!discard(LHS))
+ return false;
+ if (!this->Visit(RHS))
+ return false;
+ return true;
+ default:
+ break;
+ }
+
+ // Typecheck the args.
+ Optional<PrimType> LT = classify(LHS->getType());
+ Optional<PrimType> RT = classify(RHS->getType());
+ if (!LT || !RT) {
+ return this->bail(BO);
+ }
+
+ if (Optional<PrimType> T = classify(BO->getType())) {
+ if (!visit(LHS))
+ return false;
+ if (!visit(RHS))
+ return false;
+
+ auto Discard = [this, T, BO](bool Result) {
+ if (!Result)
+ return false;
+ return DiscardResult ? this->emitPop(*T, BO) : true;
+ };
+
+ switch (BO->getOpcode()) {
+ case BO_EQ:
+ return Discard(this->emitEQ(*LT, BO));
+ case BO_NE:
+ return Discard(this->emitNE(*LT, BO));
+ case BO_LT:
+ return Discard(this->emitLT(*LT, BO));
+ case BO_LE:
+ return Discard(this->emitLE(*LT, BO));
+ case BO_GT:
+ return Discard(this->emitGT(*LT, BO));
+ case BO_GE:
+ return Discard(this->emitGE(*LT, BO));
+ case BO_Sub:
+ return Discard(this->emitSub(*T, BO));
+ case BO_Add:
+ return Discard(this->emitAdd(*T, BO));
+ case BO_Mul:
+ return Discard(this->emitMul(*T, BO));
+ default:
+ return this->bail(BO);
+ }
+ }
+
+ return this->bail(BO);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
+ OptionScope<Emitter> Scope(this, /*discardResult=*/true);
+ return this->Visit(E);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::visit(const Expr *E) {
+ OptionScope<Emitter> Scope(this, /*discardResult=*/false);
+ return this->Visit(E);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::visitBool(const Expr *E) {
+ if (Optional<PrimType> T = classify(E->getType())) {
+ return visit(E);
+ } else {
+ return this->bail(E);
+ }
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::visitZeroInitializer(PrimType T, const Expr *E) {
+ switch (T) {
+ case PT_Bool:
+ return this->emitZeroBool(E);
+ case PT_Sint8:
+ return this->emitZeroSint8(E);
+ case PT_Uint8:
+ return this->emitZeroUint8(E);
+ case PT_Sint16:
+ return this->emitZeroSint16(E);
+ case PT_Uint16:
+ return this->emitZeroUint16(E);
+ case PT_Sint32:
+ return this->emitZeroSint32(E);
+ case PT_Uint32:
+ return this->emitZeroUint32(E);
+ case PT_Sint64:
+ return this->emitZeroSint64(E);
+ case PT_Uint64:
+ return this->emitZeroUint64(E);
+ case PT_Ptr:
+ return this->emitNullPtr(E);
+ }
+ llvm_unreachable("unknown primitive type");
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::dereference(
+ const Expr *LV, DerefKind AK, llvm::function_ref<bool(PrimType)> Direct,
+ llvm::function_ref<bool(PrimType)> Indirect) {
+ if (Optional<PrimType> T = classify(LV->getType())) {
+ if (!LV->refersToBitField()) {
+ // Only primitive, non bit-field types can be dereferenced directly.
+ if (auto *DE = dyn_cast<DeclRefExpr>(LV)) {
+ if (!DE->getDecl()->getType()->isReferenceType()) {
+ if (auto *PD = dyn_cast<ParmVarDecl>(DE->getDecl()))
+ return dereferenceParam(LV, *T, PD, AK, Direct, Indirect);
+ if (auto *VD = dyn_cast<VarDecl>(DE->getDecl()))
+ return dereferenceVar(LV, *T, VD, AK, Direct, Indirect);
+ }
+ }
+ }
+
+ if (!visit(LV))
+ return false;
+ return Indirect(*T);
+ }
+
+ return false;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::dereferenceParam(
+ const Expr *LV, PrimType T, const ParmVarDecl *PD, DerefKind AK,
+ llvm::function_ref<bool(PrimType)> Direct,
+ llvm::function_ref<bool(PrimType)> Indirect) {
+ auto It = this->Params.find(PD);
+ if (It != this->Params.end()) {
+ unsigned Idx = It->second;
+ switch (AK) {
+ case DerefKind::Read:
+ return DiscardResult ? true : this->emitGetParam(T, Idx, LV);
+
+ case DerefKind::Write:
+ if (!Direct(T))
+ return false;
+ if (!this->emitSetParam(T, Idx, LV))
+ return false;
+ return DiscardResult ? true : this->emitGetPtrParam(Idx, LV);
+
+ case DerefKind::ReadWrite:
+ if (!this->emitGetParam(T, Idx, LV))
+ return false;
+ if (!Direct(T))
+ return false;
+ if (!this->emitSetParam(T, Idx, LV))
+ return false;
+ return DiscardResult ? true : this->emitGetPtrParam(Idx, LV);
+ }
+ return true;
+ }
+
+ // If the param is a pointer, we can dereference a dummy value.
+ if (!DiscardResult && T == PT_Ptr && AK == DerefKind::Read) {
+ if (auto Idx = P.getOrCreateDummy(PD))
+ return this->emitGetPtrGlobal(*Idx, PD);
+ return false;
+ }
+
+ // Value cannot be produced - try to emit pointer and do stuff with it.
+ return visit(LV) && Indirect(T);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::dereferenceVar(
+ const Expr *LV, PrimType T, const VarDecl *VD, DerefKind AK,
+ llvm::function_ref<bool(PrimType)> Direct,
+ llvm::function_ref<bool(PrimType)> Indirect) {
+ auto It = Locals.find(VD);
+ if (It != Locals.end()) {
+ const auto &L = It->second;
+ switch (AK) {
+ case DerefKind::Read:
+ if (!this->emitGetLocal(T, L.Offset, LV))
+ return false;
+ return DiscardResult ? this->emitPop(T, LV) : true;
+
+ case DerefKind::Write:
+ if (!Direct(T))
+ return false;
+ if (!this->emitSetLocal(T, L.Offset, LV))
+ return false;
+ return DiscardResult ? true : this->emitGetPtrLocal(L.Offset, LV);
+
+ case DerefKind::ReadWrite:
+ if (!this->emitGetLocal(T, L.Offset, LV))
+ return false;
+ if (!Direct(T))
+ return false;
+ if (!this->emitSetLocal(T, L.Offset, LV))
+ return false;
+ return DiscardResult ? true : this->emitGetPtrLocal(L.Offset, LV);
+ }
+ } else if (auto Idx = getGlobalIdx(VD)) {
+ switch (AK) {
+ case DerefKind::Read:
+ if (!this->emitGetGlobal(T, *Idx, LV))
+ return false;
+ return DiscardResult ? this->emitPop(T, LV) : true;
+
+ case DerefKind::Write:
+ if (!Direct(T))
+ return false;
+ if (!this->emitSetGlobal(T, *Idx, LV))
+ return false;
+ return DiscardResult ? true : this->emitGetPtrGlobal(*Idx, LV);
+
+ case DerefKind::ReadWrite:
+ if (!this->emitGetGlobal(T, *Idx, LV))
+ return false;
+ if (!Direct(T))
+ return false;
+ if (!this->emitSetGlobal(T, *Idx, LV))
+ return false;
+ return DiscardResult ? true : this->emitGetPtrGlobal(*Idx, LV);
+ }
+ }
+
+ // If the declaration is a constant value, emit it here even
+ // though the declaration was not evaluated in the current scope.
+ // The access mode can only be read in this case.
+ if (!DiscardResult && AK == DerefKind::Read) {
+ if (VD->hasLocalStorage() && VD->hasInit() && !VD->isConstexpr()) {
+ QualType VT = VD->getType();
+ if (VT.isConstQualified() && VT->isFundamentalType())
+ return this->Visit(VD->getInit());
+ }
+ }
+
+ // Value cannot be produced - try to emit pointer.
+ return visit(LV) && Indirect(T);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::emitConst(PrimType T, unsigned NumBits,
+ const APInt &Value, const Expr *E) {
+ switch (T) {
+ case PT_Sint8:
+ return this->emitConstSint8(Value.getSExtValue(), E);
+ case PT_Uint8:
+ return this->emitConstUint8(Value.getZExtValue(), E);
+ case PT_Sint16:
+ return this->emitConstSint16(Value.getSExtValue(), E);
+ case PT_Uint16:
+ return this->emitConstUint16(Value.getZExtValue(), E);
+ case PT_Sint32:
+ return this->emitConstSint32(Value.getSExtValue(), E);
+ case PT_Uint32:
+ return this->emitConstUint32(Value.getZExtValue(), E);
+ case PT_Sint64:
+ return this->emitConstSint64(Value.getSExtValue(), E);
+ case PT_Uint64:
+ return this->emitConstUint64(Value.getZExtValue(), E);
+ case PT_Bool:
+ return this->emitConstBool(Value.getBoolValue(), E);
+ case PT_Ptr:
+ llvm_unreachable("Invalid integral type");
+ break;
+ }
+ llvm_unreachable("unknown primitive type");
+}
+
+template <class Emitter>
+unsigned ByteCodeExprGen<Emitter>::allocateLocalPrimitive(DeclTy &&Src,
+ PrimType Ty,
+ bool IsConst,
+ bool IsExtended) {
+ Descriptor *D = P.createDescriptor(Src, Ty, 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});
+ VarScope->add(Local, IsExtended);
+ return Local.Offset;
+}
+
+template <class Emitter>
+llvm::Optional<unsigned>
+ByteCodeExprGen<Emitter>::allocateLocal(DeclTy &&Src, bool IsExtended) {
+ QualType Ty;
+
+ const ValueDecl *Key = nullptr;
+ bool IsTemporary = false;
+ if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>())) {
+ Key = VD;
+ Ty = VD->getType();
+ }
+ if (auto *E = Src.dyn_cast<const Expr *>()) {
+ IsTemporary = true;
+ Ty = E->getType();
+ }
+
+ Descriptor *D = P.createDescriptor(Src, Ty.getTypePtr(),
+ Ty.isConstQualified(), IsTemporary);
+ if (!D)
+ return {};
+
+ Scope::Local Local = this->createLocal(D);
+ if (Key)
+ Locals.insert({Key, Local});
+ VarScope->add(Local, IsExtended);
+ return Local.Offset;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::visitInitializer(
+ const Expr *Init, InitFnRef InitFn) {
+ OptionScope<Emitter> Scope(this, InitFn);
+ return this->Visit(Init);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::getPtrVarDecl(const VarDecl *VD, const Expr *E) {
+ // Generate a pointer to the local, loading refs.
+ if (Optional<unsigned> Idx = getGlobalIdx(VD)) {
+ if (VD->getType()->isReferenceType())
+ return this->emitGetGlobalPtr(*Idx, E);
+ else
+ return this->emitGetPtrGlobal(*Idx, E);
+ }
+ return this->bail(VD);
+}
+
+template <class Emitter>
+llvm::Optional<unsigned>
+ByteCodeExprGen<Emitter>::getGlobalIdx(const VarDecl *VD) {
+ if (VD->isConstexpr()) {
+ // Constexpr decl - it must have already been defined.
+ return P.getGlobal(VD);
+ }
+ if (!VD->hasLocalStorage()) {
+ // Not constexpr, but a global var - can have pointer taken.
+ Program::DeclScope Scope(P, VD);
+ return P.getOrCreateGlobal(VD);
+ }
+ return {};
+}
+
+template <class Emitter>
+const RecordType *ByteCodeExprGen<Emitter>::getRecordTy(QualType Ty) {
+ if (auto *PT = dyn_cast<PointerType>(Ty))
+ return PT->getPointeeType()->getAs<RecordType>();
+ else
+ return Ty->getAs<RecordType>();
+}
+
+template <class Emitter>
+Record *ByteCodeExprGen<Emitter>::getRecord(QualType Ty) {
+ if (auto *RecordTy = getRecordTy(Ty)) {
+ return getRecord(RecordTy->getDecl());
+ }
+ return nullptr;
+}
+
+template <class Emitter>
+Record *ByteCodeExprGen<Emitter>::getRecord(const RecordDecl *RD) {
+ return P.getOrCreateRecord(RD);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::visitExpr(const Expr *Exp) {
+ ExprScope<Emitter> RootScope(this);
+ if (!visit(Exp))
+ return false;
+
+ if (Optional<PrimType> T = classify(Exp))
+ return this->emitRet(*T, Exp);
+ else
+ return this->emitRetValue(Exp);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::visitDecl(const VarDecl *VD) {
+ const Expr *Init = VD->getInit();
+
+ if (Optional<unsigned> I = P.createGlobal(VD)) {
+ if (Optional<PrimType> T = classify(VD->getType())) {
+ {
+ // Primitive declarations - compute the value and set it.
+ DeclScope<Emitter> LocalScope(this, VD);
+ if (!visit(Init))
+ return false;
+ }
+
+ // If the declaration is global, save the value for later use.
+ if (!this->emitDup(*T, VD))
+ return false;
+ if (!this->emitInitGlobal(*T, *I, VD))
+ return false;
+ return this->emitRet(*T, VD);
+ } else {
+ {
+ // Composite declarations - allocate storage and initialize it.
+ DeclScope<Emitter> LocalScope(this, VD);
+ if (!visitGlobalInitializer(Init, *I))
+ return false;
+ }
+
+ // Return a pointer to the global.
+ if (!this->emitGetPtrGlobal(*I, VD))
+ return false;
+ return this->emitRetValue(VD);
+ }
+ }
+
+ return this->bail(VD);
+}
+
+template <class Emitter>
+void ByteCodeExprGen<Emitter>::emitCleanup() {
+ for (VariableScope<Emitter> *C = VarScope; C; C = C->getParent())
+ C->emitDestruction();
+}
+
+namespace clang {
+namespace interp {
+
+template class ByteCodeExprGen<ByteCodeEmitter>;
+template class ByteCodeExprGen<EvalEmitter>;
+
+} // namespace interp
+} // namespace clang
diff --git a/lib/AST/Interp/ByteCodeExprGen.h b/lib/AST/Interp/ByteCodeExprGen.h
new file mode 100644
index 0000000000000..1d0e34fc991f2
--- /dev/null
+++ b/lib/AST/Interp/ByteCodeExprGen.h
@@ -0,0 +1,331 @@
+//===--- ByteCodeExprGen.h - Code generator for expressions -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the constexpr bytecode compiler.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_BYTECODEEXPRGEN_H
+#define LLVM_CLANG_AST_INTERP_BYTECODEEXPRGEN_H
+
+#include "ByteCodeEmitter.h"
+#include "EvalEmitter.h"
+#include "Pointer.h"
+#include "PrimType.h"
+#include "Record.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "llvm/ADT/Optional.h"
+
+namespace clang {
+class QualType;
+
+namespace interp {
+class Function;
+class State;
+
+template <class Emitter> class LocalScope;
+template <class Emitter> class RecordScope;
+template <class Emitter> class VariableScope;
+template <class Emitter> class DeclScope;
+template <class Emitter> class OptionScope;
+
+/// Compilation context for expressions.
+template <class Emitter>
+class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
+ public Emitter {
+protected:
+ // Emitters for opcodes of various arities.
+ using NullaryFn = bool (ByteCodeExprGen::*)(const SourceInfo &);
+ using UnaryFn = bool (ByteCodeExprGen::*)(PrimType, const SourceInfo &);
+ using BinaryFn = bool (ByteCodeExprGen::*)(PrimType, PrimType,
+ const SourceInfo &);
+
+ // Aliases for types defined in the emitter.
+ using LabelTy = typename Emitter::LabelTy;
+ using AddrTy = typename Emitter::AddrTy;
+
+ // Reference to a function generating the pointer of an initialized object.s
+ using InitFnRef = std::function<bool()>;
+
+ /// Current compilation context.
+ Context &Ctx;
+ /// Program to link to.
+ Program &P;
+
+public:
+ /// Initializes the compiler and the backend emitter.
+ template <typename... Tys>
+ ByteCodeExprGen(Context &Ctx, Program &P, Tys &&... Args)
+ : Emitter(Ctx, P, Args...), Ctx(Ctx), P(P) {}
+
+ // Expression visitors - result returned on stack.
+ bool VisitCastExpr(const CastExpr *E);
+ bool VisitIntegerLiteral(const IntegerLiteral *E);
+ bool VisitParenExpr(const ParenExpr *E);
+ bool VisitBinaryOperator(const BinaryOperator *E);
+
+protected:
+ bool visitExpr(const Expr *E) override;
+ bool visitDecl(const VarDecl *VD) override;
+
+protected:
+ /// Emits scope cleanup instructions.
+ void emitCleanup();
+
+ /// Returns a record type from a record or pointer type.
+ const RecordType *getRecordTy(QualType Ty);
+
+ /// Returns a record from a record or pointer type.
+ Record *getRecord(QualType Ty);
+ Record *getRecord(const RecordDecl *RD);
+
+ /// Returns the size int bits of an integer.
+ unsigned getIntWidth(QualType Ty) {
+ auto &ASTContext = Ctx.getASTContext();
+ return ASTContext.getIntWidth(Ty);
+ }
+
+ /// Returns the value of CHAR_BIT.
+ unsigned getCharBit() const {
+ auto &ASTContext = Ctx.getASTContext();
+ return ASTContext.getTargetInfo().getCharWidth();
+ }
+
+ /// Classifies a type.
+ llvm::Optional<PrimType> classify(const Expr *E) const {
+ return E->isGLValue() ? PT_Ptr : classify(E->getType());
+ }
+ llvm::Optional<PrimType> classify(QualType Ty) const {
+ return Ctx.classify(Ty);
+ }
+
+ /// Checks if a pointer needs adjustment.
+ bool needsAdjust(QualType Ty) const {
+ return true;
+ }
+
+ /// Classifies a known primitive type
+ PrimType classifyPrim(QualType Ty) const {
+ if (auto T = classify(Ty)) {
+ return *T;
+ }
+ llvm_unreachable("not a primitive type");
+ }
+
+ /// Evaluates an expression for side effects and discards the result.
+ bool discard(const Expr *E);
+ /// Evaluates an expression and places result on stack.
+ bool visit(const Expr *E);
+ /// Compiles an initializer for a local.
+ bool visitInitializer(const Expr *E, InitFnRef GenPtr);
+
+ /// Visits an expression and converts it to a boolean.
+ bool visitBool(const Expr *E);
+
+ /// Visits an initializer for a local.
+ bool visitLocalInitializer(const Expr *Init, unsigned I) {
+ return visitInitializer(Init, [this, I, Init] {
+ return this->emitGetPtrLocal(I, Init);
+ });
+ }
+
+ /// Visits an initializer for a global.
+ bool visitGlobalInitializer(const Expr *Init, unsigned I) {
+ return visitInitializer(Init, [this, I, Init] {
+ return this->emitGetPtrGlobal(I, Init);
+ });
+ }
+
+ /// Visits a delegated initializer.
+ bool visitThisInitializer(const Expr *I) {
+ return visitInitializer(I, [this, I] { return this->emitThis(I); });
+ }
+
+ /// Creates a local primitive value.
+ unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsMutable,
+ bool IsExtended = false);
+
+ /// Allocates a space storing a local given its type.
+ llvm::Optional<unsigned> allocateLocal(DeclTy &&Decl,
+ bool IsExtended = false);
+
+private:
+ friend class VariableScope<Emitter>;
+ friend class LocalScope<Emitter>;
+ friend class RecordScope<Emitter>;
+ friend class DeclScope<Emitter>;
+ friend class OptionScope<Emitter>;
+
+ /// Emits a zero initializer.
+ bool visitZeroInitializer(PrimType T, const Expr *E);
+
+ enum class DerefKind {
+ /// Value is read and pushed to stack.
+ Read,
+ /// Direct method generates a value which is written. Returns pointer.
+ Write,
+ /// Direct method receives the value, pushes mutated value. Returns pointer.
+ ReadWrite,
+ };
+
+ /// Method to directly load a value. If the value can be fetched directly,
+ /// the direct handler is called. Otherwise, a pointer is left on the stack
+ /// and the indirect handler is expected to operate on that.
+ bool dereference(const Expr *LV, DerefKind AK,
+ llvm::function_ref<bool(PrimType)> Direct,
+ llvm::function_ref<bool(PrimType)> Indirect);
+ bool dereferenceParam(const Expr *LV, PrimType T, const ParmVarDecl *PD,
+ DerefKind AK,
+ llvm::function_ref<bool(PrimType)> Direct,
+ llvm::function_ref<bool(PrimType)> Indirect);
+ bool dereferenceVar(const Expr *LV, PrimType T, const VarDecl *PD,
+ DerefKind AK, llvm::function_ref<bool(PrimType)> Direct,
+ llvm::function_ref<bool(PrimType)> Indirect);
+
+ /// Emits an APInt constant.
+ bool emitConst(PrimType T, unsigned NumBits, const llvm::APInt &Value,
+ const Expr *E);
+
+ /// Emits an integer constant.
+ template <typename T> bool emitConst(const Expr *E, T Value) {
+ QualType Ty = E->getType();
+ unsigned NumBits = getIntWidth(Ty);
+ APInt WrappedValue(NumBits, Value, std::is_signed<T>::value);
+ return emitConst(*Ctx.classify(Ty), NumBits, WrappedValue, E);
+ }
+
+ /// Returns a pointer to a variable declaration.
+ bool getPtrVarDecl(const VarDecl *VD, const Expr *E);
+
+ /// Returns the index of a global.
+ llvm::Optional<unsigned> getGlobalIdx(const VarDecl *VD);
+
+ /// Emits the initialized pointer.
+ bool emitInitFn() {
+ assert(InitFn && "missing initializer");
+ return (*InitFn)();
+ }
+
+protected:
+ /// Variable to storage mapping.
+ llvm::DenseMap<const ValueDecl *, Scope::Local> Locals;
+
+ /// OpaqueValueExpr to location mapping.
+ llvm::DenseMap<const OpaqueValueExpr *, unsigned> OpaqueExprs;
+
+ /// Current scope.
+ VariableScope<Emitter> *VarScope = nullptr;
+
+ /// Current argument index.
+ llvm::Optional<uint64_t> ArrayIndex;
+
+ /// Flag indicating if return value is to be discarded.
+ bool DiscardResult = false;
+
+ /// Expression being initialized.
+ llvm::Optional<InitFnRef> InitFn = {};
+};
+
+extern template class ByteCodeExprGen<ByteCodeEmitter>;
+extern template class ByteCodeExprGen<EvalEmitter>;
+
+/// Scope chain managing the variable lifetimes.
+template <class Emitter> class VariableScope {
+public:
+ virtual ~VariableScope() { Ctx->VarScope = this->Parent; }
+
+ void add(const Scope::Local &Local, bool IsExtended) {
+ if (IsExtended)
+ this->addExtended(Local);
+ else
+ this->addLocal(Local);
+ }
+
+ virtual void addLocal(const Scope::Local &Local) {
+ if (this->Parent)
+ this->Parent->addLocal(Local);
+ }
+
+ virtual void addExtended(const Scope::Local &Local) {
+ if (this->Parent)
+ this->Parent->addExtended(Local);
+ }
+
+ virtual void emitDestruction() {}
+
+ VariableScope *getParent() { return Parent; }
+
+protected:
+ VariableScope(ByteCodeExprGen<Emitter> *Ctx)
+ : Ctx(Ctx), Parent(Ctx->VarScope) {
+ Ctx->VarScope = this;
+ }
+
+ /// ByteCodeExprGen instance.
+ ByteCodeExprGen<Emitter> *Ctx;
+ /// Link to the parent scope.
+ VariableScope *Parent;
+};
+
+/// Scope for local variables.
+///
+/// When the scope is destroyed, instructions are emitted to tear down
+/// all variables declared in this scope.
+template <class Emitter> class LocalScope : public VariableScope<Emitter> {
+public:
+ LocalScope(ByteCodeExprGen<Emitter> *Ctx) : VariableScope<Emitter>(Ctx) {}
+
+ ~LocalScope() override { this->emitDestruction(); }
+
+ void addLocal(const Scope::Local &Local) override {
+ if (!Idx.hasValue()) {
+ Idx = this->Ctx->Descriptors.size();
+ this->Ctx->Descriptors.emplace_back();
+ }
+
+ this->Ctx->Descriptors[*Idx].emplace_back(Local);
+ }
+
+ void emitDestruction() override {
+ if (!Idx.hasValue())
+ return;
+ this->Ctx->emitDestroy(*Idx, SourceInfo{});
+ }
+
+protected:
+ /// Index of the scope in the chain.
+ Optional<unsigned> Idx;
+};
+
+/// Scope for storage declared in a compound statement.
+template <class Emitter> class BlockScope final : public LocalScope<Emitter> {
+public:
+ BlockScope(ByteCodeExprGen<Emitter> *Ctx) : LocalScope<Emitter>(Ctx) {}
+
+ void addExtended(const Scope::Local &Local) override {
+ llvm_unreachable("Cannot create temporaries in full scopes");
+ }
+};
+
+/// Expression scope which tracks potentially lifetime extended
+/// temporaries which are hoisted to the parent scope on exit.
+template <class Emitter> class ExprScope final : public LocalScope<Emitter> {
+public:
+ ExprScope(ByteCodeExprGen<Emitter> *Ctx) : LocalScope<Emitter>(Ctx) {}
+
+ void addExtended(const Scope::Local &Local) override {
+ this->Parent->addLocal(Local);
+ }
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/ByteCodeGenError.cpp b/lib/AST/Interp/ByteCodeGenError.cpp
new file mode 100644
index 0000000000000..5fd3d77c38420
--- /dev/null
+++ b/lib/AST/Interp/ByteCodeGenError.cpp
@@ -0,0 +1,14 @@
+//===--- ByteCodeGenError.h - Byte code generation error --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ByteCodeGenError.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+char ByteCodeGenError::ID;
diff --git a/lib/AST/Interp/ByteCodeGenError.h b/lib/AST/Interp/ByteCodeGenError.h
new file mode 100644
index 0000000000000..a4fa4917705dd
--- /dev/null
+++ b/lib/AST/Interp/ByteCodeGenError.h
@@ -0,0 +1,46 @@
+//===--- ByteCodeGenError.h - Byte code generation error ----------*- C -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_BYTECODEGENERROR_H
+#define LLVM_CLANG_AST_INTERP_BYTECODEGENERROR_H
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace interp {
+
+/// Error thrown by the compiler.
+struct ByteCodeGenError : public llvm::ErrorInfo<ByteCodeGenError> {
+public:
+ ByteCodeGenError(SourceLocation Loc) : Loc(Loc) {}
+ ByteCodeGenError(const Stmt *S) : ByteCodeGenError(S->getBeginLoc()) {}
+ ByteCodeGenError(const Decl *D) : ByteCodeGenError(D->getBeginLoc()) {}
+
+ void log(raw_ostream &OS) const override { OS << "unimplemented feature"; }
+
+ const SourceLocation &getLoc() const { return Loc; }
+
+ static char ID;
+
+private:
+ // Start of the item where the error occurred.
+ SourceLocation Loc;
+
+ // Users are not expected to use error_code.
+ std::error_code convertToErrorCode() const override {
+ return llvm::inconvertibleErrorCode();
+ }
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/ByteCodeStmtGen.cpp b/lib/AST/Interp/ByteCodeStmtGen.cpp
new file mode 100644
index 0000000000000..c71301598bde6
--- /dev/null
+++ b/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -0,0 +1,265 @@
+//===--- ByteCodeStmtGen.cpp - Code generator for expressions ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ByteCodeStmtGen.h"
+#include "ByteCodeEmitter.h"
+#include "ByteCodeGenError.h"
+#include "Context.h"
+#include "Function.h"
+#include "PrimType.h"
+#include "Program.h"
+#include "State.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+template <typename T> using Expected = llvm::Expected<T>;
+template <typename T> using Optional = llvm::Optional<T>;
+
+namespace clang {
+namespace interp {
+
+/// Scope managing label targets.
+template <class Emitter> class LabelScope {
+public:
+ virtual ~LabelScope() { }
+
+protected:
+ LabelScope(ByteCodeStmtGen<Emitter> *Ctx) : Ctx(Ctx) {}
+ /// ByteCodeStmtGen instance.
+ ByteCodeStmtGen<Emitter> *Ctx;
+};
+
+/// Sets the context for break/continue statements.
+template <class Emitter> class LoopScope final : public LabelScope<Emitter> {
+public:
+ using LabelTy = typename ByteCodeStmtGen<Emitter>::LabelTy;
+ using OptLabelTy = typename ByteCodeStmtGen<Emitter>::OptLabelTy;
+
+ LoopScope(ByteCodeStmtGen<Emitter> *Ctx, LabelTy BreakLabel,
+ LabelTy ContinueLabel)
+ : LabelScope<Emitter>(Ctx), OldBreakLabel(Ctx->BreakLabel),
+ OldContinueLabel(Ctx->ContinueLabel) {
+ this->Ctx->BreakLabel = BreakLabel;
+ this->Ctx->ContinueLabel = ContinueLabel;
+ }
+
+ ~LoopScope() {
+ this->Ctx->BreakLabel = OldBreakLabel;
+ this->Ctx->ContinueLabel = OldContinueLabel;
+ }
+
+private:
+ OptLabelTy OldBreakLabel;
+ OptLabelTy OldContinueLabel;
+};
+
+// Sets the context for a switch scope, mapping labels.
+template <class Emitter> class SwitchScope final : public LabelScope<Emitter> {
+public:
+ using LabelTy = typename ByteCodeStmtGen<Emitter>::LabelTy;
+ using OptLabelTy = typename ByteCodeStmtGen<Emitter>::OptLabelTy;
+ using CaseMap = typename ByteCodeStmtGen<Emitter>::CaseMap;
+
+ SwitchScope(ByteCodeStmtGen<Emitter> *Ctx, CaseMap &&CaseLabels,
+ LabelTy BreakLabel, OptLabelTy DefaultLabel)
+ : LabelScope<Emitter>(Ctx), OldBreakLabel(Ctx->BreakLabel),
+ OldDefaultLabel(this->Ctx->DefaultLabel),
+ OldCaseLabels(std::move(this->Ctx->CaseLabels)) {
+ this->Ctx->BreakLabel = BreakLabel;
+ this->Ctx->DefaultLabel = DefaultLabel;
+ this->Ctx->CaseLabels = std::move(CaseLabels);
+ }
+
+ ~SwitchScope() {
+ this->Ctx->BreakLabel = OldBreakLabel;
+ this->Ctx->DefaultLabel = OldDefaultLabel;
+ this->Ctx->CaseLabels = std::move(OldCaseLabels);
+ }
+
+private:
+ OptLabelTy OldBreakLabel;
+ OptLabelTy OldDefaultLabel;
+ CaseMap OldCaseLabels;
+};
+
+} // namespace interp
+} // namespace clang
+
+template <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
+ // Classify the return type.
+ ReturnType = this->classify(F->getReturnType());
+
+ // Set up fields and context if a constructor.
+ if (auto *MD = dyn_cast<CXXMethodDecl>(F))
+ return this->bail(MD);
+
+ if (auto *Body = F->getBody())
+ if (!visitStmt(Body))
+ return false;
+
+ // Emit a guard return to protect against a code path missing one.
+ if (F->getReturnType()->isVoidType())
+ return this->emitRetVoid(SourceInfo{});
+ else
+ return this->emitNoRet(SourceInfo{});
+}
+
+template <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitStmt(const Stmt *S) {
+ switch (S->getStmtClass()) {
+ case Stmt::CompoundStmtClass:
+ return visitCompoundStmt(cast<CompoundStmt>(S));
+ case Stmt::DeclStmtClass:
+ return visitDeclStmt(cast<DeclStmt>(S));
+ case Stmt::ReturnStmtClass:
+ return visitReturnStmt(cast<ReturnStmt>(S));
+ case Stmt::IfStmtClass:
+ return visitIfStmt(cast<IfStmt>(S));
+ case Stmt::NullStmtClass:
+ return true;
+ default: {
+ if (auto *Exp = dyn_cast<Expr>(S))
+ return this->discard(Exp);
+ return this->bail(S);
+ }
+ }
+}
+
+template <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitCompoundStmt(
+ const CompoundStmt *CompoundStmt) {
+ BlockScope<Emitter> Scope(this);
+ for (auto *InnerStmt : CompoundStmt->body())
+ if (!visitStmt(InnerStmt))
+ return false;
+ return true;
+}
+
+template <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitDeclStmt(const DeclStmt *DS) {
+ for (auto *D : DS->decls()) {
+ // Variable declarator.
+ if (auto *VD = dyn_cast<VarDecl>(D)) {
+ if (!visitVarDecl(VD))
+ return false;
+ continue;
+ }
+
+ // Decomposition declarator.
+ if (auto *DD = dyn_cast<DecompositionDecl>(D)) {
+ return this->bail(DD);
+ }
+ }
+
+ return true;
+}
+
+template <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitReturnStmt(const ReturnStmt *RS) {
+ if (const Expr *RE = RS->getRetValue()) {
+ ExprScope<Emitter> RetScope(this);
+ if (ReturnType) {
+ // Primitive types are simply returned.
+ if (!this->visit(RE))
+ return false;
+ this->emitCleanup();
+ return this->emitRet(*ReturnType, RS);
+ } else {
+ // RVO - construct the value in the return location.
+ auto ReturnLocation = [this, RE] { return this->emitGetParamPtr(0, RE); };
+ if (!this->visitInitializer(RE, ReturnLocation))
+ return false;
+ this->emitCleanup();
+ return this->emitRetVoid(RS);
+ }
+ } else {
+ this->emitCleanup();
+ if (!this->emitRetVoid(RS))
+ return false;
+ return true;
+ }
+}
+
+template <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitIfStmt(const IfStmt *IS) {
+ BlockScope<Emitter> IfScope(this);
+ if (auto *CondInit = IS->getInit())
+ if (!visitStmt(IS->getInit()))
+ return false;
+
+ if (const DeclStmt *CondDecl = IS->getConditionVariableDeclStmt())
+ if (!visitDeclStmt(CondDecl))
+ return false;
+
+ if (!this->visitBool(IS->getCond()))
+ return false;
+
+ if (const Stmt *Else = IS->getElse()) {
+ LabelTy LabelElse = this->getLabel();
+ LabelTy LabelEnd = this->getLabel();
+ if (!this->jumpFalse(LabelElse))
+ return false;
+ if (!visitStmt(IS->getThen()))
+ return false;
+ if (!this->jump(LabelEnd))
+ return false;
+ this->emitLabel(LabelElse);
+ if (!visitStmt(Else))
+ return false;
+ this->emitLabel(LabelEnd);
+ } else {
+ LabelTy LabelEnd = this->getLabel();
+ if (!this->jumpFalse(LabelEnd))
+ return false;
+ if (!visitStmt(IS->getThen()))
+ return false;
+ this->emitLabel(LabelEnd);
+ }
+
+ return true;
+}
+
+template <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitVarDecl(const VarDecl *VD) {
+ auto DT = VD->getType();
+
+ if (!VD->hasLocalStorage()) {
+ // No code generation required.
+ return true;
+ }
+
+ // Integers, pointers, primitives.
+ if (Optional<PrimType> T = this->classify(DT)) {
+ auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified());
+ // Compile the initialiser in its own scope.
+ {
+ ExprScope<Emitter> Scope(this);
+ if (!this->visit(VD->getInit()))
+ return false;
+ }
+ // Set the value.
+ return this->emitSetLocal(*T, Off, VD);
+ } else {
+ // Composite types - allocate storage and initialize it.
+ if (auto Off = this->allocateLocal(VD)) {
+ return this->visitLocalInitializer(VD->getInit(), *Off);
+ } else {
+ return this->bail(VD);
+ }
+ }
+}
+
+namespace clang {
+namespace interp {
+
+template class ByteCodeStmtGen<ByteCodeEmitter>;
+
+} // namespace interp
+} // namespace clang
diff --git a/lib/AST/Interp/ByteCodeStmtGen.h b/lib/AST/Interp/ByteCodeStmtGen.h
new file mode 100644
index 0000000000000..d9c0b64ed4b82
--- /dev/null
+++ b/lib/AST/Interp/ByteCodeStmtGen.h
@@ -0,0 +1,89 @@
+//===--- ByteCodeStmtGen.h - Code generator for expressions -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the constexpr bytecode compiler.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_BYTECODESTMTGEN_H
+#define LLVM_CLANG_AST_INTERP_BYTECODESTMTGEN_H
+
+#include "ByteCodeEmitter.h"
+#include "ByteCodeExprGen.h"
+#include "EvalEmitter.h"
+#include "Pointer.h"
+#include "PrimType.h"
+#include "Record.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/StmtVisitor.h"
+#include "llvm/ADT/Optional.h"
+
+namespace clang {
+class QualType;
+
+namespace interp {
+class Function;
+class State;
+
+template <class Emitter> class LoopScope;
+template <class Emitter> class SwitchScope;
+template <class Emitter> class LabelScope;
+
+/// Compilation context for statements.
+template <class Emitter>
+class ByteCodeStmtGen : public ByteCodeExprGen<Emitter> {
+ using LabelTy = typename Emitter::LabelTy;
+ using AddrTy = typename Emitter::AddrTy;
+ using OptLabelTy = llvm::Optional<LabelTy>;
+ using CaseMap = llvm::DenseMap<const SwitchCase *, LabelTy>;
+
+public:
+ template<typename... Tys>
+ ByteCodeStmtGen(Tys&&... Args)
+ : ByteCodeExprGen<Emitter>(std::forward<Tys>(Args)...) {}
+
+protected:
+ bool visitFunc(const FunctionDecl *F) override;
+
+private:
+ friend class LabelScope<Emitter>;
+ friend class LoopScope<Emitter>;
+ friend class SwitchScope<Emitter>;
+
+ // Statement visitors.
+ bool visitStmt(const Stmt *S);
+ bool visitCompoundStmt(const CompoundStmt *S);
+ bool visitDeclStmt(const DeclStmt *DS);
+ bool visitReturnStmt(const ReturnStmt *RS);
+ bool visitIfStmt(const IfStmt *IS);
+
+ /// Compiles a variable declaration.
+ bool visitVarDecl(const VarDecl *VD);
+
+private:
+ /// Type of the expression returned by the function.
+ llvm::Optional<PrimType> ReturnType;
+
+ /// Switch case mapping.
+ CaseMap CaseLabels;
+
+ /// Point to break to.
+ OptLabelTy BreakLabel;
+ /// Point to continue to.
+ OptLabelTy ContinueLabel;
+ /// Default case label.
+ OptLabelTy DefaultLabel;
+};
+
+extern template class ByteCodeExprGen<EvalEmitter>;
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/Context.cpp b/lib/AST/Interp/Context.cpp
new file mode 100644
index 0000000000000..4f8f7b96e7c32
--- /dev/null
+++ b/lib/AST/Interp/Context.cpp
@@ -0,0 +1,148 @@
+//===--- Context.cpp - Context for the constexpr VM -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Context.h"
+#include "ByteCodeEmitter.h"
+#include "ByteCodeExprGen.h"
+#include "ByteCodeStmtGen.h"
+#include "EvalEmitter.h"
+#include "Interp.h"
+#include "InterpFrame.h"
+#include "InterpStack.h"
+#include "PrimType.h"
+#include "Program.h"
+#include "clang/AST/Expr.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+Context::Context(ASTContext &Ctx)
+ : Ctx(Ctx), ForceInterp(getLangOpts().ForceNewConstInterp),
+ P(new Program(*this)) {}
+
+Context::~Context() {}
+
+InterpResult Context::isPotentialConstantExpr(State &Parent,
+ const FunctionDecl *FD) {
+ Function *Func = P->getFunction(FD);
+ if (!Func) {
+ if (auto R = ByteCodeStmtGen<ByteCodeEmitter>(*this, *P).compileFunc(FD)) {
+ Func = *R;
+ } else if (ForceInterp) {
+ handleAllErrors(R.takeError(), [&Parent](ByteCodeGenError &Err) {
+ Parent.FFDiag(Err.getLoc(), diag::err_experimental_clang_interp_failed);
+ });
+ return InterpResult::Fail;
+ } else {
+ consumeError(R.takeError());
+ return InterpResult::Bail;
+ }
+ }
+
+ if (!Func->isConstexpr())
+ return InterpResult::Fail;
+
+ APValue Dummy;
+ return Run(Parent, Func, Dummy);
+}
+
+InterpResult Context::evaluateAsRValue(State &Parent, const Expr *E,
+ APValue &Result) {
+ ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
+ return Check(Parent, C.interpretExpr(E));
+}
+
+InterpResult Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
+ APValue &Result) {
+ ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
+ return Check(Parent, C.interpretDecl(VD));
+}
+
+const LangOptions &Context::getLangOpts() const { return Ctx.getLangOpts(); }
+
+llvm::Optional<PrimType> Context::classify(QualType T) {
+ if (T->isReferenceType() || T->isPointerType()) {
+ return PT_Ptr;
+ }
+
+ if (T->isBooleanType())
+ return PT_Bool;
+
+ if (T->isSignedIntegerOrEnumerationType()) {
+ switch (Ctx.getIntWidth(T)) {
+ case 64:
+ return PT_Sint64;
+ case 32:
+ return PT_Sint32;
+ case 16:
+ return PT_Sint16;
+ case 8:
+ return PT_Sint8;
+ default:
+ return {};
+ }
+ }
+
+ if (T->isUnsignedIntegerOrEnumerationType()) {
+ switch (Ctx.getIntWidth(T)) {
+ case 64:
+ return PT_Uint64;
+ case 32:
+ return PT_Uint32;
+ case 16:
+ return PT_Uint16;
+ case 8:
+ return PT_Uint8;
+ default:
+ return {};
+ }
+ }
+
+ if (T->isNullPtrType())
+ return PT_Ptr;
+
+ if (auto *AT = dyn_cast<AtomicType>(T))
+ return classify(AT->getValueType());
+
+ return {};
+}
+
+unsigned Context::getCharBit() const {
+ return Ctx.getTargetInfo().getCharWidth();
+}
+
+InterpResult Context::Run(State &Parent, Function *Func, APValue &Result) {
+ InterpResult Flag;
+ {
+ InterpState State(Parent, *P, Stk, *this);
+ State.Current = new InterpFrame(State, Func, nullptr, {}, {});
+ if (Interpret(State, Result)) {
+ Flag = InterpResult::Success;
+ } else {
+ Flag = InterpResult::Fail;
+ }
+ }
+
+ if (Flag != InterpResult::Success)
+ Stk.clear();
+ return Flag;
+}
+
+InterpResult Context::Check(State &Parent, llvm::Expected<bool> &&R) {
+ if (R) {
+ return *R ? InterpResult::Success : InterpResult::Fail;
+ } else if (ForceInterp) {
+ handleAllErrors(R.takeError(), [&Parent](ByteCodeGenError &Err) {
+ Parent.FFDiag(Err.getLoc(), diag::err_experimental_clang_interp_failed);
+ });
+ return InterpResult::Fail;
+ } else {
+ consumeError(R.takeError());
+ return InterpResult::Bail;
+ }
+}
diff --git a/lib/AST/Interp/Context.h b/lib/AST/Interp/Context.h
new file mode 100644
index 0000000000000..96368b6e5f02f
--- /dev/null
+++ b/lib/AST/Interp/Context.h
@@ -0,0 +1,100 @@
+//===--- Context.h - Context for the constexpr VM ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the constexpr execution context.
+//
+// The execution context manages cached bytecode and the global context.
+// It invokes the compiler and interpreter, propagating errors.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_CONTEXT_H
+#define LLVM_CLANG_AST_INTERP_CONTEXT_H
+
+#include "Context.h"
+#include "InterpStack.h"
+#include "clang/AST/APValue.h"
+#include "llvm/ADT/PointerIntPair.h"
+
+namespace clang {
+class ASTContext;
+class LangOptions;
+class Stmt;
+class FunctionDecl;
+class VarDecl;
+
+namespace interp {
+class Function;
+class Program;
+class State;
+enum PrimType : unsigned;
+
+/// Wrapper around interpreter termination results.
+enum class InterpResult {
+ /// Interpreter successfully computed a value.
+ Success,
+ /// Interpreter encountered an error and quit.
+ Fail,
+ /// Interpreter encountered an unimplemented feature, AST fallback.
+ Bail,
+};
+
+/// Holds all information required to evaluate constexpr code in a module.
+class Context {
+public:
+ /// Initialises the constexpr VM.
+ Context(ASTContext &Ctx);
+
+ /// Cleans up the constexpr VM.
+ ~Context();
+
+ /// Checks if a function is a potential constant expression.
+ InterpResult isPotentialConstantExpr(State &Parent,
+ const FunctionDecl *FnDecl);
+
+ /// Evaluates a toplevel expression as an rvalue.
+ InterpResult evaluateAsRValue(State &Parent, const Expr *E, APValue &Result);
+
+ /// Evaluates a toplevel initializer.
+ InterpResult evaluateAsInitializer(State &Parent, const VarDecl *VD,
+ APValue &Result);
+
+ /// Returns the AST context.
+ ASTContext &getASTContext() const { return Ctx; }
+ /// Returns the language options.
+ const LangOptions &getLangOpts() const;
+ /// Returns the interpreter stack.
+ InterpStack &getStack() { return Stk; }
+ /// Returns CHAR_BIT.
+ unsigned getCharBit() const;
+
+ /// Classifies an expression.
+ llvm::Optional<PrimType> classify(QualType T);
+
+private:
+ /// Runs a function.
+ InterpResult Run(State &Parent, Function *Func, APValue &Result);
+
+ /// Checks a result fromt the interpreter.
+ InterpResult Check(State &Parent, llvm::Expected<bool> &&R);
+
+private:
+ /// Current compilation context.
+ ASTContext &Ctx;
+ /// Flag to indicate if the use of the interpreter is mandatory.
+ bool ForceInterp;
+ /// Interpreter stack, shared across invocations.
+ InterpStack Stk;
+ /// Constexpr program.
+ std::unique_ptr<Program> P;
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/Descriptor.cpp b/lib/AST/Interp/Descriptor.cpp
new file mode 100644
index 0000000000000..5c1a8a9cf3066
--- /dev/null
+++ b/lib/AST/Interp/Descriptor.cpp
@@ -0,0 +1,292 @@
+//===--- Descriptor.cpp - Types for the constexpr VM ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Descriptor.h"
+#include "Pointer.h"
+#include "PrimType.h"
+#include "Record.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+template <typename T>
+static void ctorTy(Block *, char *Ptr, bool, bool, bool, Descriptor *) {
+ new (Ptr) T();
+}
+
+template <typename T> static void dtorTy(Block *, char *Ptr, Descriptor *) {
+ reinterpret_cast<T *>(Ptr)->~T();
+}
+
+template <typename T>
+static void moveTy(Block *, char *Src, char *Dst, Descriptor *) {
+ auto *SrcPtr = reinterpret_cast<T *>(Src);
+ auto *DstPtr = reinterpret_cast<T *>(Dst);
+ new (DstPtr) T(std::move(*SrcPtr));
+}
+
+template <typename T>
+static void ctorArrayTy(Block *, char *Ptr, bool, bool, bool, Descriptor *D) {
+ for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
+ new (&reinterpret_cast<T *>(Ptr)[I]) T();
+ }
+}
+
+template <typename T>
+static void dtorArrayTy(Block *, char *Ptr, Descriptor *D) {
+ for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
+ reinterpret_cast<T *>(Ptr)[I].~T();
+ }
+}
+
+template <typename T>
+static void moveArrayTy(Block *, char *Src, char *Dst, Descriptor *D) {
+ for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
+ auto *SrcPtr = &reinterpret_cast<T *>(Src)[I];
+ auto *DstPtr = &reinterpret_cast<T *>(Dst)[I];
+ new (DstPtr) T(std::move(*SrcPtr));
+ }
+}
+
+static void ctorArrayDesc(Block *B, char *Ptr, bool IsConst, bool IsMutable,
+ bool IsActive, Descriptor *D) {
+ const unsigned NumElems = D->getNumElems();
+ const unsigned ElemSize =
+ D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
+
+ unsigned ElemOffset = 0;
+ for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) {
+ auto *ElemPtr = Ptr + ElemOffset;
+ auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr);
+ auto *ElemLoc = reinterpret_cast<char *>(Desc + 1);
+ auto *SD = D->ElemDesc;
+
+ Desc->Offset = ElemOffset + sizeof(InlineDescriptor);
+ Desc->Desc = SD;
+ Desc->IsInitialized = true;
+ Desc->IsBase = false;
+ Desc->IsActive = IsActive;
+ Desc->IsConst = IsConst || D->IsConst;
+ Desc->IsMutable = IsMutable || D->IsMutable;
+ if (auto Fn = D->ElemDesc->CtorFn)
+ Fn(B, ElemLoc, Desc->IsConst, Desc->IsMutable, IsActive, D->ElemDesc);
+ }
+}
+
+static void dtorArrayDesc(Block *B, char *Ptr, Descriptor *D) {
+ const unsigned NumElems = D->getNumElems();
+ const unsigned ElemSize =
+ D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
+
+ unsigned ElemOffset = 0;
+ for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) {
+ auto *ElemPtr = Ptr + ElemOffset;
+ auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr);
+ auto *ElemLoc = reinterpret_cast<char *>(Desc + 1);
+ if (auto Fn = D->ElemDesc->DtorFn)
+ Fn(B, ElemLoc, D->ElemDesc);
+ }
+}
+
+static void moveArrayDesc(Block *B, char *Src, char *Dst, Descriptor *D) {
+ const unsigned NumElems = D->getNumElems();
+ const unsigned ElemSize =
+ D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
+
+ unsigned ElemOffset = 0;
+ for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) {
+ auto *SrcPtr = Src + ElemOffset;
+ auto *DstPtr = Dst + ElemOffset;
+
+ auto *SrcDesc = reinterpret_cast<InlineDescriptor *>(SrcPtr);
+ auto *SrcElemLoc = reinterpret_cast<char *>(SrcDesc + 1);
+ auto *DstDesc = reinterpret_cast<InlineDescriptor *>(DstPtr);
+ auto *DstElemLoc = reinterpret_cast<char *>(DstDesc + 1);
+
+ *DstDesc = *SrcDesc;
+ if (auto Fn = D->ElemDesc->MoveFn)
+ Fn(B, SrcElemLoc, DstElemLoc, D->ElemDesc);
+ }
+}
+
+static void ctorRecord(Block *B, char *Ptr, bool IsConst, bool IsMutable,
+ bool IsActive, Descriptor *D) {
+ const bool IsUnion = D->ElemRecord->isUnion();
+ auto CtorSub = [=](unsigned SubOff, Descriptor *F, bool IsBase) {
+ auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + SubOff) - 1;
+ Desc->Offset = SubOff;
+ Desc->Desc = F;
+ Desc->IsInitialized = (B->isStatic() || F->IsArray) && !IsBase;
+ Desc->IsBase = IsBase;
+ Desc->IsActive = IsActive && !IsUnion;
+ Desc->IsConst = IsConst || F->IsConst;
+ Desc->IsMutable = IsMutable || F->IsMutable;
+ if (auto Fn = F->CtorFn)
+ Fn(B, Ptr + SubOff, Desc->IsConst, Desc->IsMutable, Desc->IsActive, F);
+ };
+ for (const auto &B : D->ElemRecord->bases())
+ CtorSub(B.Offset, B.Desc, /*isBase=*/true);
+ for (const auto &F : D->ElemRecord->fields())
+ CtorSub(F.Offset, F.Desc, /*isBase=*/false);
+ for (const auto &V : D->ElemRecord->virtual_bases())
+ CtorSub(V.Offset, V.Desc, /*isBase=*/true);
+}
+
+static void dtorRecord(Block *B, char *Ptr, Descriptor *D) {
+ auto DtorSub = [=](unsigned SubOff, Descriptor *F) {
+ if (auto Fn = F->DtorFn)
+ Fn(B, Ptr + SubOff, F);
+ };
+ for (const auto &F : D->ElemRecord->bases())
+ DtorSub(F.Offset, F.Desc);
+ for (const auto &F : D->ElemRecord->fields())
+ DtorSub(F.Offset, F.Desc);
+ for (const auto &F : D->ElemRecord->virtual_bases())
+ DtorSub(F.Offset, F.Desc);
+}
+
+static void moveRecord(Block *B, char *Src, char *Dst, Descriptor *D) {
+ for (const auto &F : D->ElemRecord->fields()) {
+ auto FieldOff = F.Offset;
+ auto FieldDesc = F.Desc;
+
+ *(reinterpret_cast<Descriptor **>(Dst + FieldOff) - 1) = FieldDesc;
+ if (auto Fn = FieldDesc->MoveFn)
+ Fn(B, Src + FieldOff, Dst + FieldOff, FieldDesc);
+ }
+}
+
+static BlockCtorFn getCtorPrim(PrimType Type) {
+ COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr);
+}
+
+static BlockDtorFn getDtorPrim(PrimType Type) {
+ COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr);
+}
+
+static BlockMoveFn getMovePrim(PrimType Type) {
+ COMPOSITE_TYPE_SWITCH(Type, return moveTy<T>, return nullptr);
+}
+
+static BlockCtorFn getCtorArrayPrim(PrimType Type) {
+ COMPOSITE_TYPE_SWITCH(Type, return ctorArrayTy<T>, return nullptr);
+}
+
+static BlockDtorFn getDtorArrayPrim(PrimType Type) {
+ COMPOSITE_TYPE_SWITCH(Type, return dtorArrayTy<T>, return nullptr);
+}
+
+static BlockMoveFn getMoveArrayPrim(PrimType Type) {
+ 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)) {
+ assert(Source && "Missing source");
+}
+
+Descriptor::Descriptor(const DeclTy &D, PrimType Type, size_t NumElems,
+ bool IsConst, bool IsTemporary, bool IsMutable)
+ : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
+ AllocSize(align(Size) + sizeof(InitMap *)), IsConst(IsConst),
+ IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true),
+ CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
+ MoveFn(getMoveArrayPrim(Type)) {
+ assert(Source && "Missing source");
+}
+
+Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary,
+ UnknownSize)
+ : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark),
+ 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)
+ : 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) {
+ 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) {
+ assert(Source && "Missing source");
+}
+
+Descriptor::Descriptor(const DeclTy &D, Record *R, 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) {
+ assert(Source && "Missing source");
+}
+
+QualType Descriptor::getType() const {
+ if (auto *E = asExpr())
+ return E->getType();
+ if (auto *D = asValueDecl())
+ return D->getType();
+ llvm_unreachable("Invalid descriptor type");
+}
+
+SourceLocation Descriptor::getLocation() const {
+ if (auto *D = Source.dyn_cast<const Decl *>())
+ return D->getLocation();
+ if (auto *E = Source.dyn_cast<const Expr *>())
+ return E->getExprLoc();
+ llvm_unreachable("Invalid descriptor type");
+}
+
+InitMap::InitMap(unsigned N) : UninitFields(N) {
+ for (unsigned I = 0; I < N / PER_FIELD; ++I) {
+ data()[I] = 0;
+ }
+}
+
+InitMap::T *InitMap::data() {
+ auto *Start = reinterpret_cast<char *>(this) + align(sizeof(InitMap));
+ return reinterpret_cast<T *>(Start);
+}
+
+bool InitMap::initialize(unsigned I) {
+ unsigned Bucket = I / PER_FIELD;
+ unsigned Mask = 1ull << static_cast<uint64_t>(I % PER_FIELD);
+ if (!(data()[Bucket] & Mask)) {
+ data()[Bucket] |= Mask;
+ UninitFields -= 1;
+ }
+ return UninitFields == 0;
+}
+
+bool InitMap::isInitialized(unsigned I) {
+ unsigned Bucket = I / PER_FIELD;
+ unsigned Mask = 1ull << static_cast<uint64_t>(I % PER_FIELD);
+ return data()[Bucket] & Mask;
+}
+
+InitMap *InitMap::allocate(unsigned N) {
+ const size_t NumFields = ((N + PER_FIELD - 1) / PER_FIELD);
+ const size_t Size = align(sizeof(InitMap)) + NumFields * PER_FIELD;
+ return new (malloc(Size)) InitMap(N);
+}
diff --git a/lib/AST/Interp/Descriptor.h b/lib/AST/Interp/Descriptor.h
new file mode 100644
index 0000000000000..b260b7600974d
--- /dev/null
+++ b/lib/AST/Interp/Descriptor.h
@@ -0,0 +1,220 @@
+//===--- Descriptor.h - Types for the constexpr VM --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines descriptors which characterise allocations.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_DESCRIPTOR_H
+#define LLVM_CLANG_AST_INTERP_DESCRIPTOR_H
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+
+namespace clang {
+namespace interp {
+class Block;
+class Record;
+struct Descriptor;
+enum PrimType : unsigned;
+
+using DeclTy = llvm::PointerUnion<const Decl *, const Expr *>;
+
+/// Invoked whenever a block is created. The constructor method fills in the
+/// inline descriptors of all fields and array elements. It also initializes
+/// all the fields which contain non-trivial types.
+using BlockCtorFn = void (*)(Block *Storage, char *FieldPtr, bool IsConst,
+ bool IsMutable, bool IsActive,
+ Descriptor *FieldDesc);
+
+/// Invoked when a block is destroyed. Invokes the destructors of all
+/// non-trivial nested fields of arrays and records.
+using BlockDtorFn = void (*)(Block *Storage, char *FieldPtr,
+ Descriptor *FieldDesc);
+
+/// Invoked when a block with pointers referencing it goes out of scope. Such
+/// blocks are persisted: the move function copies all inline descriptors and
+/// non-trivial fields, as existing pointers might need to reference those
+/// descriptors. Data is not copied since it cannot be legally read.
+using BlockMoveFn = void (*)(Block *Storage, char *SrcFieldPtr,
+ char *DstFieldPtr, Descriptor *FieldDesc);
+
+/// Object size as used by the interpreter.
+using InterpSize = unsigned;
+
+/// Describes a memory block created by an allocation site.
+struct Descriptor {
+private:
+ /// Original declaration, used to emit the error message.
+ const DeclTy Source;
+ /// Size of an element, in host bytes.
+ const InterpSize ElemSize;
+ /// Size of the storage, in host bytes.
+ const InterpSize Size;
+ /// Size of the allocation (storage + metadata), in host bytes.
+ const InterpSize AllocSize;
+
+ /// Value to denote arrays of unknown size.
+ static constexpr unsigned UnknownSizeMark = (unsigned)-1;
+
+public:
+ /// Token to denote structures of unknown size.
+ struct UnknownSize {};
+
+ /// Pointer to the record, if block contains records.
+ Record *const ElemRecord = nullptr;
+ /// Descriptor of the array element.
+ Descriptor *const ElemDesc = nullptr;
+ /// Flag indicating if the block is mutable.
+ const bool IsConst = false;
+ /// Flag indicating if a field is mutable.
+ const bool IsMutable = false;
+ /// Flag indicating if the block is a temporary.
+ const bool IsTemporary = false;
+ /// Flag indicating if the block is an array.
+ const bool IsArray = false;
+
+ /// Storage management methods.
+ const BlockCtorFn CtorFn = nullptr;
+ const BlockDtorFn DtorFn = nullptr;
+ const BlockMoveFn MoveFn = nullptr;
+
+ /// Allocates a descriptor for a primitive.
+ Descriptor(const DeclTy &D, PrimType Type, 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);
+
+ /// 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);
+
+ /// 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);
+
+ QualType getType() const;
+ SourceLocation getLocation() const;
+
+ const Decl *asDecl() const { return Source.dyn_cast<const Decl *>(); }
+ const Expr *asExpr() const { return Source.dyn_cast<const Expr *>(); }
+
+ const ValueDecl *asValueDecl() const {
+ return dyn_cast_or_null<ValueDecl>(asDecl());
+ }
+
+ const FieldDecl *asFieldDecl() const {
+ return dyn_cast_or_null<FieldDecl>(asDecl());
+ }
+
+ const RecordDecl *asRecordDecl() const {
+ return dyn_cast_or_null<RecordDecl>(asDecl());
+ }
+
+ /// Returns the size of the object without metadata.
+ unsigned getSize() const {
+ assert(!isUnknownSizeArray() && "Array of unknown size");
+ return Size;
+ }
+
+ /// Returns the allocated size, including metadata.
+ 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 number of elements stored in the block.
+ unsigned getNumElems() const {
+ return Size == UnknownSizeMark ? 0 : (getSize() / getElemSize());
+ }
+
+ /// Checks if the descriptor is of an array of primitives.
+ bool isPrimitiveArray() const { return IsArray && !ElemDesc; }
+ /// Checks if the descriptor is of an array of zero size.
+ bool isZeroSizeArray() const { return Size == 0; }
+ /// Checks if the descriptor is of an array of unknown size.
+ bool isUnknownSizeArray() const { return Size == UnknownSizeMark; }
+
+ /// Checks if the descriptor is of a primitive.
+ bool isPrimitive() const { return !IsArray && !ElemRecord; }
+
+ /// Checks if the descriptor is of an array.
+ 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 initialied, the pointer to
+/// this structure is 0. If the object was fully initialized, the pointer is -1.
+struct InitMap {
+private:
+ /// Type packing bits.
+ using T = uint64_t;
+ /// Bits stored in a single field.
+ static constexpr uint64_t PER_FIELD = sizeof(T) * CHAR_BIT;
+
+ /// Initializes the map with no fields set.
+ InitMap(unsigned N);
+
+ /// Returns a pointer to storage.
+ T *data();
+
+public:
+ /// Initializes an element. Returns true when object if fully initialized.
+ bool initialize(unsigned I);
+
+ /// Checks if an element was initialized.
+ bool isInitialized(unsigned I);
+
+ /// Allocates a map holding N elements.
+ static InitMap *allocate(unsigned N);
+
+private:
+ /// Number of fields initialized.
+ unsigned UninitFields;
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/Disasm.cpp b/lib/AST/Interp/Disasm.cpp
new file mode 100644
index 0000000000000..e77a825eb1f23
--- /dev/null
+++ b/lib/AST/Interp/Disasm.cpp
@@ -0,0 +1,69 @@
+//===--- Disasm.cpp - Disassembler for bytecode functions -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Dump method for Function which disassembles the bytecode.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Function.h"
+#include "Opcode.h"
+#include "PrimType.h"
+#include "Program.h"
+#include "clang/AST/DeclCXX.h"
+#include "llvm/Support/Compiler.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); }
+
+LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const {
+ if (F) {
+ if (auto *Cons = dyn_cast<CXXConstructorDecl>(F)) {
+ const std::string &Name = Cons->getParent()->getNameAsString();
+ OS << Name << "::" << Name << ":\n";
+ } else {
+ OS << F->getNameAsString() << ":\n";
+ }
+ } else {
+ OS << "<<expr>>\n";
+ }
+
+ OS << "frame size: " << getFrameSize() << "\n";
+ OS << "arg size: " << getArgSize() << "\n";
+ OS << "rvo: " << hasRVO() << "\n";
+
+ auto PrintName = [&OS](const char *Name) {
+ OS << Name;
+ for (long I = 0, N = strlen(Name); I < 30 - N; ++I) {
+ OS << ' ';
+ }
+ };
+
+ for (CodePtr Start = getCodeBegin(), PC = Start; PC != getCodeEnd();) {
+ size_t Addr = PC - Start;
+ auto Op = PC.read<Opcode>();
+ OS << llvm::format("%8d", Addr) << " ";
+ switch (Op) {
+#define GET_DISASM
+#include "Opcodes.inc"
+#undef GET_DISASM
+ }
+ }
+}
+
+LLVM_DUMP_METHOD void Program::dump() const { dump(llvm::errs()); }
+
+LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const {
+ for (auto &Func : Funcs) {
+ Func.second->dump();
+ }
+ for (auto &Anon : AnonFuncs) {
+ Anon->dump();
+ }
+}
diff --git a/lib/AST/Interp/EvalEmitter.cpp b/lib/AST/Interp/EvalEmitter.cpp
new file mode 100644
index 0000000000000..22e8695b92112
--- /dev/null
+++ b/lib/AST/Interp/EvalEmitter.cpp
@@ -0,0 +1,253 @@
+//===--- EvalEmitter.cpp - Instruction emitter for the VM -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "EvalEmitter.h"
+#include "Context.h"
+#include "Interp.h"
+#include "Opcode.h"
+#include "Program.h"
+#include "clang/AST/DeclCXX.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+using APSInt = llvm::APSInt;
+template <typename T> using Expected = llvm::Expected<T>;
+
+EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
+ 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());
+}
+
+llvm::Expected<bool> EvalEmitter::interpretExpr(const Expr *E) {
+ if (this->visitExpr(E))
+ return true;
+ if (BailLocation)
+ return llvm::make_error<ByteCodeGenError>(*BailLocation);
+ return false;
+}
+
+llvm::Expected<bool> EvalEmitter::interpretDecl(const VarDecl *VD) {
+ if (this->visitDecl(VD))
+ return true;
+ if (BailLocation)
+ return llvm::make_error<ByteCodeGenError>(*BailLocation);
+ return false;
+}
+
+void EvalEmitter::emitLabel(LabelTy Label) {
+ CurrentLabel = Label;
+}
+
+EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }
+
+Scope::Local EvalEmitter::createLocal(Descriptor *D) {
+ // Allocate memory for a local.
+ auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize());
+ auto *B = new (Memory.get()) Block(D, /*isStatic=*/false);
+ B->invokeCtor();
+
+ // Register the local.
+ unsigned Off = Locals.size();
+ Locals.insert({Off, std::move(Memory)});
+ return {Off, D};
+}
+
+bool EvalEmitter::bail(const SourceLocation &Loc) {
+ if (!BailLocation)
+ BailLocation = Loc;
+ return false;
+}
+
+bool EvalEmitter::jumpTrue(const LabelTy &Label) {
+ if (isActive()) {
+ if (S.Stk.pop<bool>())
+ ActiveLabel = Label;
+ }
+ return true;
+}
+
+bool EvalEmitter::jumpFalse(const LabelTy &Label) {
+ if (isActive()) {
+ if (!S.Stk.pop<bool>())
+ ActiveLabel = Label;
+ }
+ return true;
+}
+
+bool EvalEmitter::jump(const LabelTy &Label) {
+ if (isActive())
+ CurrentLabel = ActiveLabel = Label;
+ return true;
+}
+
+bool EvalEmitter::fallthrough(const LabelTy &Label) {
+ if (isActive())
+ ActiveLabel = Label;
+ CurrentLabel = Label;
+ return true;
+}
+
+template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
+ if (!isActive())
+ return true;
+ using T = typename PrimConv<OpType>::T;
+ return ReturnValue<T>(S.Stk.pop<T>(), Result);
+}
+
+bool EvalEmitter::emitRetVoid(const SourceInfo &Info) { return true; }
+
+bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
+ // Method to recursively traverse composites.
+ std::function<bool(QualType, const Pointer &, APValue &)> Composite;
+ Composite = [this, &Composite](QualType Ty, const Pointer &Ptr, APValue &R) {
+ if (auto *AT = Ty->getAs<AtomicType>())
+ Ty = AT->getValueType();
+
+ if (auto *RT = Ty->getAs<RecordType>()) {
+ auto *Record = Ptr.getRecord();
+ assert(Record && "Missing record descriptor");
+
+ bool Ok = true;
+ if (RT->getDecl()->isUnion()) {
+ const FieldDecl *ActiveField = nullptr;
+ APValue Value;
+ for (auto &F : Record->fields()) {
+ const Pointer &FP = Ptr.atField(F.Offset);
+ QualType FieldTy = F.Decl->getType();
+ if (FP.isActive()) {
+ if (llvm::Optional<PrimType> T = Ctx.classify(FieldTy)) {
+ TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), Value));
+ } else {
+ Ok &= Composite(FieldTy, FP, Value);
+ }
+ break;
+ }
+ }
+ R = APValue(ActiveField, Value);
+ } else {
+ unsigned NF = Record->getNumFields();
+ unsigned NB = Record->getNumBases();
+ unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases();
+
+ R = APValue(APValue::UninitStruct(), NB, NF);
+
+ for (unsigned I = 0; I < NF; ++I) {
+ const Record::Field *FD = Record->getField(I);
+ QualType FieldTy = FD->Decl->getType();
+ const Pointer &FP = Ptr.atField(FD->Offset);
+ APValue &Value = R.getStructField(I);
+
+ if (llvm::Optional<PrimType> T = Ctx.classify(FieldTy)) {
+ TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), Value));
+ } else {
+ Ok &= Composite(FieldTy, FP, Value);
+ }
+ }
+
+ for (unsigned I = 0; I < NB; ++I) {
+ const Record::Base *BD = Record->getBase(I);
+ QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl);
+ const Pointer &BP = Ptr.atField(BD->Offset);
+ Ok &= Composite(BaseTy, BP, R.getStructBase(I));
+ }
+
+ for (unsigned I = 0; I < NV; ++I) {
+ const Record::Base *VD = Record->getVirtualBase(I);
+ QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl);
+ const Pointer &VP = Ptr.atField(VD->Offset);
+ Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I));
+ }
+ }
+ return Ok;
+ }
+ if (auto *AT = Ty->getAsArrayTypeUnsafe()) {
+ const size_t NumElems = Ptr.getNumElems();
+ QualType ElemTy = AT->getElementType();
+ R = APValue(APValue::UninitArray{}, NumElems, NumElems);
+
+ bool Ok = true;
+ for (unsigned I = 0; I < NumElems; ++I) {
+ APValue &Slot = R.getArrayInitializedElt(I);
+ const Pointer &EP = Ptr.atIndex(I);
+ if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) {
+ TYPE_SWITCH(*T, Ok &= ReturnValue<T>(EP.deref<T>(), Slot));
+ } else {
+ Ok &= Composite(ElemTy, EP.narrow(), Slot);
+ }
+ }
+ return Ok;
+ }
+ llvm_unreachable("invalid value to return");
+ };
+
+ // Return the composite type.
+ const auto &Ptr = S.Stk.pop<Pointer>();
+ return Composite(Ptr.getType(), Ptr, Result);
+}
+
+bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) {
+ if (!isActive())
+ return true;
+
+ auto It = Locals.find(I);
+ assert(It != Locals.end() && "Missing local variable");
+ S.Stk.push<Pointer>(reinterpret_cast<Block *>(It->second.get()));
+ return true;
+}
+
+template <PrimType OpType>
+bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) {
+ if (!isActive())
+ return true;
+
+ using T = typename PrimConv<OpType>::T;
+
+ auto It = Locals.find(I);
+ assert(It != Locals.end() && "Missing local variable");
+ auto *B = reinterpret_cast<Block *>(It->second.get());
+ S.Stk.push<T>(*reinterpret_cast<T *>(B + 1));
+ return true;
+}
+
+template <PrimType OpType>
+bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) {
+ if (!isActive())
+ return true;
+
+ using T = typename PrimConv<OpType>::T;
+
+ auto It = Locals.find(I);
+ assert(It != Locals.end() && "Missing local variable");
+ auto *B = reinterpret_cast<Block *>(It->second.get());
+ *reinterpret_cast<T *>(B + 1) = S.Stk.pop<T>();
+ return true;
+}
+
+bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) {
+ if (!isActive())
+ return true;
+
+ for (auto &Local : Descriptors[I]) {
+ auto It = Locals.find(Local.Offset);
+ assert(It != Locals.end() && "Missing local variable");
+ S.deallocate(reinterpret_cast<Block *>(It->second.get()));
+ }
+
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Opcode evaluators
+//===----------------------------------------------------------------------===//
+
+#define GET_EVAL_IMPL
+#include "Opcodes.inc"
+#undef GET_EVAL_IMPL
diff --git a/lib/AST/Interp/EvalEmitter.h b/lib/AST/Interp/EvalEmitter.h
new file mode 100644
index 0000000000000..eec2ff8ee753f
--- /dev/null
+++ b/lib/AST/Interp/EvalEmitter.h
@@ -0,0 +1,129 @@
+//===--- EvalEmitter.h - Instruction emitter for the VM ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the instruction emitters.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_EVALEMITTER_H
+#define LLVM_CLANG_AST_INTERP_EVALEMITTER_H
+
+#include "ByteCodeGenError.h"
+#include "Context.h"
+#include "InterpStack.h"
+#include "InterpState.h"
+#include "PrimType.h"
+#include "Program.h"
+#include "Source.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+class FunctionDecl;
+namespace interp {
+class Context;
+class Function;
+class InterpState;
+class Program;
+class SourceInfo;
+enum Opcode : uint32_t;
+
+/// An emitter which evaluates opcodes as they are emitted.
+class EvalEmitter : public SourceMapper {
+public:
+ using LabelTy = uint32_t;
+ using AddrTy = uintptr_t;
+ using Local = Scope::Local;
+
+ llvm::Expected<bool> interpretExpr(const Expr *E);
+ llvm::Expected<bool> interpretDecl(const VarDecl *VD);
+
+protected:
+ EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk,
+ APValue &Result);
+
+ virtual ~EvalEmitter() {}
+
+ /// Define a label.
+ void emitLabel(LabelTy Label);
+ /// Create a label.
+ LabelTy getLabel();
+
+ /// Methods implemented by the compiler.
+ virtual bool visitExpr(const Expr *E) = 0;
+ virtual bool visitDecl(const VarDecl *VD) = 0;
+
+ bool bail(const Stmt *S) { return bail(S->getBeginLoc()); }
+ bool bail(const Decl *D) { return bail(D->getBeginLoc()); }
+ bool bail(const SourceLocation &Loc);
+
+ /// Emits jumps.
+ bool jumpTrue(const LabelTy &Label);
+ bool jumpFalse(const LabelTy &Label);
+ bool jump(const LabelTy &Label);
+ bool fallthrough(const LabelTy &Label);
+
+ /// Callback for registering a local.
+ Local createLocal(Descriptor *D);
+
+ /// Returns the source location of the current opcode.
+ SourceInfo getSource(Function *F, CodePtr PC) const override {
+ return F ? F->getSource(PC) : CurrentSource;
+ }
+
+ /// Parameter indices.
+ llvm::DenseMap<const ParmVarDecl *, unsigned> Params;
+ /// Local descriptors.
+ llvm::SmallVector<SmallVector<Local, 8>, 2> Descriptors;
+
+private:
+ /// Current compilation context.
+ Context &Ctx;
+ /// Current program.
+ Program &P;
+ /// Callee evaluation state.
+ InterpState S;
+ /// Location to write the result to.
+ APValue &Result;
+
+ /// Temporaries which require storage.
+ llvm::DenseMap<unsigned, std::unique_ptr<char[]>> Locals;
+
+ // The emitter always tracks the current instruction and sets OpPC to a token
+ // value which is mapped to the location of the opcode being evaluated.
+ CodePtr OpPC;
+ /// Location of a failure.
+ llvm::Optional<SourceLocation> BailLocation;
+ /// Location of the current instruction.
+ SourceInfo CurrentSource;
+
+ /// Next label ID to generate - first label is 1.
+ LabelTy NextLabel = 1;
+ /// Label being executed - 0 is the entry label.
+ LabelTy CurrentLabel = 0;
+ /// Active block which should be executed.
+ LabelTy ActiveLabel = 0;
+
+ /// Since expressions can only jump forward, predicated execution is
+ /// used to deal with if-else statements.
+ bool isActive() { return CurrentLabel == ActiveLabel; }
+
+ /// Helper to invoke a method.
+ bool ExecuteCall(Function *F, Pointer &&This, const SourceInfo &Info);
+ /// Helper to emit a diagnostic on a missing method.
+ bool ExecuteNoCall(const FunctionDecl *F, const SourceInfo &Info);
+
+protected:
+#define GET_EVAL_PROTO
+#include "Opcodes.inc"
+#undef GET_EVAL_PROTO
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/Frame.cpp b/lib/AST/Interp/Frame.cpp
new file mode 100644
index 0000000000000..16134aa1db36c
--- /dev/null
+++ b/lib/AST/Interp/Frame.cpp
@@ -0,0 +1,14 @@
+//===--- Frame.cpp - Call frame for the VM and AST Walker -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Frame.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+Frame::~Frame() {}
diff --git a/lib/AST/Interp/Frame.h b/lib/AST/Interp/Frame.h
new file mode 100644
index 0000000000000..b9a0ea9412f83
--- /dev/null
+++ b/lib/AST/Interp/Frame.h
@@ -0,0 +1,45 @@
+//===--- Frame.h - Call frame for the VM and AST Walker ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the base class of interpreter and evaluator stack frames.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_FRAME_H
+#define LLVM_CLANG_AST_INTERP_FRAME_H
+
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+class FunctionDecl;
+
+namespace interp {
+
+/// Base class for stack frames, shared between VM and walker.
+class Frame {
+public:
+ virtual ~Frame();
+
+ /// Generates a human-readable description of the call site.
+ virtual void describe(llvm::raw_ostream &OS) = 0;
+
+ /// Returns a pointer to the caller frame.
+ virtual Frame *getCaller() const = 0;
+
+ /// Returns the location of the call site.
+ virtual SourceLocation getCallLocation() const = 0;
+
+ /// Returns the called function's declaration.
+ virtual const FunctionDecl *getCallee() const = 0;
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/Function.cpp b/lib/AST/Interp/Function.cpp
new file mode 100644
index 0000000000000..0ed13a92aa38d
--- /dev/null
+++ b/lib/AST/Interp/Function.cpp
@@ -0,0 +1,48 @@
+//===--- Function.h - Bytecode function for the VM --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Function.h"
+#include "Program.h"
+#include "Opcode.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+Function::Function(Program &P, const FunctionDecl *F, unsigned ArgSize,
+ llvm::SmallVector<PrimType, 8> &&ParamTypes,
+ llvm::DenseMap<unsigned, ParamDescriptor> &&Params)
+ : P(P), Loc(F->getBeginLoc()), F(F), ArgSize(ArgSize),
+ ParamTypes(std::move(ParamTypes)), Params(std::move(Params)) {}
+
+CodePtr Function::getCodeBegin() const { return Code.data(); }
+
+CodePtr Function::getCodeEnd() const { return Code.data() + Code.size(); }
+
+Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const {
+ auto It = Params.find(Offset);
+ assert(It != Params.end() && "Invalid parameter offset");
+ return It->second;
+}
+
+SourceInfo Function::getSource(CodePtr PC) const {
+ unsigned Offset = PC - getCodeBegin();
+ using Elem = std::pair<unsigned, SourceInfo>;
+ auto It = std::lower_bound(SrcMap.begin(), SrcMap.end(), Elem{Offset, {}},
+ [](Elem A, Elem B) { return A.first < B.first; });
+ if (It == SrcMap.end() || It->first != Offset)
+ llvm::report_fatal_error("missing source location");
+ return It->second;
+}
+
+bool Function::isVirtual() const {
+ if (auto *M = dyn_cast<CXXMethodDecl>(F))
+ return M->isVirtual();
+ return false;
+}
diff --git a/lib/AST/Interp/Function.h b/lib/AST/Interp/Function.h
new file mode 100644
index 0000000000000..28531f04b6e95
--- /dev/null
+++ b/lib/AST/Interp/Function.h
@@ -0,0 +1,163 @@
+//===--- Function.h - Bytecode function for the VM --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the Function class which holds all bytecode function-specific data.
+//
+// The scope class which describes local variables is also defined here.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_FUNCTION_H
+#define LLVM_CLANG_AST_INTERP_FUNCTION_H
+
+#include "Pointer.h"
+#include "Source.h"
+#include "clang/AST/Decl.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace interp {
+class Program;
+class ByteCodeEmitter;
+enum PrimType : uint32_t;
+
+/// Describes a scope block.
+///
+/// The block gathers all the descriptors of the locals defined in this block.
+class Scope {
+public:
+ /// Information about a local's storage.
+ struct Local {
+ /// Offset of the local in frame.
+ unsigned Offset;
+ /// Descriptor of the local.
+ Descriptor *Desc;
+ };
+
+ using LocalVectorTy = llvm::SmallVector<Local, 8>;
+
+ Scope(LocalVectorTy &&Descriptors) : Descriptors(std::move(Descriptors)) {}
+
+ llvm::iterator_range<LocalVectorTy::iterator> locals() {
+ return llvm::make_range(Descriptors.begin(), Descriptors.end());
+ }
+
+private:
+ /// Object descriptors in this block.
+ LocalVectorTy Descriptors;
+};
+
+/// Bytecode function.
+///
+/// Contains links to the bytecode of the function, as well as metadata
+/// describing all arguments and stack-local variables.
+class Function {
+public:
+ using ParamDescriptor = std::pair<PrimType, Descriptor *>;
+
+ /// Returns the size of the function's local stack.
+ unsigned getFrameSize() const { return FrameSize; }
+ /// Returns the size of the argument stackx
+ unsigned getArgSize() const { return ArgSize; }
+
+ /// Returns a pointer to the start of the code.
+ CodePtr getCodeBegin() const;
+ /// Returns a pointer to the end of the code.
+ CodePtr getCodeEnd() const;
+
+ /// Returns the original FunctionDecl.
+ const FunctionDecl *getDecl() const { return F; }
+
+ /// Returns the lcoation.
+ SourceLocation getLoc() const { return Loc; }
+
+ /// Returns a parameter descriptor.
+ ParamDescriptor getParamDescriptor(unsigned Offset) const;
+
+ /// Checks if the first argument is a RVO pointer.
+ bool hasRVO() const { return ParamTypes.size() != Params.size(); }
+
+ /// Range over the scope blocks.
+ llvm::iterator_range<llvm::SmallVector<Scope, 2>::iterator> scopes() {
+ return llvm::make_range(Scopes.begin(), Scopes.end());
+ }
+
+ /// Range over argument types.
+ using arg_reverse_iterator = SmallVectorImpl<PrimType>::reverse_iterator;
+ llvm::iterator_range<arg_reverse_iterator> args_reverse() {
+ return llvm::make_range(ParamTypes.rbegin(), ParamTypes.rend());
+ }
+
+ /// Returns a specific scope.
+ Scope &getScope(unsigned Idx) { return Scopes[Idx]; }
+
+ /// Returns the source information at a given PC.
+ SourceInfo getSource(CodePtr PC) const;
+
+ /// Checks if the function is valid to call in constexpr.
+ bool isConstexpr() const { return IsValid; }
+
+ /// Checks if the function is virtual.
+ bool isVirtual() const;
+
+ /// Checks if the function is a constructor.
+ bool isConstructor() const { return isa<CXXConstructorDecl>(F); }
+
+private:
+ /// Construct a function representing an actual function.
+ Function(Program &P, const FunctionDecl *F, unsigned ArgSize,
+ llvm::SmallVector<PrimType, 8> &&ParamTypes,
+ llvm::DenseMap<unsigned, ParamDescriptor> &&Params);
+
+ /// Sets the code of a function.
+ void setCode(unsigned NewFrameSize, std::vector<char> &&NewCode, SourceMap &&NewSrcMap,
+ llvm::SmallVector<Scope, 2> &&NewScopes) {
+ FrameSize = NewFrameSize;
+ Code = std::move(NewCode);
+ SrcMap = std::move(NewSrcMap);
+ Scopes = std::move(NewScopes);
+ IsValid = true;
+ }
+
+private:
+ friend class Program;
+ friend class ByteCodeEmitter;
+
+ /// Program reference.
+ Program &P;
+ /// Location of the executed code.
+ SourceLocation Loc;
+ /// Declaration this function was compiled from.
+ const FunctionDecl *F;
+ /// Local area size: storage + metadata.
+ unsigned FrameSize;
+ /// Size of the argument stack.
+ unsigned ArgSize;
+ /// Program code.
+ std::vector<char> Code;
+ /// Opcode-to-expression mapping.
+ SourceMap SrcMap;
+ /// List of block descriptors.
+ llvm::SmallVector<Scope, 2> Scopes;
+ /// List of argument types.
+ llvm::SmallVector<PrimType, 8> ParamTypes;
+ /// Map from byte offset to parameter descriptor.
+ llvm::DenseMap<unsigned, ParamDescriptor> Params;
+ /// Flag to indicate if the function is valid.
+ bool IsValid = false;
+
+public:
+ /// Dumps the disassembled bytecode to \c llvm::errs().
+ void dump() const;
+ void dump(llvm::raw_ostream &OS) const;
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/Integral.h b/lib/AST/Interp/Integral.h
new file mode 100644
index 0000000000000..7cc788070de84
--- /dev/null
+++ b/lib/AST/Interp/Integral.h
@@ -0,0 +1,269 @@
+//===--- Integral.h - Wrapper for numeric types for the VM ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the VM types and helpers operating on types.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_INTEGRAL_H
+#define LLVM_CLANG_AST_INTERP_INTEGRAL_H
+
+#include "clang/AST/ComparisonCategories.h"
+#include "clang/AST/APValue.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstddef>
+#include <cstdint>
+
+namespace clang {
+namespace interp {
+
+using APInt = llvm::APInt;
+using APSInt = llvm::APSInt;
+
+/// Helper to compare two comparable types.
+template <typename T>
+ComparisonCategoryResult Compare(const T &X, const T &Y) {
+ if (X < Y)
+ return ComparisonCategoryResult::Less;
+ if (X > Y)
+ return ComparisonCategoryResult::Greater;
+ return ComparisonCategoryResult::Equal;
+}
+
+// Helper structure to select the representation.
+template <unsigned Bits, bool Signed> struct Repr;
+template <> struct Repr<8, false> { using Type = uint8_t; };
+template <> struct Repr<16, false> { using Type = uint16_t; };
+template <> struct Repr<32, false> { using Type = uint32_t; };
+template <> struct Repr<64, false> { using Type = uint64_t; };
+template <> struct Repr<8, true> { using Type = int8_t; };
+template <> struct Repr<16, true> { using Type = int16_t; };
+template <> struct Repr<32, true> { using Type = int32_t; };
+template <> struct Repr<64, true> { using Type = int64_t; };
+
+/// Wrapper around numeric types.
+///
+/// These wrappers are required to shared an interface between APSint and
+/// builtin primitive numeral types, while optimising for storage and
+/// allowing methods operating on primitive type to compile to fast code.
+template <unsigned Bits, bool Signed> class Integral {
+private:
+ template <unsigned OtherBits, bool OtherSigned> friend class Integral;
+
+ // The primitive representing the integral.
+ using T = typename Repr<Bits, Signed>::Type;
+ T V;
+
+ /// Primitive representing limits.
+ static const auto Min = std::numeric_limits<T>::min();
+ static const auto Max = std::numeric_limits<T>::max();
+
+ /// Construct an integral from anything that is convertible to storage.
+ template <typename T> explicit Integral(T V) : V(V) {}
+
+public:
+ /// Zero-initializes an integral.
+ Integral() : V(0) {}
+
+ /// Constructs an integral from another integral.
+ template <unsigned SrcBits, bool SrcSign>
+ explicit Integral(Integral<SrcBits, SrcSign> V) : V(V.V) {}
+
+ /// Construct an integral from a value based on signedness.
+ explicit Integral(const APSInt &V)
+ : V(V.isSigned() ? V.getSExtValue() : V.getZExtValue()) {}
+
+ bool operator<(Integral RHS) const { return V < RHS.V; }
+ bool operator>(Integral RHS) const { return V > RHS.V; }
+ bool operator<=(Integral RHS) const { return V <= RHS.V; }
+ bool operator>=(Integral RHS) const { return V >= RHS.V; }
+ bool operator==(Integral RHS) const { return V == RHS.V; }
+ bool operator!=(Integral RHS) const { return V != RHS.V; }
+
+ bool operator>(unsigned RHS) const {
+ return V >= 0 && static_cast<unsigned>(V) > RHS;
+ }
+
+ Integral operator-() const { return Integral(-V); }
+ Integral operator~() const { return Integral(~V); }
+
+ template <unsigned DstBits, bool DstSign>
+ explicit operator Integral<DstBits, DstSign>() const {
+ return Integral<DstBits, DstSign>(V);
+ }
+
+ explicit operator unsigned() const { return V; }
+ explicit operator int64_t() const { return V; }
+ explicit operator uint64_t() const { return V; }
+
+ APSInt toAPSInt() const {
+ return APSInt(APInt(Bits, static_cast<uint64_t>(V), Signed), !Signed);
+ }
+ APSInt toAPSInt(unsigned NumBits) const {
+ if (Signed)
+ return APSInt(toAPSInt().sextOrTrunc(NumBits), !Signed);
+ else
+ return APSInt(toAPSInt().zextOrTrunc(NumBits), !Signed);
+ }
+ APValue toAPValue() const { return APValue(toAPSInt()); }
+
+ Integral<Bits, false> toUnsigned() const {
+ return Integral<Bits, false>(*this);
+ }
+
+ constexpr static unsigned bitWidth() { return Bits; }
+
+ bool isZero() const { return !V; }
+
+ bool isMin() const { return *this == min(bitWidth()); }
+
+ bool isMinusOne() const { return Signed && V == T(-1); }
+
+ constexpr static bool isSigned() { return Signed; }
+
+ bool isNegative() const { return V < T(0); }
+ bool isPositive() const { return !isNegative(); }
+
+ ComparisonCategoryResult compare(const Integral &RHS) const {
+ return Compare(V, RHS.V);
+ }
+
+ unsigned countLeadingZeros() const { return llvm::countLeadingZeros<T>(V); }
+
+ Integral truncate(unsigned TruncBits) const {
+ if (TruncBits >= Bits)
+ return *this;
+ const T BitMask = (T(1) << T(TruncBits)) - 1;
+ const T SignBit = T(1) << (TruncBits - 1);
+ const T ExtMask = ~BitMask;
+ return Integral((V & BitMask) | (Signed && (V & SignBit) ? ExtMask : 0));
+ }
+
+ void print(llvm::raw_ostream &OS) const { OS << V; }
+
+ static Integral min(unsigned NumBits) {
+ return Integral(Min);
+ }
+ static Integral max(unsigned NumBits) {
+ return Integral(Max);
+ }
+
+ template <typename T>
+ static typename std::enable_if<std::is_integral<T>::value, Integral>::type
+ from(T Value) {
+ return Integral(Value);
+ }
+
+ template <unsigned SrcBits, bool SrcSign>
+ static typename std::enable_if<SrcBits != 0, Integral>::type
+ from(Integral<SrcBits, SrcSign> Value) {
+ return Integral(Value.V);
+ }
+
+ template <bool SrcSign> static Integral from(Integral<0, SrcSign> Value) {
+ if (SrcSign)
+ return Integral(Value.V.getSExtValue());
+ else
+ return Integral(Value.V.getZExtValue());
+ }
+
+ static Integral zero() { return from(0); }
+
+ template <typename T> static Integral from(T Value, unsigned NumBits) {
+ return Integral(Value);
+ }
+
+ static bool inRange(int64_t Value, unsigned NumBits) {
+ return CheckRange<T, Min, Max>(Value);
+ }
+
+ static bool increment(Integral A, Integral *R) {
+ return add(A, Integral(T(1)), A.bitWidth(), R);
+ }
+
+ static bool decrement(Integral A, Integral *R) {
+ return sub(A, Integral(T(1)), A.bitWidth(), R);
+ }
+
+ static bool add(Integral A, Integral B, unsigned OpBits, Integral *R) {
+ return CheckAddUB(A.V, B.V, R->V);
+ }
+
+ static bool sub(Integral A, Integral B, unsigned OpBits, Integral *R) {
+ return CheckSubUB(A.V, B.V, R->V);
+ }
+
+ static bool mul(Integral A, Integral B, unsigned OpBits, Integral *R) {
+ return CheckMulUB(A.V, B.V, R->V);
+ }
+
+private:
+ template <typename T>
+ static typename std::enable_if<std::is_signed<T>::value, bool>::type
+ CheckAddUB(T A, T B, T &R) {
+ return llvm::AddOverflow<T>(A, B, R);
+ }
+
+ template <typename T>
+ static typename std::enable_if<std::is_unsigned<T>::value, bool>::type
+ CheckAddUB(T A, T B, T &R) {
+ R = A + B;
+ return false;
+ }
+
+ template <typename T>
+ static typename std::enable_if<std::is_signed<T>::value, bool>::type
+ CheckSubUB(T A, T B, T &R) {
+ return llvm::SubOverflow<T>(A, B, R);
+ }
+
+ template <typename T>
+ static typename std::enable_if<std::is_unsigned<T>::value, bool>::type
+ CheckSubUB(T A, T B, T &R) {
+ R = A - B;
+ return false;
+ }
+
+ template <typename T>
+ static typename std::enable_if<std::is_signed<T>::value, bool>::type
+ CheckMulUB(T A, T B, T &R) {
+ return llvm::MulOverflow<T>(A, B, R);
+ }
+
+ template <typename T>
+ static typename std::enable_if<std::is_unsigned<T>::value, bool>::type
+ CheckMulUB(T A, T B, T &R) {
+ R = A * B;
+ return false;
+ }
+
+ template <typename T, T Min, T Max>
+ static typename std::enable_if<std::is_signed<T>::value, bool>::type
+ CheckRange(int64_t V) {
+ return Min <= V && V <= Max;
+ }
+
+ template <typename T, T Min, T Max>
+ static typename std::enable_if<std::is_unsigned<T>::value, bool>::type
+ CheckRange(int64_t V) {
+ return V >= 0 && static_cast<uint64_t>(V) <= Max;
+ }
+};
+
+template <unsigned Bits, bool Signed>
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Integral<Bits, Signed> I) {
+ I.print(OS);
+ return OS;
+}
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/Interp.cpp b/lib/AST/Interp/Interp.cpp
new file mode 100644
index 0000000000000..1a8109cedf769
--- /dev/null
+++ b/lib/AST/Interp/Interp.cpp
@@ -0,0 +1,417 @@
+//===--- InterpState.cpp - Interpreter for the constexpr VM -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Interp.h"
+#include <limits>
+#include <vector>
+#include "Function.h"
+#include "InterpFrame.h"
+#include "InterpStack.h"
+#include "Opcode.h"
+#include "PrimType.h"
+#include "Program.h"
+#include "State.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTDiagnostic.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "llvm/ADT/APSInt.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+//===----------------------------------------------------------------------===//
+// Ret
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+static bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
+ S.CallStackDepth--;
+ const T &Ret = S.Stk.pop<T>();
+
+ assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
+ if (!S.checkingPotentialConstantExpression())
+ S.Current->popArgs();
+
+ if (InterpFrame *Caller = S.Current->Caller) {
+ PC = S.Current->getRetPC();
+ delete S.Current;
+ S.Current = Caller;
+ S.Stk.push<T>(Ret);
+ } else {
+ delete S.Current;
+ S.Current = nullptr;
+ if (!ReturnValue<T>(Ret, Result))
+ return false;
+ }
+ return true;
+}
+
+static bool RetVoid(InterpState &S, CodePtr &PC, APValue &Result) {
+ S.CallStackDepth--;
+
+ assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
+ if (!S.checkingPotentialConstantExpression())
+ S.Current->popArgs();
+
+ if (InterpFrame *Caller = S.Current->Caller) {
+ PC = S.Current->getRetPC();
+ delete S.Current;
+ S.Current = Caller;
+ } else {
+ delete S.Current;
+ S.Current = nullptr;
+ }
+ return true;
+}
+
+static bool RetValue(InterpState &S, CodePtr &Pt, APValue &Result) {
+ llvm::report_fatal_error("Interpreter cannot return values");
+}
+
+//===----------------------------------------------------------------------===//
+// Jmp, Jt, Jf
+//===----------------------------------------------------------------------===//
+
+static bool Jmp(InterpState &S, CodePtr &PC, int32_t Offset) {
+ PC += Offset;
+ return true;
+}
+
+static bool Jt(InterpState &S, CodePtr &PC, int32_t Offset) {
+ if (S.Stk.pop<bool>()) {
+ PC += Offset;
+ }
+ return true;
+}
+
+static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) {
+ if (!S.Stk.pop<bool>()) {
+ PC += Offset;
+ }
+ return true;
+}
+
+static bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ AccessKinds AK) {
+ if (Ptr.isInitialized())
+ return true;
+ if (!S.checkingPotentialConstantExpression()) {
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.FFDiag(Loc, diag::note_constexpr_access_uninit) << AK << false;
+ }
+ return false;
+}
+
+static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ AccessKinds AK) {
+ if (Ptr.isActive())
+ return true;
+
+ // Get the inactive field descriptor.
+ const FieldDecl *InactiveField = Ptr.getField();
+
+ // Walk up the pointer chain to find the union which is not active.
+ Pointer U = Ptr.getBase();
+ while (!U.isActive()) {
+ U = U.getBase();
+ }
+
+ // Find the active field of the union.
+ Record *R = U.getRecord();
+ assert(R && R->isUnion() && "Not a union");
+ const FieldDecl *ActiveField = nullptr;
+ for (unsigned I = 0, N = R->getNumFields(); I < N; ++I) {
+ const Pointer &Field = U.atField(R->getField(I)->Offset);
+ if (Field.isActive()) {
+ ActiveField = Field.getField();
+ break;
+ }
+ }
+
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.FFDiag(Loc, diag::note_constexpr_access_inactive_union_member)
+ << AK << InactiveField << !ActiveField << ActiveField;
+ return false;
+}
+
+static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ AccessKinds AK) {
+ if (auto ID = Ptr.getDeclID()) {
+ if (!Ptr.isStaticTemporary())
+ return true;
+
+ if (Ptr.getDeclDesc()->getType().isConstQualified())
+ return true;
+
+ if (S.P.getCurrentDecl() == ID)
+ return true;
+
+ const SourceInfo &E = S.Current->getSource(OpPC);
+ S.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;
+ S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
+ return false;
+ }
+ return true;
+}
+
+static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ if (auto ID = Ptr.getDeclID()) {
+ if (!Ptr.isStatic())
+ return true;
+
+ if (S.P.getCurrentDecl() == ID)
+ return true;
+
+ S.FFDiag(S.Current->getLocation(OpPC), diag::note_constexpr_modify_global);
+ return false;
+ }
+ return true;
+}
+
+namespace clang {
+namespace interp {
+
+bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ if (!Ptr.isExtern())
+ return true;
+
+ if (!S.checkingPotentialConstantExpression()) {
+ auto *VD = Ptr.getDeclDesc()->asValueDecl();
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.FFDiag(Loc, diag::note_constexpr_ltor_non_constexpr, 1) << VD;
+ S.Note(VD->getLocation(), diag::note_declared_at);
+ }
+ return false;
+}
+
+bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ if (!Ptr.isUnknownSizeArray())
+ return true;
+ const SourceInfo &E = S.Current->getSource(OpPC);
+ S.FFDiag(E, diag::note_constexpr_unsized_array_indexed);
+ return false;
+}
+
+bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ AccessKinds AK) {
+ const auto &Src = S.Current->getSource(OpPC);
+ if (Ptr.isZero()) {
+
+ if (Ptr.isField())
+ S.FFDiag(Src, diag::note_constexpr_null_subobject) << CSK_Field;
+ else
+ S.FFDiag(Src, diag::note_constexpr_access_null) << AK;
+
+ return false;
+ }
+
+ if (!Ptr.isLive()) {
+ bool IsTemp = Ptr.isTemporary();
+
+ S.FFDiag(Src, diag::note_constexpr_lifetime_ended, 1) << AK << !IsTemp;
+
+ if (IsTemp)
+ S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
+ else
+ S.Note(Ptr.getDeclLoc(), diag::note_declared_at);
+
+ return false;
+ }
+
+ return true;
+}
+
+bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ CheckSubobjectKind CSK) {
+ if (!Ptr.isZero())
+ return true;
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.FFDiag(Loc, diag::note_constexpr_null_subobject) << CSK;
+ return false;
+}
+
+bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ AccessKinds AK) {
+ if (!Ptr.isOnePastEnd())
+ return true;
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.FFDiag(Loc, diag::note_constexpr_access_past_end) << AK;
+ return false;
+}
+
+bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ CheckSubobjectKind CSK) {
+ if (!Ptr.isElementPastEnd())
+ return true;
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.FFDiag(Loc, diag::note_constexpr_past_end_subobject) << CSK;
+ return false;
+}
+
+bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ assert(Ptr.isLive() && "Pointer is not live");
+ if (!Ptr.isConst()) {
+ return true;
+ }
+
+ const QualType Ty = Ptr.getType();
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.FFDiag(Loc, diag::note_constexpr_modify_const_type) << Ty;
+ return false;
+}
+
+bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ assert(Ptr.isLive() && "Pointer is not live");
+ if (!Ptr.isMutable()) {
+ return true;
+ }
+
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ const FieldDecl *Field = Ptr.getField();
+ S.FFDiag(Loc, diag::note_constexpr_access_mutable, 1) << AK_Read << Field;
+ S.Note(Field->getLocation(), diag::note_declared_at);
+ return false;
+}
+
+bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ if (!CheckLive(S, OpPC, Ptr, AK_Read))
+ return false;
+ if (!CheckExtern(S, OpPC, Ptr))
+ return false;
+ if (!CheckRange(S, OpPC, Ptr, AK_Read))
+ return false;
+ if (!CheckInitialized(S, OpPC, Ptr, AK_Read))
+ return false;
+ if (!CheckActive(S, OpPC, Ptr, AK_Read))
+ return false;
+ if (!CheckTemporary(S, OpPC, Ptr, AK_Read))
+ return false;
+ if (!CheckMutable(S, OpPC, Ptr))
+ return false;
+ return true;
+}
+
+bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ if (!CheckLive(S, OpPC, Ptr, AK_Assign))
+ return false;
+ if (!CheckExtern(S, OpPC, Ptr))
+ return false;
+ if (!CheckRange(S, OpPC, Ptr, AK_Assign))
+ return false;
+ if (!CheckGlobal(S, OpPC, Ptr))
+ return false;
+ if (!CheckConst(S, OpPC, Ptr))
+ return false;
+ return true;
+}
+
+bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
+ return false;
+ if (!CheckExtern(S, OpPC, Ptr))
+ return false;
+ if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
+ return false;
+ return true;
+}
+
+bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ if (!CheckLive(S, OpPC, Ptr, AK_Assign))
+ return false;
+ if (!CheckRange(S, OpPC, Ptr, AK_Assign))
+ return false;
+ return true;
+}
+
+bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F) {
+ const SourceLocation &Loc = S.Current->getLocation(OpPC);
+
+ if (F->isVirtual()) {
+ if (!S.getLangOpts().CPlusPlus2a) {
+ S.CCEDiag(Loc, diag::note_constexpr_virtual_call);
+ return false;
+ }
+ }
+
+ if (!F->isConstexpr()) {
+ if (S.getLangOpts().CPlusPlus11) {
+ const FunctionDecl *DiagDecl = F->getDecl();
+
+ // If this function is not constexpr because it is an inherited
+ // non-constexpr constructor, diagnose that directly.
+ auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl);
+ if (CD && CD->isInheritingConstructor()) {
+ auto *Inherited = CD->getInheritedConstructor().getConstructor();
+ if (!Inherited->isConstexpr())
+ DiagDecl = CD = Inherited;
+ }
+
+ // FIXME: If DiagDecl is an implicitly-declared special member function
+ // or an inheriting constructor, we should be much more explicit about why
+ // it's not constexpr.
+ if (CD && CD->isInheritingConstructor())
+ S.FFDiag(Loc, diag::note_constexpr_invalid_inhctor, 1)
+ << CD->getInheritedConstructor().getConstructor()->getParent();
+ else
+ S.FFDiag(Loc, diag::note_constexpr_invalid_function, 1)
+ << DiagDecl->isConstexpr() << (bool)CD << DiagDecl;
+ S.Note(DiagDecl->getLocation(), diag::note_declared_at);
+ } else {
+ S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This) {
+ if (!This.isZero())
+ return true;
+
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+
+ bool IsImplicit = false;
+ if (auto *E = dyn_cast_or_null<CXXThisExpr>(Loc.asExpr()))
+ IsImplicit = E->isImplicit();
+
+ if (S.getLangOpts().CPlusPlus11)
+ S.FFDiag(Loc, diag::note_constexpr_this) << IsImplicit;
+ else
+ S.FFDiag(Loc);
+
+ return false;
+}
+
+bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) {
+ if (!MD->isPure())
+ return true;
+ const SourceInfo &E = S.Current->getSource(OpPC);
+ S.FFDiag(E, diag::note_constexpr_pure_virtual_call, 1) << MD;
+ S.Note(MD->getLocation(), diag::note_declared_at);
+ return false;
+}
+bool Interpret(InterpState &S, APValue &Result) {
+ CodePtr PC = S.Current->getPC();
+
+ for (;;) {
+ auto Op = PC.read<Opcode>();
+ CodePtr OpPC = PC;
+
+ switch (Op) {
+#define GET_INTERP
+#include "Opcodes.inc"
+#undef GET_INTERP
+ }
+ }
+}
+
+} // namespace interp
+} // namespace clang
diff --git a/lib/AST/Interp/Interp.h b/lib/AST/Interp/Interp.h
new file mode 100644
index 0000000000000..8934efa13b9ce
--- /dev/null
+++ b/lib/AST/Interp/Interp.h
@@ -0,0 +1,960 @@
+//===--- Interp.h - Interpreter for the constexpr VM ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Definition of the interpreter state and entry point.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_INTERP_H
+#define LLVM_CLANG_AST_INTERP_INTERP_H
+
+#include <limits>
+#include <vector>
+#include "Function.h"
+#include "InterpFrame.h"
+#include "InterpStack.h"
+#include "InterpState.h"
+#include "Opcode.h"
+#include "PrimType.h"
+#include "Program.h"
+#include "State.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTDiagnostic.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Expr.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/Support/Endian.h"
+
+namespace clang {
+namespace interp {
+
+using APInt = llvm::APInt;
+using APSInt = llvm::APSInt;
+
+/// Convers a value to an APValue.
+template <typename T> bool ReturnValue(const T &V, APValue &R) {
+ R = V.toAPValue();
+ return true;
+}
+
+/// Checks if the variable has externally defined storage.
+bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+
+/// Checks if the array is offsetable.
+bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+
+/// Checks if a pointer is live and accesible.
+bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ AccessKinds AK);
+/// Checks if a pointer is null.
+bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ CheckSubobjectKind CSK);
+
+/// Checks if a pointer is in range.
+bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ AccessKinds AK);
+
+/// Checks if a field from which a pointer is going to be derived is valid.
+bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ CheckSubobjectKind CSK);
+
+/// Checks if a pointer points to const storage.
+bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+
+/// Checks if a pointer points to a mutable field.
+bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+
+/// Checks if a value can be loaded from a block.
+bool CheckLoad(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, Function *F);
+
+/// Checks the 'this' pointer.
+bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This);
+
+/// Checks if a method is pure virtual.
+bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD);
+
+template <typename T> inline bool IsTrue(const T &V) { return !V.isZero(); }
+
+//===----------------------------------------------------------------------===//
+// Add, Sub, Mul
+//===----------------------------------------------------------------------===//
+
+template <typename T, bool (*OpFW)(T, T, unsigned, T *),
+ template <typename U> class OpAP>
+bool AddSubMulHelper(InterpState &S, CodePtr OpPC, unsigned Bits, const T &LHS,
+ const T &RHS) {
+ // Fast path - add the numbers with fixed width.
+ T Result;
+ if (!OpFW(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+
+ // If for some reason evaluation continues, use the truncated results.
+ S.Stk.push<T>(Result);
+
+ // Slow path - compute the result using another bit of precision.
+ APSInt Value = OpAP<APSInt>()(LHS.toAPSInt(Bits), RHS.toAPSInt(Bits));
+
+ // Report undefined behaviour, stopping if required.
+ const Expr *E = S.Current->getExpr(OpPC);
+ QualType Type = E->getType();
+ if (S.checkingForUndefinedBehavior()) {
+ auto Trunc = Value.trunc(Result.bitWidth()).toString(10);
+ auto Loc = E->getExprLoc();
+ S.report(Loc, diag::warn_integer_constant_overflow) << Trunc << Type;
+ return true;
+ } else {
+ S.CCEDiag(E, diag::note_constexpr_overflow) << Value << Type;
+ return S.noteUndefinedBehavior();
+ }
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Add(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+ const unsigned Bits = RHS.bitWidth() + 1;
+ return AddSubMulHelper<T, T::add, std::plus>(S, OpPC, Bits, LHS, RHS);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Sub(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+ const unsigned Bits = RHS.bitWidth() + 1;
+ return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, Bits, LHS, RHS);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Mul(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+ const unsigned Bits = RHS.bitWidth() * 2;
+ return AddSubMulHelper<T, T::mul, std::multiplies>(S, OpPC, Bits, LHS, RHS);
+}
+
+//===----------------------------------------------------------------------===//
+// EQ, NE, GT, GE, LT, LE
+//===----------------------------------------------------------------------===//
+
+using CompareFn = llvm::function_ref<bool(ComparisonCategoryResult)>;
+
+template <typename T>
+bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) {
+ using BoolT = PrimConv<PT_Bool>::T;
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+ S.Stk.push<BoolT>(BoolT::from(Fn(LHS.compare(RHS))));
+ return true;
+}
+
+template <typename T>
+bool CmpHelperEQ(InterpState &S, CodePtr OpPC, CompareFn Fn) {
+ return CmpHelper<T>(S, OpPC, Fn);
+}
+
+template <>
+inline bool CmpHelper<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
+ using BoolT = PrimConv<PT_Bool>::T;
+ const Pointer &RHS = S.Stk.pop<Pointer>();
+ const Pointer &LHS = S.Stk.pop<Pointer>();
+
+ if (!Pointer::hasSameBase(LHS, RHS)) {
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr);
+ return false;
+ } else {
+ unsigned VL = LHS.getByteOffset();
+ unsigned VR = RHS.getByteOffset();
+ S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR))));
+ return true;
+ }
+}
+
+template <>
+inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
+ using BoolT = PrimConv<PT_Bool>::T;
+ const Pointer &RHS = S.Stk.pop<Pointer>();
+ const Pointer &LHS = S.Stk.pop<Pointer>();
+
+ if (LHS.isZero() || RHS.isZero()) {
+ if (LHS.isZero() && RHS.isZero())
+ S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Equal)));
+ else
+ S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Nonequal)));
+ return true;
+ }
+
+ if (!Pointer::hasSameBase(LHS, RHS)) {
+ S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Unordered)));
+ return true;
+ } else {
+ unsigned VL = LHS.getByteOffset();
+ unsigned VR = RHS.getByteOffset();
+ S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR))));
+ return true;
+ }
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool EQ(InterpState &S, CodePtr OpPC) {
+ return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) {
+ return R == ComparisonCategoryResult::Equal;
+ });
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool NE(InterpState &S, CodePtr OpPC) {
+ return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) {
+ return R != ComparisonCategoryResult::Equal;
+ });
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool LT(InterpState &S, CodePtr OpPC) {
+ return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
+ return R == ComparisonCategoryResult::Less;
+ });
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool LE(InterpState &S, CodePtr OpPC) {
+ return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
+ return R == ComparisonCategoryResult::Less ||
+ R == ComparisonCategoryResult::Equal;
+ });
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GT(InterpState &S, CodePtr OpPC) {
+ return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
+ return R == ComparisonCategoryResult::Greater;
+ });
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GE(InterpState &S, CodePtr OpPC) {
+ return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
+ return R == ComparisonCategoryResult::Greater ||
+ R == ComparisonCategoryResult::Equal;
+ });
+}
+
+//===----------------------------------------------------------------------===//
+// InRange
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InRange(InterpState &S, CodePtr OpPC) {
+ const T RHS = S.Stk.pop<T>();
+ const T LHS = S.Stk.pop<T>();
+ const T Value = S.Stk.pop<T>();
+
+ S.Stk.push<bool>(LHS <= Value && Value <= RHS);
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Dup, Pop, Test
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Dup(InterpState &S, CodePtr OpPC) {
+ S.Stk.push<T>(S.Stk.peek<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Pop(InterpState &S, CodePtr OpPC) {
+ S.Stk.pop<T>();
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Const
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Const(InterpState &S, CodePtr OpPC, const T &Arg) {
+ S.Stk.push<T>(Arg);
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Get/Set Local/Param/Global/This
+//===----------------------------------------------------------------------===//
+
+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));
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool SetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
+ S.Current->setLocal<T>(I, S.Stk.pop<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GetParam(InterpState &S, CodePtr OpPC, uint32_t I) {
+ if (S.checkingPotentialConstantExpression()) {
+ return false;
+ }
+ S.Stk.push<T>(S.Current->getParam<T>(I));
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool SetParam(InterpState &S, CodePtr OpPC, uint32_t I) {
+ S.Current->setParam<T>(I, S.Stk.pop<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) {
+ const Pointer &Obj = S.Stk.peek<Pointer>();
+ if (!CheckNull(S, OpPC, Obj, CSK_Field))
+ return false;
+ if (!CheckRange(S, OpPC, Obj, CSK_Field))
+ return false;
+ const Pointer &Field = Obj.atField(I);
+ if (!CheckLoad(S, OpPC, Field))
+ return false;
+ S.Stk.push<T>(Field.deref<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool SetField(InterpState &S, CodePtr OpPC, uint32_t I) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Obj = S.Stk.peek<Pointer>();
+ if (!CheckNull(S, OpPC, Obj, CSK_Field))
+ return false;
+ if (!CheckRange(S, OpPC, Obj, CSK_Field))
+ return false;
+ const Pointer &Field = Obj.atField(I);
+ if (!CheckStore(S, OpPC, Field))
+ return false;
+ Field.deref<T>() = Value;
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) {
+ const Pointer &Obj = S.Stk.pop<Pointer>();
+ if (!CheckNull(S, OpPC, Obj, CSK_Field))
+ return false;
+ if (!CheckRange(S, OpPC, Obj, CSK_Field))
+ return false;
+ const Pointer &Field = Obj.atField(I);
+ if (!CheckLoad(S, OpPC, Field))
+ return false;
+ S.Stk.push<T>(Field.deref<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GetThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ const Pointer &Field = This.atField(I);
+ if (!CheckLoad(S, OpPC, Field))
+ return false;
+ S.Stk.push<T>(Field.deref<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool SetThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ const Pointer &Field = This.atField(I);
+ if (!CheckStore(S, OpPC, Field))
+ return false;
+ Field.deref<T>() = Value;
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
+ auto *B = S.P.getGlobal(I);
+ if (B->isExtern())
+ return false;
+ S.Stk.push<T>(B->deref<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool SetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
+ // TODO: emit warning.
+ return false;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
+ S.P.getGlobal(I)->deref<T>() = S.Stk.pop<T>();
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ const Pointer &Field = This.atField(I);
+ Field.deref<T>() = S.Stk.pop<T>();
+ Field.initialize();
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ const Pointer &Field = This.atField(F->Offset);
+ const auto &Value = S.Stk.pop<T>();
+ Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx()));
+ Field.initialize();
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitThisFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ const Pointer &Field = This.atField(I);
+ Field.deref<T>() = S.Stk.pop<T>();
+ Field.activate();
+ Field.initialize();
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Field = S.Stk.pop<Pointer>().atField(I);
+ Field.deref<T>() = Value;
+ Field.activate();
+ Field.initialize();
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Field = S.Stk.pop<Pointer>().atField(F->Offset);
+ Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx()));
+ Field.activate();
+ Field.initialize();
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ const Pointer &Field = Ptr.atField(I);
+ Field.deref<T>() = Value;
+ Field.activate();
+ Field.initialize();
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// GetPtr Local/Param/Global/Field/This
+//===----------------------------------------------------------------------===//
+
+inline bool GetPtrLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
+ S.Stk.push<Pointer>(S.Current->getLocalPointer(I));
+ return true;
+}
+
+inline bool GetPtrParam(InterpState &S, CodePtr OpPC, uint32_t I) {
+ if (S.checkingPotentialConstantExpression()) {
+ return false;
+ }
+ S.Stk.push<Pointer>(S.Current->getParamPointer(I));
+ return true;
+}
+
+inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
+ S.Stk.push<Pointer>(S.P.getPtrGlobal(I));
+ return true;
+}
+
+inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckNull(S, OpPC, Ptr, CSK_Field))
+ return false;
+ if (!CheckExtern(S, OpPC, Ptr))
+ return false;
+ if (!CheckRange(S, OpPC, Ptr, CSK_Field))
+ return false;
+ S.Stk.push<Pointer>(Ptr.atField(Off));
+ return true;
+}
+
+inline bool GetPtrThisField(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;
+ S.Stk.push<Pointer>(This.atField(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 GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckNull(S, OpPC, Ptr, CSK_Base))
+ return false;
+ S.Stk.push<Pointer>(Ptr.atField(Off));
+ return true;
+}
+
+inline bool GetPtrThisBase(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;
+ S.Stk.push<Pointer>(This.atField(Off));
+ return true;
+}
+
+inline bool VirtBaseHelper(InterpState &S, CodePtr OpPC, const RecordDecl *Decl,
+ const Pointer &Ptr) {
+ Pointer Base = Ptr;
+ while (Base.isBaseClass())
+ Base = Base.getBase();
+
+ auto *Field = Base.getRecord()->getVirtualBase(Decl);
+ S.Stk.push<Pointer>(Base.atField(Field->Offset));
+ return true;
+}
+
+inline bool GetPtrVirtBase(InterpState &S, CodePtr OpPC, const RecordDecl *D) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckNull(S, OpPC, Ptr, CSK_Base))
+ return false;
+ return VirtBaseHelper(S, OpPC, D, Ptr);
+}
+
+inline bool GetPtrThisVirtBase(InterpState &S, CodePtr OpPC,
+ const RecordDecl *D) {
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+ return VirtBaseHelper(S, OpPC, D, S.Current->getThis());
+}
+
+//===----------------------------------------------------------------------===//
+// Load, Store, Init
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Load(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+ if (!CheckLoad(S, OpPC, Ptr))
+ return false;
+ S.Stk.push<T>(Ptr.deref<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool LoadPop(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckLoad(S, OpPC, Ptr))
+ return false;
+ S.Stk.push<T>(Ptr.deref<T>());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Store(InterpState &S, CodePtr OpPC) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+ if (!CheckStore(S, OpPC, Ptr))
+ return false;
+ Ptr.deref<T>() = Value;
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool StorePop(InterpState &S, CodePtr OpPC) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckStore(S, OpPC, Ptr))
+ return false;
+ Ptr.deref<T>() = Value;
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool StoreBitField(InterpState &S, CodePtr OpPC) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+ if (!CheckStore(S, OpPC, Ptr))
+ return false;
+ if (auto *FD = Ptr.getField()) {
+ Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
+ } else {
+ Ptr.deref<T>() = Value;
+ }
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckStore(S, OpPC, Ptr))
+ return false;
+ if (auto *FD = Ptr.getField()) {
+ Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
+ } else {
+ Ptr.deref<T>() = Value;
+ }
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitPop(InterpState &S, CodePtr OpPC) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckInit(S, OpPC, Ptr))
+ return false;
+ Ptr.initialize();
+ new (&Ptr.deref<T>()) T(Value);
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.peek<Pointer>().atIndex(Idx);
+ if (!CheckInit(S, OpPC, Ptr))
+ return false;
+ Ptr.initialize();
+ new (&Ptr.deref<T>()) T(Value);
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) {
+ const T &Value = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>().atIndex(Idx);
+ if (!CheckInit(S, OpPC, Ptr))
+ return false;
+ Ptr.initialize();
+ new (&Ptr.deref<T>()) T(Value);
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// AddOffset, SubOffset
+//===----------------------------------------------------------------------===//
+
+template <class T, bool Add> bool OffsetHelper(InterpState &S, CodePtr OpPC) {
+ // Fetch the pointer and the offset.
+ const T &Offset = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex))
+ return false;
+ if (!CheckRange(S, OpPC, Ptr, CSK_ArrayToPointer))
+ return false;
+
+ // Get a version of the index comparable to the type.
+ T Index = T::from(Ptr.getIndex(), Offset.bitWidth());
+ // A zero offset does not change the pointer, but in the case of an array
+ // it has to be adjusted to point to the first element instead of the array.
+ if (Offset.isZero()) {
+ S.Stk.push<Pointer>(Index.isZero() ? Ptr.atIndex(0) : Ptr);
+ return true;
+ }
+ // Arrays of unknown bounds cannot have pointers into them.
+ if (!CheckArray(S, OpPC, Ptr))
+ return false;
+
+ // Compute the largest index into the array.
+ unsigned MaxIndex = Ptr.getNumElems();
+
+ // Helper to report an invalid offset, computed as APSInt.
+ auto InvalidOffset = [&]() {
+ const unsigned Bits = Offset.bitWidth();
+ APSInt APOffset(Offset.toAPSInt().extend(Bits + 2), false);
+ APSInt APIndex(Index.toAPSInt().extend(Bits + 2), false);
+ APSInt NewIndex = Add ? (APIndex + APOffset) : (APIndex - APOffset);
+ S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
+ << NewIndex
+ << /*array*/ static_cast<int>(!Ptr.inArray())
+ << static_cast<unsigned>(MaxIndex);
+ return false;
+ };
+
+ // If the new offset would be negative, bail out.
+ if (Add && Offset.isNegative() && (Offset.isMin() || -Offset > Index))
+ return InvalidOffset();
+ if (!Add && Offset.isPositive() && Index < Offset)
+ return InvalidOffset();
+
+ // If the new offset would be out of bounds, bail out.
+ unsigned MaxOffset = MaxIndex - Ptr.getIndex();
+ if (Add && Offset.isPositive() && Offset > MaxOffset)
+ return InvalidOffset();
+ if (!Add && Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset))
+ return InvalidOffset();
+
+ // Offset is valid - compute it on unsigned.
+ int64_t WideIndex = static_cast<int64_t>(Index);
+ int64_t WideOffset = static_cast<int64_t>(Offset);
+ int64_t Result = Add ? (WideIndex + WideOffset) : (WideIndex - WideOffset);
+ S.Stk.push<Pointer>(Ptr.atIndex(static_cast<unsigned>(Result)));
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool AddOffset(InterpState &S, CodePtr OpPC) {
+ return OffsetHelper<T, true>(S, OpPC);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool SubOffset(InterpState &S, CodePtr OpPC) {
+ return OffsetHelper<T, false>(S, OpPC);
+}
+
+
+//===----------------------------------------------------------------------===//
+// Destroy
+//===----------------------------------------------------------------------===//
+
+inline bool Destroy(InterpState &S, CodePtr OpPC, uint32_t I) {
+ S.Current->destroy(I);
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Cast, CastFP
+//===----------------------------------------------------------------------===//
+
+template <PrimType TIn, PrimType TOut> bool Cast(InterpState &S, CodePtr OpPC) {
+ using T = typename PrimConv<TIn>::T;
+ using U = typename PrimConv<TOut>::T;
+ S.Stk.push<U>(U::from(S.Stk.pop<T>()));
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Zero, Nullptr
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Zero(InterpState &S, CodePtr OpPC) {
+ S.Stk.push<T>(T::zero());
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+inline bool Null(InterpState &S, CodePtr OpPC) {
+ S.Stk.push<T>();
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// This, ImplicitThis
+//===----------------------------------------------------------------------===//
+
+inline bool This(InterpState &S, CodePtr OpPC) {
+ // Cannot read 'this' in this mode.
+ if (S.checkingPotentialConstantExpression()) {
+ return false;
+ }
+
+ const Pointer &This = S.Current->getThis();
+ if (!CheckThis(S, OpPC, This))
+ return false;
+
+ S.Stk.push<Pointer>(This);
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Shr, Shl
+//===----------------------------------------------------------------------===//
+
+template <PrimType TR, PrimType TL, class T = typename PrimConv<TR>::T>
+unsigned Trunc(InterpState &S, CodePtr OpPC, unsigned Bits, const T &V) {
+ // C++11 [expr.shift]p1: Shift width must be less than the bit width of
+ // the shifted type.
+ if (Bits > 1 && V >= T::from(Bits, V.bitWidth())) {
+ const Expr *E = S.Current->getExpr(OpPC);
+ const APSInt Val = V.toAPSInt();
+ QualType Ty = E->getType();
+ S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits;
+ return Bits;
+ } else {
+ return static_cast<unsigned>(V);
+ }
+}
+
+template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T>
+inline bool ShiftRight(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) {
+ if (RHS >= V.bitWidth()) {
+ S.Stk.push<T>(T::from(0, V.bitWidth()));
+ } else {
+ S.Stk.push<T>(T::from(V >> RHS, V.bitWidth()));
+ }
+ return true;
+}
+
+template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T>
+inline bool ShiftLeft(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) {
+ if (V.isSigned() && !S.getLangOpts().CPlusPlus2a) {
+ // C++11 [expr.shift]p2: A signed left shift must have a non-negative
+ // operand, and must not overflow the corresponding unsigned type.
+ // C++2a [expr.shift]p2: E1 << E2 is the unique value congruent to
+ // E1 x 2^E2 module 2^N.
+ if (V.isNegative()) {
+ const Expr *E = S.Current->getExpr(OpPC);
+ S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << V.toAPSInt();
+ } else if (V.countLeadingZeros() < RHS) {
+ S.CCEDiag(S.Current->getExpr(OpPC), diag::note_constexpr_lshift_discards);
+ }
+ }
+
+ if (V.bitWidth() == 1) {
+ S.Stk.push<T>(V);
+ } else if (RHS >= V.bitWidth()) {
+ S.Stk.push<T>(T::from(0, V.bitWidth()));
+ } else {
+ S.Stk.push<T>(T::from(V.toUnsigned() << RHS, V.bitWidth()));
+ }
+ return true;
+}
+
+template <PrimType TL, PrimType TR>
+inline bool Shr(InterpState &S, CodePtr OpPC) {
+ const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>();
+ const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>();
+ const unsigned Bits = LHS.bitWidth();
+
+ if (RHS.isSigned() && RHS.isNegative()) {
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
+ return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS));
+ } else {
+ return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
+ }
+}
+
+template <PrimType TL, PrimType TR>
+inline bool Shl(InterpState &S, CodePtr OpPC) {
+ const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>();
+ const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>();
+ const unsigned Bits = LHS.bitWidth();
+
+ if (RHS.isSigned() && RHS.isNegative()) {
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
+ return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS));
+ } else {
+ return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// NoRet
+//===----------------------------------------------------------------------===//
+
+inline bool NoRet(InterpState &S, CodePtr OpPC) {
+ SourceLocation EndLoc = S.Current->getCallee()->getEndLoc();
+ S.FFDiag(EndLoc, diag::note_constexpr_no_return);
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// NarrowPtr, ExpandPtr
+//===----------------------------------------------------------------------===//
+
+inline bool NarrowPtr(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ S.Stk.push<Pointer>(Ptr.narrow());
+ return true;
+}
+
+inline bool ExpandPtr(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ S.Stk.push<Pointer>(Ptr.expand());
+ return true;
+}
+
+/// Interpreter entry point.
+bool Interpret(InterpState &S, APValue &Result);
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/InterpFrame.cpp b/lib/AST/Interp/InterpFrame.cpp
new file mode 100644
index 0000000000000..9d01bf0333fe1
--- /dev/null
+++ b/lib/AST/Interp/InterpFrame.cpp
@@ -0,0 +1,193 @@
+//===--- InterpFrame.cpp - Call Frame implementation for the VM -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "InterpFrame.h"
+#include "Function.h"
+#include "Interp.h"
+#include "InterpStack.h"
+#include "PrimType.h"
+#include "Program.h"
+#include "clang/AST/DeclCXX.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+InterpFrame::InterpFrame(InterpState &S, Function *Func, InterpFrame *Caller,
+ CodePtr RetPC, Pointer &&This)
+ : Caller(Caller), S(S), Func(Func), This(std::move(This)), RetPC(RetPC),
+ ArgSize(Func ? Func->getArgSize() : 0),
+ Args(static_cast<char *>(S.Stk.top())), FrameOffset(S.Stk.size()) {
+ if (Func) {
+ 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() {
+ if (Func && Func->isConstructor() && This.isBaseClass())
+ This.initialize();
+ for (auto &Param : Params)
+ S.deallocate(reinterpret_cast<Block *>(Param.second.get()));
+}
+
+void InterpFrame::destroy(unsigned Idx) {
+ for (auto &Local : Func->getScope(Idx).locals()) {
+ S.deallocate(reinterpret_cast<Block *>(localBlock(Local.Offset)));
+ }
+}
+
+void InterpFrame::popArgs() {
+ for (PrimType Ty : Func->args_reverse())
+ TYPE_SWITCH(Ty, S.Stk.discard<T>());
+}
+
+template <typename T>
+static void print(llvm::raw_ostream &OS, const T &V, ASTContext &, QualType) {
+ OS << V;
+}
+
+template <>
+void print(llvm::raw_ostream &OS, const Pointer &P, ASTContext &Ctx,
+ QualType Ty) {
+ if (P.isZero()) {
+ OS << "nullptr";
+ return;
+ }
+
+ auto printDesc = [&OS, &Ctx](Descriptor *Desc) {
+ if (auto *D = Desc->asDecl()) {
+ // Subfields or named values.
+ if (auto *VD = dyn_cast<ValueDecl>(D)) {
+ OS << *VD;
+ return;
+ }
+ // Base classes.
+ if (isa<RecordDecl>(D)) {
+ return;
+ }
+ }
+ // Temporary expression.
+ if (auto *E = Desc->asExpr()) {
+ E->printPretty(OS, nullptr, Ctx.getPrintingPolicy());
+ return;
+ }
+ llvm_unreachable("Invalid descriptor type");
+ };
+
+ if (!Ty->isReferenceType())
+ OS << "&";
+ llvm::SmallVector<Pointer, 2> Levels;
+ for (Pointer F = P; !F.isRoot(); ) {
+ Levels.push_back(F);
+ F = F.isArrayElement() ? F.getArray().expand() : F.getBase();
+ }
+
+ printDesc(P.getDeclDesc());
+ for (auto It = Levels.rbegin(); It != Levels.rend(); ++It) {
+ if (It->inArray()) {
+ OS << "[" << It->expand().getIndex() << "]";
+ continue;
+ }
+ if (auto Index = It->getIndex()) {
+ OS << " + " << Index;
+ continue;
+ }
+ OS << ".";
+ printDesc(It->getFieldDesc());
+ }
+}
+
+void InterpFrame::describe(llvm::raw_ostream &OS) {
+ const FunctionDecl *F = getCallee();
+ auto *M = dyn_cast<CXXMethodDecl>(F);
+ if (M && M->isInstance() && !isa<CXXConstructorDecl>(F)) {
+ print(OS, This, S.getCtx(), S.getCtx().getRecordType(M->getParent()));
+ OS << "->";
+ }
+ OS << *F << "(";
+ unsigned Off = Func->hasRVO() ? primSize(PT_Ptr) : 0;
+ for (unsigned I = 0, N = F->getNumParams(); I < N; ++I) {
+ QualType Ty = F->getParamDecl(I)->getType();
+
+ PrimType PrimTy;
+ if (llvm::Optional<PrimType> T = S.Ctx.classify(Ty)) {
+ PrimTy = *T;
+ } else {
+ PrimTy = PT_Ptr;
+ }
+
+ TYPE_SWITCH(PrimTy, print(OS, stackRef<T>(Off), S.getCtx(), Ty));
+ Off += align(primSize(PrimTy));
+ if (I + 1 != N)
+ OS << ", ";
+ }
+ OS << ")";
+}
+
+Frame *InterpFrame::getCaller() const {
+ if (Caller->Caller)
+ return Caller;
+ return S.getSplitFrame();
+}
+
+SourceLocation InterpFrame::getCallLocation() const {
+ if (!Caller->Func)
+ return S.getLocation(nullptr, {});
+ return S.getLocation(Caller->Func, RetPC - sizeof(uintptr_t));
+}
+
+const FunctionDecl *InterpFrame::getCallee() const {
+ return Func->getDecl();
+}
+
+Pointer InterpFrame::getLocalPointer(unsigned Offset) {
+ assert(Offset < Func->getFrameSize() && "Invalid local offset.");
+ return Pointer(
+ reinterpret_cast<Block *>(Locals.get() + Offset - sizeof(Block)));
+}
+
+Pointer InterpFrame::getParamPointer(unsigned Off) {
+ // Return the block if it was created previously.
+ auto Pt = Params.find(Off);
+ if (Pt != Params.end()) {
+ return Pointer(reinterpret_cast<Block *>(Pt->second.get()));
+ }
+
+ // Allocate memory to store the parameter and the block metadata.
+ const auto &Desc = Func->getParamDescriptor(Off);
+ size_t BlockSize = sizeof(Block) + Desc.second->getAllocSize();
+ auto Memory = std::make_unique<char[]>(BlockSize);
+ auto *B = new (Memory.get()) Block(Desc.second);
+
+ // Copy the initial value.
+ TYPE_SWITCH(Desc.first, new (B->data()) T(stackRef<T>(Off)));
+
+ // Record the param.
+ Params.insert({Off, std::move(Memory)});
+ return Pointer(B);
+}
+
+SourceInfo InterpFrame::getSource(CodePtr PC) const {
+ return S.getSource(Func, PC);
+}
+
+const Expr *InterpFrame::getExpr(CodePtr PC) const {
+ return S.getExpr(Func, PC);
+}
+
+SourceLocation InterpFrame::getLocation(CodePtr PC) const {
+ return S.getLocation(Func, PC);
+}
+
diff --git a/lib/AST/Interp/InterpFrame.h b/lib/AST/Interp/InterpFrame.h
new file mode 100644
index 0000000000000..b8391b0bcf92c
--- /dev/null
+++ b/lib/AST/Interp/InterpFrame.h
@@ -0,0 +1,153 @@
+//===--- InterpFrame.h - Call Frame implementation for the VM ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the class storing information about stack frames in the interpreter.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_INTERPFRAME_H
+#define LLVM_CLANG_AST_INTERP_INTERPFRAME_H
+
+#include "Frame.h"
+#include "Pointer.h"
+#include "Program.h"
+#include "State.h"
+#include <cstdint>
+#include <vector>
+
+namespace clang {
+namespace interp {
+class Function;
+class InterpState;
+
+/// Frame storing local variables.
+class InterpFrame final : public Frame {
+public:
+ /// The frame of the previous function.
+ InterpFrame *Caller;
+
+ /// Creates a new frame for a method call.
+ InterpFrame(InterpState &S, Function *Func, InterpFrame *Caller,
+ CodePtr RetPC, Pointer &&This);
+
+ /// Destroys the frame, killing all live pointers to stack slots.
+ ~InterpFrame();
+
+ /// Invokes the destructors for a scope.
+ void destroy(unsigned Idx);
+
+ /// Pops the arguments off the stack.
+ void popArgs();
+
+ /// Describes the frame with arguments for diagnostic purposes.
+ void describe(llvm::raw_ostream &OS);
+
+ /// Returns the parent frame object.
+ Frame *getCaller() const;
+
+ /// Returns the location of the call to the frame.
+ SourceLocation getCallLocation() const;
+
+ /// Returns the caller.
+ const FunctionDecl *getCallee() const;
+
+ /// Returns the current function.
+ Function *getFunction() const { return Func; }
+
+ /// Returns the offset on the stack at which the frame starts.
+ size_t getFrameOffset() const { return FrameOffset; }
+
+ /// Returns the value of a local variable.
+ template <typename T> const T &getLocal(unsigned Offset) {
+ return localRef<T>(Offset);
+ }
+
+ /// Mutates a local variable.
+ template <typename T> void setLocal(unsigned Offset, const T &Value) {
+ localRef<T>(Offset) = Value;
+ }
+
+ /// Returns a pointer to a local variables.
+ Pointer getLocalPointer(unsigned Offset);
+
+ /// Returns the value of an argument.
+ template <typename T> const T &getParam(unsigned Offset) {
+ auto Pt = Params.find(Offset);
+ if (Pt == Params.end()) {
+ return stackRef<T>(Offset);
+ } else {
+ return Pointer(reinterpret_cast<Block *>(Pt->second.get())).deref<T>();
+ }
+ }
+
+ /// Mutates a local copy of a parameter.
+ template <typename T> void setParam(unsigned Offset, const T &Value) {
+ getParamPointer(Offset).deref<T>() = Value;
+ }
+
+ /// Returns a pointer to an argument - lazily creates a block.
+ Pointer getParamPointer(unsigned Offset);
+
+ /// Returns the 'this' pointer.
+ const Pointer &getThis() const { return This; }
+
+ /// Checks if the frame is a root frame - return should quit the interpreter.
+ bool isRoot() const { return !Func; }
+
+ /// Returns the PC of the frame's code start.
+ CodePtr getPC() const { return Func->getCodeBegin(); }
+
+ /// Returns the return address of the frame.
+ CodePtr getRetPC() const { return RetPC; }
+
+ /// Map a location to a source.
+ virtual SourceInfo getSource(CodePtr PC) const;
+ const Expr *getExpr(CodePtr PC) const;
+ SourceLocation getLocation(CodePtr PC) const;
+
+private:
+ /// Returns an original argument from the stack.
+ template <typename T> const T &stackRef(unsigned Offset) {
+ return *reinterpret_cast<const T *>(Args - ArgSize + Offset);
+ }
+
+ /// Returns an offset to a local.
+ template <typename T> T &localRef(unsigned Offset) {
+ return *reinterpret_cast<T *>(Locals.get() + Offset);
+ }
+
+ /// Returns a pointer to a local's block.
+ void *localBlock(unsigned Offset) {
+ return Locals.get() + Offset - sizeof(Block);
+ }
+
+private:
+ /// Reference to the interpreter state.
+ InterpState &S;
+ /// Reference to the function being executed.
+ Function *Func;
+ /// Current object pointer for methods.
+ Pointer This;
+ /// Return address.
+ CodePtr RetPC;
+ /// The size of all the arguments.
+ const unsigned ArgSize;
+ /// Pointer to the arguments in the callee's frame.
+ char *Args = nullptr;
+ /// Fixed, initial storage for known local variables.
+ std::unique_ptr<char[]> Locals;
+ /// Offset on the stack at entry.
+ const size_t FrameOffset;
+ /// Mapping from arg offsets to their argument blocks.
+ llvm::DenseMap<unsigned, std::unique_ptr<char[]>> Params;
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/InterpStack.cpp b/lib/AST/Interp/InterpStack.cpp
new file mode 100644
index 0000000000000..5c803f3d94244
--- /dev/null
+++ b/lib/AST/Interp/InterpStack.cpp
@@ -0,0 +1,78 @@
+//===--- InterpStack.cpp - Stack implementation for the VM ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <cassert>
+#include <cstdlib>
+#include "InterpStack.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+InterpStack::~InterpStack() {
+ clear();
+}
+
+void InterpStack::clear() {
+ if (Chunk && Chunk->Next)
+ free(Chunk->Next);
+ if (Chunk)
+ free(Chunk);
+ Chunk = nullptr;
+ StackSize = 0;
+}
+
+void *InterpStack::grow(size_t Size) {
+ assert(Size < ChunkSize - sizeof(StackChunk) && "Object too large");
+
+ if (!Chunk || sizeof(StackChunk) + Chunk->size() + Size > ChunkSize) {
+ if (Chunk && Chunk->Next) {
+ Chunk = Chunk->Next;
+ } else {
+ StackChunk *Next = new (malloc(ChunkSize)) StackChunk(Chunk);
+ if (Chunk)
+ Chunk->Next = Next;
+ Chunk = Next;
+ }
+ }
+
+ auto *Object = reinterpret_cast<void *>(Chunk->End);
+ Chunk->End += Size;
+ StackSize += Size;
+ return Object;
+}
+
+void *InterpStack::peek(size_t Size) {
+ assert(Chunk && "Stack is empty!");
+
+ StackChunk *Ptr = Chunk;
+ while (Size > Ptr->size()) {
+ Size -= Ptr->size();
+ Ptr = Ptr->Prev;
+ assert(Ptr && "Offset too large");
+ }
+
+ return reinterpret_cast<void *>(Ptr->End - Size);
+}
+
+void InterpStack::shrink(size_t Size) {
+ assert(Chunk && "Chunk is empty!");
+
+ while (Size > Chunk->size()) {
+ Size -= Chunk->size();
+ if (Chunk->Next) {
+ free(Chunk->Next);
+ Chunk->Next = nullptr;
+ }
+ Chunk->End = Chunk->start();
+ Chunk = Chunk->Prev;
+ assert(Chunk && "Offset too large");
+ }
+
+ Chunk->End -= Size;
+ StackSize -= Size;
+}
diff --git a/lib/AST/Interp/InterpStack.h b/lib/AST/Interp/InterpStack.h
new file mode 100644
index 0000000000000..127adb6b8ebab
--- /dev/null
+++ b/lib/AST/Interp/InterpStack.h
@@ -0,0 +1,113 @@
+//===--- InterpStack.h - Stack implementation for the VM --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the upwards-growing stack used by the interpreter.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_INTERPSTACK_H
+#define LLVM_CLANG_AST_INTERP_INTERPSTACK_H
+
+#include <memory>
+
+namespace clang {
+namespace interp {
+
+/// Stack frame storing temporaries and parameters.
+class InterpStack final {
+public:
+ InterpStack() {}
+
+ /// Destroys the stack, freeing up storage.
+ ~InterpStack();
+
+ /// Constructs a value in place on the top of the stack.
+ template <typename T, typename... Tys> void push(Tys &&... Args) {
+ new (grow(aligned_size<T>())) T(std::forward<Tys>(Args)...);
+ }
+
+ /// Returns the value from the top of the stack and removes it.
+ template <typename T> T pop() {
+ auto *Ptr = &peek<T>();
+ auto Value = std::move(*Ptr);
+ Ptr->~T();
+ shrink(aligned_size<T>());
+ return Value;
+ }
+
+ /// Discards the top value from the stack.
+ template <typename T> void discard() {
+ auto *Ptr = &peek<T>();
+ Ptr->~T();
+ shrink(aligned_size<T>());
+ }
+
+ /// Returns a reference to the value on the top of the stack.
+ template <typename T> T &peek() {
+ return *reinterpret_cast<T *>(peek(aligned_size<T>()));
+ }
+
+ /// Returns a pointer to the top object.
+ void *top() { return Chunk ? peek(0) : nullptr; }
+
+ /// Returns the size of the stack in bytes.
+ size_t size() const { return StackSize; }
+
+ /// Clears the stack without calling any destructors.
+ void clear();
+
+private:
+ /// All stack slots are aligned to the native pointer alignment for storage.
+ /// The size of an object is rounded up to a pointer alignment multiple.
+ template <typename T> constexpr size_t aligned_size() const {
+ constexpr size_t PtrAlign = alignof(void *);
+ return ((sizeof(T) + PtrAlign - 1) / PtrAlign) * PtrAlign;
+ }
+
+ /// Grows the stack to accomodate a value and returns a pointer to it.
+ void *grow(size_t Size);
+ /// Returns a pointer from the top of the stack.
+ void *peek(size_t Size);
+ /// Shrinks the stack.
+ void shrink(size_t Size);
+
+ /// Allocate stack space in 1Mb chunks.
+ static constexpr size_t ChunkSize = 1024 * 1024;
+
+ /// Metadata for each stack chunk.
+ ///
+ /// The stack is composed of a linked list of chunks. Whenever an allocation
+ /// is out of bounds, a new chunk is linked. When a chunk becomes empty,
+ /// it is not immediately freed: a chunk is deallocated only when the
+ /// predecessor becomes empty.
+ struct StackChunk {
+ StackChunk *Next;
+ StackChunk *Prev;
+ char *End;
+
+ StackChunk(StackChunk *Prev = nullptr)
+ : Next(nullptr), Prev(Prev), End(reinterpret_cast<char *>(this + 1)) {}
+
+ /// Returns the size of the chunk, minus the header.
+ size_t size() { return End - start(); }
+
+ /// Returns a pointer to the start of the data region.
+ char *start() { return reinterpret_cast<char *>(this + 1); }
+ };
+ static_assert(sizeof(StackChunk) < ChunkSize, "Invalid chunk size");
+
+ /// First chunk on the stack.
+ StackChunk *Chunk = nullptr;
+ /// Total size of the stack.
+ size_t StackSize = 0;
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/InterpState.cpp b/lib/AST/Interp/InterpState.cpp
new file mode 100644
index 0000000000000..25684f3c09397
--- /dev/null
+++ b/lib/AST/Interp/InterpState.cpp
@@ -0,0 +1,74 @@
+//===--- InterpState.cpp - Interpreter for the constexpr VM -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "InterpState.h"
+#include <limits>
+#include "Function.h"
+#include "InterpFrame.h"
+#include "InterpStack.h"
+#include "Opcode.h"
+#include "PrimType.h"
+#include "Program.h"
+#include "State.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+using APSInt = llvm::APSInt;
+
+InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
+ Context &Ctx, SourceMapper *M)
+ : Parent(Parent), M(M), P(P), Stk(Stk), Ctx(Ctx), Current(nullptr),
+ CallStackDepth(Parent.getCallStackDepth() + 1) {}
+
+InterpState::~InterpState() {
+ while (Current) {
+ InterpFrame *Next = Current->Caller;
+ delete Current;
+ Current = Next;
+ }
+
+ while (DeadBlocks) {
+ DeadBlock *Next = DeadBlocks->Next;
+ free(DeadBlocks);
+ DeadBlocks = Next;
+ }
+}
+
+Frame *InterpState::getCurrentFrame() {
+ if (Current && Current->Caller) {
+ return Current;
+ } else {
+ return Parent.getCurrentFrame();
+ }
+}
+
+bool InterpState::reportOverflow(const Expr *E, const llvm::APSInt &Value) {
+ QualType Type = E->getType();
+ CCEDiag(E, diag::note_constexpr_overflow) << Value << Type;
+ return noteUndefinedBehavior();
+}
+
+void InterpState::deallocate(Block *B) {
+ Descriptor *Desc = B->getDescriptor();
+ if (B->hasPointers()) {
+ size_t Size = B->getSize();
+
+ // Allocate a new block, transferring over pointers.
+ char *Memory = reinterpret_cast<char *>(malloc(sizeof(DeadBlock) + Size));
+ auto *D = new (Memory) DeadBlock(DeadBlocks, B);
+
+ // Move data from one block to another.
+ if (Desc->MoveFn)
+ Desc->MoveFn(B, B->data(), D->data(), Desc);
+ } else {
+ // Free storage, if necessary.
+ if (Desc->DtorFn)
+ Desc->DtorFn(B, B->data(), Desc);
+ }
+}
diff --git a/lib/AST/Interp/InterpState.h b/lib/AST/Interp/InterpState.h
new file mode 100644
index 0000000000000..c2209bbcbb925
--- /dev/null
+++ b/lib/AST/Interp/InterpState.h
@@ -0,0 +1,112 @@
+//===--- InterpState.h - Interpreter state for the constexpr VM -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Definition of the interpreter state and entry point.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_INTERPSTATE_H
+#define LLVM_CLANG_AST_INTERP_INTERPSTATE_H
+
+#include "Context.h"
+#include "Function.h"
+#include "InterpStack.h"
+#include "State.h"
+#include "clang/AST/APValue.h"
+#include "clang/AST/ASTDiagnostic.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/OptionalDiagnostic.h"
+
+namespace clang {
+namespace interp {
+class Context;
+class Function;
+class InterpStack;
+class InterpFrame;
+class SourceMapper;
+
+/// Interpreter context.
+class InterpState final : public State, public SourceMapper {
+public:
+ InterpState(State &Parent, Program &P, InterpStack &Stk, Context &Ctx,
+ SourceMapper *M = nullptr);
+
+ ~InterpState();
+
+ // Stack frame accessors.
+ Frame *getSplitFrame() { return Parent.getCurrentFrame(); }
+ Frame *getCurrentFrame() override;
+ unsigned getCallStackDepth() override { return CallStackDepth; }
+ const Frame *getBottomFrame() const override {
+ return Parent.getBottomFrame();
+ }
+
+ // Acces objects from the walker context.
+ Expr::EvalStatus &getEvalStatus() const override {
+ return Parent.getEvalStatus();
+ }
+ ASTContext &getCtx() const override { return Parent.getCtx(); }
+
+ // Forward status checks and updates to the walker.
+ bool checkingForUndefinedBehavior() const override {
+ return Parent.checkingForUndefinedBehavior();
+ }
+ bool keepEvaluatingAfterFailure() const override {
+ return Parent.keepEvaluatingAfterFailure();
+ }
+ bool checkingPotentialConstantExpression() const override {
+ return Parent.checkingPotentialConstantExpression();
+ }
+ bool noteUndefinedBehavior() override {
+ return Parent.noteUndefinedBehavior();
+ }
+ bool hasActiveDiagnostic() override { return Parent.hasActiveDiagnostic(); }
+ void setActiveDiagnostic(bool Flag) override {
+ Parent.setActiveDiagnostic(Flag);
+ }
+ void setFoldFailureDiagnostic(bool Flag) override {
+ Parent.setFoldFailureDiagnostic(Flag);
+ }
+ bool hasPriorDiagnostic() override { return Parent.hasPriorDiagnostic(); }
+
+ /// Reports overflow and return true if evaluation should continue.
+ bool reportOverflow(const Expr *E, const llvm::APSInt &Value);
+
+ /// Deallocates a pointer.
+ void deallocate(Block *B);
+
+ /// Delegates source mapping to the mapper.
+ SourceInfo getSource(Function *F, CodePtr PC) const override {
+ return M ? M->getSource(F, PC) : F->getSource(PC);
+ }
+
+private:
+ /// AST Walker state.
+ State &Parent;
+ /// Dead block chain.
+ DeadBlock *DeadBlocks = nullptr;
+ /// Reference to the offset-source mapping.
+ SourceMapper *M;
+
+public:
+ /// Reference to the module containing all bytecode.
+ Program &P;
+ /// Temporary stack.
+ InterpStack &Stk;
+ /// Interpreter Context.
+ Context &Ctx;
+ /// The current frame.
+ InterpFrame *Current = nullptr;
+ /// Call stack depth.
+ unsigned CallStackDepth;
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/Opcode.h b/lib/AST/Interp/Opcode.h
new file mode 100644
index 0000000000000..d2daa1ea52ac1
--- /dev/null
+++ b/lib/AST/Interp/Opcode.h
@@ -0,0 +1,30 @@
+//===--- Opcode.h - Opcodes for the constexpr VM ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines all opcodes executed by the VM and emitted by the compiler.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_OPCODE_H
+#define LLVM_CLANG_AST_INTERP_OPCODE_H
+
+#include <cstdint>
+
+namespace clang {
+namespace interp {
+
+enum Opcode : uint32_t {
+#define GET_OPCODE_NAMES
+#include "Opcodes.inc"
+#undef GET_OPCODE_NAMES
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/Opcodes.td b/lib/AST/Interp/Opcodes.td
new file mode 100644
index 0000000000000..4aba5f5cd83cd
--- /dev/null
+++ b/lib/AST/Interp/Opcodes.td
@@ -0,0 +1,422 @@
+//===--- Opcodes.td - Opcode defitions for the constexpr VM -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Helper file used to generate opcodes, the interpreter and the disassembler.
+//
+//===----------------------------------------------------------------------===//
+
+
+//===----------------------------------------------------------------------===//
+// Types evaluated by the interpreter.
+//===----------------------------------------------------------------------===//
+
+class Type;
+def Bool : Type;
+def Sint8 : Type;
+def Uint8 : Type;
+def Sint16 : Type;
+def Uint16 : Type;
+def Sint32 : Type;
+def Uint32 : Type;
+def Sint64 : Type;
+def Uint64 : Type;
+def Ptr : Type;
+
+//===----------------------------------------------------------------------===//
+// Types transferred to the interpreter.
+//===----------------------------------------------------------------------===//
+
+class ArgType { string Name = ?; }
+def ArgSint8 : ArgType { let Name = "int8_t"; }
+def ArgUint8 : ArgType { let Name = "uint8_t"; }
+def ArgSint16 : ArgType { let Name = "int16_t"; }
+def ArgUint16 : ArgType { let Name = "uint16_t"; }
+def ArgSint32 : ArgType { let Name = "int32_t"; }
+def ArgUint32 : ArgType { let Name = "uint32_t"; }
+def ArgSint64 : ArgType { let Name = "int64_t"; }
+def ArgUint64 : ArgType { let Name = "uint64_t"; }
+def ArgBool : ArgType { let Name = "bool"; }
+
+def ArgFunction : ArgType { let Name = "Function *"; }
+def ArgRecord : ArgType { let Name = "Record *"; }
+
+def ArgSema : ArgType { let Name = "const fltSemantics *"; }
+
+def ArgExpr : ArgType { let Name = "const Expr *"; }
+def ArgFloatingLiteral : ArgType { let Name = "const FloatingLiteral *"; }
+def ArgCXXMethodDecl : ArgType { let Name = "const CXXMethodDecl *"; }
+def ArgFunctionDecl : ArgType { let Name = "const FunctionDecl *"; }
+def ArgRecordDecl : ArgType { let Name = "const RecordDecl *"; }
+def ArgCXXRecordDecl : ArgType { let Name = "const CXXRecordDecl *"; }
+def ArgValueDecl : ArgType { let Name = "const ValueDecl *"; }
+def ArgRecordField : ArgType { let Name = "const Record::Field *"; }
+
+//===----------------------------------------------------------------------===//
+// Classes of types intructions operate on.
+//===----------------------------------------------------------------------===//
+
+class TypeClass {
+ list<Type> Types;
+}
+
+def AluTypeClass : TypeClass {
+ let Types = [Sint8, Uint8, Sint16, Uint16, Sint32,
+ Uint32, Sint64, Uint64, Bool];
+}
+
+def PtrTypeClass : TypeClass {
+ let Types = [Ptr];
+}
+
+def AllTypeClass : TypeClass {
+ let Types = !listconcat(AluTypeClass.Types, PtrTypeClass.Types);
+}
+
+def ComparableTypeClass : TypeClass {
+ let Types = !listconcat(AluTypeClass.Types, [Ptr]);
+}
+
+class SingletonTypeClass<Type Ty> : TypeClass {
+ let Types = [Ty];
+}
+
+//===----------------------------------------------------------------------===//
+// Record describing all opcodes.
+//===----------------------------------------------------------------------===//
+
+class Opcode {
+ list<TypeClass> Types = [];
+ list<ArgType> Args = [];
+ string Name = "";
+ bit CanReturn = 0;
+ bit ChangesPC = 0;
+ bit HasCustomLink = 0;
+ bit HasCustomEval = 0;
+ bit HasGroup = 0;
+}
+
+class AluOpcode : Opcode {
+ let Types = [AluTypeClass];
+ let HasGroup = 1;
+}
+
+//===----------------------------------------------------------------------===//
+// Jump opcodes
+//===----------------------------------------------------------------------===//
+
+class JumpOpcode : Opcode {
+ let Args = [ArgSint32];
+ let ChangesPC = 1;
+ let HasCustomEval = 1;
+}
+
+// [] -> []
+def Jmp : JumpOpcode;
+// [Bool] -> [], jumps if true.
+def Jt : JumpOpcode;
+// [Bool] -> [], jumps if false.
+def Jf : JumpOpcode;
+
+//===----------------------------------------------------------------------===//
+// Returns
+//===----------------------------------------------------------------------===//
+
+// [Value] -> []
+def Ret : Opcode {
+ let Types = [AllTypeClass];
+ let ChangesPC = 1;
+ let CanReturn = 1;
+ let HasGroup = 1;
+ let HasCustomEval = 1;
+}
+// [] -> []
+def RetVoid : Opcode {
+ let CanReturn = 1;
+ let ChangesPC = 1;
+ let HasCustomEval = 1;
+}
+// [Value] -> []
+def RetValue : Opcode {
+ let CanReturn = 1;
+ let ChangesPC = 1;
+ let HasCustomEval = 1;
+}
+// [] -> EXIT
+def NoRet : Opcode {}
+
+//===----------------------------------------------------------------------===//
+// Frame management
+//===----------------------------------------------------------------------===//
+
+// [] -> []
+def Destroy : Opcode {
+ let Args = [ArgUint32];
+ let HasCustomEval = 1;
+}
+
+//===----------------------------------------------------------------------===//
+// Constants
+//===----------------------------------------------------------------------===//
+
+class ConstOpcode<Type Ty, ArgType ArgTy> : Opcode {
+ let Types = [SingletonTypeClass<Ty>];
+ let Args = [ArgTy];
+ let Name = "Const";
+}
+
+// [] -> [Integer]
+def ConstSint8 : ConstOpcode<Sint8, ArgSint8>;
+def ConstUint8 : ConstOpcode<Uint8, ArgUint8>;
+def ConstSint16 : ConstOpcode<Sint16, ArgSint16>;
+def ConstUint16 : ConstOpcode<Uint16, ArgUint16>;
+def ConstSint32 : ConstOpcode<Sint32, ArgSint32>;
+def ConstUint32 : ConstOpcode<Uint32, ArgUint32>;
+def ConstSint64 : ConstOpcode<Sint64, ArgSint64>;
+def ConstUint64 : ConstOpcode<Uint64, ArgUint64>;
+def ConstBool : ConstOpcode<Bool, ArgBool>;
+
+// [] -> [Integer]
+def Zero : Opcode {
+ let Types = [AluTypeClass];
+}
+
+// [] -> [Pointer]
+def Null : Opcode {
+ let Types = [PtrTypeClass];
+}
+
+//===----------------------------------------------------------------------===//
+// Pointer generation
+//===----------------------------------------------------------------------===//
+
+// [] -> [Pointer]
+def GetPtrLocal : Opcode {
+ // Offset of local.
+ let Args = [ArgUint32];
+ bit HasCustomEval = 1;
+}
+// [] -> [Pointer]
+def GetPtrParam : Opcode {
+ // Offset of parameter.
+ let Args = [ArgUint32];
+}
+// [] -> [Pointer]
+def GetPtrGlobal : Opcode {
+ // Index of global.
+ let Args = [ArgUint32];
+}
+// [Pointer] -> [Pointer]
+def GetPtrField : Opcode {
+ // Offset of field.
+ let Args = [ArgUint32];
+}
+// [Pointer] -> [Pointer]
+def GetPtrActiveField : Opcode {
+ // Offset of field.
+ let Args = [ArgUint32];
+}
+// [] -> [Pointer]
+def GetPtrActiveThisField : Opcode {
+ // Offset of field.
+ let Args = [ArgUint32];
+}
+// [] -> [Pointer]
+def GetPtrThisField : Opcode {
+ // Offset of field.
+ let Args = [ArgUint32];
+}
+// [Pointer] -> [Pointer]
+def GetPtrBase : Opcode {
+ // Offset of field, which is a base.
+ let Args = [ArgUint32];
+}
+// [Pointer] -> [Pointer]
+def GetPtrVirtBase : Opcode {
+ // RecordDecl of base class.
+ let Args = [ArgRecordDecl];
+}
+// [] -> [Pointer]
+def GetPtrThisBase : Opcode {
+ // Offset of field, which is a base.
+ let Args = [ArgUint32];
+}
+// [] -> [Pointer]
+def GetPtrThisVirtBase : Opcode {
+ // RecordDecl of base class.
+ let Args = [ArgRecordDecl];
+}
+// [] -> [Pointer]
+def This : Opcode;
+
+// [Pointer] -> [Pointer]
+def NarrowPtr : Opcode;
+// [Pointer] -> [Pointer]
+def ExpandPtr : Opcode;
+
+//===----------------------------------------------------------------------===//
+// Direct field accessors
+//===----------------------------------------------------------------------===//
+
+class AccessOpcode : Opcode {
+ let Types = [AllTypeClass];
+ let Args = [ArgUint32];
+ let HasGroup = 1;
+}
+
+class BitFieldOpcode : Opcode {
+ let Types = [AluTypeClass];
+ let Args = [ArgRecordField];
+ let HasGroup = 1;
+}
+
+// [] -> [Pointer]
+def GetLocal : AccessOpcode { let HasCustomEval = 1; }
+// [] -> [Pointer]
+def SetLocal : AccessOpcode { let HasCustomEval = 1; }
+
+// [] -> [Value]
+def GetGlobal : AccessOpcode;
+// [Value] -> []
+def InitGlobal : AccessOpcode;
+// [Value] -> []
+def SetGlobal : AccessOpcode;
+
+// [] -> [Value]
+def GetParam : AccessOpcode;
+// [Value] -> []
+def SetParam : AccessOpcode;
+
+// [Pointer] -> [Pointer, Value]
+def GetField : AccessOpcode;
+// [Pointer] -> [Value]
+def GetFieldPop : AccessOpcode;
+// [] -> [Value]
+def GetThisField : AccessOpcode;
+
+// [Pointer, Value] -> [Pointer]
+def SetField : AccessOpcode;
+// [Value] -> []
+def SetThisField : AccessOpcode;
+
+// [Value] -> []
+def InitThisField : AccessOpcode;
+// [Value] -> []
+def InitThisFieldActive : AccessOpcode;
+// [Value] -> []
+def InitThisBitField : BitFieldOpcode;
+// [Pointer, Value] -> []
+def InitField : AccessOpcode;
+// [Pointer, Value] -> []
+def InitBitField : BitFieldOpcode;
+// [Pointer, Value] -> []
+def InitFieldActive : AccessOpcode;
+
+//===----------------------------------------------------------------------===//
+// Pointer access
+//===----------------------------------------------------------------------===//
+
+class LoadOpcode : Opcode {
+ let Types = [AllTypeClass];
+ let HasGroup = 1;
+}
+
+// [Pointer] -> [Pointer, Value]
+def Load : LoadOpcode {}
+// [Pointer] -> [Value]
+def LoadPop : LoadOpcode {}
+
+class StoreOpcode : Opcode {
+ let Types = [AllTypeClass];
+ let HasGroup = 1;
+}
+
+class StoreBitFieldOpcode : Opcode {
+ let Types = [AluTypeClass];
+ let HasGroup = 1;
+}
+
+// [Pointer, Value] -> [Pointer]
+def Store : StoreOpcode {}
+// [Pointer, Value] -> []
+def StorePop : StoreOpcode {}
+
+// [Pointer, Value] -> [Pointer]
+def StoreBitField : StoreBitFieldOpcode {}
+// [Pointer, Value] -> []
+def StoreBitFieldPop : StoreBitFieldOpcode {}
+
+// [Pointer, Value] -> []
+def InitPop : StoreOpcode {}
+// [Pointer, Value] -> [Pointer]
+def InitElem : Opcode {
+ let Types = [AllTypeClass];
+ let Args = [ArgUint32];
+ let HasGroup = 1;
+}
+// [Pointer, Value] -> []
+def InitElemPop : Opcode {
+ let Types = [AllTypeClass];
+ let Args = [ArgUint32];
+ let HasGroup = 1;
+}
+
+//===----------------------------------------------------------------------===//
+// Pointer arithmetic.
+//===----------------------------------------------------------------------===//
+
+// [Pointer, Integral] -> [Pointer]
+def AddOffset : AluOpcode;
+// [Pointer, Integral] -> [Pointer]
+def SubOffset : AluOpcode;
+
+//===----------------------------------------------------------------------===//
+// Binary operators.
+//===----------------------------------------------------------------------===//
+
+// [Real, Real] -> [Real]
+def Sub : AluOpcode;
+def Add : AluOpcode;
+def Mul : AluOpcode;
+
+//===----------------------------------------------------------------------===//
+// Comparison opcodes.
+//===----------------------------------------------------------------------===//
+
+class EqualityOpcode : Opcode {
+ let Types = [AllTypeClass];
+ let HasGroup = 1;
+}
+
+def EQ : EqualityOpcode;
+def NE : EqualityOpcode;
+
+class ComparisonOpcode : Opcode {
+ let Types = [ComparableTypeClass];
+ let HasGroup = 1;
+}
+
+def LT : ComparisonOpcode;
+def LE : ComparisonOpcode;
+def GT : ComparisonOpcode;
+def GE : ComparisonOpcode;
+
+//===----------------------------------------------------------------------===//
+// Stack management.
+//===----------------------------------------------------------------------===//
+
+// [Value] -> []
+def Pop : Opcode {
+ let Types = [AllTypeClass];
+ let HasGroup = 1;
+}
+
+// [Value] -> [Value, Value]
+def Dup : Opcode {
+ let Types = [AllTypeClass];
+ let HasGroup = 1;
+}
diff --git a/lib/AST/Interp/Pointer.cpp b/lib/AST/Interp/Pointer.cpp
new file mode 100644
index 0000000000000..1a10723aaca53
--- /dev/null
+++ b/lib/AST/Interp/Pointer.cpp
@@ -0,0 +1,193 @@
+//===--- Pointer.cpp - Types for the constexpr VM ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Pointer.h"
+#include "Block.h"
+#include "Function.h"
+#include "PrimType.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {}
+
+Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {}
+
+Pointer::Pointer(Pointer &&P)
+ : Pointee(P.Pointee), Base(P.Base), Offset(P.Offset) {
+ if (Pointee)
+ Pointee->movePointer(&P, this);
+}
+
+Pointer::Pointer(Block *Pointee, unsigned Base, unsigned Offset)
+ : Pointee(Pointee), Base(Base), Offset(Offset) {
+ assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");
+ if (Pointee)
+ Pointee->addPointer(this);
+}
+
+Pointer::~Pointer() {
+ if (Pointee) {
+ Pointee->removePointer(this);
+ Pointee->cleanup();
+ }
+}
+
+void Pointer::operator=(const Pointer &P) {
+ Block *Old = Pointee;
+
+ if (Pointee)
+ Pointee->removePointer(this);
+
+ Offset = P.Offset;
+ Base = P.Base;
+
+ Pointee = P.Pointee;
+ if (Pointee)
+ Pointee->addPointer(this);
+
+ if (Old)
+ Old->cleanup();
+}
+
+void Pointer::operator=(Pointer &&P) {
+ Block *Old = Pointee;
+
+ if (Pointee)
+ Pointee->removePointer(this);
+
+ Offset = P.Offset;
+ Base = P.Base;
+
+ Pointee = P.Pointee;
+ if (Pointee)
+ Pointee->movePointer(&P, this);
+
+ if (Old)
+ Old->cleanup();
+}
+
+APValue Pointer::toAPValue() const {
+ APValue::LValueBase Base;
+ llvm::SmallVector<APValue::LValuePathEntry, 5> Path;
+ CharUnits Offset;
+ bool IsNullPtr;
+ bool IsOnePastEnd;
+
+ if (isZero()) {
+ Base = static_cast<const Expr *>(nullptr);
+ IsNullPtr = true;
+ IsOnePastEnd = false;
+ Offset = CharUnits::Zero();
+ } else {
+ // Build the lvalue base from the block.
+ Descriptor *Desc = getDeclDesc();
+ if (auto *VD = Desc->asValueDecl())
+ Base = VD;
+ else if (auto *E = Desc->asExpr())
+ Base = E;
+ else
+ llvm_unreachable("Invalid allocation type");
+
+ // Not a null pointer.
+ IsNullPtr = false;
+
+ if (isUnknownSizeArray()) {
+ IsOnePastEnd = false;
+ Offset = CharUnits::Zero();
+ } else {
+ // TODO: compute the offset into the object.
+ Offset = CharUnits::Zero();
+
+ // Build the path into the object.
+ Pointer Ptr = *this;
+ while (Ptr.isField()) {
+ if (Ptr.isArrayElement()) {
+ Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getIndex()));
+ Ptr = Ptr.getArray();
+ } else {
+ // TODO: figure out if base is virtual
+ bool IsVirtual = false;
+
+ // Create a path entry for the field.
+ Descriptor *Desc = Ptr.getFieldDesc();
+ if (auto *BaseOrMember = Desc->asDecl()) {
+ Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));
+ Ptr = Ptr.getBase();
+ continue;
+ }
+ llvm_unreachable("Invalid field type");
+ }
+ }
+
+ IsOnePastEnd = isOnePastEnd();
+ }
+ }
+
+ return APValue(Base, Offset, Path, IsOnePastEnd, IsNullPtr);
+}
+
+bool Pointer::isInitialized() const {
+ assert(Pointee && "Cannot check if null pointer was initialized");
+ Descriptor *Desc = getFieldDesc();
+ if (Desc->isPrimitiveArray()) {
+ if (Pointee->IsStatic)
+ return true;
+ // Primitive array field are stored in a bitset.
+ InitMap *Map = getInitMap();
+ if (!Map)
+ return false;
+ if (Map == (InitMap *)-1)
+ return true;
+ return Map->isInitialized(getIndex());
+ } else {
+ // Field has its bit in an inline descriptor.
+ return Base == 0 || getInlineDesc()->IsInitialized;
+ }
+}
+
+void Pointer::initialize() const {
+ assert(Pointee && "Cannot initialize null pointer");
+ Descriptor *Desc = getFieldDesc();
+ if (Desc->isPrimitiveArray()) {
+ if (!Pointee->IsStatic) {
+ // Primitive array initializer.
+ InitMap *&Map = getInitMap();
+ if (Map == (InitMap *)-1)
+ return;
+ if (Map == nullptr)
+ Map = InitMap::allocate(Desc->getNumElems());
+ if (Map->initialize(getIndex())) {
+ free(Map);
+ Map = (InitMap *)-1;
+ }
+ }
+ } else {
+ // Field has its bit in an inline descriptor.
+ assert(Base != 0 && "Only composite fields can be initialised");
+ getInlineDesc()->IsInitialized = true;
+ }
+}
+
+void Pointer::activate() const {
+ // Field has its bit in an inline descriptor.
+ assert(Base != 0 && "Only composite fields can be initialised");
+ getInlineDesc()->IsActive = true;
+}
+
+void Pointer::deactivate() const {
+ // TODO: this only appears in constructors, so nothing to deactivate.
+}
+
+bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
+ return A.Pointee == B.Pointee;
+}
+
+bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
+ return A.Base == B.Base && A.getFieldDesc()->IsArray;
+}
diff --git a/lib/AST/Interp/Pointer.h b/lib/AST/Interp/Pointer.h
new file mode 100644
index 0000000000000..b8fa98e24faab
--- /dev/null
+++ b/lib/AST/Interp/Pointer.h
@@ -0,0 +1,353 @@
+//===--- Pointer.h - Types for the constexpr VM -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the classes responsible for pointer tracking.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_POINTER_H
+#define LLVM_CLANG_AST_INTERP_POINTER_H
+
+#include "Block.h"
+#include "Descriptor.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ComparisonCategories.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace interp {
+class Block;
+class DeadBlock;
+class Context;
+class InterpState;
+class Pointer;
+class Function;
+enum PrimType : unsigned;
+
+/// A pointer to a memory block, live or dead.
+///
+/// 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.
+class Pointer {
+private:
+ static constexpr unsigned PastEndMark = (unsigned)-1;
+ static constexpr unsigned RootPtrMark = (unsigned)-1;
+
+public:
+ Pointer() {}
+ Pointer(Block *B);
+ Pointer(const Pointer &P);
+ Pointer(Pointer &&P);
+ ~Pointer();
+
+ void operator=(const Pointer &P);
+ void operator=(Pointer &&P);
+
+ /// Converts the pointer to an APValue.
+ APValue toAPValue() const;
+
+ /// Offsets a pointer inside an array.
+ Pointer atIndex(unsigned Idx) const {
+ if (Base == RootPtrMark)
+ return Pointer(Pointee, RootPtrMark, getDeclDesc()->getSize());
+ unsigned Off = Idx * elemSize();
+ if (getFieldDesc()->ElemDesc)
+ Off += sizeof(InlineDescriptor);
+ else
+ Off += sizeof(InitMap *);
+ return Pointer(Pointee, Base, Base + Off);
+ }
+
+ /// Creates a pointer to a field.
+ Pointer atField(unsigned Off) const {
+ unsigned Field = Offset + Off;
+ return Pointer(Pointee, Field, Field);
+ }
+
+ /// Restricts the scope of an array element pointer.
+ Pointer narrow() const {
+ // Null pointers cannot be narrowed.
+ if (isZero() || isUnknownSizeArray())
+ return *this;
+
+ // Pointer to an array of base types - enter block.
+ if (Base == RootPtrMark)
+ return Pointer(Pointee, 0, Offset == 0 ? Offset : PastEndMark);
+
+ // Pointer is one past end - magic offset marks that.
+ if (isOnePastEnd())
+ return Pointer(Pointee, Base, PastEndMark);
+
+ // Primitive arrays are a bit special since they do not have inline
+ // descriptors. If Offset != Base, then the pointer already points to
+ // an element and there is nothing to do. Otherwise, the pointer is
+ // adjusted to the first element of the array.
+ if (inPrimitiveArray()) {
+ if (Offset != Base)
+ return *this;
+ return Pointer(Pointee, Base, Offset + sizeof(InitMap *));
+ }
+
+ // Pointer is to a field or array element - enter it.
+ if (Offset != Base)
+ return Pointer(Pointee, Offset, Offset);
+
+ // Enter the first element of an array.
+ if (!getFieldDesc()->isArray())
+ return *this;
+
+ const unsigned NewBase = Base + sizeof(InlineDescriptor);
+ return Pointer(Pointee, NewBase, NewBase);
+ }
+
+ /// Expands a pointer to the containing array, undoing narrowing.
+ Pointer expand() const {
+ if (isElementPastEnd()) {
+ // Revert to an outer one-past-end pointer.
+ unsigned Adjust;
+ if (inPrimitiveArray())
+ Adjust = sizeof(InitMap *);
+ else
+ Adjust = sizeof(InlineDescriptor);
+ return Pointer(Pointee, Base, Base + getSize() + Adjust);
+ }
+
+ // Do not step out of array elements.
+ if (Base != Offset)
+ return *this;
+
+ // If at base, point to an array of base types.
+ if (Base == 0)
+ return Pointer(Pointee, RootPtrMark, 0);
+
+ // Step into the containing array, if inside one.
+ unsigned Next = Base - getInlineDesc()->Offset;
+ Descriptor *Desc = Next == 0 ? getDeclDesc() : getDescriptor(Next)->Desc;
+ if (!Desc->IsArray)
+ return *this;
+ return Pointer(Pointee, Next, Offset);
+ }
+
+ /// Checks if the pointer is null.
+ bool isZero() const { return Pointee == nullptr; }
+ /// Checks if the pointer is live.
+ bool isLive() const { return Pointee && !Pointee->IsDead; }
+ /// Checks if the item is a field in an object.
+ bool isField() const { return Base != 0 && Base != RootPtrMark; }
+
+ /// Accessor for information about the declaration site.
+ Descriptor *getDeclDesc() const { return Pointee->Desc; }
+ SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); }
+
+ /// Returns a pointer to the object of which this pointer is a field.
+ Pointer getBase() const {
+ if (Base == RootPtrMark) {
+ assert(Offset == PastEndMark && "cannot get base of a block");
+ return Pointer(Pointee, Base, 0);
+ }
+ assert(Offset == Base && "not an inner field");
+ unsigned NewBase = Base - getInlineDesc()->Offset;
+ return Pointer(Pointee, NewBase, NewBase);
+ }
+ /// Returns the parent array.
+ Pointer getArray() const {
+ if (Base == RootPtrMark) {
+ assert(Offset != 0 && Offset != PastEndMark && "not an array element");
+ return Pointer(Pointee, Base, 0);
+ }
+ assert(Offset != Base && "not an array element");
+ return Pointer(Pointee, Base, Base);
+ }
+
+ /// Accessors for information about the innermost field.
+ Descriptor *getFieldDesc() const {
+ if (Base == 0 || Base == RootPtrMark)
+ return getDeclDesc();
+ return getInlineDesc()->Desc;
+ }
+
+ /// Returns the type of the innermost field.
+ QualType getType() const { return getFieldDesc()->getType(); }
+
+ /// Returns the element size of the innermost field.
+ size_t elemSize() const {
+ if (Base == RootPtrMark)
+ return getDeclDesc()->getSize();
+ return getFieldDesc()->getElemSize();
+ }
+ /// Returns the total size of the innermost field.
+ size_t getSize() const { return getFieldDesc()->getSize(); }
+
+ /// Returns the offset into an array.
+ unsigned getOffset() const {
+ assert(Offset != PastEndMark && "invalid offset");
+ if (Base == RootPtrMark)
+ return Offset;
+
+ unsigned Adjust = 0;
+ if (Offset != Base) {
+ if (getFieldDesc()->ElemDesc)
+ Adjust = sizeof(InlineDescriptor);
+ else
+ Adjust = sizeof(InitMap *);
+ }
+ return Offset - Base - Adjust;
+ }
+
+ /// Checks if the innermost field is an array.
+ bool inArray() const { return getFieldDesc()->IsArray; }
+ /// Checks if the structure is a primitive array.
+ bool inPrimitiveArray() const { return getFieldDesc()->isPrimitiveArray(); }
+ /// Checks if the structure is an array of unknown size.
+ bool isUnknownSizeArray() const {
+ return getFieldDesc()->isUnknownSizeArray();
+ }
+ /// Checks if the pointer points to an array.
+ bool isArrayElement() const { return Base != Offset; }
+ /// Pointer points directly to a block.
+ bool isRoot() const {
+ return (Base == 0 || Base == RootPtrMark) && Offset == 0;
+ }
+
+ /// Returns the record descriptor of a class.
+ Record *getRecord() const { return getFieldDesc()->ElemRecord; }
+ /// Returns the field information.
+ const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); }
+
+ /// Checks if the object is a union.
+ bool isUnion() const;
+
+ /// Checks if the storage is extern.
+ bool isExtern() const { return Pointee->isExtern(); }
+ /// Checks if the storage is static.
+ bool isStatic() const { return Pointee->isStatic(); }
+ /// Checks if the storage is temporary.
+ bool isTemporary() const { return Pointee->isTemporary(); }
+ /// Checks if the storage is a static temporary.
+ bool isStaticTemporary() const { return isStatic() && isTemporary(); }
+
+ /// Checks if the field is mutable.
+ bool isMutable() const { return Base != 0 && getInlineDesc()->IsMutable; }
+ /// Checks if an object was initialized.
+ bool isInitialized() const;
+ /// Checks if the object is active.
+ bool isActive() const { return Base == 0 || getInlineDesc()->IsActive; }
+ /// Checks if a structure is a base class.
+ bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
+
+ /// Checks if an object or a subfield is mutable.
+ bool isConst() const {
+ return Base == 0 ? getDeclDesc()->IsConst : getInlineDesc()->IsConst;
+ }
+
+ /// Returns the declaration ID.
+ llvm::Optional<unsigned> getDeclID() const { return Pointee->getDeclID(); }
+
+ /// Returns the byte offset from the start.
+ unsigned getByteOffset() const {
+ return Offset;
+ }
+
+ /// Returns the number of elements.
+ unsigned getNumElems() const { return getSize() / elemSize(); }
+
+ /// Returns the index into an array.
+ int64_t getIndex() const {
+ if (isElementPastEnd())
+ return 1;
+ if (auto ElemSize = elemSize())
+ return getOffset() / ElemSize;
+ return 0;
+ }
+
+ /// Checks if the index is one past end.
+ bool isOnePastEnd() const {
+ return isElementPastEnd() || getSize() == getOffset();
+ }
+
+ /// Checks if the pointer is an out-of-bounds element pointer.
+ bool isElementPastEnd() const { return Offset == PastEndMark; }
+
+ /// Dereferences the pointer, if it's live.
+ template <typename T> T &deref() const {
+ assert(isLive() && "Invalid pointer");
+ return *reinterpret_cast<T *>(Pointee->data() + Offset);
+ }
+
+ /// Dereferences a primitive element.
+ template <typename T> T &elem(unsigned I) const {
+ return reinterpret_cast<T *>(Pointee->data())[I];
+ }
+
+ /// Initializes a field.
+ void initialize() const;
+ /// Activats a field.
+ void activate() const;
+ /// Deactivates an entire strurcutre.
+ void deactivate() const;
+
+ /// Checks if two pointers are comparable.
+ static bool hasSameBase(const Pointer &A, const Pointer &B);
+ /// Checks if two pointers can be subtracted.
+ static bool hasSameArray(const Pointer &A, const Pointer &B);
+
+ /// Prints the pointer.
+ void print(llvm::raw_ostream &OS) const {
+ OS << "{" << Base << ", " << Offset << ", ";
+ if (Pointee)
+ OS << Pointee->getSize();
+ else
+ OS << "nullptr";
+ OS << "}";
+ }
+
+private:
+ friend class Block;
+ friend class DeadBlock;
+
+ Pointer(Block *Pointee, unsigned Base, unsigned Offset);
+
+ /// Returns the embedded descriptor preceding a field.
+ InlineDescriptor *getInlineDesc() const { return getDescriptor(Base); }
+
+ /// 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;
+ }
+
+ /// Returns a reference to the pointer which stores the initialization map.
+ InitMap *&getInitMap() const {
+ return *reinterpret_cast<InitMap **>(Pointee->data() + Base);
+ }
+
+ /// The block the pointer is pointing to.
+ Block *Pointee = nullptr;
+ /// Start of the current subfield.
+ unsigned Base = 0;
+ /// Offset into the block.
+ unsigned Offset = 0;
+
+ /// Previous link in the pointer chain.
+ Pointer *Prev = nullptr;
+ /// Next link in the pointer chain.
+ Pointer *Next = nullptr;
+};
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) {
+ P.print(OS);
+ return OS;
+}
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/PrimType.cpp b/lib/AST/Interp/PrimType.cpp
new file mode 100644
index 0000000000000..082bfaf3c2079
--- /dev/null
+++ b/lib/AST/Interp/PrimType.cpp
@@ -0,0 +1,23 @@
+//===--- Type.cpp - Types for the constexpr VM ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PrimType.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+namespace clang {
+namespace interp {
+
+size_t primSize(PrimType Type) {
+ TYPE_SWITCH(Type, return sizeof(T));
+ llvm_unreachable("not a primitive type");
+}
+
+} // namespace interp
+} // namespace clang
diff --git a/lib/AST/Interp/PrimType.h b/lib/AST/Interp/PrimType.h
new file mode 100644
index 0000000000000..f5f4f8e5c32d6
--- /dev/null
+++ b/lib/AST/Interp/PrimType.h
@@ -0,0 +1,115 @@
+//===--- PrimType.h - Types for the constexpr VM --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the VM types and helpers operating on types.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_TYPE_H
+#define LLVM_CLANG_AST_INTERP_TYPE_H
+
+#include <climits>
+#include <cstddef>
+#include <cstdint>
+#include "Boolean.h"
+#include "Integral.h"
+#include "Pointer.h"
+
+namespace clang {
+namespace interp {
+
+/// Enumeration of the primitive types of the VM.
+enum PrimType : unsigned {
+ PT_Sint8,
+ PT_Uint8,
+ PT_Sint16,
+ PT_Uint16,
+ PT_Sint32,
+ PT_Uint32,
+ PT_Sint64,
+ PT_Uint64,
+ PT_Bool,
+ PT_Ptr,
+};
+
+/// Mapping from primitive types to their representation.
+template <PrimType T> struct PrimConv;
+template <> struct PrimConv<PT_Sint8> { using T = Integral<8, true>; };
+template <> struct PrimConv<PT_Uint8> { using T = Integral<8, false>; };
+template <> struct PrimConv<PT_Sint16> { using T = Integral<16, true>; };
+template <> struct PrimConv<PT_Uint16> { using T = Integral<16, false>; };
+template <> struct PrimConv<PT_Sint32> { using T = Integral<32, true>; };
+template <> struct PrimConv<PT_Uint32> { using T = Integral<32, false>; };
+template <> struct PrimConv<PT_Sint64> { using T = Integral<64, true>; };
+template <> struct PrimConv<PT_Uint64> { using T = Integral<64, false>; };
+template <> struct PrimConv<PT_Bool> { using T = Boolean; };
+template <> struct PrimConv<PT_Ptr> { using T = Pointer; };
+
+/// Returns the size of a primitive type in bytes.
+size_t primSize(PrimType Type);
+
+/// Aligns a size to the pointer alignment.
+constexpr size_t align(size_t Size) {
+ return ((Size + alignof(void *) - 1) / alignof(void *)) * alignof(void *);
+}
+
+inline bool isPrimitiveIntegral(PrimType Type) {
+ switch (Type) {
+ case PT_Bool:
+ case PT_Sint8:
+ case PT_Uint8:
+ case PT_Sint16:
+ case PT_Uint16:
+ case PT_Sint32:
+ case PT_Uint32:
+ case PT_Sint64:
+ case PT_Uint64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace interp
+} // namespace clang
+
+/// Helper macro to simplify type switches.
+/// The macro implicitly exposes a type T in the scope of the inner block.
+#define TYPE_SWITCH_CASE(Name, B) \
+ case Name: { using T = PrimConv<Name>::T; do {B;} while(0); break; }
+#define TYPE_SWITCH(Expr, B) \
+ switch (Expr) { \
+ TYPE_SWITCH_CASE(PT_Sint8, B) \
+ TYPE_SWITCH_CASE(PT_Uint8, B) \
+ TYPE_SWITCH_CASE(PT_Sint16, B) \
+ TYPE_SWITCH_CASE(PT_Uint16, B) \
+ TYPE_SWITCH_CASE(PT_Sint32, B) \
+ TYPE_SWITCH_CASE(PT_Uint32, B) \
+ TYPE_SWITCH_CASE(PT_Sint64, B) \
+ TYPE_SWITCH_CASE(PT_Uint64, B) \
+ TYPE_SWITCH_CASE(PT_Bool, B) \
+ TYPE_SWITCH_CASE(PT_Ptr, B) \
+ }
+#define COMPOSITE_TYPE_SWITCH(Expr, B, D) \
+ switch (Expr) { \
+ TYPE_SWITCH_CASE(PT_Ptr, B) \
+ default: do { D; } while(0); break; \
+ }
+#define INT_TYPE_SWITCH(Expr, B) \
+ switch (Expr) { \
+ TYPE_SWITCH_CASE(PT_Sint8, B) \
+ TYPE_SWITCH_CASE(PT_Uint8, B) \
+ TYPE_SWITCH_CASE(PT_Sint16, B) \
+ TYPE_SWITCH_CASE(PT_Uint16, B) \
+ TYPE_SWITCH_CASE(PT_Sint32, B) \
+ TYPE_SWITCH_CASE(PT_Uint32, B) \
+ TYPE_SWITCH_CASE(PT_Sint64, B) \
+ TYPE_SWITCH_CASE(PT_Uint64, B) \
+ default: llvm_unreachable("not an integer"); \
+ }
+#endif
diff --git a/lib/AST/Interp/Program.cpp b/lib/AST/Interp/Program.cpp
new file mode 100644
index 0000000000000..fcbab0ea8172c
--- /dev/null
+++ b/lib/AST/Interp/Program.cpp
@@ -0,0 +1,364 @@
+//===--- Program.cpp - Bytecode for the constexpr VM ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Program.h"
+#include "ByteCodeStmtGen.h"
+#include "Context.h"
+#include "Function.h"
+#include "Opcode.h"
+#include "PrimType.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+unsigned Program::createGlobalString(const StringLiteral *S) {
+ const size_t CharWidth = S->getCharByteWidth();
+ const size_t BitWidth = CharWidth * Ctx.getCharBit();
+
+ PrimType CharType;
+ switch (CharWidth) {
+ case 1:
+ CharType = PT_Sint8;
+ break;
+ case 2:
+ CharType = PT_Uint16;
+ break;
+ case 4:
+ CharType = PT_Uint32;
+ break;
+ default:
+ llvm_unreachable("unsupported character width");
+ }
+
+ // Create a descriptor for the string.
+ Descriptor *Desc = allocateDescriptor(S, CharType, S->getLength() + 1,
+ /*isConst=*/true,
+ /*isTemporary=*/false,
+ /*isMutable=*/false);
+
+ // Allocate storage for the string.
+ // The byte length does not include the null terminator.
+ unsigned I = Globals.size();
+ unsigned Sz = Desc->getAllocSize();
+ auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true,
+ /*isExtern=*/false);
+ Globals.push_back(G);
+
+ // Construct the string in storage.
+ const Pointer Ptr(G->block());
+ for (unsigned I = 0, N = S->getLength(); I <= N; ++I) {
+ Pointer Field = Ptr.atIndex(I).narrow();
+ const uint32_t CodePoint = I == N ? 0 : S->getCodeUnit(I);
+ switch (CharType) {
+ case PT_Sint8: {
+ using T = PrimConv<PT_Sint8>::T;
+ Field.deref<T>() = T::from(CodePoint, BitWidth);
+ break;
+ }
+ case PT_Uint16: {
+ using T = PrimConv<PT_Uint16>::T;
+ Field.deref<T>() = T::from(CodePoint, BitWidth);
+ break;
+ }
+ case PT_Uint32: {
+ using T = PrimConv<PT_Uint32>::T;
+ Field.deref<T>() = T::from(CodePoint, BitWidth);
+ break;
+ }
+ default:
+ llvm_unreachable("unsupported character type");
+ }
+ }
+ return I;
+}
+
+Pointer Program::getPtrGlobal(unsigned Idx) {
+ assert(Idx < Globals.size());
+ return Pointer(Globals[Idx]->block());
+}
+
+llvm::Optional<unsigned> Program::getGlobal(const ValueDecl *VD) {
+ auto It = GlobalIndices.find(VD);
+ if (It != GlobalIndices.end())
+ return It->second;
+
+ // Find any previous declarations which were aleady evaluated.
+ llvm::Optional<unsigned> Index;
+ for (const Decl *P = VD; P; P = P->getPreviousDecl()) {
+ auto It = GlobalIndices.find(P);
+ if (It != GlobalIndices.end()) {
+ Index = It->second;
+ break;
+ }
+ }
+
+ // Map the decl to the existing index.
+ if (Index) {
+ GlobalIndices[VD] = *Index;
+ return {};
+ }
+
+ return Index;
+}
+
+llvm::Optional<unsigned> Program::getOrCreateGlobal(const ValueDecl *VD) {
+ if (auto Idx = getGlobal(VD))
+ return Idx;
+
+ if (auto Idx = createGlobal(VD)) {
+ GlobalIndices[VD] = *Idx;
+ return Idx;
+ }
+ return {};
+}
+
+llvm::Optional<unsigned> Program::getOrCreateDummy(const ParmVarDecl *PD) {
+ auto &ASTCtx = Ctx.getASTContext();
+
+ // Create a pointer to an incomplete array of the specified elements.
+ QualType ElemTy = PD->getType()->castAs<PointerType>()->getPointeeType();
+ QualType Ty = ASTCtx.getIncompleteArrayType(ElemTy, ArrayType::Normal, 0);
+
+ // Dedup blocks since they are immutable and pointers cannot be compared.
+ auto It = DummyParams.find(PD);
+ if (It != DummyParams.end())
+ return It->second;
+
+ if (auto Idx = createGlobal(PD, Ty, /*isStatic=*/true, /*isExtern=*/true)) {
+ DummyParams[PD] = *Idx;
+ return Idx;
+ }
+ return {};
+}
+
+llvm::Optional<unsigned> Program::createGlobal(const ValueDecl *VD) {
+ bool IsStatic, IsExtern;
+ if (auto *Var = dyn_cast<VarDecl>(VD)) {
+ IsStatic = !Var->hasLocalStorage();
+ IsExtern = !Var->getAnyInitializer();
+ } else {
+ IsStatic = false;
+ IsExtern = true;
+ }
+ if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern)) {
+ for (const Decl *P = VD; P; P = P->getPreviousDecl())
+ GlobalIndices[P] = *Idx;
+ return *Idx;
+ }
+ return {};
+}
+
+llvm::Optional<unsigned> Program::createGlobal(const Expr *E) {
+ return createGlobal(E, E->getType(), /*isStatic=*/true, /*isExtern=*/false);
+}
+
+llvm::Optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
+ bool IsStatic, bool IsExtern) {
+ // Create a descriptor for the global.
+ Descriptor *Desc;
+ 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);
+ } else {
+ Desc = createDescriptor(D, Ty.getTypePtr(), IsConst, IsTemporary);
+ }
+ if (!Desc)
+ return {};
+
+ // Allocate a block for storage.
+ unsigned I = Globals.size();
+
+ auto *G = new (Allocator, Desc->getAllocSize())
+ Global(getCurrentDecl(), Desc, IsStatic, IsExtern);
+ G->block()->invokeCtor();
+
+ Globals.push_back(G);
+
+ return I;
+}
+
+Function *Program::getFunction(const FunctionDecl *F) {
+ F = F->getDefinition();
+ auto It = Funcs.find(F);
+ return It == Funcs.end() ? nullptr : It->second.get();
+}
+
+llvm::Expected<Function *> Program::getOrCreateFunction(const FunctionDecl *F) {
+ if (Function *Func = getFunction(F)) {
+ return Func;
+ }
+
+ // Try to compile the function if it wasn't compiled yet.
+ if (const FunctionDecl *FD = F->getDefinition())
+ return ByteCodeStmtGen<ByteCodeEmitter>(Ctx, *this).compileFunc(FD);
+
+ // A relocation which traps if not resolved.
+ return nullptr;
+}
+
+Record *Program::getOrCreateRecord(const RecordDecl *RD) {
+ // Use the actual definition as a key.
+ RD = RD->getDefinition();
+ if (!RD)
+ return nullptr;
+
+ // Deduplicate records.
+ auto It = Records.find(RD);
+ if (It != Records.end()) {
+ return It->second;
+ }
+
+ // Number of bytes required by fields and base classes.
+ unsigned Size = 0;
+ // Number of bytes required by virtual base.
+ unsigned VirtSize = 0;
+
+ // Helper to get a base descriptor.
+ auto GetBaseDesc = [this](const RecordDecl *BD, Record *BR) -> Descriptor * {
+ if (!BR)
+ return nullptr;
+ return allocateDescriptor(BD, BR, /*isConst=*/false,
+ /*isTemporary=*/false,
+ /*isMutable=*/false);
+ };
+
+ // Reserve space for base classes.
+ Record::BaseList Bases;
+ Record::VirtualBaseList VirtBases;
+ if (auto *CD = dyn_cast<CXXRecordDecl>(RD)) {
+ for (const CXXBaseSpecifier &Spec : CD->bases()) {
+ if (Spec.isVirtual())
+ continue;
+
+ const RecordDecl *BD = Spec.getType()->castAs<RecordType>()->getDecl();
+ Record *BR = getOrCreateRecord(BD);
+ if (Descriptor *Desc = GetBaseDesc(BD, BR)) {
+ Size += align(sizeof(InlineDescriptor));
+ Bases.push_back({BD, Size, Desc, BR});
+ Size += align(BR->getSize());
+ continue;
+ }
+ return nullptr;
+ }
+
+ for (const CXXBaseSpecifier &Spec : CD->vbases()) {
+ const RecordDecl *BD = Spec.getType()->castAs<RecordType>()->getDecl();
+ Record *BR = getOrCreateRecord(BD);
+
+ if (Descriptor *Desc = GetBaseDesc(BD, BR)) {
+ VirtSize += align(sizeof(InlineDescriptor));
+ VirtBases.push_back({BD, VirtSize, Desc, BR});
+ VirtSize += align(BR->getSize());
+ continue;
+ }
+ return nullptr;
+ }
+ }
+
+ // Reserve space for fields.
+ Record::FieldList Fields;
+ for (const FieldDecl *FD : RD->fields()) {
+ // Reserve space for the field's descriptor and the offset.
+ Size += align(sizeof(InlineDescriptor));
+
+ // Classify the field and add its metadata.
+ QualType FT = FD->getType();
+ const bool IsConst = FT.isConstQualified();
+ const bool IsMutable = FD->isMutable();
+ Descriptor *Desc;
+ if (llvm::Optional<PrimType> T = Ctx.classify(FT)) {
+ Desc = createDescriptor(FD, *T, IsConst, /*isTemporary=*/false,
+ IsMutable);
+ } else {
+ Desc = createDescriptor(FD, FT.getTypePtr(), IsConst,
+ /*isTemporary=*/false, IsMutable);
+ }
+ if (!Desc)
+ return nullptr;
+ Fields.push_back({FD, Size, Desc});
+ Size += align(Desc->getAllocSize());
+ }
+
+ Record *R = new (Allocator) Record(RD, std::move(Bases), std::move(Fields),
+ std::move(VirtBases), VirtSize, Size);
+ Records.insert({RD, R});
+ return R;
+}
+
+Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
+ bool IsConst, bool IsTemporary,
+ bool IsMutable) {
+ // Classes and structures.
+ if (auto *RT = Ty->getAs<RecordType>()) {
+ if (auto *Record = getOrCreateRecord(RT->getDecl()))
+ return allocateDescriptor(D, Record, IsConst, IsTemporary, IsMutable);
+ }
+
+ // Arrays.
+ if (auto ArrayType = Ty->getAsArrayTypeUnsafe()) {
+ QualType ElemTy = ArrayType->getElementType();
+ // Array of well-known bounds.
+ if (auto CAT = dyn_cast<ConstantArrayType>(ArrayType)) {
+ size_t NumElems = CAT->getSize().getZExtValue();
+ if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) {
+ // Arrays of primitives.
+ unsigned ElemSize = primSize(*T);
+ if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems) {
+ return {};
+ }
+ return allocateDescriptor(D, *T, NumElems, IsConst, IsTemporary,
+ IsMutable);
+ } else {
+ // Arrays of composites. In this case, the array is a list of pointers,
+ // followed by the actual elements.
+ Descriptor *Desc =
+ createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary);
+ if (!Desc)
+ return nullptr;
+ InterpSize ElemSize = Desc->getAllocSize() + sizeof(InlineDescriptor);
+ if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems)
+ return {};
+ return allocateDescriptor(D, Desc, NumElems, IsConst, IsTemporary,
+ IsMutable);
+ }
+ }
+
+ // Array of unknown bounds - cannot be accessed and pointer arithmetic
+ // is forbidden on pointers to such objects.
+ if (isa<IncompleteArrayType>(ArrayType)) {
+ if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) {
+ return allocateDescriptor(D, *T, IsTemporary,
+ Descriptor::UnknownSize{});
+ } else {
+ Descriptor *Desc =
+ createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary);
+ if (!Desc)
+ return nullptr;
+ return allocateDescriptor(D, Desc, IsTemporary,
+ Descriptor::UnknownSize{});
+ }
+ }
+ }
+
+ // Atomic types.
+ if (auto *AT = Ty->getAs<AtomicType>()) {
+ const Type *InnerTy = AT->getValueType().getTypePtr();
+ return createDescriptor(D, InnerTy, 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 nullptr;
+}
diff --git a/lib/AST/Interp/Program.h b/lib/AST/Interp/Program.h
new file mode 100644
index 0000000000000..5f0012db9b3f2
--- /dev/null
+++ b/lib/AST/Interp/Program.h
@@ -0,0 +1,220 @@
+//===--- Program.h - Bytecode for the constexpr VM --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a program which organises and links multiple bytecode functions.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_PROGRAM_H
+#define LLVM_CLANG_AST_INTERP_PROGRAM_H
+
+#include <map>
+#include <vector>
+#include "Function.h"
+#include "Pointer.h"
+#include "PrimType.h"
+#include "Record.h"
+#include "Source.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Allocator.h"
+
+namespace clang {
+class RecordDecl;
+class Expr;
+class FunctionDecl;
+class Stmt;
+class StringLiteral;
+class VarDecl;
+
+namespace interp {
+class Context;
+class State;
+class Record;
+class Scope;
+
+/// The program contains and links the bytecode for all functions.
+class Program {
+public:
+ Program(Context &Ctx) : Ctx(Ctx) {}
+
+ /// Emits a string literal among global data.
+ unsigned createGlobalString(const StringLiteral *S);
+
+ /// Returns a pointer to a global.
+ Pointer getPtrGlobal(unsigned Idx);
+
+ /// Returns the value of a global.
+ Block *getGlobal(unsigned Idx) {
+ assert(Idx < Globals.size());
+ return Globals[Idx]->block();
+ }
+
+ /// Finds a global's index.
+ llvm::Optional<unsigned> getGlobal(const ValueDecl *VD);
+
+ /// Returns or creates a global an creates an index to it.
+ llvm::Optional<unsigned> getOrCreateGlobal(const ValueDecl *VD);
+
+ /// Returns or creates a dummy value for parameters.
+ llvm::Optional<unsigned> getOrCreateDummy(const ParmVarDecl *PD);
+
+ /// Creates a global and returns its index.
+ llvm::Optional<unsigned> createGlobal(const ValueDecl *VD);
+
+ /// Creates a global from a lifetime-extended temporary.
+ llvm::Optional<unsigned> createGlobal(const Expr *E);
+
+ /// Creates a new function from a code range.
+ template <typename... Ts>
+ Function *createFunction(const FunctionDecl *Def, Ts &&... Args) {
+ auto *Func = new Function(*this, Def, std::forward<Ts>(Args)...);
+ Funcs.insert({Def, std::unique_ptr<Function>(Func)});
+ return Func;
+ }
+ /// Creates an anonymous function.
+ template <typename... Ts>
+ Function *createFunction(Ts &&... Args) {
+ auto *Func = new Function(*this, std::forward<Ts>(Args)...);
+ AnonFuncs.emplace_back(Func);
+ return Func;
+ }
+
+ /// Returns a function.
+ Function *getFunction(const FunctionDecl *F);
+
+ /// Returns a pointer to a function if it exists and can be compiled.
+ /// If a function couldn't be compiled, an error is returned.
+ /// If a function was not yet defined, a null pointer is returned.
+ llvm::Expected<Function *> getOrCreateFunction(const FunctionDecl *F);
+
+ /// Returns a record or creates one if it does not exist.
+ Record *getOrCreateRecord(const RecordDecl *RD);
+
+ /// Creates a descriptor for a primitive type.
+ Descriptor *createDescriptor(const DeclTy &D, PrimType Type,
+ bool IsConst = false,
+ bool IsTemporary = false,
+ bool IsMutable = false) {
+ return allocateDescriptor(D, Type, IsConst, IsTemporary, IsMutable);
+ }
+
+ /// Creates a descriptor for a composite type.
+ Descriptor *createDescriptor(const DeclTy &D, const Type *Ty,
+ bool IsConst = false, bool IsTemporary = false,
+ bool IsMutable = false);
+
+ /// Context to manage declaration lifetimes.
+ class DeclScope {
+ public:
+ DeclScope(Program &P, const VarDecl *VD) : P(P) { P.startDeclaration(VD); }
+ ~DeclScope() { P.endDeclaration(); }
+
+ private:
+ Program &P;
+ };
+
+ /// Returns the current declaration ID.
+ llvm::Optional<unsigned> getCurrentDecl() const {
+ if (CurrentDeclaration == NoDeclaration)
+ return llvm::Optional<unsigned>{};
+ return LastDeclaration;
+ }
+
+private:
+ friend class DeclScope;
+
+ llvm::Optional<unsigned> createGlobal(const DeclTy &D, QualType Ty,
+ bool IsStatic, bool IsExtern);
+
+ /// Reference to the VM context.
+ Context &Ctx;
+ /// Mapping from decls to cached bytecode functions.
+ llvm::DenseMap<const FunctionDecl *, std::unique_ptr<Function>> Funcs;
+ /// List of anonymous functions.
+ std::vector<std::unique_ptr<Function>> AnonFuncs;
+
+ /// Function relocation locations.
+ llvm::DenseMap<const FunctionDecl *, std::vector<unsigned>> Relocs;
+
+ /// Custom allocator for global storage.
+ using PoolAllocTy = llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator>;
+
+ /// Descriptor + storage for a global object.
+ ///
+ /// Global objects never go out of scope, thus they do not track pointers.
+ class Global {
+ public:
+ /// Create a global descriptor for string literals.
+ template <typename... Tys>
+ Global(Tys... Args) : B(std::forward<Tys>(Args)...) {}
+
+ /// Allocates the global in the pool, reserving storate for data.
+ void *operator new(size_t Meta, PoolAllocTy &Alloc, size_t Data) {
+ return Alloc.Allocate(Meta + Data, alignof(void *));
+ }
+
+ /// Return a pointer to the data.
+ char *data() { return B.data(); }
+ /// Return a pointer to the block.
+ Block *block() { return &B; }
+
+ private:
+ /// Required metadata - does not actually track pointers.
+ Block B;
+ };
+
+ /// Allocator for globals.
+ PoolAllocTy Allocator;
+
+ /// Global objects.
+ std::vector<Global *> Globals;
+ /// Cached global indices.
+ llvm::DenseMap<const void *, unsigned> GlobalIndices;
+
+ /// Mapping from decls to record metadata.
+ llvm::DenseMap<const RecordDecl *, Record *> Records;
+
+ /// Dummy parameter to generate pointers from.
+ llvm::DenseMap<const ParmVarDecl *, unsigned> DummyParams;
+
+ /// Creates a new descriptor.
+ template <typename... Ts>
+ Descriptor *allocateDescriptor(Ts &&... Args) {
+ return new (Allocator) Descriptor(std::forward<Ts>(Args)...);
+ }
+
+ /// No declaration ID.
+ static constexpr unsigned NoDeclaration = (unsigned)-1;
+ /// Last declaration ID.
+ unsigned LastDeclaration = 0;
+ /// Current declaration ID.
+ unsigned CurrentDeclaration = NoDeclaration;
+
+ /// Starts evaluating a declaration.
+ void startDeclaration(const VarDecl *Decl) {
+ LastDeclaration += 1;
+ CurrentDeclaration = LastDeclaration;
+ }
+
+ /// Ends a global declaration.
+ void endDeclaration() {
+ CurrentDeclaration = NoDeclaration;
+ }
+
+public:
+ /// Dumps the disassembled bytecode to \c llvm::errs().
+ void dump() const;
+ void dump(llvm::raw_ostream &OS) const;
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/Record.cpp b/lib/AST/Interp/Record.cpp
new file mode 100644
index 0000000000000..f440c4705051e
--- /dev/null
+++ b/lib/AST/Interp/Record.cpp
@@ -0,0 +1,46 @@
+//===--- Record.cpp - struct and class metadata for the VM ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Record.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+Record::Record(const RecordDecl *Decl, BaseList &&SrcBases,
+ FieldList &&SrcFields, VirtualBaseList &&SrcVirtualBases,
+ unsigned VirtualSize, unsigned BaseSize)
+ : Decl(Decl), Bases(std::move(SrcBases)), Fields(std::move(SrcFields)),
+ BaseSize(BaseSize), VirtualSize(VirtualSize) {
+ for (Base &V : SrcVirtualBases)
+ VirtualBases.push_back({ V.Decl, V.Offset + BaseSize, V.Desc, V.R });
+
+ for (Base &B : Bases)
+ BaseMap[B.Decl] = &B;
+ for (Field &F : Fields)
+ FieldMap[F.Decl] = &F;
+ for (Base &V : VirtualBases)
+ VirtualBaseMap[V.Decl] = &V;
+}
+
+const Record::Field *Record::getField(const FieldDecl *FD) const {
+ auto It = FieldMap.find(FD);
+ assert(It != FieldMap.end() && "Missing field");
+ return It->second;
+}
+
+const Record::Base *Record::getBase(const RecordDecl *FD) const {
+ auto It = BaseMap.find(FD);
+ assert(It != BaseMap.end() && "Missing base");
+ return It->second;
+}
+
+const Record::Base *Record::getVirtualBase(const RecordDecl *FD) const {
+ auto It = VirtualBaseMap.find(FD);
+ assert(It != VirtualBaseMap.end() && "Missing virtual base");
+ return It->second;
+}
diff --git a/lib/AST/Interp/Record.h b/lib/AST/Interp/Record.h
new file mode 100644
index 0000000000000..9cdee9003752e
--- /dev/null
+++ b/lib/AST/Interp/Record.h
@@ -0,0 +1,121 @@
+//===--- Record.h - struct and class metadata for the VM --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// A record is part of a program to describe the layout and methods of a struct.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_RECORD_H
+#define LLVM_CLANG_AST_INTERP_RECORD_H
+
+#include "Pointer.h"
+
+namespace clang {
+namespace interp {
+class Program;
+
+/// Structure/Class descriptor.
+class Record {
+public:
+ /// Describes a record field.
+ struct Field {
+ const FieldDecl *Decl;
+ unsigned Offset;
+ Descriptor *Desc;
+ };
+
+ /// Describes a base class.
+ struct Base {
+ const RecordDecl *Decl;
+ unsigned Offset;
+ Descriptor *Desc;
+ Record *R;
+ };
+
+ /// Mapping from identifiers to field descriptors.
+ using FieldList = llvm::SmallVector<Field, 8>;
+ /// Mapping from identifiers to base classes.
+ using BaseList = llvm::SmallVector<Base, 8>;
+ /// List of virtual base classes.
+ using VirtualBaseList = llvm::SmallVector<Base, 2>;
+
+public:
+ /// Returns the underlying declaration.
+ const RecordDecl *getDecl() const { return Decl; }
+ /// Checks if the record is a union.
+ bool isUnion() const { return getDecl()->isUnion(); }
+ /// Returns the size of the record.
+ unsigned getSize() const { return BaseSize; }
+ /// Returns the full size of the record, including records.
+ unsigned getFullSize() const { return BaseSize + VirtualSize; }
+ /// Returns a field.
+ const Field *getField(const FieldDecl *FD) const;
+ /// Returns a base descriptor.
+ const Base *getBase(const RecordDecl *FD) const;
+ /// Returns a virtual base descriptor.
+ const Base *getVirtualBase(const RecordDecl *RD) const;
+
+ using const_field_iter = FieldList::const_iterator;
+ llvm::iterator_range<const_field_iter> fields() const {
+ return llvm::make_range(Fields.begin(), Fields.end());
+ }
+
+ unsigned getNumFields() { return Fields.size(); }
+ Field *getField(unsigned I) { return &Fields[I]; }
+
+ using const_base_iter = BaseList::const_iterator;
+ llvm::iterator_range<const_base_iter> bases() const {
+ return llvm::make_range(Bases.begin(), Bases.end());
+ }
+
+ unsigned getNumBases() { return Bases.size(); }
+ Base *getBase(unsigned I) { return &Bases[I]; }
+
+ using const_virtual_iter = VirtualBaseList::const_iterator;
+ llvm::iterator_range<const_virtual_iter> virtual_bases() const {
+ return llvm::make_range(VirtualBases.begin(), VirtualBases.end());
+ }
+
+ unsigned getNumVirtualBases() { return VirtualBases.size(); }
+ Base *getVirtualBase(unsigned I) { return &VirtualBases[I]; }
+
+private:
+ /// Constructor used by Program to create record descriptors.
+ Record(const RecordDecl *, BaseList &&Bases, FieldList &&Fields,
+ VirtualBaseList &&VirtualBases, unsigned VirtualSize,
+ unsigned BaseSize);
+
+private:
+ friend class Program;
+
+ /// Original declaration.
+ const RecordDecl *Decl;
+ /// List of all base classes.
+ BaseList Bases;
+ /// List of all the fields in the record.
+ FieldList Fields;
+ /// List o fall virtual bases.
+ VirtualBaseList VirtualBases;
+
+ /// Mapping from declarations to bases.
+ llvm::DenseMap<const RecordDecl *, Base *> BaseMap;
+ /// Mapping from field identifiers to descriptors.
+ llvm::DenseMap<const FieldDecl *, Field *> FieldMap;
+ /// Mapping from declarations to virtual bases.
+ llvm::DenseMap<const RecordDecl *, Base *> VirtualBaseMap;
+ /// Mapping from
+ /// Size of the structure.
+ unsigned BaseSize;
+ /// Size of all virtual bases.
+ unsigned VirtualSize;
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/Source.cpp b/lib/AST/Interp/Source.cpp
new file mode 100644
index 0000000000000..4bec878126384
--- /dev/null
+++ b/lib/AST/Interp/Source.cpp
@@ -0,0 +1,39 @@
+//===--- Source.cpp - Source expression tracking ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Source.h"
+#include "clang/AST/Expr.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+SourceLocation SourceInfo::getLoc() const {
+ if (const Expr *E = asExpr())
+ return E->getExprLoc();
+ if (const Stmt *S = asStmt())
+ return S->getBeginLoc();
+ if (const Decl *D = asDecl())
+ return D->getBeginLoc();
+ return SourceLocation();
+}
+
+const Expr *SourceInfo::asExpr() const {
+ if (auto *S = Source.dyn_cast<const Stmt *>())
+ return dyn_cast<Expr>(S);
+ return nullptr;
+}
+
+const Expr *SourceMapper::getExpr(Function *F, CodePtr PC) const {
+ if (const Expr *E = getSource(F, PC).asExpr())
+ return E;
+ llvm::report_fatal_error("missing source expression");
+}
+
+SourceLocation SourceMapper::getLocation(Function *F, CodePtr PC) const {
+ return getSource(F, PC).getLoc();
+}
diff --git a/lib/AST/Interp/Source.h b/lib/AST/Interp/Source.h
new file mode 100644
index 0000000000000..e591c3399d7c1
--- /dev/null
+++ b/lib/AST/Interp/Source.h
@@ -0,0 +1,118 @@
+//===--- Source.h - Source location provider for the VM --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a program which organises and links multiple bytecode functions.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_SOURCE_H
+#define LLVM_CLANG_AST_INTERP_SOURCE_H
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/Stmt.h"
+#include "llvm/Support/Endian.h"
+
+namespace clang {
+namespace interp {
+class Function;
+
+/// Pointer into the code segment.
+class CodePtr {
+public:
+ CodePtr() : Ptr(nullptr) {}
+
+ CodePtr &operator+=(int32_t Offset) {
+ Ptr += Offset;
+ return *this;
+ }
+
+ int32_t operator-(const CodePtr &RHS) const {
+ assert(Ptr != nullptr && RHS.Ptr != nullptr && "Invalid code pointer");
+ return Ptr - RHS.Ptr;
+ }
+
+ CodePtr operator-(size_t RHS) const {
+ assert(Ptr != nullptr && "Invalid code pointer");
+ return CodePtr(Ptr - RHS);
+ }
+
+ bool operator!=(const CodePtr &RHS) const { return Ptr != RHS.Ptr; }
+
+ /// Reads data and advances the pointer.
+ template <typename T> T read() {
+ T Value = ReadHelper<T>(Ptr);
+ Ptr += sizeof(T);
+ return Value;
+ }
+
+private:
+ /// Constructor used by Function to generate pointers.
+ CodePtr(const char *Ptr) : Ptr(Ptr) {}
+
+ /// Helper to decode a value or a pointer.
+ template <typename T>
+ static typename std::enable_if<!std::is_pointer<T>::value, T>::type
+ ReadHelper(const char *Ptr) {
+ using namespace llvm::support;
+ return endian::read<T, endianness::native, 1>(Ptr);
+ }
+
+ template <typename T>
+ static typename std::enable_if<std::is_pointer<T>::value, T>::type
+ ReadHelper(const char *Ptr) {
+ using namespace llvm::support;
+ auto Punned = endian::read<uintptr_t, endianness::native, 1>(Ptr);
+ return reinterpret_cast<T>(Punned);
+ }
+
+private:
+ friend class Function;
+
+ /// Pointer into the code owned by a function.
+ const char *Ptr;
+};
+
+/// Describes the statement/declaration an opcode was generated from.
+class SourceInfo {
+public:
+ SourceInfo() {}
+ SourceInfo(const Stmt *E) : Source(E) {}
+ SourceInfo(const Decl *D) : Source(D) {}
+
+ SourceLocation getLoc() const;
+
+ const Stmt *asStmt() const { return Source.dyn_cast<const Stmt *>(); }
+ const Decl *asDecl() const { return Source.dyn_cast<const Decl *>(); }
+ const Expr *asExpr() const;
+
+ operator bool() const { return !Source.isNull(); }
+
+private:
+ llvm::PointerUnion<const Decl *, const Stmt *> Source;
+};
+
+using SourceMap = std::vector<std::pair<unsigned, SourceInfo>>;
+
+/// Interface for classes which map locations to sources.
+class SourceMapper {
+public:
+ virtual ~SourceMapper() {}
+
+ /// Returns source information for a given PC in a function.
+ virtual SourceInfo getSource(Function *F, CodePtr PC) const = 0;
+
+ /// Returns the expression if an opcode belongs to one, null otherwise.
+ const Expr *getExpr(Function *F, CodePtr PC) const;
+ /// Returns the location from which an opcode originates.
+ SourceLocation getLocation(Function *F, CodePtr PC) const;
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/Interp/State.cpp b/lib/AST/Interp/State.cpp
new file mode 100644
index 0000000000000..692cc2e8d69bf
--- /dev/null
+++ b/lib/AST/Interp/State.cpp
@@ -0,0 +1,158 @@
+//===--- State.cpp - State chain for the VM and AST Walker ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "State.h"
+#include "Frame.h"
+#include "Program.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/CXXInheritance.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+State::~State() {}
+
+OptionalDiagnostic State::FFDiag(SourceLocation Loc, diag::kind DiagId,
+ unsigned ExtraNotes) {
+ return diag(Loc, DiagId, ExtraNotes, false);
+}
+
+OptionalDiagnostic State::FFDiag(const Expr *E, diag::kind DiagId,
+ unsigned ExtraNotes) {
+ if (getEvalStatus().Diag)
+ return diag(E->getExprLoc(), DiagId, ExtraNotes, false);
+ setActiveDiagnostic(false);
+ return OptionalDiagnostic();
+}
+
+OptionalDiagnostic State::FFDiag(const SourceInfo &SI, diag::kind DiagId,
+ unsigned ExtraNotes) {
+ if (getEvalStatus().Diag)
+ return diag(SI.getLoc(), DiagId, ExtraNotes, false);
+ setActiveDiagnostic(false);
+ return OptionalDiagnostic();
+}
+
+OptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId,
+ unsigned ExtraNotes) {
+ // Don't override a previous diagnostic. Don't bother collecting
+ // diagnostics if we're evaluating for overflow.
+ if (!getEvalStatus().Diag || !getEvalStatus().Diag->empty()) {
+ setActiveDiagnostic(false);
+ return OptionalDiagnostic();
+ }
+ return diag(Loc, DiagId, ExtraNotes, true);
+}
+
+OptionalDiagnostic State::CCEDiag(const Expr *E, diag::kind DiagId,
+ unsigned ExtraNotes) {
+ return CCEDiag(E->getExprLoc(), DiagId, ExtraNotes);
+}
+
+OptionalDiagnostic State::CCEDiag(const SourceInfo &SI, diag::kind DiagId,
+ unsigned ExtraNotes) {
+ return CCEDiag(SI.getLoc(), DiagId, ExtraNotes);
+}
+
+OptionalDiagnostic State::Note(SourceLocation Loc, diag::kind DiagId) {
+ if (!hasActiveDiagnostic())
+ return OptionalDiagnostic();
+ return OptionalDiagnostic(&addDiag(Loc, DiagId));
+}
+
+void State::addNotes(ArrayRef<PartialDiagnosticAt> Diags) {
+ if (hasActiveDiagnostic()) {
+ getEvalStatus().Diag->insert(getEvalStatus().Diag->end(), Diags.begin(),
+ Diags.end());
+ }
+}
+
+DiagnosticBuilder State::report(SourceLocation Loc, diag::kind DiagId) {
+ return getCtx().getDiagnostics().Report(Loc, DiagId);
+}
+
+/// Add a diagnostic to the diagnostics list.
+PartialDiagnostic &State::addDiag(SourceLocation Loc, diag::kind DiagId) {
+ PartialDiagnostic PD(DiagId, getCtx().getDiagAllocator());
+ getEvalStatus().Diag->push_back(std::make_pair(Loc, PD));
+ return getEvalStatus().Diag->back().second;
+}
+
+OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId,
+ unsigned ExtraNotes, bool IsCCEDiag) {
+ Expr::EvalStatus &EvalStatus = getEvalStatus();
+ if (EvalStatus.Diag) {
+ if (hasPriorDiagnostic()) {
+ return OptionalDiagnostic();
+ }
+
+ unsigned CallStackNotes = getCallStackDepth() - 1;
+ unsigned Limit = getCtx().getDiagnostics().getConstexprBacktraceLimit();
+ if (Limit)
+ CallStackNotes = std::min(CallStackNotes, Limit + 1);
+ if (checkingPotentialConstantExpression())
+ CallStackNotes = 0;
+
+ setActiveDiagnostic(true);
+ setFoldFailureDiagnostic(!IsCCEDiag);
+ EvalStatus.Diag->clear();
+ EvalStatus.Diag->reserve(1 + ExtraNotes + CallStackNotes);
+ addDiag(Loc, DiagId);
+ if (!checkingPotentialConstantExpression()) {
+ addCallStack(Limit);
+ }
+ return OptionalDiagnostic(&(*EvalStatus.Diag)[0].second);
+ }
+ setActiveDiagnostic(false);
+ return OptionalDiagnostic();
+}
+
+const LangOptions &State::getLangOpts() const { return getCtx().getLangOpts(); }
+
+void State::addCallStack(unsigned Limit) {
+ // Determine which calls to skip, if any.
+ unsigned ActiveCalls = getCallStackDepth() - 1;
+ unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart;
+ if (Limit && Limit < ActiveCalls) {
+ SkipStart = Limit / 2 + Limit % 2;
+ SkipEnd = ActiveCalls - Limit / 2;
+ }
+
+ // Walk the call stack and add the diagnostics.
+ unsigned CallIdx = 0;
+ Frame *Top = getCurrentFrame();
+ const Frame *Bottom = getBottomFrame();
+ for (Frame *F = Top; F != Bottom; F = F->getCaller(), ++CallIdx) {
+ SourceLocation CallLocation = F->getCallLocation();
+
+ // Skip this call?
+ if (CallIdx >= SkipStart && CallIdx < SkipEnd) {
+ if (CallIdx == SkipStart) {
+ // Note that we're skipping calls.
+ addDiag(CallLocation, diag::note_constexpr_calls_suppressed)
+ << unsigned(ActiveCalls - Limit);
+ }
+ continue;
+ }
+
+ // Use a different note for an inheriting constructor, because from the
+ // user's perspective it's not really a function at all.
+ if (auto *CD = dyn_cast_or_null<CXXConstructorDecl>(F->getCallee())) {
+ if (CD->isInheritingConstructor()) {
+ addDiag(CallLocation, diag::note_constexpr_inherited_ctor_call_here)
+ << CD->getParent();
+ continue;
+ }
+ }
+
+ SmallVector<char, 128> Buffer;
+ llvm::raw_svector_ostream Out(Buffer);
+ F->describe(Out);
+ addDiag(CallLocation, diag::note_constexpr_call_here) << Out.str();
+ }
+}
diff --git a/lib/AST/Interp/State.h b/lib/AST/Interp/State.h
new file mode 100644
index 0000000000000..d9a645a3eb3eb
--- /dev/null
+++ b/lib/AST/Interp/State.h
@@ -0,0 +1,133 @@
+//===--- State.h - State chain for the VM and AST Walker --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the base class of the interpreter and evaluator state.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_STATE_H
+#define LLVM_CLANG_AST_INTERP_STATE_H
+
+#include "clang/AST/ASTDiagnostic.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/OptionalDiagnostic.h"
+
+namespace clang {
+
+/// Kinds of access we can perform on an object, for diagnostics. Note that
+/// we consider a member function call to be a kind of access, even though
+/// it is not formally an access of the object, because it has (largely) the
+/// same set of semantic restrictions.
+enum AccessKinds {
+ AK_Read,
+ AK_ReadObjectRepresentation,
+ AK_Assign,
+ AK_Increment,
+ AK_Decrement,
+ AK_MemberCall,
+ AK_DynamicCast,
+ AK_TypeId,
+ AK_Construct,
+ AK_Destroy,
+};
+
+// The order of this enum is important for diagnostics.
+enum CheckSubobjectKind {
+ CSK_Base,
+ CSK_Derived,
+ CSK_Field,
+ CSK_ArrayToPointer,
+ CSK_ArrayIndex,
+ CSK_Real,
+ CSK_Imag
+};
+
+namespace interp {
+class Frame;
+class SourceInfo;
+
+/// Interface for the VM to interact with the AST walker's context.
+class State {
+public:
+ virtual ~State();
+
+ virtual bool checkingForUndefinedBehavior() const = 0;
+ virtual bool checkingPotentialConstantExpression() const = 0;
+ virtual bool noteUndefinedBehavior() = 0;
+ virtual bool keepEvaluatingAfterFailure() const = 0;
+ virtual Frame *getCurrentFrame() = 0;
+ virtual const Frame *getBottomFrame() const = 0;
+ virtual bool hasActiveDiagnostic() = 0;
+ virtual void setActiveDiagnostic(bool Flag) = 0;
+ virtual void setFoldFailureDiagnostic(bool Flag) = 0;
+ virtual Expr::EvalStatus &getEvalStatus() const = 0;
+ virtual ASTContext &getCtx() const = 0;
+ virtual bool hasPriorDiagnostic() = 0;
+ virtual unsigned getCallStackDepth() = 0;
+
+public:
+ // Diagnose that the evaluation could not be folded (FF => FoldFailure)
+ OptionalDiagnostic
+ FFDiag(SourceLocation Loc,
+ diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr,
+ unsigned ExtraNotes = 0);
+
+ OptionalDiagnostic
+ FFDiag(const Expr *E,
+ diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr,
+ unsigned ExtraNotes = 0);
+
+ OptionalDiagnostic
+ FFDiag(const SourceInfo &SI,
+ diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr,
+ unsigned ExtraNotes = 0);
+
+ /// Diagnose that the evaluation does not produce a C++11 core constant
+ /// expression.
+ ///
+ /// FIXME: Stop evaluating if we're in EM_ConstantExpression or
+ /// EM_PotentialConstantExpression mode and we produce one of these.
+ OptionalDiagnostic
+ CCEDiag(SourceLocation Loc,
+ diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr,
+ unsigned ExtraNotes = 0);
+
+ OptionalDiagnostic
+ CCEDiag(const Expr *E,
+ diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr,
+ unsigned ExtraNotes = 0);
+
+ OptionalDiagnostic
+ CCEDiag(const SourceInfo &SI,
+ diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr,
+ unsigned ExtraNotes = 0);
+
+ /// Add a note to a prior diagnostic.
+ OptionalDiagnostic Note(SourceLocation Loc, diag::kind DiagId);
+
+ /// Add a stack of notes to a prior diagnostic.
+ void addNotes(ArrayRef<PartialDiagnosticAt> Diags);
+
+ /// Directly reports a diagnostic message.
+ DiagnosticBuilder report(SourceLocation Loc, diag::kind DiagId);
+
+ const LangOptions &getLangOpts() const;
+
+private:
+ void addCallStack(unsigned Limit);
+
+ PartialDiagnostic &addDiag(SourceLocation Loc, diag::kind DiagId);
+
+ OptionalDiagnostic diag(SourceLocation Loc, diag::kind DiagId,
+ unsigned ExtraNotes, bool IsCCEDiag);
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/lib/AST/ItaniumCXXABI.cpp b/lib/AST/ItaniumCXXABI.cpp
index 727a905d08a14..069add8464ae8 100644
--- a/lib/AST/ItaniumCXXABI.cpp
+++ b/lib/AST/ItaniumCXXABI.cpp
@@ -19,10 +19,12 @@
#include "CXXABI.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Mangle.h"
#include "clang/AST/MangleNumberingContext.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/Type.h"
#include "clang/Basic/TargetInfo.h"
+#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/iterator.h"
using namespace clang;
@@ -73,10 +75,33 @@ struct DecompositionDeclName {
}
namespace llvm {
+template<typename T> bool isDenseMapKeyEmpty(T V) {
+ return llvm::DenseMapInfo<T>::isEqual(
+ V, llvm::DenseMapInfo<T>::getEmptyKey());
+}
+template<typename T> bool isDenseMapKeyTombstone(T V) {
+ return llvm::DenseMapInfo<T>::isEqual(
+ V, llvm::DenseMapInfo<T>::getTombstoneKey());
+}
+
+template<typename T>
+Optional<bool> areDenseMapKeysEqualSpecialValues(T LHS, T RHS) {
+ bool LHSEmpty = isDenseMapKeyEmpty(LHS);
+ bool RHSEmpty = isDenseMapKeyEmpty(RHS);
+ if (LHSEmpty || RHSEmpty)
+ return LHSEmpty && RHSEmpty;
+
+ bool LHSTombstone = isDenseMapKeyTombstone(LHS);
+ bool RHSTombstone = isDenseMapKeyTombstone(RHS);
+ if (LHSTombstone || RHSTombstone)
+ return LHSTombstone && RHSTombstone;
+
+ return None;
+}
+
template<>
struct DenseMapInfo<DecompositionDeclName> {
using ArrayInfo = llvm::DenseMapInfo<ArrayRef<const BindingDecl*>>;
- using IdentInfo = llvm::DenseMapInfo<const IdentifierInfo*>;
static DecompositionDeclName getEmptyKey() {
return {ArrayInfo::getEmptyKey()};
}
@@ -88,10 +113,10 @@ struct DenseMapInfo<DecompositionDeclName> {
return llvm::hash_combine_range(Key.begin(), Key.end());
}
static bool isEqual(DecompositionDeclName LHS, DecompositionDeclName RHS) {
- if (ArrayInfo::isEqual(LHS.Bindings, ArrayInfo::getEmptyKey()))
- return ArrayInfo::isEqual(RHS.Bindings, ArrayInfo::getEmptyKey());
- if (ArrayInfo::isEqual(LHS.Bindings, ArrayInfo::getTombstoneKey()))
- return ArrayInfo::isEqual(RHS.Bindings, ArrayInfo::getTombstoneKey());
+ if (Optional<bool> Result = areDenseMapKeysEqualSpecialValues(
+ LHS.Bindings, RHS.Bindings))
+ return *Result;
+
return LHS.Bindings.size() == RHS.Bindings.size() &&
std::equal(LHS.begin(), LHS.end(), RHS.begin());
}
@@ -103,29 +128,32 @@ namespace {
/// Keeps track of the mangled names of lambda expressions and block
/// literals within a particular context.
class ItaniumNumberingContext : public MangleNumberingContext {
- llvm::DenseMap<const Type *, unsigned> ManglingNumbers;
+ ItaniumMangleContext *Mangler;
+ llvm::StringMap<unsigned> LambdaManglingNumbers;
+ unsigned BlockManglingNumber = 0;
llvm::DenseMap<const IdentifierInfo *, unsigned> VarManglingNumbers;
llvm::DenseMap<const IdentifierInfo *, unsigned> TagManglingNumbers;
llvm::DenseMap<DecompositionDeclName, unsigned>
DecompsitionDeclManglingNumbers;
public:
+ ItaniumNumberingContext(ItaniumMangleContext *Mangler) : Mangler(Mangler) {}
+
unsigned getManglingNumber(const CXXMethodDecl *CallOperator) override {
- const FunctionProtoType *Proto =
- CallOperator->getType()->getAs<FunctionProtoType>();
- ASTContext &Context = CallOperator->getASTContext();
+ const CXXRecordDecl *Lambda = CallOperator->getParent();
+ assert(Lambda->isLambda());
+
+ // Computation of the <lambda-sig> is non-trivial and subtle. Rather than
+ // duplicating it here, just mangle the <lambda-sig> directly.
+ llvm::SmallString<128> LambdaSig;
+ llvm::raw_svector_ostream Out(LambdaSig);
+ Mangler->mangleLambdaSig(Lambda, Out);
- FunctionProtoType::ExtProtoInfo EPI;
- EPI.Variadic = Proto->isVariadic();
- QualType Key =
- Context.getFunctionType(Context.VoidTy, Proto->getParamTypes(), EPI);
- Key = Context.getCanonicalType(Key);
- return ++ManglingNumbers[Key->castAs<FunctionProtoType>()];
+ return ++LambdaManglingNumbers[LambdaSig];
}
unsigned getManglingNumber(const BlockDecl *BD) override {
- const Type *Ty = nullptr;
- return ++ManglingNumbers[Ty];
+ return ++BlockManglingNumber;
}
unsigned getStaticLocalNumber(const VarDecl *VD) override {
@@ -154,10 +182,13 @@ public:
};
class ItaniumCXXABI : public CXXABI {
+private:
+ std::unique_ptr<MangleContext> Mangler;
protected:
ASTContext &Context;
public:
- ItaniumCXXABI(ASTContext &Ctx) : Context(Ctx) { }
+ ItaniumCXXABI(ASTContext &Ctx)
+ : Mangler(Ctx.createMangleContext()), Context(Ctx) {}
MemberPointerInfo
getMemberPointerInfo(const MemberPointerType *MPT) const override {
@@ -177,7 +208,7 @@ public:
if (!isVariadic && T.isWindowsGNUEnvironment() &&
T.getArch() == llvm::Triple::x86)
return CC_X86ThisCall;
- return CC_C;
+ return Context.getTargetInfo().getDefaultCallingConv();
}
// We cheat and just check that the class has a vtable pointer, and that it's
@@ -218,7 +249,8 @@ public:
std::unique_ptr<MangleNumberingContext>
createMangleNumberingContext() const override {
- return llvm::make_unique<ItaniumNumberingContext>();
+ return std::make_unique<ItaniumNumberingContext>(
+ cast<ItaniumMangleContext>(Mangler.get()));
}
};
}
diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp
index 6c813f09a4b3c..c55a901375787 100644
--- a/lib/AST/ItaniumMangle.cpp
+++ b/lib/AST/ItaniumMangle.cpp
@@ -170,6 +170,8 @@ public:
void mangleStringLiteral(const StringLiteral *, raw_ostream &) override;
+ void mangleLambdaSig(const CXXRecordDecl *Lambda, raw_ostream &) override;
+
bool getNextDiscriminator(const NamedDecl *ND, unsigned &disc) {
// Lambda closure types are already numbered.
if (isLambda(ND))
@@ -424,6 +426,7 @@ public:
void mangleName(const NamedDecl *ND);
void mangleType(QualType T);
void mangleNameOrStandardSubstitution(const NamedDecl *ND);
+ void mangleLambdaSig(const CXXRecordDecl *Lambda);
private:
@@ -513,7 +516,7 @@ private:
#define ABSTRACT_TYPE(CLASS, PARENT)
#define NON_CANONICAL_TYPE(CLASS, PARENT)
#define TYPE(CLASS, PARENT) void mangleType(const CLASS##Type *T);
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
void mangleType(const TagType*);
void mangleType(TemplateName);
@@ -550,7 +553,7 @@ private:
void mangleTemplateArgs(const TemplateArgumentList &AL);
void mangleTemplateArg(TemplateArgument A);
- void mangleTemplateParameter(unsigned Index);
+ void mangleTemplateParameter(unsigned Depth, unsigned Index);
void mangleFunctionParam(const ParmVarDecl *parm);
@@ -965,8 +968,8 @@ void CXXNameMangler::mangleUnscopedTemplateName(
if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(ND)) {
assert(!AdditionalAbiTags &&
"template template param cannot have abi tags");
- mangleTemplateParameter(TTP->getIndex());
- } else if (isa<BuiltinTemplateDecl>(ND)) {
+ mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
+ } else if (isa<BuiltinTemplateDecl>(ND) || isa<ConceptDecl>(ND)) {
mangleUnscopedName(ND, AdditionalAbiTags);
} else {
mangleUnscopedName(ND->getTemplatedDecl(), AdditionalAbiTags);
@@ -1321,7 +1324,7 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND,
if (const VarDecl *VD = dyn_cast<VarDecl>(ND)) {
// We must have an anonymous union or struct declaration.
- const RecordDecl *RD = VD->getType()->getAs<RecordType>()->getDecl();
+ const RecordDecl *RD = VD->getType()->castAs<RecordType>()->getDecl();
// Itanium C++ ABI 5.1.2:
//
@@ -1685,17 +1688,45 @@ void CXXNameMangler::mangleUnqualifiedBlock(const BlockDecl *Block) {
// ::= Ty # template type parameter
// ::= Tn <type> # template non-type parameter
// ::= Tt <template-param-decl>* E # template template parameter
+// ::= Tp <template-param-decl> # template parameter pack
void CXXNameMangler::mangleTemplateParamDecl(const NamedDecl *Decl) {
- if (isa<TemplateTypeParmDecl>(Decl)) {
+ if (auto *Ty = dyn_cast<TemplateTypeParmDecl>(Decl)) {
+ if (Ty->isParameterPack())
+ Out << "Tp";
Out << "Ty";
} else if (auto *Tn = dyn_cast<NonTypeTemplateParmDecl>(Decl)) {
- Out << "Tn";
- mangleType(Tn->getType());
+ if (Tn->isExpandedParameterPack()) {
+ for (unsigned I = 0, N = Tn->getNumExpansionTypes(); I != N; ++I) {
+ Out << "Tn";
+ mangleType(Tn->getExpansionType(I));
+ }
+ } else {
+ QualType T = Tn->getType();
+ if (Tn->isParameterPack()) {
+ Out << "Tp";
+ if (auto *PackExpansion = T->getAs<PackExpansionType>())
+ T = PackExpansion->getPattern();
+ }
+ Out << "Tn";
+ mangleType(T);
+ }
} else if (auto *Tt = dyn_cast<TemplateTemplateParmDecl>(Decl)) {
- Out << "Tt";
- for (auto *Param : *Tt->getTemplateParameters())
- mangleTemplateParamDecl(Param);
- Out << "E";
+ if (Tt->isExpandedParameterPack()) {
+ for (unsigned I = 0, N = Tt->getNumExpansionTemplateParameters(); I != N;
+ ++I) {
+ Out << "Tt";
+ for (auto *Param : *Tt->getExpansionTemplateParameters(I))
+ mangleTemplateParamDecl(Param);
+ Out << "E";
+ }
+ } else {
+ if (Tt->isParameterPack())
+ Out << "Tp";
+ Out << "Tt";
+ for (auto *Param : *Tt->getTemplateParameters())
+ mangleTemplateParamDecl(Param);
+ Out << "E";
+ }
}
}
@@ -1726,12 +1757,7 @@ void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) {
}
Out << "Ul";
- for (auto *D : Lambda->getLambdaExplicitTemplateParameters())
- mangleTemplateParamDecl(D);
- const FunctionProtoType *Proto = Lambda->getLambdaTypeInfo()->getType()->
- getAs<FunctionProtoType>();
- mangleBareFunctionType(Proto, /*MangleReturnType=*/false,
- Lambda->getLambdaStaticInvoker());
+ mangleLambdaSig(Lambda);
Out << "E";
// The number is omitted for the first closure type with a given
@@ -1746,6 +1772,15 @@ void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) {
Out << '_';
}
+void CXXNameMangler::mangleLambdaSig(const CXXRecordDecl *Lambda) {
+ for (auto *D : Lambda->getLambdaExplicitTemplateParameters())
+ mangleTemplateParamDecl(D);
+ const FunctionProtoType *Proto = Lambda->getLambdaTypeInfo()->getType()->
+ getAs<FunctionProtoType>();
+ mangleBareFunctionType(Proto, /*MangleReturnType=*/false,
+ Lambda->getLambdaStaticInvoker());
+}
+
void CXXNameMangler::manglePrefix(NestedNameSpecifier *qualifier) {
switch (qualifier->getKind()) {
case NestedNameSpecifier::Global:
@@ -1852,10 +1887,10 @@ void CXXNameMangler::mangleTemplatePrefix(const TemplateDecl *ND,
// <template-template-param> ::= <template-param>
if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(ND)) {
- mangleTemplateParameter(TTP->getIndex());
+ mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
} else {
manglePrefix(getEffectiveDeclContext(ND), NoFunction);
- if (isa<BuiltinTemplateDecl>(ND))
+ if (isa<BuiltinTemplateDecl>(ND) || isa<ConceptDecl>(ND))
mangleUnqualifiedName(ND, nullptr);
else
mangleUnqualifiedName(ND->getTemplatedDecl(), nullptr);
@@ -1885,8 +1920,8 @@ void CXXNameMangler::mangleType(TemplateName TN) {
goto HaveDecl;
HaveDecl:
- if (isa<TemplateTemplateParmDecl>(TD))
- mangleTemplateParameter(cast<TemplateTemplateParmDecl>(TD)->getIndex());
+ if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TD))
+ mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
else
mangleName(TD);
break;
@@ -2464,7 +2499,7 @@ void CXXNameMangler::mangleType(QualType T) {
case Type::CLASS: \
mangleType(static_cast<const CLASS##Type*>(ty)); \
break;
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
}
}
@@ -2671,6 +2706,15 @@ void CXXNameMangler::mangleType(const BuiltinType *T) {
Out << type_name.size() << type_name; \
break;
#include "clang/Basic/OpenCLExtensionTypes.def"
+ // The SVE types are effectively target-specific. The mangling scheme
+ // is defined in the appendices to the Procedure Call Standard for the
+ // Arm Architecture.
+#define SVE_TYPE(Name, Id, SingletonId) \
+ case BuiltinType::Id: \
+ type_name = Name; \
+ Out << 'u' << type_name.size() << type_name; \
+ break;
+#include "clang/Basic/AArch64SVEACLETypes.def"
}
}
@@ -2955,7 +2999,7 @@ void CXXNameMangler::mangleType(const MemberPointerType *T) {
// <type> ::= <template-param>
void CXXNameMangler::mangleType(const TemplateTypeParmType *T) {
- mangleTemplateParameter(T->getIndex());
+ mangleTemplateParameter(T->getDepth(), T->getIndex());
}
// <type> ::= <template-param>
@@ -3526,7 +3570,7 @@ void CXXNameMangler::mangleDeclRefExpr(const NamedDecl *D) {
case Decl::NonTypeTemplateParm:
const NonTypeTemplateParmDecl *PD = cast<NonTypeTemplateParmDecl>(D);
- mangleTemplateParameter(PD->getIndex());
+ mangleTemplateParameter(PD->getDepth(), PD->getIndex());
break;
}
}
@@ -4046,6 +4090,17 @@ recurse:
break;
}
+ case Expr::CXXRewrittenBinaryOperatorClass: {
+ // The mangled form represents the original syntax.
+ CXXRewrittenBinaryOperator::DecomposedForm Decomposed =
+ cast<CXXRewrittenBinaryOperator>(E)->getDecomposedForm();
+ mangleOperatorName(BinaryOperator::getOverloadedOperator(Decomposed.Opcode),
+ /*Arity=*/2);
+ mangleExpression(Decomposed.LHS);
+ mangleExpression(Decomposed.RHS);
+ break;
+ }
+
case Expr::ConditionalOperatorClass: {
const ConditionalOperator *CO = cast<ConditionalOperator>(E);
mangleOperatorName(OO_Conditional, /*Arity=*/3);
@@ -4123,6 +4178,18 @@ recurse:
mangleExpression(cast<ParenExpr>(E)->getSubExpr(), Arity);
break;
+
+ case Expr::ConceptSpecializationExprClass: {
+ // <expr-primary> ::= L <mangled-name> E # external name
+ Out << "L_Z";
+ auto *CSE = cast<ConceptSpecializationExpr>(E);
+ mangleTemplateName(CSE->getNamedConcept(),
+ CSE->getTemplateArguments().data(),
+ CSE->getTemplateArguments().size());
+ Out << 'E';
+ break;
+ }
+
case Expr::DeclRefExprClass:
mangleDeclRefExpr(cast<DeclRefExpr>(E)->getDecl());
break;
@@ -4229,8 +4296,11 @@ recurse:
}
case Expr::GNUNullExprClass:
- // FIXME: should this really be mangled the same as nullptr?
- // fallthrough
+ // Mangle as if an integer literal 0.
+ Out << 'L';
+ mangleType(E->getType());
+ Out << "0E";
+ break;
case Expr::CXXNullPtrLiteralExprClass: {
Out << "LDnE";
@@ -4255,13 +4325,13 @@ recurse:
Out << "sZ";
const NamedDecl *Pack = SPE->getPack();
if (const TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(Pack))
- mangleTemplateParameter(TTP->getIndex());
+ mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
else if (const NonTypeTemplateParmDecl *NTTP
= dyn_cast<NonTypeTemplateParmDecl>(Pack))
- mangleTemplateParameter(NTTP->getIndex());
+ mangleTemplateParameter(NTTP->getDepth(), NTTP->getIndex());
else if (const TemplateTemplateParmDecl *TempTP
= dyn_cast<TemplateTemplateParmDecl>(Pack))
- mangleTemplateParameter(TempTP->getIndex());
+ mangleTemplateParameter(TempTP->getDepth(), TempTP->getIndex());
else
mangleFunctionParam(cast<ParmVarDecl>(Pack));
break;
@@ -4548,13 +4618,21 @@ void CXXNameMangler::mangleTemplateArg(TemplateArgument A) {
}
}
-void CXXNameMangler::mangleTemplateParameter(unsigned Index) {
+void CXXNameMangler::mangleTemplateParameter(unsigned Depth, unsigned Index) {
// <template-param> ::= T_ # first template parameter
// ::= T <parameter-2 non-negative number> _
- if (Index == 0)
- Out << "T_";
- else
- Out << 'T' << (Index - 1) << '_';
+ // ::= TL <L-1 non-negative number> __
+ // ::= TL <L-1 non-negative number> _
+ // <parameter-2 non-negative number> _
+ //
+ // The latter two manglings are from a proposal here:
+ // https://github.com/itanium-cxx-abi/cxx-abi/issues/31#issuecomment-528122117
+ Out << 'T';
+ if (Depth != 0)
+ Out << 'L' << (Depth - 1) << '_';
+ if (Index != 0)
+ Out << (Index - 1);
+ Out << '_';
}
void CXXNameMangler::mangleSeqID(unsigned SeqID) {
@@ -5071,6 +5149,12 @@ void ItaniumMangleContextImpl::mangleStringLiteral(const StringLiteral *, raw_os
llvm_unreachable("Can't mangle string literals");
}
+void ItaniumMangleContextImpl::mangleLambdaSig(const CXXRecordDecl *Lambda,
+ raw_ostream &Out) {
+ CXXNameMangler Mangler(*this, Out);
+ Mangler.mangleLambdaSig(Lambda);
+}
+
ItaniumMangleContext *
ItaniumMangleContext::create(ASTContext &Context, DiagnosticsEngine &Diags) {
return new ItaniumMangleContextImpl(Context, Diags);
diff --git a/lib/AST/JSONNodeDumper.cpp b/lib/AST/JSONNodeDumper.cpp
index 04b933b0fb306..f60d761c996c5 100644
--- a/lib/AST/JSONNodeDumper.cpp
+++ b/lib/AST/JSONNodeDumper.cpp
@@ -66,6 +66,10 @@ void JSONNodeDumper::Visit(const Stmt *S) {
void JSONNodeDumper::Visit(const Type *T) {
JOS.attribute("id", createPointerRepresentation(T));
+
+ if (!T)
+ return;
+
JOS.attribute("kind", (llvm::Twine(T->getTypeClassName()) + "Type").str());
JOS.attribute("type", createQualType(QualType(T, 0), /*Desugar*/ false));
attributeOnlyIfTrue("isDependent", T->isDependentType());
@@ -107,9 +111,14 @@ void JSONNodeDumper::Visit(const Decl *D) {
if (const auto *ND = dyn_cast<NamedDecl>(D))
attributeOnlyIfTrue("isHidden", ND->isHidden());
- if (D->getLexicalDeclContext() != D->getDeclContext())
- JOS.attribute("parentDeclContext",
- createPointerRepresentation(D->getDeclContext()));
+ if (D->getLexicalDeclContext() != D->getDeclContext()) {
+ // Because of multiple inheritance, a DeclContext pointer does not produce
+ // the same pointer representation as a Decl pointer that references the
+ // same AST Node.
+ const auto *ParentDeclContextDecl = dyn_cast<Decl>(D->getDeclContext());
+ JOS.attribute("parentDeclContextId",
+ createPointerRepresentation(ParentDeclContextDecl));
+ }
addPreviousDeclaration(D);
InnerDeclVisitor::Visit(D);
@@ -171,12 +180,30 @@ void JSONNodeDumper::Visit(const GenericSelectionExpr::ConstAssociation &A) {
attributeOnlyIfTrue("selected", A.isSelected());
}
+void JSONNodeDumper::writeIncludeStack(PresumedLoc Loc, bool JustFirst) {
+ if (Loc.isInvalid())
+ return;
+
+ JOS.attributeBegin("includedFrom");
+ JOS.objectBegin();
+
+ if (!JustFirst) {
+ // Walk the stack recursively, then print out the presumed location.
+ writeIncludeStack(SM.getPresumedLoc(Loc.getIncludeLoc()));
+ }
+
+ JOS.attribute("file", Loc.getFilename());
+ JOS.objectEnd();
+ JOS.attributeEnd();
+}
+
void JSONNodeDumper::writeBareSourceLocation(SourceLocation Loc,
bool IsSpelling) {
PresumedLoc Presumed = SM.getPresumedLoc(Loc);
unsigned ActualLine = IsSpelling ? SM.getSpellingLineNumber(Loc)
: SM.getExpansionLineNumber(Loc);
if (Presumed.isValid()) {
+ JOS.attribute("offset", SM.getDecomposedLoc(Loc).second);
if (LastLocFilename != Presumed.getFilename()) {
JOS.attribute("file", Presumed.getFilename());
JOS.attribute("line", ActualLine);
@@ -193,6 +220,12 @@ void JSONNodeDumper::writeBareSourceLocation(SourceLocation Loc,
LastLocFilename = Presumed.getFilename();
LastLocPresumedLine = PresumedLine;
LastLocLine = ActualLine;
+
+ // Orthogonal to the file, line, and column de-duplication is whether the
+ // given location was a result of an include. If so, print where the
+ // include location came from.
+ writeIncludeStack(SM.getPresumedLoc(Presumed.getIncludeLoc()),
+ /*JustFirst*/ true);
}
}
@@ -238,6 +271,8 @@ llvm::json::Object JSONNodeDumper::createQualType(QualType QT, bool Desugar) {
SplitQualType DSQT = QT.getSplitDesugaredType();
if (DSQT != SQT)
Ret["desugaredQualType"] = QualType::getAsString(DSQT, PrintPolicy);
+ if (const auto *TT = QT->getAs<TypedefType>())
+ Ret["typeAliasDeclId"] = createPointerRepresentation(TT->getDecl());
}
return Ret;
}
@@ -275,7 +310,7 @@ llvm::json::Array JSONNodeDumper::createCastPath(const CastExpr *C) {
for (auto I = C->path_begin(), E = C->path_end(); I != E; ++I) {
const CXXBaseSpecifier *Base = *I;
const auto *RD =
- cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl());
+ cast<CXXRecordDecl>(Base->getType()->castAs<RecordType>()->getDecl());
llvm::json::Object Val{{"name", RD->getName()}};
if (Base->isVirtual())
@@ -839,6 +874,12 @@ void JSONNodeDumper::VisitLinkageSpecDecl(const LinkageSpecDecl *LSD) {
switch (LSD->getLanguage()) {
case LinkageSpecDecl::lang_c: Lang = "C"; break;
case LinkageSpecDecl::lang_cxx: Lang = "C++"; break;
+ case LinkageSpecDecl::lang_cxx_11:
+ Lang = "C++11";
+ break;
+ case LinkageSpecDecl::lang_cxx_14:
+ Lang = "C++14";
+ break;
}
JOS.attribute("language", Lang);
attributeOnlyIfTrue("hasBraces", LSD->hasBraces());
diff --git a/lib/AST/Mangle.cpp b/lib/AST/Mangle.cpp
index 625282368a4d1..32d466cb57180 100644
--- a/lib/AST/Mangle.cpp
+++ b/lib/AST/Mangle.cpp
@@ -122,15 +122,21 @@ void MangleContext::mangleName(const NamedDecl *D, raw_ostream &Out) {
if (const AsmLabelAttr *ALA = D->getAttr<AsmLabelAttr>()) {
// If we have an asm name, then we use it as the mangling.
+ // If the label isn't literal, or if this is an alias for an LLVM intrinsic,
+ // do not add a "\01" prefix.
+ if (!ALA->getIsLiteralLabel() || ALA->getLabel().startswith("llvm.")) {
+ Out << ALA->getLabel();
+ return;
+ }
+
// Adding the prefix can cause problems when one file has a "foo" and
// another has a "\01foo". That is known to happen on ELF with the
// tricks normally used for producing aliases (PR9177). Fortunately the
// llvm mangler on ELF is a nop, so we can just avoid adding the \01
- // marker. We also avoid adding the marker if this is an alias for an
- // LLVM intrinsic.
+ // marker.
char GlobalPrefix =
getASTContext().getTargetInfo().getDataLayout().getGlobalPrefix();
- if (GlobalPrefix && !ALA->getLabel().startswith("llvm."))
+ if (GlobalPrefix)
Out << '\01'; // LLVM IR Marker for __asm("foo")
Out << ALA->getLabel();
@@ -380,7 +386,7 @@ public:
auto hasDefaultCXXMethodCC = [](ASTContext &C, const CXXMethodDecl *MD) {
auto DefaultCC = C.getDefaultCallingConvention(/*IsVariadic=*/false,
/*IsCXXMethod=*/true);
- auto CC = MD->getType()->getAs<FunctionProtoType>()->getCallConv();
+ auto CC = MD->getType()->castAs<FunctionProtoType>()->getCallConv();
return CC == DefaultCC;
};
@@ -470,7 +476,7 @@ private:
};
ASTNameGenerator::ASTNameGenerator(ASTContext &Ctx)
- : Impl(llvm::make_unique<Implementation>(Ctx)) {}
+ : Impl(std::make_unique<Implementation>(Ctx)) {}
ASTNameGenerator::~ASTNameGenerator() {}
diff --git a/lib/AST/MicrosoftCXXABI.cpp b/lib/AST/MicrosoftCXXABI.cpp
index 4dc4156df9ca4..074abba3d458b 100644
--- a/lib/AST/MicrosoftCXXABI.cpp
+++ b/lib/AST/MicrosoftCXXABI.cpp
@@ -82,7 +82,7 @@ public:
if (!isVariadic &&
Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86)
return CC_X86ThisCall;
- return CC_C;
+ return Context.getTargetInfo().getDefaultCallingConv();
}
bool isNearlyEmpty(const CXXRecordDecl *RD) const override {
@@ -132,7 +132,7 @@ public:
std::unique_ptr<MangleNumberingContext>
createMangleNumberingContext() const override {
- return llvm::make_unique<MicrosoftNumberingContext>();
+ return std::make_unique<MicrosoftNumberingContext>();
}
};
}
diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp
index 5e9358e24fc9d..f871a1b999006 100644
--- a/lib/AST/MicrosoftMangle.cpp
+++ b/lib/AST/MicrosoftMangle.cpp
@@ -27,11 +27,11 @@
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/JamCRC.h"
-#include "llvm/Support/xxhash.h"
+#include "llvm/Support/CRC.h"
#include "llvm/Support/MD5.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/StringSaver.h"
+#include "llvm/Support/xxhash.h"
using namespace clang;
@@ -364,7 +364,7 @@ private:
#define TYPE(CLASS, PARENT) void mangleType(const CLASS##Type *T, \
Qualifiers Quals, \
SourceRange Range);
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
#undef ABSTRACT_TYPE
#undef NON_CANONICAL_TYPE
#undef TYPE
@@ -615,6 +615,8 @@ void MicrosoftCXXNameMangler::mangleMemberDataPointer(const CXXRecordDecl *RD,
case MSInheritanceAttr::Keyword_multiple_inheritance: Code = '0'; break;
case MSInheritanceAttr::Keyword_virtual_inheritance: Code = 'F'; break;
case MSInheritanceAttr::Keyword_unspecified_inheritance: Code = 'G'; break;
+ case MSInheritanceAttr::SpellingNotCalculated:
+ llvm_unreachable("not reachable");
}
Out << '$' << Code;
@@ -646,6 +648,8 @@ MicrosoftCXXNameMangler::mangleMemberFunctionPointer(const CXXRecordDecl *RD,
case MSInheritanceAttr::Keyword_multiple_inheritance: Code = 'H'; break;
case MSInheritanceAttr::Keyword_virtual_inheritance: Code = 'I'; break;
case MSInheritanceAttr::Keyword_unspecified_inheritance: Code = 'J'; break;
+ case MSInheritanceAttr::SpellingNotCalculated:
+ llvm_unreachable("not reachable");
}
// If non-virtual, mangle the name. If virtual, mangle as a virtual memptr
@@ -842,7 +846,7 @@ void MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND,
TemplateArgStringStorage.save(TemplateMangling.str());
}
} else {
- Out << Found->second; // Outputs a StringRef.
+ Out << Found->second << '@'; // Outputs a StringRef.
}
} else {
Out << Found->second; // Outputs a back reference (an int).
@@ -868,16 +872,11 @@ void MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND,
}
if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(ND)) {
- // FIXME: Invented mangling for decomposition declarations:
- // [X,Y,Z]
- // where X,Y,Z are the names of the bindings.
- llvm::SmallString<128> Name("[");
- for (auto *BD : DD->bindings()) {
- if (Name.size() > 1)
- Name += ',';
- Name += BD->getDeclName().getAsIdentifierInfo()->getName();
- }
- Name += ']';
+ // Decomposition declarations are considered anonymous, and get
+ // numbered with a $S prefix.
+ llvm::SmallString<64> Name("$S");
+ // Get a unique id for the anonymous struct.
+ Name += llvm::utostr(Context.getAnonymousStructId(DD) + 1);
mangleSourceName(Name);
break;
}
@@ -1942,7 +1941,7 @@ void MicrosoftCXXNameMangler::mangleType(QualType T, SourceRange Range,
case Type::CLASS: \
mangleType(cast<CLASS##Type>(ty), Quals, Range); \
break;
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
#undef ABSTRACT_TYPE
#undef NON_CANONICAL_TYPE
#undef TYPE
@@ -2109,6 +2108,9 @@ void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T, Qualifiers,
mangleArtificialTagType(TTK_Struct, "_Half", {"__clang"});
break;
+#define SVE_TYPE(Name, Id, SingletonId) \
+ case BuiltinType::Id:
+#include "clang/Basic/AArch64SVEACLETypes.def"
case BuiltinType::ShortAccum:
case BuiltinType::Accum:
case BuiltinType::LongAccum:
diff --git a/lib/AST/NSAPI.cpp b/lib/AST/NSAPI.cpp
index 5104dc59d621e..ae6ff04f5126d 100644
--- a/lib/AST/NSAPI.cpp
+++ b/lib/AST/NSAPI.cpp
@@ -75,17 +75,6 @@ Selector NSAPI::getNSStringSelector(NSStringMethodKind MK) const {
return NSStringSelectors[MK];
}
-Optional<NSAPI::NSStringMethodKind>
-NSAPI::getNSStringMethodKind(Selector Sel) const {
- for (unsigned i = 0; i != NumNSStringMethods; ++i) {
- NSStringMethodKind MK = NSStringMethodKind(i);
- if (Sel == getNSStringSelector(MK))
- return MK;
- }
-
- return None;
-}
-
Selector NSAPI::getNSArraySelector(NSArrayMethodKind MK) const {
if (NSArraySelectors[MK].isNull()) {
Selector Sel;
@@ -482,6 +471,9 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) const {
case BuiltinType::OCLClkEvent:
case BuiltinType::OCLQueue:
case BuiltinType::OCLReserveID:
+#define SVE_TYPE(Name, Id, SingletonId) \
+ case BuiltinType::Id:
+#include "clang/Basic/AArch64SVEACLETypes.def"
case BuiltinType::BoundMember:
case BuiltinType::Dependent:
case BuiltinType::Overload:
diff --git a/lib/AST/OpenMPClause.cpp b/lib/AST/OpenMPClause.cpp
index 9d8a7ebc3023e..fe1334469d486 100644
--- a/lib/AST/OpenMPClause.cpp
+++ b/lib/AST/OpenMPClause.cpp
@@ -43,6 +43,8 @@ OMPClause::child_range OMPClause::used_children() {
#include "clang/Basic/OpenMPKinds.def"
case OMPC_threadprivate:
case OMPC_uniform:
+ case OMPC_device_type:
+ case OMPC_match:
case OMPC_unknown:
break;
}
@@ -82,9 +84,16 @@ const OMPClauseWithPreInit *OMPClauseWithPreInit::get(const OMPClause *C) {
return static_cast<const OMPThreadLimitClause *>(C);
case OMPC_device:
return static_cast<const OMPDeviceClause *>(C);
+ case OMPC_grainsize:
+ return static_cast<const OMPGrainsizeClause *>(C);
+ case OMPC_num_tasks:
+ return static_cast<const OMPNumTasksClause *>(C);
+ case OMPC_final:
+ return static_cast<const OMPFinalClause *>(C);
+ case OMPC_priority:
+ return static_cast<const OMPPriorityClause *>(C);
case OMPC_default:
case OMPC_proc_bind:
- case OMPC_final:
case OMPC_safelen:
case OMPC_simdlen:
case OMPC_allocator:
@@ -110,10 +119,7 @@ const OMPClauseWithPreInit *OMPClauseWithPreInit::get(const OMPClause *C) {
case OMPC_threads:
case OMPC_simd:
case OMPC_map:
- case OMPC_priority:
- case OMPC_grainsize:
case OMPC_nogroup:
- case OMPC_num_tasks:
case OMPC_hint:
case OMPC_defaultmap:
case OMPC_unknown:
@@ -127,6 +133,8 @@ const OMPClauseWithPreInit *OMPClauseWithPreInit::get(const OMPClause *C) {
case OMPC_reverse_offload:
case OMPC_dynamic_allocators:
case OMPC_atomic_default_mem_order:
+ case OMPC_device_type:
+ case OMPC_match:
break;
}
@@ -203,6 +211,8 @@ const OMPClauseWithPostUpdate *OMPClauseWithPostUpdate::get(const OMPClause *C)
case OMPC_reverse_offload:
case OMPC_dynamic_allocators:
case OMPC_atomic_default_mem_order:
+ case OMPC_device_type:
+ case OMPC_match:
break;
}
@@ -228,6 +238,30 @@ OMPClause::child_range OMPIfClause::used_children() {
return child_range(&Condition, &Condition + 1);
}
+OMPClause::child_range OMPGrainsizeClause::used_children() {
+ if (Stmt **C = getAddrOfExprAsWritten(getPreInitStmt()))
+ return child_range(C, C + 1);
+ return child_range(&Grainsize, &Grainsize + 1);
+}
+
+OMPClause::child_range OMPNumTasksClause::used_children() {
+ if (Stmt **C = getAddrOfExprAsWritten(getPreInitStmt()))
+ return child_range(C, C + 1);
+ return child_range(&NumTasks, &NumTasks + 1);
+}
+
+OMPClause::child_range OMPFinalClause::used_children() {
+ if (Stmt **C = getAddrOfExprAsWritten(getPreInitStmt()))
+ return child_range(C, C + 1);
+ return child_range(&Condition, &Condition + 1);
+}
+
+OMPClause::child_range OMPPriorityClause::used_children() {
+ if (Stmt **C = getAddrOfExprAsWritten(getPreInitStmt()))
+ return child_range(C, C + 1);
+ return child_range(&Priority, &Priority + 1);
+}
+
OMPOrderedClause *OMPOrderedClause::Create(const ASTContext &C, Expr *Num,
unsigned NumLoops,
SourceLocation StartLoc,
@@ -429,15 +463,23 @@ void OMPLinearClause::setFinals(ArrayRef<Expr *> FL) {
std::copy(FL.begin(), FL.end(), getUpdates().end());
}
+void OMPLinearClause::setUsedExprs(ArrayRef<Expr *> UE) {
+ assert(
+ UE.size() == varlist_size() + 1 &&
+ "Number of used expressions is not the same as the preallocated buffer");
+ std::copy(UE.begin(), UE.end(), getFinals().end() + 2);
+}
+
OMPLinearClause *OMPLinearClause::Create(
const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc,
OpenMPLinearClauseKind Modifier, SourceLocation ModifierLoc,
SourceLocation ColonLoc, SourceLocation EndLoc, ArrayRef<Expr *> VL,
ArrayRef<Expr *> PL, ArrayRef<Expr *> IL, Expr *Step, Expr *CalcStep,
Stmt *PreInit, Expr *PostUpdate) {
- // Allocate space for 4 lists (Vars, Inits, Updates, Finals) and 2 expressions
- // (Step and CalcStep).
- void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(5 * VL.size() + 2));
+ // Allocate space for 5 lists (Vars, Inits, Updates, Finals), 2 expressions
+ // (Step and CalcStep), list of used expression + step.
+ void *Mem =
+ C.Allocate(totalSizeToAlloc<Expr *>(5 * VL.size() + 2 + VL.size() + 1));
OMPLinearClause *Clause = new (Mem) OMPLinearClause(
StartLoc, LParenLoc, Modifier, ModifierLoc, ColonLoc, EndLoc, VL.size());
Clause->setVarRefs(VL);
@@ -449,6 +491,8 @@ OMPLinearClause *OMPLinearClause::Create(
nullptr);
std::fill(Clause->getUpdates().end(), Clause->getUpdates().end() + VL.size(),
nullptr);
+ std::fill(Clause->getUsedExprs().begin(), Clause->getUsedExprs().end(),
+ nullptr);
Clause->setStep(Step);
Clause->setCalcStep(CalcStep);
Clause->setPreInitStmt(PreInit);
@@ -458,12 +502,19 @@ OMPLinearClause *OMPLinearClause::Create(
OMPLinearClause *OMPLinearClause::CreateEmpty(const ASTContext &C,
unsigned NumVars) {
- // Allocate space for 4 lists (Vars, Inits, Updates, Finals) and 2 expressions
- // (Step and CalcStep).
- void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(5 * NumVars + 2));
+ // Allocate space for 5 lists (Vars, Inits, Updates, Finals), 2 expressions
+ // (Step and CalcStep), list of used expression + step.
+ void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(5 * NumVars + 2 + NumVars +1));
return new (Mem) OMPLinearClause(NumVars);
}
+OMPClause::child_range OMPLinearClause::used_children() {
+ // Range includes only non-nullptr elements.
+ return child_range(
+ reinterpret_cast<Stmt **>(getUsedExprs().begin()),
+ reinterpret_cast<Stmt **>(llvm::find(getUsedExprs(), nullptr)));
+}
+
OMPAlignedClause *
OMPAlignedClause::Create(const ASTContext &C, SourceLocation StartLoc,
SourceLocation LParenLoc, SourceLocation ColonLoc,
diff --git a/lib/AST/PrintfFormatString.cpp b/lib/AST/PrintfFormatString.cpp
index a1207aae5aa25..bae60d4644078 100644
--- a/lib/AST/PrintfFormatString.cpp
+++ b/lib/AST/PrintfFormatString.cpp
@@ -463,6 +463,23 @@ bool clang::analyze_format_string::ParseFormatStringHasSArg(const char *I,
return false;
}
+bool clang::analyze_format_string::parseFormatStringHasFormattingSpecifiers(
+ const char *Begin, const char *End, const LangOptions &LO,
+ const TargetInfo &Target) {
+ unsigned ArgIndex = 0;
+ // Keep looking for a formatting specifier until we have exhausted the string.
+ FormatStringHandler H;
+ while (Begin != End) {
+ const PrintfSpecifierResult &FSR =
+ ParsePrintfSpecifier(H, Begin, End, ArgIndex, LO, Target, false, false);
+ if (FSR.shouldStop())
+ break;
+ if (FSR.hasValue())
+ return true;
+ }
+ return false;
+}
+
//===----------------------------------------------------------------------===//
// Methods on PrintfSpecifier.
//===----------------------------------------------------------------------===//
@@ -769,6 +786,9 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \
case BuiltinType::Id:
#include "clang/Basic/OpenCLExtensionTypes.def"
+#define SVE_TYPE(Name, Id, SingletonId) \
+ case BuiltinType::Id:
+#include "clang/Basic/AArch64SVEACLETypes.def"
#define SIGNED_TYPE(Id, SingletonId)
#define UNSIGNED_TYPE(Id, SingletonId)
#define FLOATING_TYPE(Id, SingletonId)
diff --git a/lib/AST/RawCommentList.cpp b/lib/AST/RawCommentList.cpp
index df53b7fa10047..83e8a0b942a40 100644
--- a/lib/AST/RawCommentList.cpp
+++ b/lib/AST/RawCommentList.cpp
@@ -275,27 +275,25 @@ void RawCommentList::addComment(const RawComment &RC,
if (RC.isInvalid())
return;
- // Check if the comments are not in source order.
- while (!Comments.empty() &&
- !SourceMgr.isBeforeInTranslationUnit(Comments.back()->getBeginLoc(),
- RC.getBeginLoc())) {
- // If they are, just pop a few last comments that don't fit.
- // This happens if an \#include directive contains comments.
- Comments.pop_back();
- }
-
// Ordinary comments are not interesting for us.
if (RC.isOrdinary() && !CommentOpts.ParseAllComments)
return;
+ std::pair<FileID, unsigned> Loc =
+ SourceMgr.getDecomposedLoc(RC.getBeginLoc());
+
+ const FileID CommentFile = Loc.first;
+ const unsigned CommentOffset = Loc.second;
+
// If this is the first Doxygen comment, save it (because there isn't
// anything to merge it with).
- if (Comments.empty()) {
- Comments.push_back(new (Allocator) RawComment(RC));
+ if (OrderedComments[CommentFile].empty()) {
+ OrderedComments[CommentFile][CommentOffset] =
+ new (Allocator) RawComment(RC);
return;
}
- const RawComment &C1 = *Comments.back();
+ const RawComment &C1 = *OrderedComments[CommentFile].rbegin()->second;
const RawComment &C2 = RC;
// Merge comments only if there is only whitespace between them.
@@ -318,21 +316,43 @@ void RawCommentList::addComment(const RawComment &RC,
onlyWhitespaceBetween(SourceMgr, C1.getEndLoc(), C2.getBeginLoc(),
/*MaxNewlinesAllowed=*/1)) {
SourceRange MergedRange(C1.getBeginLoc(), C2.getEndLoc());
- *Comments.back() = RawComment(SourceMgr, MergedRange, CommentOpts, true);
+ *OrderedComments[CommentFile].rbegin()->second =
+ RawComment(SourceMgr, MergedRange, CommentOpts, true);
} else {
- Comments.push_back(new (Allocator) RawComment(RC));
+ OrderedComments[CommentFile][CommentOffset] =
+ new (Allocator) RawComment(RC);
}
}
-void RawCommentList::addDeserializedComments(ArrayRef<RawComment *> DeserializedComments) {
- std::vector<RawComment *> MergedComments;
- MergedComments.reserve(Comments.size() + DeserializedComments.size());
+const std::map<unsigned, RawComment *> *
+RawCommentList::getCommentsInFile(FileID File) const {
+ auto CommentsInFile = OrderedComments.find(File);
+ if (CommentsInFile == OrderedComments.end())
+ return nullptr;
+
+ return &CommentsInFile->second;
+}
+
+bool RawCommentList::empty() const { return OrderedComments.empty(); }
+
+unsigned RawCommentList::getCommentBeginLine(RawComment *C, FileID File,
+ unsigned Offset) const {
+ auto Cached = CommentBeginLine.find(C);
+ if (Cached != CommentBeginLine.end())
+ return Cached->second;
+ const unsigned Line = SourceMgr.getLineNumber(File, Offset);
+ CommentBeginLine[C] = Line;
+ return Line;
+}
- std::merge(Comments.begin(), Comments.end(),
- DeserializedComments.begin(), DeserializedComments.end(),
- std::back_inserter(MergedComments),
- BeforeThanCompare<RawComment>(SourceMgr));
- std::swap(Comments, MergedComments);
+unsigned RawCommentList::getCommentEndOffset(RawComment *C) const {
+ auto Cached = CommentEndOffset.find(C);
+ if (Cached != CommentEndOffset.end())
+ return Cached->second;
+ const unsigned Offset =
+ SourceMgr.getDecomposedLoc(C->getSourceRange().getEnd()).second;
+ CommentEndOffset[C] = Offset;
+ return Offset;
}
std::string RawComment::getFormattedText(const SourceManager &SourceMgr,
diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp
index 0a4d403106bd4..80a1451ac789b 100644
--- a/lib/AST/Stmt.cpp
+++ b/lib/AST/Stmt.cpp
@@ -41,6 +41,7 @@
#include <cstring>
#include <string>
#include <utility>
+#include <type_traits>
using namespace clang;
@@ -83,6 +84,16 @@ const char *Stmt::getStmtClassName() const {
#CLASS " should not be polymorphic!");
#include "clang/AST/StmtNodes.inc"
+// Check that no statement / expression class has a non-trival destructor.
+// Statements and expressions are allocated with the BumpPtrAllocator from
+// ASTContext and therefore their destructor is not executed.
+#define STMT(CLASS, PARENT) \
+ static_assert(std::is_trivially_destructible<CLASS>::value, \
+ #CLASS " should be trivially destructible!");
+// FIXME: InitListExpr is not trivially destructible due to its ASTVector.
+#define INITLISTEXPR(CLASS, PARENT)
+#include "clang/AST/StmtNodes.inc"
+
void Stmt::PrintStats() {
// Ensure the table is primed.
getStmtInfoTableEntry(Stmt::NullStmtClass);
diff --git a/lib/AST/StmtOpenMP.cpp b/lib/AST/StmtOpenMP.cpp
index 4e829897cebe6..da1364ebffc4c 100644
--- a/lib/AST/StmtOpenMP.cpp
+++ b/lib/AST/StmtOpenMP.cpp
@@ -72,6 +72,25 @@ void OMPLoopDirective::setFinals(ArrayRef<Expr *> A) {
std::copy(A.begin(), A.end(), getFinals().begin());
}
+void OMPLoopDirective::setDependentCounters(ArrayRef<Expr *> A) {
+ assert(
+ A.size() == getCollapsedNumber() &&
+ "Number of dependent counters is not the same as the collapsed number");
+ llvm::copy(A, getDependentCounters().begin());
+}
+
+void OMPLoopDirective::setDependentInits(ArrayRef<Expr *> A) {
+ assert(A.size() == getCollapsedNumber() &&
+ "Number of dependent inits is not the same as the collapsed number");
+ llvm::copy(A, getDependentInits().begin());
+}
+
+void OMPLoopDirective::setFinalsConditions(ArrayRef<Expr *> A) {
+ assert(A.size() == getCollapsedNumber() &&
+ "Number of finals conditions is not the same as the collapsed number");
+ llvm::copy(A, getFinalsConditions().begin());
+}
+
OMPParallelDirective *OMPParallelDirective::Create(
const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt, bool HasCancel) {
@@ -122,6 +141,9 @@ OMPSimdDirective::Create(const ASTContext &C, SourceLocation StartLoc,
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
return Dir;
}
@@ -170,6 +192,9 @@ OMPForDirective::Create(const ASTContext &C, SourceLocation StartLoc,
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
Dir->setHasCancel(HasCancel);
return Dir;
@@ -220,6 +245,9 @@ OMPForSimdDirective::Create(const ASTContext &C, SourceLocation StartLoc,
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
return Dir;
}
@@ -383,6 +411,9 @@ OMPParallelForDirective *OMPParallelForDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
Dir->setHasCancel(HasCancel);
return Dir;
@@ -432,6 +463,9 @@ OMPParallelForSimdDirective *OMPParallelForSimdDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
return Dir;
}
@@ -772,6 +806,9 @@ OMPTargetParallelForDirective *OMPTargetParallelForDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
Dir->setHasCancel(HasCancel);
return Dir;
@@ -914,6 +951,9 @@ OMPTaskLoopDirective *OMPTaskLoopDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
return Dir;
}
@@ -963,6 +1003,9 @@ OMPTaskLoopSimdDirective *OMPTaskLoopSimdDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
return Dir;
}
@@ -978,6 +1021,167 @@ OMPTaskLoopSimdDirective::CreateEmpty(const ASTContext &C, unsigned NumClauses,
return new (Mem) OMPTaskLoopSimdDirective(CollapsedNum, NumClauses);
}
+OMPMasterTaskLoopDirective *OMPMasterTaskLoopDirective::Create(
+ const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
+ unsigned CollapsedNum, ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt,
+ const HelperExprs &Exprs) {
+ unsigned Size =
+ llvm::alignTo(sizeof(OMPMasterTaskLoopDirective), alignof(OMPClause *));
+ void *Mem = C.Allocate(
+ Size + sizeof(OMPClause *) * Clauses.size() +
+ sizeof(Stmt *) * numLoopChildren(CollapsedNum, OMPD_master_taskloop));
+ OMPMasterTaskLoopDirective *Dir = new (Mem) OMPMasterTaskLoopDirective(
+ StartLoc, EndLoc, CollapsedNum, Clauses.size());
+ Dir->setClauses(Clauses);
+ Dir->setAssociatedStmt(AssociatedStmt);
+ Dir->setIterationVariable(Exprs.IterationVarRef);
+ Dir->setLastIteration(Exprs.LastIteration);
+ Dir->setCalcLastIteration(Exprs.CalcLastIteration);
+ Dir->setPreCond(Exprs.PreCond);
+ Dir->setCond(Exprs.Cond);
+ Dir->setInit(Exprs.Init);
+ Dir->setInc(Exprs.Inc);
+ Dir->setIsLastIterVariable(Exprs.IL);
+ Dir->setLowerBoundVariable(Exprs.LB);
+ Dir->setUpperBoundVariable(Exprs.UB);
+ Dir->setStrideVariable(Exprs.ST);
+ Dir->setEnsureUpperBound(Exprs.EUB);
+ Dir->setNextLowerBound(Exprs.NLB);
+ Dir->setNextUpperBound(Exprs.NUB);
+ Dir->setNumIterations(Exprs.NumIterations);
+ Dir->setCounters(Exprs.Counters);
+ Dir->setPrivateCounters(Exprs.PrivateCounters);
+ Dir->setInits(Exprs.Inits);
+ Dir->setUpdates(Exprs.Updates);
+ Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
+ Dir->setPreInits(Exprs.PreInits);
+ return Dir;
+}
+
+OMPMasterTaskLoopDirective *
+OMPMasterTaskLoopDirective::CreateEmpty(const ASTContext &C,
+ unsigned NumClauses,
+ unsigned CollapsedNum, EmptyShell) {
+ unsigned Size =
+ llvm::alignTo(sizeof(OMPMasterTaskLoopDirective), alignof(OMPClause *));
+ void *Mem = C.Allocate(
+ Size + sizeof(OMPClause *) * NumClauses +
+ sizeof(Stmt *) * numLoopChildren(CollapsedNum, OMPD_master_taskloop));
+ return new (Mem) OMPMasterTaskLoopDirective(CollapsedNum, NumClauses);
+}
+
+OMPMasterTaskLoopSimdDirective *OMPMasterTaskLoopSimdDirective::Create(
+ const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
+ unsigned CollapsedNum, ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt,
+ const HelperExprs &Exprs) {
+ unsigned Size = llvm::alignTo(sizeof(OMPMasterTaskLoopSimdDirective),
+ alignof(OMPClause *));
+ void *Mem =
+ C.Allocate(Size + sizeof(OMPClause *) * Clauses.size() +
+ sizeof(Stmt *) *
+ numLoopChildren(CollapsedNum, OMPD_master_taskloop_simd));
+ auto *Dir = new (Mem) OMPMasterTaskLoopSimdDirective(
+ StartLoc, EndLoc, CollapsedNum, Clauses.size());
+ Dir->setClauses(Clauses);
+ Dir->setAssociatedStmt(AssociatedStmt);
+ Dir->setIterationVariable(Exprs.IterationVarRef);
+ Dir->setLastIteration(Exprs.LastIteration);
+ Dir->setCalcLastIteration(Exprs.CalcLastIteration);
+ Dir->setPreCond(Exprs.PreCond);
+ Dir->setCond(Exprs.Cond);
+ Dir->setInit(Exprs.Init);
+ Dir->setInc(Exprs.Inc);
+ Dir->setIsLastIterVariable(Exprs.IL);
+ Dir->setLowerBoundVariable(Exprs.LB);
+ Dir->setUpperBoundVariable(Exprs.UB);
+ Dir->setStrideVariable(Exprs.ST);
+ Dir->setEnsureUpperBound(Exprs.EUB);
+ Dir->setNextLowerBound(Exprs.NLB);
+ Dir->setNextUpperBound(Exprs.NUB);
+ Dir->setNumIterations(Exprs.NumIterations);
+ Dir->setCounters(Exprs.Counters);
+ Dir->setPrivateCounters(Exprs.PrivateCounters);
+ Dir->setInits(Exprs.Inits);
+ Dir->setUpdates(Exprs.Updates);
+ Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
+ Dir->setPreInits(Exprs.PreInits);
+ return Dir;
+}
+
+OMPMasterTaskLoopSimdDirective *
+OMPMasterTaskLoopSimdDirective::CreateEmpty(const ASTContext &C,
+ unsigned NumClauses,
+ unsigned CollapsedNum, EmptyShell) {
+ unsigned Size = llvm::alignTo(sizeof(OMPMasterTaskLoopSimdDirective),
+ alignof(OMPClause *));
+ void *Mem =
+ C.Allocate(Size + sizeof(OMPClause *) * NumClauses +
+ sizeof(Stmt *) *
+ numLoopChildren(CollapsedNum, OMPD_master_taskloop_simd));
+ return new (Mem) OMPMasterTaskLoopSimdDirective(CollapsedNum, NumClauses);
+}
+
+OMPParallelMasterTaskLoopDirective *OMPParallelMasterTaskLoopDirective::Create(
+ const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
+ unsigned CollapsedNum, ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt,
+ const HelperExprs &Exprs) {
+ unsigned Size = llvm::alignTo(sizeof(OMPParallelMasterTaskLoopDirective),
+ alignof(OMPClause *));
+ void *Mem = C.Allocate(
+ Size + sizeof(OMPClause *) * Clauses.size() +
+ sizeof(Stmt *) *
+ numLoopChildren(CollapsedNum, OMPD_parallel_master_taskloop));
+ auto *Dir = new (Mem) OMPParallelMasterTaskLoopDirective(
+ StartLoc, EndLoc, CollapsedNum, Clauses.size());
+ Dir->setClauses(Clauses);
+ Dir->setAssociatedStmt(AssociatedStmt);
+ Dir->setIterationVariable(Exprs.IterationVarRef);
+ Dir->setLastIteration(Exprs.LastIteration);
+ Dir->setCalcLastIteration(Exprs.CalcLastIteration);
+ Dir->setPreCond(Exprs.PreCond);
+ Dir->setCond(Exprs.Cond);
+ Dir->setInit(Exprs.Init);
+ Dir->setInc(Exprs.Inc);
+ Dir->setIsLastIterVariable(Exprs.IL);
+ Dir->setLowerBoundVariable(Exprs.LB);
+ Dir->setUpperBoundVariable(Exprs.UB);
+ Dir->setStrideVariable(Exprs.ST);
+ Dir->setEnsureUpperBound(Exprs.EUB);
+ Dir->setNextLowerBound(Exprs.NLB);
+ Dir->setNextUpperBound(Exprs.NUB);
+ Dir->setNumIterations(Exprs.NumIterations);
+ Dir->setCounters(Exprs.Counters);
+ Dir->setPrivateCounters(Exprs.PrivateCounters);
+ Dir->setInits(Exprs.Inits);
+ Dir->setUpdates(Exprs.Updates);
+ Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
+ Dir->setPreInits(Exprs.PreInits);
+ return Dir;
+}
+
+OMPParallelMasterTaskLoopDirective *
+OMPParallelMasterTaskLoopDirective::CreateEmpty(const ASTContext &C,
+ unsigned NumClauses,
+ unsigned CollapsedNum,
+ EmptyShell) {
+ unsigned Size = llvm::alignTo(sizeof(OMPParallelMasterTaskLoopDirective),
+ alignof(OMPClause *));
+ void *Mem = C.Allocate(
+ Size + sizeof(OMPClause *) * NumClauses +
+ sizeof(Stmt *) *
+ numLoopChildren(CollapsedNum, OMPD_parallel_master_taskloop));
+ return new (Mem) OMPParallelMasterTaskLoopDirective(CollapsedNum, NumClauses);
+}
+
OMPDistributeDirective *OMPDistributeDirective::Create(
const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
unsigned CollapsedNum, ArrayRef<OMPClause *> Clauses, Stmt *AssociatedStmt,
@@ -1011,6 +1215,9 @@ OMPDistributeDirective *OMPDistributeDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
return Dir;
}
@@ -1089,6 +1296,9 @@ OMPDistributeParallelForDirective *OMPDistributeParallelForDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
Dir->setCombinedLowerBoundVariable(Exprs.DistCombinedFields.LB);
Dir->setCombinedUpperBoundVariable(Exprs.DistCombinedFields.UB);
@@ -1157,6 +1367,9 @@ OMPDistributeParallelForSimdDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
Dir->setCombinedLowerBoundVariable(Exprs.DistCombinedFields.LB);
Dir->setCombinedUpperBoundVariable(Exprs.DistCombinedFields.UB);
@@ -1219,6 +1432,9 @@ OMPDistributeSimdDirective *OMPDistributeSimdDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
return Dir;
}
@@ -1271,6 +1487,9 @@ OMPTargetParallelForSimdDirective *OMPTargetParallelForSimdDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
return Dir;
}
@@ -1315,6 +1534,9 @@ OMPTargetSimdDirective::Create(const ASTContext &C, SourceLocation StartLoc,
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
return Dir;
}
@@ -1363,6 +1585,9 @@ OMPTeamsDistributeDirective *OMPTeamsDistributeDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
return Dir;
}
@@ -1414,6 +1639,9 @@ OMPTeamsDistributeSimdDirective *OMPTeamsDistributeSimdDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
return Dir;
}
@@ -1471,6 +1699,9 @@ OMPTeamsDistributeParallelForSimdDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
Dir->setCombinedLowerBoundVariable(Exprs.DistCombinedFields.LB);
Dir->setCombinedUpperBoundVariable(Exprs.DistCombinedFields.UB);
@@ -1540,6 +1771,9 @@ OMPTeamsDistributeParallelForDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
Dir->setCombinedLowerBoundVariable(Exprs.DistCombinedFields.LB);
Dir->setCombinedUpperBoundVariable(Exprs.DistCombinedFields.UB);
@@ -1628,6 +1862,9 @@ OMPTargetTeamsDistributeDirective *OMPTargetTeamsDistributeDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
return Dir;
}
@@ -1688,6 +1925,9 @@ OMPTargetTeamsDistributeParallelForDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
Dir->setCombinedLowerBoundVariable(Exprs.DistCombinedFields.LB);
Dir->setCombinedUpperBoundVariable(Exprs.DistCombinedFields.UB);
@@ -1761,6 +2001,9 @@ OMPTargetTeamsDistributeParallelForSimdDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
Dir->setCombinedLowerBoundVariable(Exprs.DistCombinedFields.LB);
Dir->setCombinedUpperBoundVariable(Exprs.DistCombinedFields.UB);
@@ -1826,6 +2069,9 @@ OMPTargetTeamsDistributeSimdDirective::Create(
Dir->setInits(Exprs.Inits);
Dir->setUpdates(Exprs.Updates);
Dir->setFinals(Exprs.Finals);
+ Dir->setDependentCounters(Exprs.DependentCounters);
+ Dir->setDependentInits(Exprs.DependentInits);
+ Dir->setFinalsConditions(Exprs.FinalsConditions);
Dir->setPreInits(Exprs.PreInits);
return Dir;
}
diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp
index 46802d765e1f0..7759ff6c13898 100644
--- a/lib/AST/StmtPrinter.cpp
+++ b/lib/AST/StmtPrinter.cpp
@@ -823,6 +823,24 @@ void StmtPrinter::VisitOMPTaskLoopSimdDirective(
PrintOMPExecutableDirective(Node);
}
+void StmtPrinter::VisitOMPMasterTaskLoopDirective(
+ OMPMasterTaskLoopDirective *Node) {
+ Indent() << "#pragma omp master taskloop";
+ PrintOMPExecutableDirective(Node);
+}
+
+void StmtPrinter::VisitOMPMasterTaskLoopSimdDirective(
+ OMPMasterTaskLoopSimdDirective *Node) {
+ Indent() << "#pragma omp master taskloop simd";
+ PrintOMPExecutableDirective(Node);
+}
+
+void StmtPrinter::VisitOMPParallelMasterTaskLoopDirective(
+ OMPParallelMasterTaskLoopDirective *Node) {
+ Indent() << "#pragma omp parallel master taskloop";
+ PrintOMPExecutableDirective(Node);
+}
+
void StmtPrinter::VisitOMPDistributeDirective(OMPDistributeDirective *Node) {
Indent() << "#pragma omp distribute";
PrintOMPExecutableDirective(Node);
@@ -1102,7 +1120,7 @@ void StmtPrinter::VisitIntegerLiteral(IntegerLiteral *Node) {
OS << Node->getValue().toString(10, isSigned);
// Emit suffixes. Integer literals are always a builtin integer type.
- switch (Node->getType()->getAs<BuiltinType>()->getKind()) {
+ switch (Node->getType()->castAs<BuiltinType>()->getKind()) {
default: llvm_unreachable("Unexpected type for integer literal!");
case BuiltinType::Char_S:
case BuiltinType::Char_U: OS << "i8"; break;
@@ -1123,7 +1141,7 @@ void StmtPrinter::VisitFixedPointLiteral(FixedPointLiteral *Node) {
return;
OS << Node->getValueAsString(/*Radix=*/10);
- switch (Node->getType()->getAs<BuiltinType>()->getKind()) {
+ switch (Node->getType()->castAs<BuiltinType>()->getKind()) {
default: llvm_unreachable("Unexpected type for fixed point literal!");
case BuiltinType::ShortFract: OS << "hr"; break;
case BuiltinType::ShortAccum: OS << "hk"; break;
@@ -1152,7 +1170,7 @@ static void PrintFloatingLiteral(raw_ostream &OS, FloatingLiteral *Node,
return;
// Emit suffixes. Float literals are always a builtin float type.
- switch (Node->getType()->getAs<BuiltinType>()->getKind()) {
+ switch (Node->getType()->castAs<BuiltinType>()->getKind()) {
default: llvm_unreachable("Unexpected type for float literal!");
case BuiltinType::Half: break; // FIXME: suffix?
case BuiltinType::Double: break; // no suffix.
@@ -1679,6 +1697,15 @@ void StmtPrinter::VisitCUDAKernelCallExpr(CUDAKernelCallExpr *Node) {
OS << ")";
}
+void StmtPrinter::VisitCXXRewrittenBinaryOperator(
+ CXXRewrittenBinaryOperator *Node) {
+ CXXRewrittenBinaryOperator::DecomposedForm Decomposed =
+ Node->getDecomposedForm();
+ PrintExpr(const_cast<Expr*>(Decomposed.LHS));
+ OS << ' ' << BinaryOperator::getOpcodeStr(Decomposed.Opcode) << ' ';
+ PrintExpr(const_cast<Expr*>(Decomposed.RHS));
+}
+
void StmtPrinter::VisitCXXNamedCastExpr(CXXNamedCastExpr *Node) {
OS << Node->getCastName() << '<';
Node->getTypeAsWritten().print(OS, Policy);
@@ -1952,7 +1979,7 @@ void StmtPrinter::VisitLambdaExpr(LambdaExpr *Node) {
if (Node->isMutable())
OS << " mutable";
- auto *Proto = Method->getType()->getAs<FunctionProtoType>();
+ auto *Proto = Method->getType()->castAs<FunctionProtoType>();
Proto->printExceptionSpecification(OS, Policy);
// FIXME: Attributes
@@ -2219,6 +2246,17 @@ void StmtPrinter::VisitCXXFoldExpr(CXXFoldExpr *E) {
OS << ")";
}
+void StmtPrinter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) {
+ NestedNameSpecifierLoc NNS = E->getNestedNameSpecifierLoc();
+ if (NNS)
+ NNS.getNestedNameSpecifier()->print(OS, Policy);
+ if (E->getTemplateKWLoc().isValid())
+ OS << "template ";
+ OS << E->getFoundDecl()->getName();
+ printTemplateArgumentList(OS, E->getTemplateArgsAsWritten()->arguments(),
+ Policy);
+}
+
// C++ Coroutines TS
void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) {
diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp
index f92c3dc60ba5c..d1e8565389325 100644
--- a/lib/AST/StmtProfile.cpp
+++ b/lib/AST/StmtProfile.cpp
@@ -440,6 +440,7 @@ void OMPClauseProfiler::VisitOMPIfClause(const OMPIfClause *C) {
}
void OMPClauseProfiler::VisitOMPFinalClause(const OMPFinalClause *C) {
+ VistOMPClauseWithPreInit(C);
if (C->getCondition())
Profiler->VisitStmt(C->getCondition());
}
@@ -736,14 +737,17 @@ void OMPClauseProfiler::VisitOMPThreadLimitClause(
Profiler->VisitStmt(C->getThreadLimit());
}
void OMPClauseProfiler::VisitOMPPriorityClause(const OMPPriorityClause *C) {
+ VistOMPClauseWithPreInit(C);
if (C->getPriority())
Profiler->VisitStmt(C->getPriority());
}
void OMPClauseProfiler::VisitOMPGrainsizeClause(const OMPGrainsizeClause *C) {
+ VistOMPClauseWithPreInit(C);
if (C->getGrainsize())
Profiler->VisitStmt(C->getGrainsize());
}
void OMPClauseProfiler::VisitOMPNumTasksClause(const OMPNumTasksClause *C) {
+ VistOMPClauseWithPreInit(C);
if (C->getNumTasks())
Profiler->VisitStmt(C->getNumTasks());
}
@@ -918,6 +922,21 @@ void StmtProfiler::VisitOMPTaskLoopSimdDirective(
VisitOMPLoopDirective(S);
}
+void StmtProfiler::VisitOMPMasterTaskLoopDirective(
+ const OMPMasterTaskLoopDirective *S) {
+ VisitOMPLoopDirective(S);
+}
+
+void StmtProfiler::VisitOMPMasterTaskLoopSimdDirective(
+ const OMPMasterTaskLoopSimdDirective *S) {
+ VisitOMPLoopDirective(S);
+}
+
+void StmtProfiler::VisitOMPParallelMasterTaskLoopDirective(
+ const OMPParallelMasterTaskLoopDirective *S) {
+ VisitOMPLoopDirective(S);
+}
+
void StmtProfiler::VisitOMPDistributeDirective(
const OMPDistributeDirective *S) {
VisitOMPLoopDirective(S);
@@ -1297,6 +1316,14 @@ void StmtProfiler::VisitAtomicExpr(const AtomicExpr *S) {
ID.AddInteger(S->getOp());
}
+void StmtProfiler::VisitConceptSpecializationExpr(
+ const ConceptSpecializationExpr *S) {
+ VisitExpr(S);
+ VisitDecl(S->getFoundDecl());
+ VisitTemplateArguments(S->getTemplateArgsAsWritten()->getTemplateArgs(),
+ S->getTemplateArgsAsWritten()->NumTemplateArgs);
+}
+
static Stmt::StmtClass DecodeOperatorCall(const CXXOperatorCallExpr *S,
UnaryOperatorKind &UnaryOp,
BinaryOperatorKind &BinaryOp) {
@@ -1530,6 +1557,16 @@ void StmtProfiler::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *S) {
ID.AddInteger(S->getOperator());
}
+void StmtProfiler::VisitCXXRewrittenBinaryOperator(
+ const CXXRewrittenBinaryOperator *S) {
+ // If a rewritten operator were ever to be type-dependent, we should profile
+ // it following its syntactic operator.
+ assert(!S->isTypeDependent() &&
+ "resolved rewritten operator should never be type-dependent");
+ ID.AddBoolean(S->isReversed());
+ VisitExpr(S->getSemanticForm());
+}
+
#if defined(_MSC_VER) && !defined(__clang__)
#if _MSC_VER == 1911
#pragma optimize("", on)
diff --git a/lib/AST/TemplateBase.cpp b/lib/AST/TemplateBase.cpp
index cb4cbd2f76a12..db16c2a06b64f 100644
--- a/lib/AST/TemplateBase.cpp
+++ b/lib/AST/TemplateBase.cpp
@@ -370,7 +370,7 @@ TemplateArgument TemplateArgument::getPackExpansionPattern() const {
switch (getKind()) {
case Type:
- return getAsType()->getAs<PackExpansionType>()->getPattern();
+ return getAsType()->castAs<PackExpansionType>()->getPattern();
case Expression:
return cast<PackExpansionExpr>(getAsExpr())->getPattern();
diff --git a/lib/AST/TextNodeDumper.cpp b/lib/AST/TextNodeDumper.cpp
index cba9091b10652..63a6510324f75 100644
--- a/lib/AST/TextNodeDumper.cpp
+++ b/lib/AST/TextNodeDumper.cpp
@@ -223,7 +223,6 @@ void TextNodeDumper::Visit(const Decl *D) {
return;
}
- Context = &D->getASTContext();
{
ColorScope Color(OS, ShowColors, DeclKindNameColor);
OS << D->getDeclKindName() << "Decl";
@@ -637,8 +636,8 @@ static void dumpBasePath(raw_ostream &OS, const CastExpr *Node) {
if (!First)
OS << " -> ";
- const CXXRecordDecl *RD =
- cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl());
+ const auto *RD =
+ cast<CXXRecordDecl>(Base->getType()->castAs<RecordType>()->getDecl());
if (Base->isVirtual())
OS << "virtual ";
@@ -688,7 +687,7 @@ void TextNodeDumper::VisitConstantExpr(const ConstantExpr *Node) {
if (Node->getResultAPValueKind() != APValue::None) {
ColorScope Color(OS, ShowColors, ValueColor);
OS << " ";
- Node->getAPValueResult().printPretty(OS, *Context, Node->getType());
+ Node->getAPValueResult().dump(OS);
}
}
@@ -1385,6 +1384,8 @@ void TextNodeDumper::VisitVarDecl(const VarDecl *D) {
break;
}
}
+ if (D->needsDestruction(D->getASTContext()))
+ OS << " destroyed";
if (D->isParameterPack())
OS << " pack";
}
@@ -1537,6 +1538,7 @@ void TextNodeDumper::VisitCXXRecordDecl(const CXXRecordDecl *D) {
FLAG(isGenericLambda, generic);
FLAG(isLambda, lambda);
+ FLAG(isAnonymousStructOrUnion, is_anonymous);
FLAG(canPassInRegisters, pass_in_registers);
FLAG(isEmpty, empty);
FLAG(isAggregate, aggregate);
@@ -1641,6 +1643,7 @@ void TextNodeDumper::VisitCXXRecordDecl(const CXXRecordDecl *D) {
FLAG(hasTrivialDestructor, trivial);
FLAG(hasNonTrivialDestructor, non_trivial);
FLAG(hasUserDeclaredDestructor, user_declared);
+ FLAG(hasConstexprDestructor, constexpr);
FLAG(needsImplicitDestructor, needs_implicit);
FLAG(needsOverloadResolutionForDestructor, needs_overload_resolution);
if (!D->needsOverloadResolutionForDestructor())
@@ -1766,6 +1769,12 @@ void TextNodeDumper::VisitLinkageSpecDecl(const LinkageSpecDecl *D) {
case LinkageSpecDecl::lang_cxx:
OS << " C++";
break;
+ case LinkageSpecDecl::lang_cxx_11:
+ OS << " C++11";
+ break;
+ case LinkageSpecDecl::lang_cxx_14:
+ OS << " C++14";
+ break;
}
}
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index ed75a0b5bcd85..4d54ea1061edc 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -50,6 +50,7 @@
#include <cassert>
#include <cstdint>
#include <cstring>
+#include <type_traits>
using namespace clang;
@@ -74,11 +75,11 @@ const IdentifierInfo* QualType::getBaseTypeIdentifier() const {
if (ty->isPointerType() || ty->isReferenceType())
return ty->getPointeeType().getBaseTypeIdentifier();
else if (ty->isRecordType())
- ND = ty->getAs<RecordType>()->getDecl();
+ ND = ty->castAs<RecordType>()->getDecl();
else if (ty->isEnumeralType())
- ND = ty->getAs<EnumType>()->getDecl();
+ ND = ty->castAs<EnumType>()->getDecl();
else if (ty->getTypeClass() == Type::Typedef)
- ND = ty->getAs<TypedefType>()->getDecl();
+ ND = ty->castAs<TypedefType>()->getDecl();
else if (ty->isArrayType())
return ty->castAsArrayTypeUnsafe()->
getElementType().getBaseTypeIdentifier();
@@ -108,6 +109,33 @@ bool QualType::isConstant(QualType T, const ASTContext &Ctx) {
return T.getAddressSpace() == LangAS::opencl_constant;
}
+// C++ [temp.dep.type]p1:
+// A type is dependent if it is...
+// - an array type constructed from any dependent type or whose
+// size is specified by a constant expression that is
+// value-dependent,
+ArrayType::ArrayType(TypeClass tc, QualType et, QualType can,
+ ArraySizeModifier sm, unsigned tq, const Expr *sz)
+ // Note, we need to check for DependentSizedArrayType explicitly here
+ // because we use a DependentSizedArrayType with no size expression as the
+ // type of a dependent array of unknown bound with a dependent braced
+ // initializer:
+ //
+ // template<int ...N> int arr[] = {N...};
+ : Type(tc, can,
+ et->isDependentType() || (sz && sz->isValueDependent()) ||
+ tc == DependentSizedArray,
+ et->isInstantiationDependentType() ||
+ (sz && sz->isInstantiationDependent()) ||
+ tc == DependentSizedArray,
+ (tc == VariableArray || et->isVariablyModifiedType()),
+ et->containsUnexpandedParameterPack() ||
+ (sz && sz->containsUnexpandedParameterPack())),
+ ElementType(et) {
+ ArrayTypeBits.IndexTypeQuals = tq;
+ ArrayTypeBits.SizeModifier = sm;
+}
+
unsigned ConstantArrayType::getNumAddressingBits(const ASTContext &Context,
QualType ElementType,
const llvm::APInt &NumElements) {
@@ -155,14 +183,26 @@ unsigned ConstantArrayType::getMaxSizeBits(const ASTContext &Context) {
return Bits;
}
+void ConstantArrayType::Profile(llvm::FoldingSetNodeID &ID,
+ const ASTContext &Context, QualType ET,
+ const llvm::APInt &ArraySize,
+ const Expr *SizeExpr, ArraySizeModifier SizeMod,
+ unsigned TypeQuals) {
+ ID.AddPointer(ET.getAsOpaquePtr());
+ ID.AddInteger(ArraySize.getZExtValue());
+ ID.AddInteger(SizeMod);
+ ID.AddInteger(TypeQuals);
+ ID.AddBoolean(SizeExpr != 0);
+ if (SizeExpr)
+ SizeExpr->Profile(ID, Context, true);
+}
+
DependentSizedArrayType::DependentSizedArrayType(const ASTContext &Context,
QualType et, QualType can,
Expr *e, ArraySizeModifier sm,
unsigned tq,
SourceRange brackets)
- : ArrayType(DependentSizedArray, et, can, sm, tq,
- (et->containsUnexpandedParameterPack() ||
- (e && e->containsUnexpandedParameterPack()))),
+ : ArrayType(DependentSizedArray, et, can, sm, tq, e),
Context(Context), SizeExpr((Stmt*) e), Brackets(brackets) {}
void DependentSizedArrayType::Profile(llvm::FoldingSetNodeID &ID,
@@ -297,7 +337,19 @@ QualType QualType::getSingleStepDesugaredTypeImpl(QualType type,
#define TYPE(CLASS, BASE) \
static_assert(!std::is_polymorphic<CLASS##Type>::value, \
#CLASS "Type should not be polymorphic!");
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
+
+// Check that no type class has a non-trival destructor. Types are
+// allocated with the BumpPtrAllocator from ASTContext and therefore
+// their destructor is not executed.
+//
+// FIXME: ConstantArrayType is not trivially destructible because of its
+// APInt member. It should be replaced in favor of ASTContext allocation.
+#define TYPE(CLASS, BASE) \
+ static_assert(std::is_trivially_destructible<CLASS##Type>::value || \
+ std::is_same<CLASS##Type, ConstantArrayType>::value, \
+ #CLASS "Type should be trivially destructible!");
+#include "clang/AST/TypeNodes.inc"
QualType Type::getLocallyUnqualifiedSingleStepDesugaredType() const {
switch (getTypeClass()) {
@@ -308,7 +360,7 @@ QualType Type::getLocallyUnqualifiedSingleStepDesugaredType() const {
if (!ty->isSugared()) return QualType(ty, 0); \
return ty->desugar(); \
}
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
}
llvm_unreachable("bad type kind!");
}
@@ -329,7 +381,7 @@ SplitQualType QualType::getSplitDesugaredType(QualType T) {
Cur = Ty->desugar(); \
break; \
}
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
}
}
}
@@ -357,7 +409,7 @@ SplitQualType QualType::getSplitUnqualifiedTypeImpl(QualType type) {
next = ty->desugar(); \
break; \
}
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
}
// Otherwise, split the underlying type. If that yields qualifiers,
@@ -396,7 +448,7 @@ template<typename T> static const T *getAsSugar(const Type *Cur) {
Cur = Ty->desugar().getTypePtr(); \
break; \
}
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
}
}
}
@@ -429,7 +481,7 @@ const Type *Type::getUnqualifiedDesugaredType() const {
Cur = Ty->desugar().getTypePtr(); \
break; \
}
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
}
}
}
@@ -611,6 +663,10 @@ ObjCTypeParamType::ObjCTypeParamType(const ObjCTypeParamDecl *D,
initialize(protocols);
}
+QualType ObjCTypeParamType::desugar() const {
+ return getDecl()->getUnderlyingType();
+}
+
ObjCObjectType::ObjCObjectType(QualType Canonical, QualType Base,
ArrayRef<QualType> typeArgs,
ArrayRef<ObjCProtocolDecl *> protocols,
@@ -753,7 +809,7 @@ public:
#define TYPE(Class, Base)
#define DEPENDENT_TYPE(Class, Base) \
QualType Visit##Class##Type(const Class##Type *T) { return QualType(T, 0); }
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
#define TRIVIAL_TYPE_CLASS(Class) \
QualType Visit##Class##Type(const Class##Type *T) { return QualType(T, 0); }
@@ -847,7 +903,7 @@ public:
if (elementType.getAsOpaquePtr() == T->getElementType().getAsOpaquePtr())
return QualType(T, 0);
- return Ctx.getConstantArrayType(elementType, T->getSize(),
+ return Ctx.getConstantArrayType(elementType, T->getSize(), T->getSizeExpr(),
T->getSizeModifier(),
T->getIndexTypeCVRQualifiers());
}
@@ -2477,6 +2533,15 @@ bool QualType::isCXX11PODType(const ASTContext &Context) const {
return false;
}
+bool Type::isNothrowT() const {
+ if (const auto *RD = getAsCXXRecordDecl()) {
+ IdentifierInfo *II = RD->getIdentifier();
+ if (II && II->isStr("nothrow_t") && RD->isInStdNamespace())
+ return true;
+ }
+ return false;
+}
+
bool Type::isAlignValT() const {
if (const auto *ET = getAs<EnumType>()) {
IdentifierInfo *II = ET->getDecl()->getIdentifier();
@@ -2690,7 +2755,7 @@ const char *Type::getTypeClassName() const {
switch (TypeBits.TC) {
#define ABSTRACT_TYPE(Derived, Base)
#define TYPE(Derived, Base) case Derived: return #Derived;
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
}
llvm_unreachable("Invalid type class.");
@@ -2841,6 +2906,10 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const {
case Id: \
return #ExtType;
#include "clang/Basic/OpenCLExtensionTypes.def"
+#define SVE_TYPE(Name, Id, SingletonId) \
+ case Id: \
+ return Name;
+#include "clang/Basic/AArch64SVEACLETypes.def"
}
llvm_unreachable("Invalid builtin type.");
@@ -3556,14 +3625,15 @@ static CachedProperties computeCachedProperties(const Type *T) {
switch (T->getTypeClass()) {
#define TYPE(Class,Base)
#define NON_CANONICAL_TYPE(Class,Base) case Type::Class:
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
llvm_unreachable("didn't expect a non-canonical type here");
#define TYPE(Class,Base)
#define DEPENDENT_TYPE(Class,Base) case Type::Class:
#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class,Base) case Type::Class:
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
// Treat instantiation-dependent types as external.
+ if (!T->isInstantiationDependentType()) T->dump();
assert(T->isInstantiationDependentType());
return CachedProperties(ExternalLinkage, false);
@@ -3659,13 +3729,13 @@ LinkageInfo LinkageComputer::computeTypeLinkageInfo(const Type *T) {
switch (T->getTypeClass()) {
#define TYPE(Class,Base)
#define NON_CANONICAL_TYPE(Class,Base) case Type::Class:
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
llvm_unreachable("didn't expect a non-canonical type here");
#define TYPE(Class,Base)
#define DEPENDENT_TYPE(Class,Base) case Type::Class:
#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class,Base) case Type::Class:
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
// Treat instantiation-dependent types as external.
assert(T->isInstantiationDependentType());
return LinkageInfo::external();
@@ -3774,7 +3844,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
case Type::Class: \
llvm_unreachable("non-canonical type");
#define TYPE(Class, Parent)
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
// Pointer types.
case Type::Pointer:
@@ -3842,6 +3912,9 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
case BuiltinType::OCLClkEvent:
case BuiltinType::OCLQueue:
case BuiltinType::OCLReserveID:
+#define SVE_TYPE(Name, Id, SingletonId) \
+ case BuiltinType::Id:
+#include "clang/Basic/AArch64SVEACLETypes.def"
case BuiltinType::BuiltinFn:
case BuiltinType::NullPtr:
case BuiltinType::OMPArraySection:
diff --git a/lib/AST/TypeLoc.cpp b/lib/AST/TypeLoc.cpp
index abe4c4eb25e6f..e4788f32b2656 100644
--- a/lib/AST/TypeLoc.cpp
+++ b/lib/AST/TypeLoc.cpp
@@ -391,6 +391,9 @@ TypeSpecifierType BuiltinTypeLoc::getWrittenTypeSpec() const {
case BuiltinType::OCLClkEvent:
case BuiltinType::OCLQueue:
case BuiltinType::OCLReserveID:
+#define SVE_TYPE(Name, Id, SingletonId) \
+ case BuiltinType::Id:
+#include "clang/Basic/AArch64SVEACLETypes.def"
case BuiltinType::BuiltinFn:
case BuiltinType::OMPArraySection:
return TST_unspecified;
diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp
index 8d5c37299e5fb..dacbf9a96d814 100644
--- a/lib/AST/TypePrinter.cpp
+++ b/lib/AST/TypePrinter.cpp
@@ -125,7 +125,7 @@ namespace {
#define TYPE(CLASS, PARENT) \
void print##CLASS##Before(const CLASS##Type *T, raw_ostream &OS); \
void print##CLASS##After(const CLASS##Type *T, raw_ostream &OS);
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
private:
void printBefore(const Type *ty, Qualifiers qs, raw_ostream &OS);
@@ -321,7 +321,7 @@ void TypePrinter::printBefore(const Type *T,Qualifiers Quals, raw_ostream &OS) {
#define TYPE(CLASS, PARENT) case Type::CLASS: \
print##CLASS##Before(cast<CLASS##Type>(T), OS); \
break;
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
}
if (hasAfterQuals) {
@@ -347,7 +347,7 @@ void TypePrinter::printAfter(const Type *T, Qualifiers Quals, raw_ostream &OS) {
#define TYPE(CLASS, PARENT) case Type::CLASS: \
print##CLASS##After(cast<CLASS##Type>(T), OS); \
break;
-#include "clang/AST/TypeNodes.def"
+#include "clang/AST/TypeNodes.inc"
}
}
@@ -1204,7 +1204,8 @@ void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) {
// arguments.
if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
ArrayRef<TemplateArgument> Args;
- if (TypeSourceInfo *TAW = Spec->getTypeAsWritten()) {
+ TypeSourceInfo *TAW = Spec->getTypeAsWritten();
+ if (!Policy.PrintCanonicalTypes && TAW) {
const TemplateSpecializationType *TST =
cast<TemplateSpecializationType>(TAW->getType());
Args = TST->template_arguments();
@@ -1537,7 +1538,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
QualType t = T->getEquivalentType();
while (!t->isFunctionType())
t = t->getPointeeType();
- OS << (t->getAs<FunctionType>()->getCallConv() == CC_AAPCS ?
+ OS << (t->castAs<FunctionType>()->getCallConv() == CC_AAPCS ?
"\"aapcs\"" : "\"aapcs-vfp\"");
OS << ')';
break;
diff --git a/lib/AST/VTTBuilder.cpp b/lib/AST/VTTBuilder.cpp
index 53d0ef09f14c5..d58e875177852 100644
--- a/lib/AST/VTTBuilder.cpp
+++ b/lib/AST/VTTBuilder.cpp
@@ -64,8 +64,8 @@ void VTTBuilder::LayoutSecondaryVTTs(BaseSubobject Base) {
if (I.isVirtual())
continue;
- const CXXRecordDecl *BaseDecl =
- cast<CXXRecordDecl>(I.getType()->getAs<RecordType>()->getDecl());
+ const auto *BaseDecl =
+ cast<CXXRecordDecl>(I.getType()->castAs<RecordType>()->getDecl());
const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
CharUnits BaseOffset = Base.getBaseOffset() +
@@ -90,8 +90,8 @@ VTTBuilder::LayoutSecondaryVirtualPointers(BaseSubobject Base,
return;
for (const auto &I : RD->bases()) {
- const CXXRecordDecl *BaseDecl =
- cast<CXXRecordDecl>(I.getType()->getAs<RecordType>()->getDecl());
+ const auto *BaseDecl =
+ cast<CXXRecordDecl>(I.getType()->castAs<RecordType>()->getDecl());
// Itanium C++ ABI 2.6.2:
// Secondary virtual pointers are present for all bases with either
@@ -154,8 +154,8 @@ VTTBuilder::LayoutSecondaryVirtualPointers(BaseSubobject Base,
void VTTBuilder::LayoutVirtualVTTs(const CXXRecordDecl *RD,
VisitedVirtualBasesSetTy &VBases) {
for (const auto &I : RD->bases()) {
- const CXXRecordDecl *BaseDecl =
- cast<CXXRecordDecl>(I.getType()->getAs<RecordType>()->getDecl());
+ const auto *BaseDecl =
+ cast<CXXRecordDecl>(I.getType()->castAs<RecordType>()->getDecl());
// Check if this is a virtual base.
if (I.isVirtual()) {
diff --git a/lib/AST/VTableBuilder.cpp b/lib/AST/VTableBuilder.cpp
index 0c699571555d5..5688042dadd91 100644
--- a/lib/AST/VTableBuilder.cpp
+++ b/lib/AST/VTableBuilder.cpp
@@ -2268,7 +2268,7 @@ CreateVTableLayout(const ItaniumVTableBuilder &Builder) {
SmallVector<VTableLayout::VTableThunkTy, 1>
VTableThunks(Builder.vtable_thunks_begin(), Builder.vtable_thunks_end());
- return llvm::make_unique<VTableLayout>(
+ return std::make_unique<VTableLayout>(
Builder.VTableIndices, Builder.vtable_components(), VTableThunks,
Builder.getAddressPoints());
}
@@ -3253,7 +3253,7 @@ void MicrosoftVTableContext::computeVTablePaths(bool ForVBTables,
// Base case: this subobject has its own vptr.
if (ForVBTables ? Layout.hasOwnVBPtr() : Layout.hasOwnVFPtr())
- Paths.push_back(llvm::make_unique<VPtrInfo>(RD));
+ Paths.push_back(std::make_unique<VPtrInfo>(RD));
// Recursive case: get all the vbtables from our bases and remove anything
// that shares a virtual base.
@@ -3276,7 +3276,7 @@ void MicrosoftVTableContext::computeVTablePaths(bool ForVBTables,
continue;
// Copy the path and adjust it as necessary.
- auto P = llvm::make_unique<VPtrInfo>(*BaseInfo);
+ auto P = std::make_unique<VPtrInfo>(*BaseInfo);
// We mangle Base into the path if the path would've been ambiguous and it
// wasn't already extended with Base.
@@ -3562,7 +3562,7 @@ void MicrosoftVTableContext::computeVTableRelatedInformation(
const VTableLayout::AddressPointsMapTy EmptyAddressPointsMap;
{
- auto VFPtrs = llvm::make_unique<VPtrInfoVector>();
+ auto VFPtrs = std::make_unique<VPtrInfoVector>();
computeVTablePaths(/*ForVBTables=*/false, RD, *VFPtrs);
computeFullPathsForVFTables(Context, RD, *VFPtrs);
VFPtrLocations[RD] = std::move(VFPtrs);
@@ -3576,7 +3576,7 @@ void MicrosoftVTableContext::computeVTableRelatedInformation(
assert(VFTableLayouts.count(id) == 0);
SmallVector<VTableLayout::VTableThunkTy, 1> VTableThunks(
Builder.vtable_thunks_begin(), Builder.vtable_thunks_end());
- VFTableLayouts[id] = llvm::make_unique<VTableLayout>(
+ VFTableLayouts[id] = std::make_unique<VTableLayout>(
ArrayRef<size_t>{0}, Builder.vtable_components(), VTableThunks,
EmptyAddressPointsMap);
Thunks.insert(Builder.thunks_begin(), Builder.thunks_end());
@@ -3668,7 +3668,7 @@ const VirtualBaseInfo &MicrosoftVTableContext::computeVBTableRelatedInformation(
std::unique_ptr<VirtualBaseInfo> &Entry = VBaseInfo[RD];
if (Entry)
return *Entry;
- Entry = llvm::make_unique<VirtualBaseInfo>();
+ Entry = std::make_unique<VirtualBaseInfo>();
VBI = Entry.get();
}