diff options
Diffstat (limited to 'lib/AST')
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(); } |