diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2012-08-15 20:02:54 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2012-08-15 20:02:54 +0000 |
commit | 56d91b49b13fe55c918afbda19f6165b5fbff87a (patch) | |
tree | 9abb1a658a297776086f4e0dfa6ca533de02104e /lib/AST | |
parent | 41e20f564abdb05101d6b2b29c59459a966c22cc (diff) |
Vendor import of clang trunk r161861:vendor/clang/clang-trunk-r161861
Notes
Notes:
svn path=/vendor/clang/dist/; revision=239313
svn path=/vendor/clang/clang-trunk-r161861/; revision=239314; tag=vendor/clang/clang-trunk-r161861
Diffstat (limited to 'lib/AST')
48 files changed, 8134 insertions, 1761 deletions
diff --git a/lib/AST/APValue.cpp b/lib/AST/APValue.cpp index a31b3c5bfb37..a74ef14a9e2b 100644 --- a/lib/AST/APValue.cpp +++ b/lib/AST/APValue.cpp @@ -467,9 +467,9 @@ void APValue::printPretty(raw_ostream &Out, ASTContext &Ctx, QualType Ty) const{ FI != RD->field_end(); ++FI) { if (!First) Out << ", "; - if ((*FI)->isUnnamedBitfield()) continue; - getStructField((*FI)->getFieldIndex()). - printPretty(Out, Ctx, (*FI)->getType()); + if (FI->isUnnamedBitfield()) continue; + getStructField(FI->getFieldIndex()). + printPretty(Out, Ctx, FI->getType()); First = false; } Out << '}'; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index cb4d336de157..ad48dffb4d4f 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -13,6 +13,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/CommentCommandTraits.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" @@ -53,6 +54,218 @@ enum FloatingRank { HalfRank, FloatRank, DoubleRank, LongDoubleRank }; +RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { + if (!CommentsLoaded && ExternalSource) { + ExternalSource->ReadComments(); + CommentsLoaded = true; + } + + assert(D); + + // User can not attach documentation to implicit declarations. + if (D->isImplicit()) + return NULL; + + // TODO: handle comments for function parameters properly. + if (isa<ParmVarDecl>(D)) + return NULL; + + // TODO: we could look up template parameter documentation in the template + // documentation. + if (isa<TemplateTypeParmDecl>(D) || + isa<NonTypeTemplateParmDecl>(D) || + isa<TemplateTemplateParmDecl>(D)) + return NULL; + + ArrayRef<RawComment *> RawComments = Comments.getComments(); + + // If there are no comments anywhere, we won't find anything. + if (RawComments.empty()) + return NULL; + + // Find declaration location. + // For Objective-C declarations we generally don't expect to have multiple + // declarators, thus use declaration starting location as the "declaration + // 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->getLocStart(); + else + DeclLoc = D->getLocation(); + + // 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 NULL; + + // 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)); + 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 == std::lower_bound(RawComments.begin(), RawComments.end(), + &CommentAtDeclLoc, Compare)); + } else { + // Slow path. + Comment = std::lower_bound(RawComments.begin(), RawComments.end(), + &CommentAtDeclLoc, Compare); + } + } + + // Decompose the location for the declaration and find the beginning of the + // file buffer. + std::pair<FileID, unsigned> DeclLocDecomp = SourceMgr.getDecomposedLoc(DeclLoc); + + // First check whether we have a trailing comment. + if (Comment != RawComments.end() && + (*Comment)->isDocumentation() && (*Comment)->isTrailingComment() && + (isa<FieldDecl>(D) || isa<EnumConstantDecl>(D) || isa<VarDecl>(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)->setDecl(D); + return *Comment; + } + } + + // The comment just after the declaration was not a trailing comment. + // Let's look at the previous comment. + if (Comment == RawComments.begin()) + return NULL; + --Comment; + + // Check that we actually have a non-member Doxygen comment. + if (!(*Comment)->isDocumentation() || (*Comment)->isTrailingComment()) + return NULL; + + // 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 NULL; + + // Get the corresponding buffer. + bool Invalid = false; + const char *Buffer = SourceMgr.getBufferData(DeclLocDecomp.first, + &Invalid).data(); + if (Invalid) + return NULL; + + // Extract text between the comment and declaration. + StringRef Text(Buffer + CommentEndDecomp.second, + DeclLocDecomp.second - CommentEndDecomp.second); + + // There should be no other declarations or preprocessor directives between + // comment and declaration. + if (Text.find_first_of(",;{}#@") != StringRef::npos) + return NULL; + + (*Comment)->setDecl(D); + return *Comment; +} + +const RawComment *ASTContext::getRawCommentForAnyRedecl(const Decl *D) const { + // If we have a 'templated' declaration for a template, adjust 'D' to + // refer to the actual template. + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (const FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate()) + D = FTD; + } else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) { + if (const ClassTemplateDecl *CTD = RD->getDescribedClassTemplate()) + D = CTD; + } + // FIXME: Alias templates? + + // Check whether we have cached a comment for this declaration already. + { + llvm::DenseMap<const Decl *, RawCommentAndCacheFlags>::iterator Pos = + RedeclComments.find(D); + if (Pos != RedeclComments.end()) { + const RawCommentAndCacheFlags &Raw = Pos->second; + if (Raw.getKind() != RawCommentAndCacheFlags::NoCommentInDecl) + return Raw.getRaw(); + } + } + + // Search for comments attached to declarations in the redeclaration chain. + const RawComment *RC = NULL; + for (Decl::redecl_iterator I = D->redecls_begin(), + E = D->redecls_end(); + I != E; ++I) { + 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(); + break; + } + } else { + RC = getRawCommentForDeclNoCache(*I); + RawCommentAndCacheFlags Raw; + if (RC) { + Raw.setRaw(RC); + Raw.setKind(RawCommentAndCacheFlags::FromDecl); + } else + Raw.setKind(RawCommentAndCacheFlags::NoCommentInDecl); + RedeclComments[*I] = Raw; + if (RC) + break; + } + } + + // If we found a comment, it should be a documentation comment. + assert(!RC || RC->isDocumentation()); + + // Update cache for every declaration in the redeclaration chain. + RawCommentAndCacheFlags Raw; + Raw.setRaw(RC); + Raw.setKind(RawCommentAndCacheFlags::FromRedecl); + + for (Decl::redecl_iterator I = D->redecls_begin(), + E = D->redecls_end(); + I != E; ++I) { + RawCommentAndCacheFlags &R = RedeclComments[*I]; + if (R.getKind() == RawCommentAndCacheFlags::NoCommentInDecl) + R = Raw; + } + + return RC; +} + +comments::FullComment *ASTContext::getCommentForDecl(const Decl *D) const { + const RawComment *RC = getRawCommentForAnyRedecl(D); + if (!RC) + return NULL; + + return RC->getParsed(*this); +} + void ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID, TemplateTemplateParmDecl *Parm) { @@ -206,7 +419,10 @@ static const LangAS::Map *getAddressSpaceMap(const TargetInfo &T, static const unsigned FakeAddrSpaceMap[] = { 1, // opencl_global 2, // opencl_local - 3 // opencl_constant + 3, // opencl_constant + 4, // cuda_device + 5, // cuda_constant + 6 // cuda_shared }; return &FakeAddrSpaceMap; } else { @@ -226,6 +442,7 @@ ASTContext::ASTContext(LangOptions& LOpts, SourceManager &SM, SubstTemplateTemplateParmPacks(this_()), GlobalNestedNameSpecifier(0), Int128Decl(0), UInt128Decl(0), + BuiltinVaListDecl(0), ObjCIdDecl(0), ObjCSelDecl(0), ObjCClassDecl(0), ObjCProtocolClassDecl(0), CFConstantStringTypeDecl(0), ObjCInstanceTypeDecl(0), FILEDecl(0), @@ -240,6 +457,7 @@ ASTContext::ASTContext(LangOptions& LOpts, SourceManager &SM, BuiltinInfo(builtins), DeclarationNames(*this), ExternalSource(0), Listener(0), + Comments(SM), CommentsLoaded(false), LastSDM(0, 0), UniqueBlockByRefTypeID(0) { @@ -436,6 +654,8 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target) { } else // C99 WCharTy = getFromTargetType(Target.getWCharType()); + WIntTy = getFromTargetType(Target.getWIntType()); + if (LangOpts.CPlusPlus) // C++0x 3.9.1p5, extension for C++ InitBuiltinType(Char16Ty, BuiltinType::Char16); else // C99 @@ -473,8 +693,6 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target) { DoubleComplexTy = getComplexType(DoubleTy); LongDoubleComplexTy = getComplexType(LongDoubleTy); - BuiltinVaListType = QualType(); - // Builtin types for 'id', 'Class', and 'SEL'. InitBuiltinType(ObjCBuiltinIdTy, BuiltinType::ObjCId); InitBuiltinType(ObjCBuiltinClassTy, BuiltinType::ObjCClass); @@ -494,6 +712,9 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target) { // half type (OpenCL 6.1.1.1) / ARM NEON __fp16 InitBuiltinType(HalfTy, BuiltinType::Half); + + // Builtin type used to help define __builtin_va_list. + VaListTagTy = QualType(); } DiagnosticsEngine &ASTContext::getDiagnostics() const { @@ -881,6 +1102,10 @@ ASTContext::getTypeInfoImpl(const Type *T) const { Align = llvm::NextPowerOf2(Align); Width = llvm::RoundUpToAlignment(Width, Align); } + // Adjust the alignment based on the target max. + uint64_t TargetVectorAlign = Target->getMaxVectorAlign(); + if (TargetVectorAlign && TargetVectorAlign < Align) + Align = TargetVectorAlign; break; } @@ -1337,14 +1562,6 @@ void ASTContext::setBlockVarCopyInits(VarDecl*VD, Expr* Init) { BlockVarCopyInits[VD] = Init; } -/// \brief Allocate an uninitialized TypeSourceInfo. -/// -/// The caller should initialize the memory held by TypeSourceInfo using -/// the TypeLoc wrappers. -/// -/// \param T the type that will be the basis for type source info. This type -/// should refer to how the declarator was written in source code, not to -/// what type semantic analysis resolved the declarator to. TypeSourceInfo *ASTContext::CreateTypeSourceInfo(QualType T, unsigned DataSize) const { if (!DataSize) @@ -2187,15 +2404,18 @@ ASTContext::getFunctionType(QualType ResultTy, // - exception types // - consumed-arguments flags // Instead of the exception types, there could be a noexcept - // expression. + // expression, or information used to resolve the exception + // specification. size_t Size = sizeof(FunctionProtoType) + NumArgs * sizeof(QualType); - if (EPI.ExceptionSpecType == EST_Dynamic) + if (EPI.ExceptionSpecType == EST_Dynamic) { Size += EPI.NumExceptions * sizeof(QualType); - else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) { + } else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) { Size += sizeof(Expr*); } else if (EPI.ExceptionSpecType == EST_Uninstantiated) { Size += 2 * sizeof(FunctionDecl*); + } else if (EPI.ExceptionSpecType == EST_Unevaluated) { + Size += sizeof(FunctionDecl*); } if (EPI.ConsumedArguments) Size += NumArgs * sizeof(bool); @@ -2730,10 +2950,17 @@ QualType ASTContext::getPackExpansionType(QualType Pattern, QualType Canon; if (!Pattern.isCanonical()) { - Canon = getPackExpansionType(getCanonicalType(Pattern), NumExpansions); - - // Find the insert position again. - PackExpansionTypes.FindNodeOrInsertPos(ID, InsertPos); + Canon = getCanonicalType(Pattern); + // The canonical type might not contain an unexpanded parameter pack, if it + // contains an alias template specialization which ignores one of its + // parameters. + if (Canon->containsUnexpandedParameterPack()) { + Canon = getPackExpansionType(getCanonicalType(Pattern), NumExpansions); + + // Find the insert position again, in case we inserted an element into + // PackExpansionTypes and invalidated our insert position. + PackExpansionTypes.FindNodeOrInsertPos(ID, InsertPos); + } } T = new (*this) PackExpansionType(Pattern, Canon, NumExpansions); @@ -2950,7 +3177,7 @@ QualType ASTContext::getDecltypeType(Expr *e, QualType UnderlyingType) const { if (Canon) { // We already have a "canonical" version of an equivalent, dependent // decltype type. Use that as our canonical type. - dt = new (*this, TypeAlignment) DecltypeType(e, DependentTy, + dt = new (*this, TypeAlignment) DecltypeType(e, UnderlyingType, QualType((DecltypeType*)Canon, 0)); } else { // Build a new, canonical typeof(expr) type. @@ -3331,8 +3558,7 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const { Arg.getNumTemplateExpansions()); case TemplateArgument::Integral: - return TemplateArgument(*Arg.getAsIntegral(), - getCanonicalType(Arg.getIntegralType())); + return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType())); case TemplateArgument::Type: return TemplateArgument(getCanonicalType(Arg.getAsType())); @@ -3471,7 +3697,7 @@ const ArrayType *ASTContext::getAsArrayType(QualType T) const { VAT->getBracketsRange())); } -QualType ASTContext::getAdjustedParameterType(QualType T) { +QualType ASTContext::getAdjustedParameterType(QualType T) const { // C99 6.7.5.3p7: // A declaration of a parameter as "array of type" shall be // adjusted to "qualified pointer to type", where the type @@ -3490,7 +3716,7 @@ QualType ASTContext::getAdjustedParameterType(QualType T) { return T; } -QualType ASTContext::getSignatureParameterType(QualType T) { +QualType ASTContext::getSignatureParameterType(QualType T) const { T = getVariableArrayDecayedType(T); T = getAdjustedParameterType(T); return T.getUnqualifiedType(); @@ -3809,7 +4035,7 @@ QualType ASTContext::getCFConstantStringType() const { FieldTypes[i], /*TInfo=*/0, /*BitWidth=*/0, /*Mutable=*/false, - /*HasInit=*/false); + ICIS_NoInit); Field->setAccess(AS_public); CFConstantStringTypeDecl->addDecl(Field); } @@ -3853,7 +4079,7 @@ QualType ASTContext::getBlockDescriptorType() const { FieldTypes[i], /*TInfo=*/0, /*BitWidth=*/0, /*Mutable=*/false, - /*HasInit=*/false); + ICIS_NoInit); Field->setAccess(AS_public); T->addDecl(Field); } @@ -3896,7 +4122,7 @@ QualType ASTContext::getBlockDescriptorExtendedType() const { FieldTypes[i], /*TInfo=*/0, /*BitWidth=*/0, /*Mutable=*/false, - /*HasInit=*/false); + ICIS_NoInit); Field->setAccess(AS_public); T->addDecl(Field); } @@ -3972,7 +4198,7 @@ ASTContext::BuildByRefType(StringRef DeclName, QualType Ty) const { &Idents.get(FieldNames[i]), FieldTypes[i], /*TInfo=*/0, /*BitWidth=*/0, /*Mutable=*/false, - /*HasInit=*/false); + ICIS_NoInit); Field->setAccess(AS_public); T->addDecl(Field); } @@ -4045,6 +4271,8 @@ std::string ASTContext::getObjCEncodingForBlock(const BlockExpr *Expr) const { E = Decl->param_end(); PI != E; ++PI) { QualType PType = (*PI)->getType(); CharUnits sz = getObjCEncodingTypeSize(PType); + if (sz.isZero()) + continue; assert (sz.isPositive() && "BlockExpr - Incomplete param type"); ParmOffset += sz; } @@ -4086,8 +4314,8 @@ bool ASTContext::getObjCEncodingForFunctionDecl(const FunctionDecl *Decl, QualType PType = (*PI)->getType(); CharUnits sz = getObjCEncodingTypeSize(PType); if (sz.isZero()) - return true; - + continue; + assert (sz.isPositive() && "getObjCEncodingForFunctionDecl - Incomplete param type"); ParmOffset += sz; @@ -4155,8 +4383,8 @@ bool ASTContext::getObjCEncodingForMethodDecl(const ObjCMethodDecl *Decl, QualType PType = (*PI)->getType(); CharUnits sz = getObjCEncodingTypeSize(PType); if (sz.isZero()) - return true; - + continue; + assert (sz.isPositive() && "getObjCEncodingForMethodDecl - Incomplete param type"); ParmOffset += sz; @@ -4387,7 +4615,7 @@ static void EncodeBitField(const ASTContext *Ctx, std::string& S, // information is not especially sensible, but we're stuck with it for // compatibility with GCC, although providing it breaks anything that // actually uses runtime introspection and wants to work on both runtimes... - if (!Ctx->getLangOpts().NeXTRuntime) { + if (Ctx->getLangOpts().ObjCRuntime.isGNUFamily()) { const RecordDecl *RD = FD->getParent(); const ASTRecordLayout &RL = Ctx->getASTRecordLayout(RD); S += llvm::utostr(RL.getFieldOffset(FD->getFieldIndex())); @@ -4563,7 +4791,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string& S, // Special case bit-fields. if (Field->isBitField()) { getObjCEncodingForTypeImpl(Field->getType(), S, false, true, - (*Field)); + *Field); } else { QualType qt = Field->getType(); getLegacyIntegralTypeEncoding(qt); @@ -4746,7 +4974,7 @@ void ASTContext::getObjCEncodingForStructureImpl(RecordDecl *RDecl, CXXRecordDecl *base = BI->getType()->getAsCXXRecordDecl(); if (base->isEmpty()) continue; - uint64_t offs = layout.getBaseClassOffsetInBits(base); + uint64_t offs = toBits(layout.getBaseClassOffset(base)); FieldOrBaseOffsets.insert(FieldOrBaseOffsets.upper_bound(offs), std::make_pair(offs, base)); } @@ -4769,7 +4997,7 @@ void ASTContext::getObjCEncodingForStructureImpl(RecordDecl *RDecl, CXXRecordDecl *base = BI->getType()->getAsCXXRecordDecl(); if (base->isEmpty()) continue; - uint64_t offs = layout.getVBaseClassOffsetInBits(base); + uint64_t offs = toBits(layout.getVBaseClassOffset(base)); if (FieldOrBaseOffsets.find(offs) == FieldOrBaseOffsets.end()) FieldOrBaseOffsets.insert(FieldOrBaseOffsets.end(), std::make_pair(offs, base)); @@ -4787,11 +5015,8 @@ void ASTContext::getObjCEncodingForStructureImpl(RecordDecl *RDecl, std::multimap<uint64_t, NamedDecl *>::iterator CurLayObj = FieldOrBaseOffsets.begin(); - if ((CurLayObj != FieldOrBaseOffsets.end() && CurLayObj->first != 0) || - (CurLayObj == FieldOrBaseOffsets.end() && - CXXRec && CXXRec->isDynamicClass())) { - assert(CXXRec && CXXRec->isDynamicClass() && - "Offset 0 was empty but no VTable ?"); + if (CXXRec && CXXRec->isDynamicClass() && + (CurLayObj == FieldOrBaseOffsets.end() || CurLayObj->first != 0)) { if (FD) { S += "\"_vptr$"; std::string recname = CXXRec->getNameAsString(); @@ -4877,12 +5102,6 @@ void ASTContext::getObjCEncodingForTypeQualifier(Decl::ObjCDeclQualifier QT, S += 'V'; } -void ASTContext::setBuiltinVaListType(QualType T) { - assert(BuiltinVaListType.isNull() && "__builtin_va_list type already set!"); - - BuiltinVaListType = T; -} - TypedefDecl *ASTContext::getObjCIdDecl() const { if (!ObjCIdDecl) { QualType T = getObjCObjectType(ObjCBuiltinIdTy, 0, 0); @@ -4936,6 +5155,241 @@ ObjCInterfaceDecl *ASTContext::getObjCProtocolDecl() const { return ObjCProtocolClassDecl; } +//===----------------------------------------------------------------------===// +// __builtin_va_list Construction Functions +//===----------------------------------------------------------------------===// + +static TypedefDecl *CreateCharPtrBuiltinVaListDecl(const ASTContext *Context) { + // typedef char* __builtin_va_list; + QualType CharPtrType = Context->getPointerType(Context->CharTy); + TypeSourceInfo *TInfo + = Context->getTrivialTypeSourceInfo(CharPtrType); + + TypedefDecl *VaListTypeDecl + = TypedefDecl::Create(const_cast<ASTContext &>(*Context), + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get("__builtin_va_list"), + TInfo); + return VaListTypeDecl; +} + +static TypedefDecl *CreateVoidPtrBuiltinVaListDecl(const ASTContext *Context) { + // typedef void* __builtin_va_list; + QualType VoidPtrType = Context->getPointerType(Context->VoidTy); + TypeSourceInfo *TInfo + = Context->getTrivialTypeSourceInfo(VoidPtrType); + + TypedefDecl *VaListTypeDecl + = TypedefDecl::Create(const_cast<ASTContext &>(*Context), + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get("__builtin_va_list"), + TInfo); + return VaListTypeDecl; +} + +static TypedefDecl *CreatePowerABIBuiltinVaListDecl(const ASTContext *Context) { + // typedef struct __va_list_tag { + RecordDecl *VaListTagDecl; + + VaListTagDecl = CreateRecordDecl(*Context, TTK_Struct, + Context->getTranslationUnitDecl(), + &Context->Idents.get("__va_list_tag")); + VaListTagDecl->startDefinition(); + + const size_t NumFields = 5; + QualType FieldTypes[NumFields]; + const char *FieldNames[NumFields]; + + // unsigned char gpr; + FieldTypes[0] = Context->UnsignedCharTy; + FieldNames[0] = "gpr"; + + // unsigned char fpr; + FieldTypes[1] = Context->UnsignedCharTy; + FieldNames[1] = "fpr"; + + // unsigned short reserved; + FieldTypes[2] = Context->UnsignedShortTy; + FieldNames[2] = "reserved"; + + // void* overflow_arg_area; + FieldTypes[3] = Context->getPointerType(Context->VoidTy); + FieldNames[3] = "overflow_arg_area"; + + // void* reg_save_area; + FieldTypes[4] = Context->getPointerType(Context->VoidTy); + FieldNames[4] = "reg_save_area"; + + // Create fields + for (unsigned i = 0; i < NumFields; ++i) { + FieldDecl *Field = FieldDecl::Create(*Context, VaListTagDecl, + SourceLocation(), + SourceLocation(), + &Context->Idents.get(FieldNames[i]), + FieldTypes[i], /*TInfo=*/0, + /*BitWidth=*/0, + /*Mutable=*/false, + ICIS_NoInit); + Field->setAccess(AS_public); + VaListTagDecl->addDecl(Field); + } + VaListTagDecl->completeDefinition(); + QualType VaListTagType = Context->getRecordType(VaListTagDecl); + Context->VaListTagTy = VaListTagType; + + // } __va_list_tag; + TypedefDecl *VaListTagTypedefDecl + = TypedefDecl::Create(const_cast<ASTContext &>(*Context), + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get("__va_list_tag"), + Context->getTrivialTypeSourceInfo(VaListTagType)); + QualType VaListTagTypedefType = + Context->getTypedefType(VaListTagTypedefDecl); + + // typedef __va_list_tag __builtin_va_list[1]; + llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 1); + QualType VaListTagArrayType + = Context->getConstantArrayType(VaListTagTypedefType, + Size, ArrayType::Normal, 0); + TypeSourceInfo *TInfo + = Context->getTrivialTypeSourceInfo(VaListTagArrayType); + TypedefDecl *VaListTypedefDecl + = TypedefDecl::Create(const_cast<ASTContext &>(*Context), + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get("__builtin_va_list"), + TInfo); + + return VaListTypedefDecl; +} + +static TypedefDecl * +CreateX86_64ABIBuiltinVaListDecl(const ASTContext *Context) { + // typedef struct __va_list_tag { + RecordDecl *VaListTagDecl; + VaListTagDecl = CreateRecordDecl(*Context, TTK_Struct, + Context->getTranslationUnitDecl(), + &Context->Idents.get("__va_list_tag")); + VaListTagDecl->startDefinition(); + + const size_t NumFields = 4; + QualType FieldTypes[NumFields]; + const char *FieldNames[NumFields]; + + // unsigned gp_offset; + FieldTypes[0] = Context->UnsignedIntTy; + FieldNames[0] = "gp_offset"; + + // unsigned fp_offset; + FieldTypes[1] = Context->UnsignedIntTy; + FieldNames[1] = "fp_offset"; + + // void* overflow_arg_area; + FieldTypes[2] = Context->getPointerType(Context->VoidTy); + FieldNames[2] = "overflow_arg_area"; + + // void* reg_save_area; + FieldTypes[3] = Context->getPointerType(Context->VoidTy); + FieldNames[3] = "reg_save_area"; + + // Create fields + for (unsigned i = 0; i < NumFields; ++i) { + FieldDecl *Field = FieldDecl::Create(const_cast<ASTContext &>(*Context), + VaListTagDecl, + SourceLocation(), + SourceLocation(), + &Context->Idents.get(FieldNames[i]), + FieldTypes[i], /*TInfo=*/0, + /*BitWidth=*/0, + /*Mutable=*/false, + ICIS_NoInit); + Field->setAccess(AS_public); + VaListTagDecl->addDecl(Field); + } + VaListTagDecl->completeDefinition(); + QualType VaListTagType = Context->getRecordType(VaListTagDecl); + Context->VaListTagTy = VaListTagType; + + // } __va_list_tag; + TypedefDecl *VaListTagTypedefDecl + = TypedefDecl::Create(const_cast<ASTContext &>(*Context), + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get("__va_list_tag"), + Context->getTrivialTypeSourceInfo(VaListTagType)); + QualType VaListTagTypedefType = + Context->getTypedefType(VaListTagTypedefDecl); + + // typedef __va_list_tag __builtin_va_list[1]; + llvm::APInt Size(Context->getTypeSize(Context->getSizeType()), 1); + QualType VaListTagArrayType + = Context->getConstantArrayType(VaListTagTypedefType, + Size, ArrayType::Normal,0); + TypeSourceInfo *TInfo + = Context->getTrivialTypeSourceInfo(VaListTagArrayType); + TypedefDecl *VaListTypedefDecl + = TypedefDecl::Create(const_cast<ASTContext &>(*Context), + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get("__builtin_va_list"), + TInfo); + + return VaListTypedefDecl; +} + +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); + TypedefDecl *VaListTypedefDecl + = TypedefDecl::Create(const_cast<ASTContext &>(*Context), + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get("__builtin_va_list"), + Context->getTrivialTypeSourceInfo(IntArrayType)); + + return VaListTypedefDecl; +} + +static TypedefDecl *CreateVaListDecl(const ASTContext *Context, + TargetInfo::BuiltinVaListKind Kind) { + switch (Kind) { + case TargetInfo::CharPtrBuiltinVaList: + return CreateCharPtrBuiltinVaListDecl(Context); + case TargetInfo::VoidPtrBuiltinVaList: + return CreateVoidPtrBuiltinVaListDecl(Context); + case TargetInfo::PowerABIBuiltinVaList: + return CreatePowerABIBuiltinVaListDecl(Context); + case TargetInfo::X86_64ABIBuiltinVaList: + return CreateX86_64ABIBuiltinVaListDecl(Context); + case TargetInfo::PNaClABIBuiltinVaList: + return CreatePNaClABIBuiltinVaListDecl(Context); + } + + llvm_unreachable("Unhandled __builtin_va_list type kind"); +} + +TypedefDecl *ASTContext::getBuiltinVaListDecl() const { + if (!BuiltinVaListDecl) + BuiltinVaListDecl = CreateVaListDecl(this, Target->getBuiltinVaListKind()); + + return BuiltinVaListDecl; +} + +QualType ASTContext::getVaListTagType() const { + // Force the creation of VaListTagTy by building the __builtin_va_list + // declaration. + if (VaListTagTy.isNull()) + (void) getBuiltinVaListDecl(); + + return VaListTagTy; +} + void ASTContext::setObjCConstantStringInterface(ObjCInterfaceDecl *Decl) { assert(ObjCConstantStringType.isNull() && "'NSConstantString' type already set!"); @@ -6412,6 +6866,19 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context, VectorType::GenericVector); break; } + case 'E': { + char *End; + + unsigned NumElements = strtoul(Str, &End, 10); + assert(End != Str && "Missing vector size"); + + Str = End; + + QualType ElementType = DecodeTypeFromStr(Str, Context, Error, RequiresICE, + false); + Type = Context.getExtVectorType(ElementType, NumElements); + break; + } case 'X': { QualType ElementType = DecodeTypeFromStr(Str, Context, Error, RequiresICE, false); @@ -6712,9 +7179,15 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { return true; } -CallingConv ASTContext::getDefaultMethodCallConv() { +CallingConv ASTContext::getDefaultCXXMethodCallConv(bool isVariadic) { // Pass through to the C++ ABI object - return ABI->getDefaultMethodCallConv(); + return ABI->getDefaultMethodCallConv(isVariadic); +} + +CallingConv ASTContext::getCanonicalCallConv(CallingConv CC) const { + if (CC == CC_C && !LangOpts.MRTD && getTargetInfo().getCXXABI() != CXXABI_Microsoft) + return CC_Default; + return CC; } bool ASTContext::isNearlyEmpty(const CXXRecordDecl *RD) const { diff --git a/lib/AST/ASTDiagnostic.cpp b/lib/AST/ASTDiagnostic.cpp index ca4fe268522f..35fcd41f26e5 100644 --- a/lib/AST/ASTDiagnostic.cpp +++ b/lib/AST/ASTDiagnostic.cpp @@ -14,7 +14,11 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/TemplateBase.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/Type.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -225,6 +229,11 @@ ConvertTypeToDiagnosticString(ASTContext &Context, QualType Ty, return S; } +static bool FormatTemplateTypeDiff(ASTContext &Context, QualType FromType, + QualType ToType, bool PrintTree, + bool PrintFromType, bool ElideType, + bool ShowColors, std::string &S); + void clang::FormatASTNodeDiagnosticArgument( DiagnosticsEngine::ArgumentKind Kind, intptr_t Val, @@ -244,6 +253,33 @@ void clang::FormatASTNodeDiagnosticArgument( switch (Kind) { default: llvm_unreachable("unknown ArgumentKind"); + case DiagnosticsEngine::ak_qualtype_pair: { + TemplateDiffTypes &TDT = *reinterpret_cast<TemplateDiffTypes*>(Val); + QualType FromType = + QualType::getFromOpaquePtr(reinterpret_cast<void*>(TDT.FromType)); + QualType ToType = + QualType::getFromOpaquePtr(reinterpret_cast<void*>(TDT.ToType)); + + if (FormatTemplateTypeDiff(Context, FromType, ToType, TDT.PrintTree, + TDT.PrintFromType, TDT.ElideType, + TDT.ShowColors, S)) { + NeedQuotes = !TDT.PrintTree; + TDT.TemplateDiffUsed = true; + break; + } + + // Don't fall-back during tree printing. The caller will handle + // this case. + if (TDT.PrintTree) + return; + + // Attempting to do a templete diff on non-templates. Set the variables + // and continue with regular type printing of the appropriate type. + Val = TDT.PrintFromType ? TDT.FromType : TDT.ToType; + ModLen = 0; + ArgLen = 0; + // Fall through + } case DiagnosticsEngine::ak_qualtype: { assert(ModLen == 0 && ArgLen == 0 && "Invalid modifier for QualType argument"); @@ -329,3 +365,901 @@ void clang::FormatASTNodeDiagnosticArgument( if (NeedQuotes) Output.push_back('\''); } + +/// TemplateDiff - A class that constructs a pretty string for a pair of +/// QualTypes. For the pair of types, a diff tree will be created containing +/// all the information about the templates and template arguments. Afterwards, +/// the tree is transformed to a string according to the options passed in. +namespace { +class TemplateDiff { + /// Context - The ASTContext which is used for comparing template arguments. + ASTContext &Context; + + /// Policy - Used during expression printing. + PrintingPolicy Policy; + + /// ElideType - Option to elide identical types. + bool ElideType; + + /// PrintTree - Format output string as a tree. + bool PrintTree; + + /// ShowColor - Diagnostics support color, so bolding will be used. + bool ShowColor; + + /// FromType - When single type printing is selected, this is the type to be + /// be printed. When tree printing is selected, this type will show up first + /// in the tree. + QualType FromType; + + /// ToType - The type that FromType is compared to. Only in tree printing + /// will this type be outputed. + QualType ToType; + + /// Str - Storage for the output stream. + llvm::SmallString<128> Str; + + /// OS - The stream used to construct the output strings. + llvm::raw_svector_ostream OS; + + /// IsBold - Keeps track of the bold formatting for the output string. + bool IsBold; + + /// DiffTree - A tree representation the differences between two types. + class DiffTree { + /// DiffNode - The root node stores the original type. Each child node + /// stores template arguments of their parents. For templated types, the + /// template decl is also stored. + struct DiffNode { + /// NextNode - The index of the next sibling node or 0. + unsigned NextNode; + + /// ChildNode - The index of the first child node or 0. + unsigned ChildNode; + + /// ParentNode - The index of the parent node. + unsigned ParentNode; + + /// FromType, ToType - The type arguments. + QualType FromType, ToType; + + /// FromExpr, ToExpr - The expression arguments. + Expr *FromExpr, *ToExpr; + + /// FromTD, ToTD - The template decl for template template + /// arguments or the type arguments that are templates. + TemplateDecl *FromTD, *ToTD; + + /// FromDefault, ToDefault - Whether the argument is a default argument. + bool FromDefault, ToDefault; + + /// Same - Whether the two arguments evaluate to the same value. + bool Same; + + DiffNode(unsigned ParentNode = 0) + : NextNode(0), ChildNode(0), ParentNode(ParentNode), + FromType(), ToType(), FromExpr(0), ToExpr(0), FromTD(0), ToTD(0), + FromDefault(false), ToDefault(false), Same(false) { } + }; + + /// FlatTree - A flattened tree used to store the DiffNodes. + llvm::SmallVector<DiffNode, 16> FlatTree; + + /// CurrentNode - The index of the current node being used. + unsigned CurrentNode; + + /// NextFreeNode - The index of the next unused node. Used when creating + /// child nodes. + unsigned NextFreeNode; + + /// ReadNode - The index of the current node being read. + unsigned ReadNode; + + public: + DiffTree() : + CurrentNode(0), NextFreeNode(1) { + FlatTree.push_back(DiffNode()); + } + + // Node writing functions. + /// SetNode - Sets FromTD and ToTD of the current node. + void SetNode(TemplateDecl *FromTD, TemplateDecl *ToTD) { + FlatTree[CurrentNode].FromTD = FromTD; + FlatTree[CurrentNode].ToTD = ToTD; + } + + /// SetNode - Sets FromType and ToType of the current node. + void SetNode(QualType FromType, QualType ToType) { + FlatTree[CurrentNode].FromType = FromType; + FlatTree[CurrentNode].ToType = ToType; + } + + /// SetNode - Set FromExpr and ToExpr of the current node. + void SetNode(Expr *FromExpr, Expr *ToExpr) { + FlatTree[CurrentNode].FromExpr = FromExpr; + FlatTree[CurrentNode].ToExpr = ToExpr; + } + + /// SetSame - Sets the same flag of the current node. + void SetSame(bool Same) { + FlatTree[CurrentNode].Same = Same; + } + + /// SetDefault - Sets FromDefault and ToDefault flags of the current node. + void SetDefault(bool FromDefault, bool ToDefault) { + FlatTree[CurrentNode].FromDefault = FromDefault; + FlatTree[CurrentNode].ToDefault = ToDefault; + } + + /// Up - Changes the node to the parent of the current node. + void Up() { + CurrentNode = FlatTree[CurrentNode].ParentNode; + } + + /// AddNode - Adds a child node to the current node, then sets that node + /// node as the current node. + void AddNode() { + FlatTree.push_back(DiffNode(CurrentNode)); + DiffNode &Node = FlatTree[CurrentNode]; + if (Node.ChildNode == 0) { + // If a child node doesn't exist, add one. + Node.ChildNode = NextFreeNode; + } else { + // If a child node exists, find the last child node and add a + // next node to it. + unsigned i; + for (i = Node.ChildNode; FlatTree[i].NextNode != 0; + i = FlatTree[i].NextNode) { + } + FlatTree[i].NextNode = NextFreeNode; + } + CurrentNode = NextFreeNode; + ++NextFreeNode; + } + + // Node reading functions. + /// StartTraverse - Prepares the tree for recursive traversal. + void StartTraverse() { + ReadNode = 0; + CurrentNode = NextFreeNode; + NextFreeNode = 0; + } + + /// Parent - Move the current read node to its parent. + void Parent() { + ReadNode = FlatTree[ReadNode].ParentNode; + } + + /// NodeIsTemplate - Returns true if a template decl is set, and types are + /// set. + bool NodeIsTemplate() { + return (FlatTree[ReadNode].FromTD && + !FlatTree[ReadNode].ToType.isNull()) || + (FlatTree[ReadNode].ToTD && !FlatTree[ReadNode].ToType.isNull()); + } + + /// NodeIsQualType - Returns true if a Qualtype is set. + bool NodeIsQualType() { + return !FlatTree[ReadNode].FromType.isNull() || + !FlatTree[ReadNode].ToType.isNull(); + } + + /// NodeIsExpr - Returns true if an expr is set. + bool NodeIsExpr() { + return FlatTree[ReadNode].FromExpr || FlatTree[ReadNode].ToExpr; + } + + /// NodeIsTemplateTemplate - Returns true if the argument is a template + /// template type. + bool NodeIsTemplateTemplate() { + return FlatTree[ReadNode].FromType.isNull() && + FlatTree[ReadNode].ToType.isNull() && + (FlatTree[ReadNode].FromTD || FlatTree[ReadNode].ToTD); + } + + /// GetNode - Gets the FromType and ToType. + void GetNode(QualType &FromType, QualType &ToType) { + FromType = FlatTree[ReadNode].FromType; + ToType = FlatTree[ReadNode].ToType; + } + + /// GetNode - Gets the FromExpr and ToExpr. + void GetNode(Expr *&FromExpr, Expr *&ToExpr) { + FromExpr = FlatTree[ReadNode].FromExpr; + ToExpr = FlatTree[ReadNode].ToExpr; + } + + /// GetNode - Gets the FromTD and ToTD. + void GetNode(TemplateDecl *&FromTD, TemplateDecl *&ToTD) { + FromTD = FlatTree[ReadNode].FromTD; + ToTD = FlatTree[ReadNode].ToTD; + } + + /// NodeIsSame - Returns true the arguments are the same. + bool NodeIsSame() { + return FlatTree[ReadNode].Same; + } + + /// HasChildrend - Returns true if the node has children. + bool HasChildren() { + return FlatTree[ReadNode].ChildNode != 0; + } + + /// MoveToChild - Moves from the current node to its child. + void MoveToChild() { + ReadNode = FlatTree[ReadNode].ChildNode; + } + + /// AdvanceSibling - If there is a next sibling, advance to it and return + /// true. Otherwise, return false. + bool AdvanceSibling() { + if (FlatTree[ReadNode].NextNode == 0) + return false; + + ReadNode = FlatTree[ReadNode].NextNode; + return true; + } + + /// HasNextSibling - Return true if the node has a next sibling. + bool HasNextSibling() { + return FlatTree[ReadNode].NextNode != 0; + } + + /// FromDefault - Return true if the from argument is the default. + bool FromDefault() { + return FlatTree[ReadNode].FromDefault; + } + + /// ToDefault - Return true if the to argument is the default. + bool ToDefault() { + return FlatTree[ReadNode].ToDefault; + } + + /// Empty - Returns true if the tree has no information. + bool Empty() { + return !FlatTree[0].FromTD && !FlatTree[0].ToTD && + !FlatTree[0].FromExpr && !FlatTree[0].ToExpr && + FlatTree[0].FromType.isNull() && FlatTree[0].ToType.isNull(); + } + }; + + DiffTree Tree; + + /// TSTiterator - an iterator that is used to enter a + /// TemplateSpecializationType and read TemplateArguments inside template + /// parameter packs in order with the rest of the TemplateArguments. + struct TSTiterator { + typedef const TemplateArgument& reference; + typedef const TemplateArgument* pointer; + + /// TST - the template specialization whose arguments this iterator + /// traverse over. + const TemplateSpecializationType *TST; + + /// Index - the index of the template argument in TST. + unsigned Index; + + /// CurrentTA - if CurrentTA is not the same as EndTA, then CurrentTA + /// points to a TemplateArgument within a parameter pack. + TemplateArgument::pack_iterator CurrentTA; + + /// EndTA - the end iterator of a parameter pack + TemplateArgument::pack_iterator EndTA; + + /// TSTiterator - Constructs an iterator and sets it to the first template + /// argument. + TSTiterator(const TemplateSpecializationType *TST) + : TST(TST), Index(0), CurrentTA(0), EndTA(0) { + if (isEnd()) return; + + // Set to first template argument. If not a parameter pack, done. + TemplateArgument TA = TST->getArg(0); + if (TA.getKind() != TemplateArgument::Pack) return; + + // Start looking into the parameter pack. + CurrentTA = TA.pack_begin(); + EndTA = TA.pack_end(); + + // Found a valid template argument. + if (CurrentTA != EndTA) return; + + // Parameter pack is empty, use the increment to get to a valid + // template argument. + ++(*this); + } + + /// isEnd - Returns true if the iterator is one past the end. + bool isEnd() const { + return Index == TST->getNumArgs(); + } + + /// &operator++ - Increment the iterator to the next template argument. + TSTiterator &operator++() { + assert(!isEnd() && "Iterator incremented past end of arguments."); + + // If in a parameter pack, advance in the parameter pack. + if (CurrentTA != EndTA) { + ++CurrentTA; + if (CurrentTA != EndTA) + return *this; + } + + // Loop until a template argument is found, or the end is reached. + while (true) { + // Advance to the next template argument. Break if reached the end. + if (++Index == TST->getNumArgs()) break; + + // If the TemplateArgument is not a parameter pack, done. + TemplateArgument TA = TST->getArg(Index); + if (TA.getKind() != TemplateArgument::Pack) break; + + // Handle parameter packs. + CurrentTA = TA.pack_begin(); + EndTA = TA.pack_end(); + + // If the parameter pack is empty, try to advance again. + if (CurrentTA != EndTA) break; + } + return *this; + } + + /// operator* - Returns the appropriate TemplateArgument. + reference operator*() const { + assert(!isEnd() && "Index exceeds number of arguments."); + if (CurrentTA == EndTA) + return TST->getArg(Index); + else + return *CurrentTA; + } + + /// operator-> - Allow access to the underlying TemplateArgument. + pointer operator->() const { + return &operator*(); + } + }; + + // These functions build up the template diff tree, including functions to + // retrieve and compare template arguments. + + static const TemplateSpecializationType * GetTemplateSpecializationType( + ASTContext &Context, QualType Ty) { + if (const TemplateSpecializationType *TST = + Ty->getAs<TemplateSpecializationType>()) + return TST; + + const RecordType *RT = Ty->getAs<RecordType>(); + + if (!RT) + return 0; + + const ClassTemplateSpecializationDecl *CTSD = + dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl()); + + if (!CTSD) + return 0; + + Ty = Context.getTemplateSpecializationType( + TemplateName(CTSD->getSpecializedTemplate()), + CTSD->getTemplateArgs().data(), + CTSD->getTemplateArgs().size(), + Ty.getCanonicalType()); + + return Ty->getAs<TemplateSpecializationType>(); + } + + /// DiffTemplate - recursively visits template arguments and stores the + /// argument info into a tree. + void DiffTemplate(const TemplateSpecializationType *FromTST, + const TemplateSpecializationType *ToTST) { + // Begin descent into diffing template tree. + TemplateParameterList *Params = + FromTST->getTemplateName().getAsTemplateDecl()->getTemplateParameters(); + unsigned TotalArgs = 0; + for (TSTiterator FromIter(FromTST), ToIter(ToTST); + !FromIter.isEnd() || !ToIter.isEnd(); ++TotalArgs) { + Tree.AddNode(); + + // Get the parameter at index TotalArgs. If index is larger + // than the total number of parameters, then there is an + // argument pack, so re-use the last parameter. + NamedDecl *ParamND = Params->getParam( + (TotalArgs < Params->size()) ? TotalArgs + : Params->size() - 1); + // Handle Types + if (TemplateTypeParmDecl *DefaultTTPD = + dyn_cast<TemplateTypeParmDecl>(ParamND)) { + QualType FromType, ToType; + GetType(FromIter, DefaultTTPD, FromType); + GetType(ToIter, DefaultTTPD, ToType); + Tree.SetNode(FromType, ToType); + Tree.SetDefault(FromIter.isEnd() && !FromType.isNull(), + ToIter.isEnd() && !ToType.isNull()); + if (!FromType.isNull() && !ToType.isNull()) { + if (Context.hasSameType(FromType, ToType)) { + Tree.SetSame(true); + } else { + const TemplateSpecializationType *FromArgTST = + GetTemplateSpecializationType(Context, FromType); + const TemplateSpecializationType *ToArgTST = + GetTemplateSpecializationType(Context, ToType); + + if (FromArgTST && ToArgTST) { + bool SameTemplate = hasSameTemplate(FromArgTST, ToArgTST); + if (SameTemplate) { + Tree.SetNode(FromArgTST->getTemplateName().getAsTemplateDecl(), + ToArgTST->getTemplateName().getAsTemplateDecl()); + DiffTemplate(FromArgTST, ToArgTST); + } + } + } + } + } + + // Handle Expressions + if (NonTypeTemplateParmDecl *DefaultNTTPD = + dyn_cast<NonTypeTemplateParmDecl>(ParamND)) { + Expr *FromExpr, *ToExpr; + GetExpr(FromIter, DefaultNTTPD, FromExpr); + GetExpr(ToIter, DefaultNTTPD, ToExpr); + Tree.SetNode(FromExpr, ToExpr); + Tree.SetSame(IsEqualExpr(Context, FromExpr, ToExpr)); + Tree.SetDefault(FromIter.isEnd() && FromExpr, + ToIter.isEnd() && ToExpr); + } + + // Handle Templates + if (TemplateTemplateParmDecl *DefaultTTPD = + dyn_cast<TemplateTemplateParmDecl>(ParamND)) { + TemplateDecl *FromDecl, *ToDecl; + GetTemplateDecl(FromIter, DefaultTTPD, FromDecl); + GetTemplateDecl(ToIter, DefaultTTPD, ToDecl); + Tree.SetNode(FromDecl, ToDecl); + Tree.SetSame(FromDecl && ToDecl && + FromDecl->getIdentifier() == ToDecl->getIdentifier()); + } + + if (!FromIter.isEnd()) ++FromIter; + if (!ToIter.isEnd()) ++ToIter; + Tree.Up(); + } + } + + /// hasSameTemplate - Returns true if both types are specialized from the + /// same template declaration. If they come from different template aliases, + /// do a parallel ascension search to determine the highest template alias in + /// common and set the arguments to them. + static bool hasSameTemplate(const TemplateSpecializationType *&FromTST, + const TemplateSpecializationType *&ToTST) { + // Check the top templates if they are the same. + if (FromTST->getTemplateName().getAsTemplateDecl()->getIdentifier() == + ToTST->getTemplateName().getAsTemplateDecl()->getIdentifier()) + return true; + + // Create vectors of template aliases. + SmallVector<const TemplateSpecializationType*, 1> FromTemplateList, + ToTemplateList; + + const TemplateSpecializationType *TempToTST = ToTST, *TempFromTST = FromTST; + FromTemplateList.push_back(FromTST); + ToTemplateList.push_back(ToTST); + + // Dump every template alias into the vectors. + while (TempFromTST->isTypeAlias()) { + TempFromTST = + TempFromTST->getAliasedType()->getAs<TemplateSpecializationType>(); + if (!TempFromTST) + break; + FromTemplateList.push_back(TempFromTST); + } + while (TempToTST->isTypeAlias()) { + TempToTST = + TempToTST->getAliasedType()->getAs<TemplateSpecializationType>(); + if (!TempToTST) + break; + ToTemplateList.push_back(TempToTST); + } + + SmallVector<const TemplateSpecializationType*, 1>::reverse_iterator + FromIter = FromTemplateList.rbegin(), FromEnd = FromTemplateList.rend(), + ToIter = ToTemplateList.rbegin(), ToEnd = ToTemplateList.rend(); + + // Check if the lowest template types are the same. If not, return. + if ((*FromIter)->getTemplateName().getAsTemplateDecl()->getIdentifier() != + (*ToIter)->getTemplateName().getAsTemplateDecl()->getIdentifier()) + return false; + + // Begin searching up the template aliases. The bottom most template + // matches so move up until one pair does not match. Use the template + // right before that one. + for (; FromIter != FromEnd && ToIter != ToEnd; ++FromIter, ++ToIter) { + if ((*FromIter)->getTemplateName().getAsTemplateDecl()->getIdentifier() != + (*ToIter)->getTemplateName().getAsTemplateDecl()->getIdentifier()) + break; + } + + FromTST = FromIter[-1]; + ToTST = ToIter[-1]; + + return true; + } + + /// GetType - Retrieves the template type arguments, including default + /// arguments. + void GetType(const TSTiterator &Iter, TemplateTypeParmDecl *DefaultTTPD, + QualType &ArgType) { + ArgType = QualType(); + bool isVariadic = DefaultTTPD->isParameterPack(); + + if (!Iter.isEnd()) + ArgType = Iter->getAsType(); + else if (!isVariadic) + ArgType = DefaultTTPD->getDefaultArgument(); + } + + /// GetExpr - Retrieves the template expression argument, including default + /// arguments. + void GetExpr(const TSTiterator &Iter, NonTypeTemplateParmDecl *DefaultNTTPD, + Expr *&ArgExpr) { + ArgExpr = 0; + bool isVariadic = DefaultNTTPD->isParameterPack(); + + if (!Iter.isEnd()) + ArgExpr = Iter->getAsExpr(); + else if (!isVariadic) + ArgExpr = DefaultNTTPD->getDefaultArgument(); + + if (ArgExpr) + while (SubstNonTypeTemplateParmExpr *SNTTPE = + dyn_cast<SubstNonTypeTemplateParmExpr>(ArgExpr)) + ArgExpr = SNTTPE->getReplacement(); + } + + /// GetTemplateDecl - Retrieves the template template arguments, including + /// default arguments. + void GetTemplateDecl(const TSTiterator &Iter, + TemplateTemplateParmDecl *DefaultTTPD, + TemplateDecl *&ArgDecl) { + ArgDecl = 0; + bool isVariadic = DefaultTTPD->isParameterPack(); + + TemplateArgument TA = DefaultTTPD->getDefaultArgument().getArgument(); + TemplateDecl *DefaultTD = TA.getAsTemplate().getAsTemplateDecl(); + + if (!Iter.isEnd()) + ArgDecl = Iter->getAsTemplate().getAsTemplateDecl(); + else if (!isVariadic) + ArgDecl = DefaultTD; + } + + /// IsEqualExpr - Returns true if the expressions evaluate to the same value. + static bool IsEqualExpr(ASTContext &Context, Expr *FromExpr, Expr *ToExpr) { + if (FromExpr == ToExpr) + return true; + + if (!FromExpr || !ToExpr) + return false; + + FromExpr = FromExpr->IgnoreParens(); + ToExpr = ToExpr->IgnoreParens(); + + DeclRefExpr *FromDRE = dyn_cast<DeclRefExpr>(FromExpr), + *ToDRE = dyn_cast<DeclRefExpr>(ToExpr); + + if (FromDRE || ToDRE) { + if (!FromDRE || !ToDRE) + return false; + return FromDRE->getDecl() == ToDRE->getDecl(); + } + + Expr::EvalResult FromResult, ToResult; + if (!FromExpr->EvaluateAsRValue(FromResult, Context) || + !ToExpr->EvaluateAsRValue(ToResult, Context)) + assert(0 && "Template arguments must be known at compile time."); + + APValue &FromVal = FromResult.Val; + APValue &ToVal = ToResult.Val; + + if (FromVal.getKind() != ToVal.getKind()) return false; + + switch (FromVal.getKind()) { + case APValue::Int: + return FromVal.getInt() == ToVal.getInt(); + case APValue::LValue: { + APValue::LValueBase FromBase = FromVal.getLValueBase(); + APValue::LValueBase ToBase = ToVal.getLValueBase(); + if (FromBase.isNull() && ToBase.isNull()) + return true; + if (FromBase.isNull() || ToBase.isNull()) + return false; + return FromBase.get<const ValueDecl*>() == + ToBase.get<const ValueDecl*>(); + } + case APValue::MemberPointer: + return FromVal.getMemberPointerDecl() == ToVal.getMemberPointerDecl(); + default: + llvm_unreachable("Unknown template argument expression."); + } + } + + // These functions converts the tree representation of the template + // differences into the internal character vector. + + /// TreeToString - Converts the Tree object into a character stream which + /// will later be turned into the output string. + void TreeToString(int Indent = 1) { + if (PrintTree) { + OS << '\n'; + for (int i = 0; i < Indent; ++i) + OS << " "; + ++Indent; + } + + // Handle cases where the difference is not templates with different + // arguments. + if (!Tree.NodeIsTemplate()) { + if (Tree.NodeIsQualType()) { + QualType FromType, ToType; + Tree.GetNode(FromType, ToType); + PrintTypeNames(FromType, ToType, Tree.FromDefault(), Tree.ToDefault(), + Tree.NodeIsSame()); + return; + } + if (Tree.NodeIsExpr()) { + Expr *FromExpr, *ToExpr; + Tree.GetNode(FromExpr, ToExpr); + PrintExpr(FromExpr, ToExpr, Tree.FromDefault(), Tree.ToDefault(), + Tree.NodeIsSame()); + return; + } + if (Tree.NodeIsTemplateTemplate()) { + TemplateDecl *FromTD, *ToTD; + Tree.GetNode(FromTD, ToTD); + PrintTemplateTemplate(FromTD, ToTD, Tree.FromDefault(), + Tree.ToDefault(), Tree.NodeIsSame()); + return; + } + llvm_unreachable("Unable to deduce template difference."); + } + + // Node is root of template. Recurse on children. + TemplateDecl *FromTD, *ToTD; + Tree.GetNode(FromTD, ToTD); + + assert(Tree.HasChildren() && "Template difference not found in diff tree."); + + OS << FromTD->getNameAsString() << '<'; + Tree.MoveToChild(); + unsigned NumElideArgs = 0; + do { + if (ElideType) { + if (Tree.NodeIsSame()) { + ++NumElideArgs; + continue; + } + if (NumElideArgs > 0) { + PrintElideArgs(NumElideArgs, Indent); + NumElideArgs = 0; + OS << ", "; + } + } + TreeToString(Indent); + if (Tree.HasNextSibling()) + OS << ", "; + } while (Tree.AdvanceSibling()); + if (NumElideArgs > 0) + PrintElideArgs(NumElideArgs, Indent); + + Tree.Parent(); + OS << ">"; + } + + // To signal to the text printer that a certain text needs to be bolded, + // a special character is injected into the character stream which the + // text printer will later strip out. + + /// Bold - Start bolding text. + void Bold() { + assert(!IsBold && "Attempting to bold text that is already bold."); + IsBold = true; + if (ShowColor) + OS << ToggleHighlight; + } + + /// Unbold - Stop bolding text. + void Unbold() { + assert(IsBold && "Attempting to remove bold from unbold text."); + IsBold = false; + if (ShowColor) + OS << ToggleHighlight; + } + + // Functions to print out the arguments and highlighting the difference. + + /// PrintTypeNames - prints the typenames, bolding differences. Will detect + /// typenames that are the same and attempt to disambiguate them by using + /// canonical typenames. + void PrintTypeNames(QualType FromType, QualType ToType, + bool FromDefault, bool ToDefault, bool Same) { + assert((!FromType.isNull() || !ToType.isNull()) && + "Only one template argument may be missing."); + + if (Same) { + OS << FromType.getAsString(); + return; + } + + std::string FromTypeStr = FromType.isNull() ? "(no argument)" + : FromType.getAsString(); + std::string ToTypeStr = ToType.isNull() ? "(no argument)" + : ToType.getAsString(); + // Switch to canonical typename if it is better. + // TODO: merge this with other aka printing above. + if (FromTypeStr == ToTypeStr) { + std::string FromCanTypeStr = FromType.getCanonicalType().getAsString(); + std::string ToCanTypeStr = ToType.getCanonicalType().getAsString(); + if (FromCanTypeStr != ToCanTypeStr) { + FromTypeStr = FromCanTypeStr; + ToTypeStr = ToCanTypeStr; + } + } + + if (PrintTree) OS << '['; + OS << (FromDefault ? "(default) " : ""); + Bold(); + OS << FromTypeStr; + Unbold(); + if (PrintTree) { + OS << " != " << (ToDefault ? "(default) " : ""); + Bold(); + OS << ToTypeStr; + Unbold(); + OS << "]"; + } + return; + } + + /// PrintExpr - Prints out the expr template arguments, highlighting argument + /// differences. + void PrintExpr(const Expr *FromExpr, const Expr *ToExpr, + bool FromDefault, bool ToDefault, bool Same) { + assert((FromExpr || ToExpr) && + "Only one template argument may be missing."); + if (Same) { + PrintExpr(FromExpr); + } else if (!PrintTree) { + OS << (FromDefault ? "(default) " : ""); + Bold(); + PrintExpr(FromExpr); + Unbold(); + } else { + OS << (FromDefault ? "[(default) " : "["); + Bold(); + PrintExpr(FromExpr); + Unbold(); + OS << " != " << (ToDefault ? "(default) " : ""); + Bold(); + PrintExpr(ToExpr); + Unbold(); + OS << ']'; + } + } + + /// PrintExpr - Actual formatting and printing of expressions. + void PrintExpr(const Expr *E) { + if (!E) + OS << "(no argument)"; + else + E->printPretty(OS, Context, 0, Policy); return; + } + + /// PrintTemplateTemplate - Handles printing of template template arguments, + /// highlighting argument differences. + void PrintTemplateTemplate(TemplateDecl *FromTD, TemplateDecl *ToTD, + bool FromDefault, bool ToDefault, bool Same) { + assert((FromTD || ToTD) && "Only one template argument may be missing."); + if (Same) { + OS << "template " << FromTD->getNameAsString(); + } else if (!PrintTree) { + OS << (FromDefault ? "(default) template " : "template "); + Bold(); + OS << (FromTD ? FromTD->getNameAsString() : "(no argument)"); + Unbold(); + } else { + OS << (FromDefault ? "[(default) template " : "[template "); + Bold(); + OS << (FromTD ? FromTD->getNameAsString() : "(no argument)"); + Unbold(); + OS << " != " << (ToDefault ? "(default) template " : "template "); + Bold(); + OS << (ToTD ? ToTD->getNameAsString() : "(no argument)"); + Unbold(); + OS << ']'; + } + } + + // Prints the appropriate placeholder for elided template arguments. + void PrintElideArgs(unsigned NumElideArgs, unsigned Indent) { + if (PrintTree) { + OS << '\n'; + for (unsigned i = 0; i < Indent; ++i) + OS << " "; + } + if (NumElideArgs == 0) return; + if (NumElideArgs == 1) + OS << "[...]"; + else + OS << "[" << NumElideArgs << " * ...]"; + } + +public: + + TemplateDiff(ASTContext &Context, QualType FromType, QualType ToType, + bool PrintTree, bool PrintFromType, bool ElideType, + bool ShowColor) + : Context(Context), + Policy(Context.getLangOpts()), + ElideType(ElideType), + PrintTree(PrintTree), + ShowColor(ShowColor), + // When printing a single type, the FromType is the one printed. + FromType(PrintFromType ? FromType : ToType), + ToType(PrintFromType ? ToType : FromType), + OS(Str), + IsBold(false) { + } + + /// DiffTemplate - Start the template type diffing. + void DiffTemplate() { + const TemplateSpecializationType *FromOrigTST = + GetTemplateSpecializationType(Context, FromType); + const TemplateSpecializationType *ToOrigTST = + GetTemplateSpecializationType(Context, ToType); + + // Only checking templates. + if (!FromOrigTST || !ToOrigTST) + return; + + // Different base templates. + if (!hasSameTemplate(FromOrigTST, ToOrigTST)) { + return; + } + + Tree.SetNode(FromType, ToType); + + // Same base template, but different arguments. + Tree.SetNode(FromOrigTST->getTemplateName().getAsTemplateDecl(), + ToOrigTST->getTemplateName().getAsTemplateDecl()); + + DiffTemplate(FromOrigTST, ToOrigTST); + } + + /// MakeString - When the two types given are templated types with the same + /// base template, a string representation of the type difference will be + /// loaded into S and return true. Otherwise, return false. + bool MakeString(std::string &S) { + Tree.StartTraverse(); + if (Tree.Empty()) + return false; + + TreeToString(); + assert(!IsBold && "Bold is applied to end of string."); + S = OS.str(); + return true; + } +}; // end class TemplateDiff +} // end namespace + +/// FormatTemplateTypeDiff - A helper static function to start the template +/// diff and return the properly formatted string. Returns true if the diff +/// is successful. +static bool FormatTemplateTypeDiff(ASTContext &Context, QualType FromType, + QualType ToType, bool PrintTree, + bool PrintFromType, bool ElideType, + bool ShowColors, std::string &S) { + if (PrintTree) + PrintFromType = true; + TemplateDiff TD(Context, FromType, ToType, PrintTree, PrintFromType, + ElideType, ShowColors); + TD.DiffTemplate(); + return TD.MakeString(S); +} diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 3879907ec6da..3e952acdcb42 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -119,7 +119,8 @@ namespace clang { bool ImportTemplateArguments(const TemplateArgument *FromArgs, unsigned NumFromArgs, SmallVectorImpl<TemplateArgument> &ToArgs); - bool IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord); + bool IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord, + bool Complain = true); bool IsStructuralMatch(EnumDecl *FromEnum, EnumDecl *ToRecord); bool IsStructuralMatch(ClassTemplateDecl *From, ClassTemplateDecl *To); Decl *VisitDecl(Decl *D); @@ -201,12 +202,16 @@ namespace { /// \brief Whether we're being strict about the spelling of types when /// unifying two types. bool StrictTypeSpelling; - + + /// \brief Whether to complain about failures. + bool Complain; + StructuralEquivalenceContext(ASTContext &C1, ASTContext &C2, llvm::DenseSet<std::pair<Decl *, Decl *> > &NonEquivalentDecls, - bool StrictTypeSpelling = false) + bool StrictTypeSpelling = false, + bool Complain = true) : C1(C1), C2(C2), NonEquivalentDecls(NonEquivalentDecls), - StrictTypeSpelling(StrictTypeSpelling) { } + StrictTypeSpelling(StrictTypeSpelling), Complain(Complain) { } /// \brief Determine whether the two declarations are structurally /// equivalent. @@ -223,10 +228,16 @@ namespace { public: DiagnosticBuilder Diag1(SourceLocation Loc, unsigned DiagID) { + if (!Complain) + return DiagnosticBuilder::getEmpty(); + return C1.getDiagnostics().Report(Loc, DiagID); } DiagnosticBuilder Diag2(SourceLocation Loc, unsigned DiagID) { + if (!Complain) + return DiagnosticBuilder::getEmpty(); + return C2.getDiagnostics().Report(Loc, DiagID); } }; @@ -237,45 +248,6 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, Decl *D1, Decl *D2); -/// \brief Determine if two APInts have the same value, after zero-extending -/// one of them (if needed!) to ensure that the bit-widths match. -static bool IsSameValue(const llvm::APInt &I1, const llvm::APInt &I2) { - if (I1.getBitWidth() == I2.getBitWidth()) - return I1 == I2; - - if (I1.getBitWidth() > I2.getBitWidth()) - return I1 == I2.zext(I1.getBitWidth()); - - return I1.zext(I2.getBitWidth()) == I2; -} - -/// \brief Determine if two APSInts have the same value, zero- or sign-extending -/// as needed. -static bool IsSameValue(const llvm::APSInt &I1, const llvm::APSInt &I2) { - if (I1.getBitWidth() == I2.getBitWidth() && I1.isSigned() == I2.isSigned()) - return I1 == I2; - - // Check for a bit-width mismatch. - if (I1.getBitWidth() > I2.getBitWidth()) - return IsSameValue(I1, I2.extend(I1.getBitWidth())); - else if (I2.getBitWidth() > I1.getBitWidth()) - return IsSameValue(I1.extend(I2.getBitWidth()), I2); - - // We have a signedness mismatch. Turn the signed value into an unsigned - // value. - if (I1.isSigned()) { - if (I1.isNegative()) - return false; - - return llvm::APSInt(I1, true) == I2; - } - - if (I2.isNegative()) - return false; - - return I1 == llvm::APSInt(I2, true); -} - /// \brief Determine structural equivalence of two expressions. static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, Expr *E1, Expr *E2) { @@ -322,7 +294,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, Arg2.getIntegralType())) return false; - return IsSameValue(*Arg1.getAsIntegral(), *Arg2.getAsIntegral()); + return llvm::APSInt::isSameValue(Arg1.getAsIntegral(), Arg2.getAsIntegral()); case TemplateArgument::Declaration: if (!Arg1.getAsDecl() || !Arg2.getAsDecl()) @@ -467,7 +439,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, case Type::ConstantArray: { const ConstantArrayType *Array1 = cast<ConstantArrayType>(T1); const ConstantArrayType *Array2 = cast<ConstantArrayType>(T2); - if (!IsSameValue(Array1->getSize(), Array2->getSize())) + if (!llvm::APInt::isSameValue(Array1->getSize(), Array2->getSize())) return false; if (!IsArrayStructurallyEquivalent(Context, Array1, Array2)) @@ -1002,9 +974,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, } // Check the fields for consistency. - CXXRecordDecl::field_iterator Field2 = D2->field_begin(), + RecordDecl::field_iterator Field2 = D2->field_begin(), Field2End = D2->field_end(); - for (CXXRecordDecl::field_iterator Field1 = D1->field_begin(), + for (RecordDecl::field_iterator Field1 = D1->field_begin(), Field1End = D1->field_end(); Field1 != Field1End; ++Field1, ++Field2) { @@ -1053,7 +1025,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, llvm::APSInt Val1 = EC1->getInitVal(); llvm::APSInt Val2 = EC2->getInitVal(); - if (!IsSameValue(Val1, Val2) || + if (!llvm::APSInt::isSameValue(Val1, Val2) || !IsStructurallyEquivalent(EC1->getIdentifier(), EC2->getIdentifier())) { Context.Diag2(D2->getLocation(), diag::warn_odr_tag_type_inconsistent) << Context.C2.getTypeDeclType(D2); @@ -1852,19 +1824,14 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To, ToData.HasPublicFields = FromData.HasPublicFields; ToData.HasMutableFields = FromData.HasMutableFields; ToData.HasOnlyCMembers = FromData.HasOnlyCMembers; + ToData.HasInClassInitializer = FromData.HasInClassInitializer; ToData.HasTrivialDefaultConstructor = FromData.HasTrivialDefaultConstructor; ToData.HasConstexprNonCopyMoveConstructor = FromData.HasConstexprNonCopyMoveConstructor; ToData.DefaultedDefaultConstructorIsConstexpr = FromData.DefaultedDefaultConstructorIsConstexpr; - ToData.DefaultedCopyConstructorIsConstexpr - = FromData.DefaultedCopyConstructorIsConstexpr; - ToData.DefaultedMoveConstructorIsConstexpr - = FromData.DefaultedMoveConstructorIsConstexpr; ToData.HasConstexprDefaultConstructor = FromData.HasConstexprDefaultConstructor; - ToData.HasConstexprCopyConstructor = FromData.HasConstexprCopyConstructor; - ToData.HasConstexprMoveConstructor = FromData.HasConstexprMoveConstructor; ToData.HasTrivialCopyConstructor = FromData.HasTrivialCopyConstructor; ToData.HasTrivialMoveConstructor = FromData.HasTrivialMoveConstructor; ToData.HasTrivialCopyAssignment = FromData.HasTrivialCopyAssignment; @@ -1991,7 +1958,7 @@ ASTNodeImporter::ImportTemplateArgument(const TemplateArgument &From) { QualType ToType = Importer.Import(From.getIntegralType()); if (ToType.isNull()) return TemplateArgument(); - return TemplateArgument(*From.getAsIntegral(), ToType); + return TemplateArgument(From, ToType); } case TemplateArgument::Declaration: @@ -2052,10 +2019,11 @@ bool ASTNodeImporter::ImportTemplateArguments(const TemplateArgument *FromArgs, } bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord, - RecordDecl *ToRecord) { + RecordDecl *ToRecord, bool Complain) { StructuralEquivalenceContext Ctx(Importer.getFromContext(), Importer.getToContext(), - Importer.getNonEquivalentDecls()); + Importer.getNonEquivalentDecls(), + false, Complain); return Ctx.IsStructurallyEquivalent(FromRecord, ToRecord); } @@ -2335,7 +2303,7 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { // We may already have a record of the same name; try to find and match it. RecordDecl *AdoptDecl = 0; - if (!DC->isFunctionOrMethod() && SearchName) { + if (!DC->isFunctionOrMethod()) { SmallVector<NamedDecl *, 4> ConflictingDecls; llvm::SmallVector<NamedDecl *, 2> FoundDecls; DC->localUncachedLookup(SearchName, FoundDecls); @@ -2351,25 +2319,31 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { if (RecordDecl *FoundRecord = dyn_cast<RecordDecl>(Found)) { if (RecordDecl *FoundDef = FoundRecord->getDefinition()) { - if (!D->isCompleteDefinition() || IsStructuralMatch(D, FoundDef)) { + if ((SearchName && !D->isCompleteDefinition()) + || (D->isCompleteDefinition() && + D->isAnonymousStructOrUnion() + == FoundDef->isAnonymousStructOrUnion() && + IsStructuralMatch(D, FoundDef))) { // The record types structurally match, or the "from" translation // unit only had a forward declaration anyway; call it the same // function. // FIXME: For C++, we should also merge methods here. return Importer.Imported(D, FoundDef); } - } else { + } else if (!D->isCompleteDefinition()) { // We have a forward declaration of this type, so adopt that forward // declaration rather than building a new one. AdoptDecl = FoundRecord; continue; - } + } else if (!SearchName) { + continue; + } } ConflictingDecls.push_back(FoundDecls[I]); } - if (!ConflictingDecls.empty()) { + if (!ConflictingDecls.empty() && SearchName) { Name = Importer.HandleNameConflict(Name, DC, IDNS, ConflictingDecls.data(), ConflictingDecls.size()); @@ -2395,6 +2369,8 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { D2->setQualifierInfo(Importer.Import(D->getQualifierLoc())); D2->setLexicalDeclContext(LexicalDC); LexicalDC->addDeclInternal(D2); + if (D->isAnonymousStructOrUnion()) + D2->setAnonymousStructOrUnion(true); } Importer.Imported(D, D2); @@ -2661,7 +2637,7 @@ Decl *ASTNodeImporter::VisitFieldDecl(FieldDecl *D) { Importer.Import(D->getInnerLocStart()), Loc, Name.getAsIdentifierInfo(), T, TInfo, BitWidth, D->isMutable(), - D->hasInClassInitializer()); + D->getInClassInitStyle()); ToField->setAccess(D->getAccess()); ToField->setLexicalDeclContext(LexicalDC); if (ToField->hasInClassInitializer()) @@ -2686,11 +2662,16 @@ Decl *ASTNodeImporter::VisitIndirectFieldDecl(IndirectFieldDecl *D) { if (IndirectFieldDecl *FoundField = dyn_cast<IndirectFieldDecl>(FoundDecls[I])) { if (Importer.IsStructurallyEquivalent(D->getType(), - FoundField->getType())) { + FoundField->getType(), + Name)) { Importer.Imported(D, FoundField); return FoundField; } - + + // If there are more anonymous fields to check, continue. + if (!Name && I < N-1) + continue; + Importer.ToDiag(Loc, diag::err_odr_field_type_inconsistent) << Name << D->getType() << FoundField->getType(); Importer.ToDiag(FoundField->getLocation(), diag::note_odr_value_here) @@ -4665,12 +4646,14 @@ Decl *ASTImporter::Imported(Decl *From, Decl *To) { return To; } -bool ASTImporter::IsStructurallyEquivalent(QualType From, QualType To) { +bool ASTImporter::IsStructurallyEquivalent(QualType From, QualType To, + bool Complain) { llvm::DenseMap<const Type *, const Type *>::iterator Pos = ImportedTypes.find(From.getTypePtr()); if (Pos != ImportedTypes.end() && ToContext.hasSameType(Import(From), To)) return true; - StructuralEquivalenceContext Ctx(FromContext, ToContext, NonEquivalentDecls); + StructuralEquivalenceContext Ctx(FromContext, ToContext, NonEquivalentDecls, + false, Complain); return Ctx.IsStructurallyEquivalent(From, To); } diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 716459a930cf..bcc96f912e28 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -1,7 +1,5 @@ set(LLVM_LINK_COMPONENTS support) -set(LLVM_USED_LIBS clangBasic clangLex) - add_clang_library(clangAST APValue.cpp ASTConsumer.cpp @@ -10,6 +8,13 @@ add_clang_library(clangAST ASTImporter.cpp AttrImpl.cpp CXXInheritance.cpp + Comment.cpp + CommentBriefParser.cpp + CommentCommandTraits.cpp + CommentDumper.cpp + CommentLexer.cpp + CommentParser.cpp + CommentSema.cpp Decl.cpp DeclarationName.cpp DeclBase.cpp @@ -35,6 +40,7 @@ add_clang_library(clangAST NestedNameSpecifier.cpp NSAPI.cpp ParentMap.cpp + RawCommentList.cpp RecordLayout.cpp RecordLayoutBuilder.cpp SelectorLocationsKind.cpp @@ -53,5 +59,21 @@ add_clang_library(clangAST VTTBuilder.cpp ) -add_dependencies(clangAST ClangARMNeon ClangAttrClasses ClangAttrList - ClangAttrImpl ClangDiagnosticAST ClangDeclNodes ClangStmtNodes) +add_dependencies(clangAST + ClangARMNeon + ClangAttrClasses + ClangAttrList + ClangAttrImpl + ClangCommentNodes + ClangDeclNodes + ClangDiagnosticAST + ClangDiagnosticComment + ClangDiagnosticCommon + ClangDiagnosticSema + ClangStmtNodes + ) + +target_link_libraries(clangAST + clangBasic + clangLex + ) diff --git a/lib/AST/CXXABI.h b/lib/AST/CXXABI.h index 943c43e79198..0d9c869d87ca 100644 --- a/lib/AST/CXXABI.h +++ b/lib/AST/CXXABI.h @@ -32,7 +32,7 @@ public: virtual unsigned getMemberPointerSize(const MemberPointerType *MPT) const = 0; /// Returns the default calling convention for C++ methods. - virtual CallingConv getDefaultMethodCallConv() const = 0; + virtual CallingConv getDefaultMethodCallConv(bool isVariadic) const = 0; // Returns whether the given class is nearly empty, with just virtual pointers // and no data except possibly virtual bases. diff --git a/lib/AST/CXXInheritance.cpp b/lib/AST/CXXInheritance.cpp index 218673090aa7..cf3913bac008 100644 --- a/lib/AST/CXXInheritance.cpp +++ b/lib/AST/CXXInheritance.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// #include "clang/AST/CXXInheritance.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/DeclCXX.h" #include <algorithm> @@ -96,7 +97,7 @@ bool CXXRecordDecl::isDerivedFrom(const CXXRecordDecl *Base, Paths); } -bool CXXRecordDecl::isVirtuallyDerivedFrom(CXXRecordDecl *Base) const { +bool CXXRecordDecl::isVirtuallyDerivedFrom(const CXXRecordDecl *Base) const { if (!getNumVBases()) return false; @@ -106,8 +107,12 @@ bool CXXRecordDecl::isVirtuallyDerivedFrom(CXXRecordDecl *Base) const { if (getCanonicalDecl() == Base->getCanonicalDecl()) return false; - Paths.setOrigin(const_cast<CXXRecordDecl*>(this)); - return lookupInBases(&FindVirtualBaseClass, Base->getCanonicalDecl(), Paths); + Paths.setOrigin(const_cast<CXXRecordDecl*>(this)); + + const void *BasePtr = static_cast<const void*>(Base->getCanonicalDecl()); + return lookupInBases(&FindVirtualBaseClass, + const_cast<void *>(BasePtr), + Paths); } static bool BaseIsNot(const CXXRecordDecl *Base, void *OpaqueTarget) { @@ -160,7 +165,7 @@ bool CXXRecordDecl::forallBases(ForallBasesCallback *BaseMatches, return AllMatches; } -bool CXXBasePaths::lookupInBases(ASTContext &Context, +bool CXXBasePaths::lookupInBases(ASTContext &Context, const CXXRecordDecl *Record, CXXRecordDecl::BaseMatchesCallback *BaseMatches, void *UserData) { @@ -505,12 +510,17 @@ void FinalOverriderCollector::Collect(const CXXRecordDecl *RD, CXXFinalOverriderMap *BaseOverriders = &ComputedBaseOverriders; if (Base->isVirtual()) { CXXFinalOverriderMap *&MyVirtualOverriders = VirtualOverriders[BaseDecl]; + BaseOverriders = MyVirtualOverriders; if (!MyVirtualOverriders) { MyVirtualOverriders = new CXXFinalOverriderMap; + + // Collect may cause VirtualOverriders to reallocate, invalidating the + // MyVirtualOverriders reference. Set BaseOverriders to the right + // value now. + BaseOverriders = MyVirtualOverriders; + Collect(BaseDecl, true, BaseDecl, *MyVirtualOverriders); } - - BaseOverriders = MyVirtualOverriders; } else Collect(BaseDecl, false, InVirtualSubobject, ComputedBaseOverriders); diff --git a/lib/AST/Comment.cpp b/lib/AST/Comment.cpp new file mode 100644 index 000000000000..8a711f0c1f80 --- /dev/null +++ b/lib/AST/Comment.cpp @@ -0,0 +1,264 @@ +//===--- Comment.cpp - Comment AST node implementation --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Comment.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace comments { + +const char *Comment::getCommentKindName() const { + switch (getCommentKind()) { + case NoCommentKind: return "NoCommentKind"; +#define ABSTRACT_COMMENT(COMMENT) +#define COMMENT(CLASS, PARENT) \ + case CLASS##Kind: \ + return #CLASS; +#include "clang/AST/CommentNodes.inc" +#undef COMMENT +#undef ABSTRACT_COMMENT + } + llvm_unreachable("Unknown comment kind!"); +} + +void Comment::dump() const { + // It is important that Comment::dump() is defined in a different TU than + // Comment::dump(raw_ostream, SourceManager). If both functions were defined + // in CommentDumper.cpp, that object file would be removed by linker because + // none of its functions are referenced by other object files, despite the + // LLVM_ATTRIBUTE_USED. + dump(llvm::errs(), NULL); +} + +void Comment::dump(SourceManager &SM) const { + dump(llvm::errs(), &SM); +} + +namespace { +struct good {}; +struct bad {}; + +template <typename T> +good implements_child_begin_end(Comment::child_iterator (T::*)() const) { + return good(); +} + +static inline bad implements_child_begin_end( + Comment::child_iterator (Comment::*)() const) { + return bad(); +} + +#define ASSERT_IMPLEMENTS_child_begin(function) \ + (void) sizeof(good(implements_child_begin_end(function))) + +static inline void CheckCommentASTNodes() { +#define ABSTRACT_COMMENT(COMMENT) +#define COMMENT(CLASS, PARENT) \ + ASSERT_IMPLEMENTS_child_begin(&CLASS::child_begin); \ + ASSERT_IMPLEMENTS_child_begin(&CLASS::child_end); +#include "clang/AST/CommentNodes.inc" +#undef COMMENT +#undef ABSTRACT_COMMENT +} + +#undef ASSERT_IMPLEMENTS_child_begin + +} // end unnamed namespace + +Comment::child_iterator Comment::child_begin() const { + switch (getCommentKind()) { + case NoCommentKind: llvm_unreachable("comment without a kind"); +#define ABSTRACT_COMMENT(COMMENT) +#define COMMENT(CLASS, PARENT) \ + case CLASS##Kind: \ + return static_cast<const CLASS *>(this)->child_begin(); +#include "clang/AST/CommentNodes.inc" +#undef COMMENT +#undef ABSTRACT_COMMENT + } + llvm_unreachable("Unknown comment kind!"); +} + +Comment::child_iterator Comment::child_end() const { + switch (getCommentKind()) { + case NoCommentKind: llvm_unreachable("comment without a kind"); +#define ABSTRACT_COMMENT(COMMENT) +#define COMMENT(CLASS, PARENT) \ + case CLASS##Kind: \ + return static_cast<const CLASS *>(this)->child_end(); +#include "clang/AST/CommentNodes.inc" +#undef COMMENT +#undef ABSTRACT_COMMENT + } + llvm_unreachable("Unknown comment kind!"); +} + +bool TextComment::isWhitespaceNoCache() const { + for (StringRef::const_iterator I = Text.begin(), E = Text.end(); + I != E; ++I) { + const char C = *I; + if (C != ' ' && C != '\n' && C != '\r' && + C != '\t' && C != '\f' && C != '\v') + return false; + } + return true; +} + +bool ParagraphComment::isWhitespaceNoCache() const { + for (child_iterator I = child_begin(), E = child_end(); I != E; ++I) { + if (const TextComment *TC = dyn_cast<TextComment>(*I)) { + if (!TC->isWhitespace()) + return false; + } else + return false; + } + return true; +} + +const char *ParamCommandComment::getDirectionAsString(PassDirection D) { + switch (D) { + case ParamCommandComment::In: + return "[in]"; + case ParamCommandComment::Out: + return "[out]"; + case ParamCommandComment::InOut: + return "[in,out]"; + } + llvm_unreachable("unknown PassDirection"); +} + +void DeclInfo::fill() { + assert(!IsFilled); + + // Set defaults. + Kind = OtherKind; + TemplateKind = NotTemplate; + IsObjCMethod = false; + IsInstanceMethod = false; + IsClassMethod = false; + ParamVars = ArrayRef<const ParmVarDecl *>(); + TemplateParameters = NULL; + + if (!ThisDecl) { + // If there is no declaration, the defaults is our only guess. + IsFilled = true; + return; + } + + Decl::Kind K = ThisDecl->getKind(); + switch (K) { + default: + // Defaults are should be good for declarations we don't handle explicitly. + break; + case Decl::Function: + case Decl::CXXMethod: + case Decl::CXXConstructor: + case Decl::CXXDestructor: + case Decl::CXXConversion: { + const FunctionDecl *FD = cast<FunctionDecl>(ThisDecl); + Kind = FunctionKind; + ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(), + FD->getNumParams()); + ResultType = FD->getResultType(); + unsigned NumLists = FD->getNumTemplateParameterLists(); + if (NumLists != 0) { + TemplateKind = TemplateSpecialization; + TemplateParameters = + FD->getTemplateParameterList(NumLists - 1); + } + + if (K == Decl::CXXMethod || K == Decl::CXXConstructor || + K == Decl::CXXDestructor || K == Decl::CXXConversion) { + const CXXMethodDecl *MD = cast<CXXMethodDecl>(ThisDecl); + IsInstanceMethod = MD->isInstance(); + IsClassMethod = !IsInstanceMethod; + } + break; + } + case Decl::ObjCMethod: { + const ObjCMethodDecl *MD = cast<ObjCMethodDecl>(ThisDecl); + Kind = FunctionKind; + ParamVars = ArrayRef<const ParmVarDecl *>(MD->param_begin(), + MD->param_size()); + ResultType = MD->getResultType(); + IsObjCMethod = true; + IsInstanceMethod = MD->isInstanceMethod(); + IsClassMethod = !IsInstanceMethod; + break; + } + case Decl::FunctionTemplate: { + const FunctionTemplateDecl *FTD = cast<FunctionTemplateDecl>(ThisDecl); + Kind = FunctionKind; + TemplateKind = Template; + const FunctionDecl *FD = FTD->getTemplatedDecl(); + ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(), + FD->getNumParams()); + ResultType = FD->getResultType(); + TemplateParameters = FTD->getTemplateParameters(); + break; + } + case Decl::ClassTemplate: { + const ClassTemplateDecl *CTD = cast<ClassTemplateDecl>(ThisDecl); + Kind = ClassKind; + TemplateKind = Template; + TemplateParameters = CTD->getTemplateParameters(); + break; + } + case Decl::ClassTemplatePartialSpecialization: { + const ClassTemplatePartialSpecializationDecl *CTPSD = + cast<ClassTemplatePartialSpecializationDecl>(ThisDecl); + Kind = ClassKind; + TemplateKind = TemplatePartialSpecialization; + TemplateParameters = CTPSD->getTemplateParameters(); + break; + } + case Decl::ClassTemplateSpecialization: + Kind = ClassKind; + TemplateKind = TemplateSpecialization; + break; + case Decl::Record: + case Decl::CXXRecord: + Kind = ClassKind; + break; + case Decl::Var: + case Decl::Field: + case Decl::EnumConstant: + case Decl::ObjCIvar: + case Decl::ObjCAtDefsField: + Kind = VariableKind; + break; + case Decl::Namespace: + Kind = NamespaceKind; + break; + case Decl::Typedef: + case Decl::TypeAlias: + Kind = TypedefKind; + break; + case Decl::TypeAliasTemplate: { + const TypeAliasTemplateDecl *TAT = cast<TypeAliasTemplateDecl>(ThisDecl); + Kind = TypedefKind; + TemplateKind = Template; + TemplateParameters = TAT->getTemplateParameters(); + break; + } + case Decl::Enum: + Kind = EnumKind; + break; + } + + IsFilled = true; +} + +} // end namespace comments +} // end namespace clang + diff --git a/lib/AST/CommentBriefParser.cpp b/lib/AST/CommentBriefParser.cpp new file mode 100644 index 000000000000..0aebc1e4e3dd --- /dev/null +++ b/lib/AST/CommentBriefParser.cpp @@ -0,0 +1,122 @@ +//===--- CommentBriefParser.cpp - Dumb comment parser ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/CommentBriefParser.h" +#include "clang/AST/CommentCommandTraits.h" +#include "llvm/ADT/StringSwitch.h" + +namespace clang { +namespace comments { + +namespace { +/// Convert all whitespace into spaces, remove leading and trailing spaces, +/// compress multiple spaces into one. +void cleanupBrief(std::string &S) { + bool PrevWasSpace = true; + std::string::iterator O = S.begin(); + for (std::string::iterator I = S.begin(), E = S.end(); + I != E; ++I) { + const char C = *I; + if (C == ' ' || C == '\n' || C == '\r' || + C == '\t' || C == '\v' || C == '\f') { + if (!PrevWasSpace) { + *O++ = ' '; + PrevWasSpace = true; + } + continue; + } else { + *O++ = C; + PrevWasSpace = false; + } + } + if (O != S.begin() && *(O - 1) == ' ') + --O; + + S.resize(O - S.begin()); +} +} // unnamed namespace + +BriefParser::BriefParser(Lexer &L, const CommandTraits &Traits) : + L(L), Traits(Traits) { + // Get lookahead token. + ConsumeToken(); +} + +std::string BriefParser::Parse() { + std::string FirstParagraphOrBrief; + std::string ReturnsParagraph; + bool InFirstParagraph = true; + bool InBrief = false; + bool InReturns = false; + + while (Tok.isNot(tok::eof)) { + if (Tok.is(tok::text)) { + if (InFirstParagraph || InBrief) + FirstParagraphOrBrief += Tok.getText(); + else if (InReturns) + ReturnsParagraph += Tok.getText(); + ConsumeToken(); + continue; + } + + if (Tok.is(tok::command)) { + StringRef Name = Tok.getCommandName(); + if (Traits.isBriefCommand(Name)) { + FirstParagraphOrBrief.clear(); + InBrief = true; + ConsumeToken(); + continue; + } + if (Traits.isReturnsCommand(Name)) { + InReturns = true; + ReturnsParagraph += "Returns "; + } + // Block commands implicitly start a new paragraph. + if (Traits.isBlockCommand(Name)) { + // We found an implicit paragraph end. + InFirstParagraph = false; + if (InBrief) + break; + } + } + + if (Tok.is(tok::newline)) { + if (InFirstParagraph || InBrief) + FirstParagraphOrBrief += ' '; + else if (InReturns) + ReturnsParagraph += ' '; + ConsumeToken(); + + if (Tok.is(tok::newline)) { + ConsumeToken(); + // We found a paragraph end. + InFirstParagraph = false; + InReturns = false; + if (InBrief) + break; + } + continue; + } + + // We didn't handle this token, so just drop it. + ConsumeToken(); + } + + cleanupBrief(FirstParagraphOrBrief); + if (!FirstParagraphOrBrief.empty()) + return FirstParagraphOrBrief; + + cleanupBrief(ReturnsParagraph); + return ReturnsParagraph; +} + +} // end namespace comments +} // end namespace clang + + diff --git a/lib/AST/CommentCommandTraits.cpp b/lib/AST/CommentCommandTraits.cpp new file mode 100644 index 000000000000..d8ce1f3fe7bd --- /dev/null +++ b/lib/AST/CommentCommandTraits.cpp @@ -0,0 +1,134 @@ +//===--- CommentCommandTraits.cpp - Comment command properties --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/CommentCommandTraits.h" +#include "llvm/ADT/StringSwitch.h" + +namespace clang { +namespace comments { + +// TODO: tablegen + +bool CommandTraits::isVerbatimBlockCommand(StringRef BeginName, + StringRef &EndName) const { + const char *Result = llvm::StringSwitch<const char *>(BeginName) + .Case("code", "endcode") + .Case("verbatim", "endverbatim") + .Case("htmlonly", "endhtmlonly") + .Case("latexonly", "endlatexonly") + .Case("xmlonly", "endxmlonly") + .Case("manonly", "endmanonly") + .Case("rtfonly", "endrtfonly") + + .Case("dot", "enddot") + .Case("msc", "endmsc") + + .Case("f$", "f$") // Inline LaTeX formula + .Case("f[", "f]") // Displayed LaTeX formula + .Case("f{", "f}") // LaTeX environment + + .Default(NULL); + + if (Result) { + EndName = Result; + return true; + } + + for (VerbatimBlockCommandVector::const_iterator + I = VerbatimBlockCommands.begin(), + E = VerbatimBlockCommands.end(); + I != E; ++I) + if (I->BeginName == BeginName) { + EndName = I->EndName; + return true; + } + + return false; +} + +bool CommandTraits::isVerbatimLineCommand(StringRef Name) const { + bool Result = isDeclarationCommand(Name) || llvm::StringSwitch<bool>(Name) + .Case("defgroup", true) + .Case("ingroup", true) + .Case("addtogroup", true) + .Case("weakgroup", true) + .Case("name", true) + + .Case("section", true) + .Case("subsection", true) + .Case("subsubsection", true) + .Case("paragraph", true) + + .Case("mainpage", true) + .Case("subpage", true) + .Case("ref", true) + + .Default(false); + + if (Result) + return true; + + for (VerbatimLineCommandVector::const_iterator + I = VerbatimLineCommands.begin(), + E = VerbatimLineCommands.end(); + I != E; ++I) + if (I->Name == Name) + return true; + + return false; +} + +bool CommandTraits::isDeclarationCommand(StringRef Name) const { + return llvm::StringSwitch<bool>(Name) + // Doxygen commands. + .Case("fn", true) + .Case("var", true) + .Case("property", true) + .Case("typedef", true) + + .Case("overload", true) + + // HeaderDoc commands. + .Case("class", true) + .Case("interface", true) + .Case("protocol", true) + .Case("category", true) + .Case("template", true) + .Case("function", true) + .Case("method", true) + .Case("callback", true) + .Case("var", true) + .Case("const", true) + .Case("constant", true) + .Case("property", true) + .Case("struct", true) + .Case("union", true) + .Case("typedef", true) + .Case("enum", true) + + .Default(false); +} + +void CommandTraits::addVerbatimBlockCommand(StringRef BeginName, + StringRef EndName) { + VerbatimBlockCommand VBC; + VBC.BeginName = BeginName; + VBC.EndName = EndName; + VerbatimBlockCommands.push_back(VBC); +} + +void CommandTraits::addVerbatimLineCommand(StringRef Name) { + VerbatimLineCommand VLC; + VLC.Name = Name; + VerbatimLineCommands.push_back(VLC); +} + +} // end namespace comments +} // end namespace clang + diff --git a/lib/AST/CommentDumper.cpp b/lib/AST/CommentDumper.cpp new file mode 100644 index 000000000000..dffc8233a0ca --- /dev/null +++ b/lib/AST/CommentDumper.cpp @@ -0,0 +1,231 @@ +//===--- CommentDumper.cpp - Dumping implementation for Comment ASTs ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/CommentVisitor.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace comments { + +namespace { +class CommentDumper: public comments::ConstCommentVisitor<CommentDumper> { + raw_ostream &OS; + SourceManager *SM; + unsigned IndentLevel; + +public: + CommentDumper(raw_ostream &OS, SourceManager *SM) : + OS(OS), SM(SM), IndentLevel(0) + { } + + void dumpIndent() const { + for (unsigned i = 1, e = IndentLevel; i < e; ++i) + OS << " "; + } + + void dumpLocation(SourceLocation Loc) { + if (SM) + Loc.print(OS, *SM); + } + + void dumpSourceRange(const Comment *C); + + void dumpComment(const Comment *C); + + void dumpSubtree(const Comment *C); + + // Inline content. + void visitTextComment(const TextComment *C); + void visitInlineCommandComment(const InlineCommandComment *C); + void visitHTMLStartTagComment(const HTMLStartTagComment *C); + void visitHTMLEndTagComment(const HTMLEndTagComment *C); + + // Block content. + void visitParagraphComment(const ParagraphComment *C); + void visitBlockCommandComment(const BlockCommandComment *C); + void visitParamCommandComment(const ParamCommandComment *C); + void visitTParamCommandComment(const TParamCommandComment *C); + void visitVerbatimBlockComment(const VerbatimBlockComment *C); + void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); + void visitVerbatimLineComment(const VerbatimLineComment *C); + + void visitFullComment(const FullComment *C); +}; + +void CommentDumper::dumpSourceRange(const Comment *C) { + if (!SM) + return; + + SourceRange SR = C->getSourceRange(); + + OS << " <"; + dumpLocation(SR.getBegin()); + if (SR.getBegin() != SR.getEnd()) { + OS << ", "; + dumpLocation(SR.getEnd()); + } + OS << ">"; +} + +void CommentDumper::dumpComment(const Comment *C) { + dumpIndent(); + OS << "(" << C->getCommentKindName() + << " " << (const void *) C; + dumpSourceRange(C); +} + +void CommentDumper::dumpSubtree(const Comment *C) { + ++IndentLevel; + if (C) { + visit(C); + for (Comment::child_iterator I = C->child_begin(), + E = C->child_end(); + I != E; ++I) { + OS << '\n'; + dumpSubtree(*I); + } + OS << ')'; + } else { + dumpIndent(); + OS << "<<<NULL>>>"; + } + --IndentLevel; +} + +void CommentDumper::visitTextComment(const TextComment *C) { + dumpComment(C); + + OS << " Text=\"" << C->getText() << "\""; +} + +void CommentDumper::visitInlineCommandComment(const InlineCommandComment *C) { + dumpComment(C); + + OS << " Name=\"" << C->getCommandName() << "\""; + switch (C->getRenderKind()) { + case InlineCommandComment::RenderNormal: + OS << " RenderNormal"; + break; + case InlineCommandComment::RenderBold: + OS << " RenderBold"; + break; + case InlineCommandComment::RenderMonospaced: + OS << " RenderMonospaced"; + break; + case InlineCommandComment::RenderEmphasized: + OS << " RenderEmphasized"; + break; + } + + for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) + OS << " Arg[" << i << "]=\"" << C->getArgText(i) << "\""; +} + +void CommentDumper::visitHTMLStartTagComment(const HTMLStartTagComment *C) { + dumpComment(C); + + OS << " Name=\"" << C->getTagName() << "\""; + if (C->getNumAttrs() != 0) { + OS << " Attrs: "; + for (unsigned i = 0, e = C->getNumAttrs(); i != e; ++i) { + const HTMLStartTagComment::Attribute &Attr = C->getAttr(i); + OS << " \"" << Attr.Name << "=\"" << Attr.Value << "\""; + } + } + if (C->isSelfClosing()) + OS << " SelfClosing"; +} + +void CommentDumper::visitHTMLEndTagComment(const HTMLEndTagComment *C) { + dumpComment(C); + + OS << " Name=\"" << C->getTagName() << "\""; +} + +void CommentDumper::visitParagraphComment(const ParagraphComment *C) { + dumpComment(C); +} + +void CommentDumper::visitBlockCommandComment(const BlockCommandComment *C) { + dumpComment(C); + + OS << " Name=\"" << C->getCommandName() << "\""; + for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) + OS << " Arg[" << i << "]=\"" << C->getArgText(i) << "\""; +} + +void CommentDumper::visitParamCommandComment(const ParamCommandComment *C) { + dumpComment(C); + + OS << " " << ParamCommandComment::getDirectionAsString(C->getDirection()); + + if (C->isDirectionExplicit()) + OS << " explicitly"; + else + OS << " implicitly"; + + if (C->hasParamName()) + OS << " Param=\"" << C->getParamName() << "\""; + + if (C->isParamIndexValid()) + OS << " ParamIndex=" << C->getParamIndex(); +} + +void CommentDumper::visitTParamCommandComment(const TParamCommandComment *C) { + dumpComment(C); + + if (C->hasParamName()) { + OS << " Param=\"" << C->getParamName() << "\""; + } + + if (C->isPositionValid()) { + OS << " Position=<"; + for (unsigned i = 0, e = C->getDepth(); i != e; ++i) { + OS << C->getIndex(i); + if (i != e - 1) + OS << ", "; + } + OS << ">"; + } +} + +void CommentDumper::visitVerbatimBlockComment(const VerbatimBlockComment *C) { + dumpComment(C); + + OS << " Name=\"" << C->getCommandName() << "\"" + " CloseName=\"" << C->getCloseName() << "\""; +} + +void CommentDumper::visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C) { + dumpComment(C); + + OS << " Text=\"" << C->getText() << "\""; +} + +void CommentDumper::visitVerbatimLineComment(const VerbatimLineComment *C) { + dumpComment(C); + + OS << " Text=\"" << C->getText() << "\""; +} + +void CommentDumper::visitFullComment(const FullComment *C) { + dumpComment(C); +} + +} // unnamed namespace + +void Comment::dump(llvm::raw_ostream &OS, SourceManager *SM) const { + CommentDumper D(llvm::errs(), SM); + D.dumpSubtree(this); + llvm::errs() << '\n'; +} + +} // end namespace comments +} // end namespace clang + diff --git a/lib/AST/CommentLexer.cpp b/lib/AST/CommentLexer.cpp new file mode 100644 index 000000000000..b6516ec126f6 --- /dev/null +++ b/lib/AST/CommentLexer.cpp @@ -0,0 +1,815 @@ +#include "clang/AST/CommentLexer.h" +#include "clang/AST/CommentCommandTraits.h" +#include "clang/Basic/ConvertUTF.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ErrorHandling.h" + +namespace clang { +namespace comments { + +void Token::dump(const Lexer &L, const SourceManager &SM) const { + llvm::errs() << "comments::Token Kind=" << Kind << " "; + Loc.dump(SM); + llvm::errs() << " " << Length << " \"" << L.getSpelling(*this, SM) << "\"\n"; +} + +namespace { +bool isHTMLNamedCharacterReferenceCharacter(char C) { + return (C >= 'a' && C <= 'z') || + (C >= 'A' && C <= 'Z'); +} + +bool isHTMLDecimalCharacterReferenceCharacter(char C) { + return C >= '0' && C <= '9'; +} + +bool isHTMLHexCharacterReferenceCharacter(char C) { + return (C >= '0' && C <= '9') || + (C >= 'a' && C <= 'f') || + (C >= 'A' && C <= 'F'); +} +} // unnamed namespace + +StringRef Lexer::resolveHTMLNamedCharacterReference(StringRef Name) const { + return llvm::StringSwitch<StringRef>(Name) + .Case("amp", "&") + .Case("lt", "<") + .Case("gt", ">") + .Case("quot", "\"") + .Case("apos", "\'") + .Default(""); +} + +StringRef Lexer::resolveHTMLDecimalCharacterReference(StringRef Name) const { + unsigned CodePoint = 0; + for (unsigned i = 0, e = Name.size(); i != e; ++i) { + assert(isHTMLDecimalCharacterReferenceCharacter(Name[i])); + CodePoint *= 10; + CodePoint += Name[i] - '0'; + } + + char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT); + char *ResolvedPtr = Resolved; + if (ConvertCodePointToUTF8(CodePoint, ResolvedPtr)) + return StringRef(Resolved, ResolvedPtr - Resolved); + else + return StringRef(); +} + +StringRef Lexer::resolveHTMLHexCharacterReference(StringRef Name) const { + unsigned CodePoint = 0; + for (unsigned i = 0, e = Name.size(); i != e; ++i) { + CodePoint *= 16; + const char C = Name[i]; + assert(isHTMLHexCharacterReferenceCharacter(C)); + if (C >= '0' && C <= '9') + CodePoint += Name[i] - '0'; + else if (C >= 'a' && C <= 'f') + CodePoint += Name[i] - 'a' + 10; + else + CodePoint += Name[i] - 'A' + 10; + } + + char *Resolved = Allocator.Allocate<char>(UNI_MAX_UTF8_BYTES_PER_CODE_POINT); + char *ResolvedPtr = Resolved; + if (ConvertCodePointToUTF8(CodePoint, ResolvedPtr)) + return StringRef(Resolved, ResolvedPtr - Resolved); + else + return StringRef(); +} + +void Lexer::skipLineStartingDecorations() { + // This function should be called only for C comments + assert(CommentState == LCS_InsideCComment); + + if (BufferPtr == CommentEnd) + return; + + switch (*BufferPtr) { + case ' ': + case '\t': + case '\f': + case '\v': { + const char *NewBufferPtr = BufferPtr; + NewBufferPtr++; + if (NewBufferPtr == CommentEnd) + return; + + char C = *NewBufferPtr; + while (C == ' ' || C == '\t' || C == '\f' || C == '\v') { + NewBufferPtr++; + if (NewBufferPtr == CommentEnd) + return; + C = *NewBufferPtr; + } + if (C == '*') + BufferPtr = NewBufferPtr + 1; + break; + } + case '*': + BufferPtr++; + break; + } +} + +namespace { +/// Returns pointer to the first newline character in the string. +const char *findNewline(const char *BufferPtr, const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + const char C = *BufferPtr; + if (C == '\n' || C == '\r') + return BufferPtr; + } + return BufferEnd; +} + +const char *skipNewline(const char *BufferPtr, const char *BufferEnd) { + if (BufferPtr == BufferEnd) + return BufferPtr; + + if (*BufferPtr == '\n') + BufferPtr++; + else { + assert(*BufferPtr == '\r'); + BufferPtr++; + if (BufferPtr != BufferEnd && *BufferPtr == '\n') + BufferPtr++; + } + return BufferPtr; +} + +const char *skipNamedCharacterReference(const char *BufferPtr, + const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + if (!isHTMLNamedCharacterReferenceCharacter(*BufferPtr)) + return BufferPtr; + } + return BufferEnd; +} + +const char *skipDecimalCharacterReference(const char *BufferPtr, + const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + if (!isHTMLDecimalCharacterReferenceCharacter(*BufferPtr)) + return BufferPtr; + } + return BufferEnd; +} + +const char *skipHexCharacterReference(const char *BufferPtr, + const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + if (!isHTMLHexCharacterReferenceCharacter(*BufferPtr)) + return BufferPtr; + } + return BufferEnd; +} + +bool isHTMLIdentifierStartingCharacter(char C) { + return (C >= 'a' && C <= 'z') || + (C >= 'A' && C <= 'Z'); +} + +bool isHTMLIdentifierCharacter(char C) { + return (C >= 'a' && C <= 'z') || + (C >= 'A' && C <= 'Z') || + (C >= '0' && C <= '9'); +} + +const char *skipHTMLIdentifier(const char *BufferPtr, const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + if (!isHTMLIdentifierCharacter(*BufferPtr)) + return BufferPtr; + } + return BufferEnd; +} + +/// Skip HTML string quoted in single or double quotes. Escaping quotes inside +/// string allowed. +/// +/// Returns pointer to closing quote. +const char *skipHTMLQuotedString(const char *BufferPtr, const char *BufferEnd) +{ + const char Quote = *BufferPtr; + assert(Quote == '\"' || Quote == '\''); + + BufferPtr++; + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + const char C = *BufferPtr; + if (C == Quote && BufferPtr[-1] != '\\') + return BufferPtr; + } + return BufferEnd; +} + +bool isHorizontalWhitespace(char C) { + return C == ' ' || C == '\t' || C == '\f' || C == '\v'; +} + +bool isWhitespace(char C) { + return C == ' ' || C == '\n' || C == '\r' || + C == '\t' || C == '\f' || C == '\v'; +} + +const char *skipWhitespace(const char *BufferPtr, const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + if (!isWhitespace(*BufferPtr)) + return BufferPtr; + } + return BufferEnd; +} + +bool isWhitespace(const char *BufferPtr, const char *BufferEnd) { + return skipWhitespace(BufferPtr, BufferEnd) == BufferEnd; +} + +bool isCommandNameCharacter(char C) { + return (C >= 'a' && C <= 'z') || + (C >= 'A' && C <= 'Z') || + (C >= '0' && C <= '9'); +} + +const char *skipCommandName(const char *BufferPtr, const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + if (!isCommandNameCharacter(*BufferPtr)) + return BufferPtr; + } + return BufferEnd; +} + +/// Return the one past end pointer for BCPL comments. +/// Handles newlines escaped with backslash or trigraph for backslahs. +const char *findBCPLCommentEnd(const char *BufferPtr, const char *BufferEnd) { + const char *CurPtr = BufferPtr; + while (CurPtr != BufferEnd) { + char C = *CurPtr; + while (C != '\n' && C != '\r') { + CurPtr++; + if (CurPtr == BufferEnd) + return BufferEnd; + C = *CurPtr; + } + // We found a newline, check if it is escaped. + const char *EscapePtr = CurPtr - 1; + while(isHorizontalWhitespace(*EscapePtr)) + EscapePtr--; + + if (*EscapePtr == '\\' || + (EscapePtr - 2 >= BufferPtr && EscapePtr[0] == '/' && + EscapePtr[-1] == '?' && EscapePtr[-2] == '?')) { + // We found an escaped newline. + CurPtr = skipNewline(CurPtr, BufferEnd); + } else + return CurPtr; // Not an escaped newline. + } + return BufferEnd; +} + +/// Return the one past end pointer for C comments. +/// Very dumb, does not handle escaped newlines or trigraphs. +const char *findCCommentEnd(const char *BufferPtr, const char *BufferEnd) { + for ( ; BufferPtr != BufferEnd; ++BufferPtr) { + if (*BufferPtr == '*') { + assert(BufferPtr + 1 != BufferEnd); + if (*(BufferPtr + 1) == '/') + return BufferPtr; + } + } + llvm_unreachable("buffer end hit before '*/' was seen"); +} +} // unnamed namespace + +void Lexer::lexCommentText(Token &T) { + assert(CommentState == LCS_InsideBCPLComment || + CommentState == LCS_InsideCComment); + + switch (State) { + case LS_Normal: + break; + case LS_VerbatimBlockFirstLine: + lexVerbatimBlockFirstLine(T); + return; + case LS_VerbatimBlockBody: + lexVerbatimBlockBody(T); + return; + case LS_VerbatimLineText: + lexVerbatimLineText(T); + return; + case LS_HTMLStartTag: + lexHTMLStartTag(T); + return; + case LS_HTMLEndTag: + lexHTMLEndTag(T); + return; + } + + assert(State == LS_Normal); + + const char *TokenPtr = BufferPtr; + assert(TokenPtr < CommentEnd); + while (TokenPtr != CommentEnd) { + switch(*TokenPtr) { + case '\\': + case '@': { + TokenPtr++; + if (TokenPtr == CommentEnd) { + formTextToken(T, TokenPtr); + return; + } + char C = *TokenPtr; + switch (C) { + default: + break; + + case '\\': case '@': case '&': case '$': + case '#': case '<': case '>': case '%': + case '\"': case '.': case ':': + // This is one of \\ \@ \& \$ etc escape sequences. + TokenPtr++; + if (C == ':' && TokenPtr != CommentEnd && *TokenPtr == ':') { + // This is the \:: escape sequence. + TokenPtr++; + } + StringRef UnescapedText(BufferPtr + 1, TokenPtr - (BufferPtr + 1)); + formTokenWithChars(T, TokenPtr, tok::text); + T.setText(UnescapedText); + return; + } + + // Don't make zero-length commands. + if (!isCommandNameCharacter(*TokenPtr)) { + formTextToken(T, TokenPtr); + return; + } + + TokenPtr = skipCommandName(TokenPtr, CommentEnd); + unsigned Length = TokenPtr - (BufferPtr + 1); + + // Hardcoded support for lexing LaTeX formula commands + // \f$ \f[ \f] \f{ \f} as a single command. + if (Length == 1 && TokenPtr[-1] == 'f' && TokenPtr != CommentEnd) { + C = *TokenPtr; + if (C == '$' || C == '[' || C == ']' || C == '{' || C == '}') { + TokenPtr++; + Length++; + } + } + + const StringRef CommandName(BufferPtr + 1, Length); + StringRef EndName; + + if (Traits.isVerbatimBlockCommand(CommandName, EndName)) { + setupAndLexVerbatimBlock(T, TokenPtr, *BufferPtr, EndName); + return; + } + if (Traits.isVerbatimLineCommand(CommandName)) { + setupAndLexVerbatimLine(T, TokenPtr); + return; + } + formTokenWithChars(T, TokenPtr, tok::command); + T.setCommandName(CommandName); + return; + } + + case '&': + lexHTMLCharacterReference(T); + return; + + case '<': { + TokenPtr++; + if (TokenPtr == CommentEnd) { + formTextToken(T, TokenPtr); + return; + } + const char C = *TokenPtr; + if (isHTMLIdentifierStartingCharacter(C)) + setupAndLexHTMLStartTag(T); + else if (C == '/') + setupAndLexHTMLEndTag(T); + else + formTextToken(T, TokenPtr); + + return; + } + + case '\n': + case '\r': + TokenPtr = skipNewline(TokenPtr, CommentEnd); + formTokenWithChars(T, TokenPtr, tok::newline); + + if (CommentState == LCS_InsideCComment) + skipLineStartingDecorations(); + return; + + default: { + while (true) { + TokenPtr++; + if (TokenPtr == CommentEnd) + break; + const char C = *TokenPtr; + if(C == '\n' || C == '\r' || + C == '\\' || C == '@' || C == '&' || C == '<') + break; + } + formTextToken(T, TokenPtr); + return; + } + } + } +} + +void Lexer::setupAndLexVerbatimBlock(Token &T, + const char *TextBegin, + char Marker, StringRef EndName) { + VerbatimBlockEndCommandName.clear(); + VerbatimBlockEndCommandName.append(Marker == '\\' ? "\\" : "@"); + VerbatimBlockEndCommandName.append(EndName); + + StringRef Name(BufferPtr + 1, TextBegin - (BufferPtr + 1)); + formTokenWithChars(T, TextBegin, tok::verbatim_block_begin); + T.setVerbatimBlockName(Name); + + // If there is a newline following the verbatim opening command, skip the + // newline so that we don't create an tok::verbatim_block_line with empty + // text content. + if (BufferPtr != CommentEnd) { + const char C = *BufferPtr; + if (C == '\n' || C == '\r') { + BufferPtr = skipNewline(BufferPtr, CommentEnd); + State = LS_VerbatimBlockBody; + return; + } + } + + State = LS_VerbatimBlockFirstLine; +} + +void Lexer::lexVerbatimBlockFirstLine(Token &T) { +again: + assert(BufferPtr < CommentEnd); + + // FIXME: It would be better to scan the text once, finding either the block + // end command or newline. + // + // Extract current line. + const char *Newline = findNewline(BufferPtr, CommentEnd); + StringRef Line(BufferPtr, Newline - BufferPtr); + + // Look for end command in current line. + size_t Pos = Line.find(VerbatimBlockEndCommandName); + const char *TextEnd; + const char *NextLine; + if (Pos == StringRef::npos) { + // Current line is completely verbatim. + TextEnd = Newline; + NextLine = skipNewline(Newline, CommentEnd); + } else if (Pos == 0) { + // Current line contains just an end command. + const char *End = BufferPtr + VerbatimBlockEndCommandName.size(); + StringRef Name(BufferPtr + 1, End - (BufferPtr + 1)); + formTokenWithChars(T, End, tok::verbatim_block_end); + T.setVerbatimBlockName(Name); + State = LS_Normal; + return; + } else { + // There is some text, followed by end command. Extract text first. + TextEnd = BufferPtr + Pos; + NextLine = TextEnd; + // If there is only whitespace before end command, skip whitespace. + if (isWhitespace(BufferPtr, TextEnd)) { + BufferPtr = TextEnd; + goto again; + } + } + + StringRef Text(BufferPtr, TextEnd - BufferPtr); + formTokenWithChars(T, NextLine, tok::verbatim_block_line); + T.setVerbatimBlockText(Text); + + State = LS_VerbatimBlockBody; +} + +void Lexer::lexVerbatimBlockBody(Token &T) { + assert(State == LS_VerbatimBlockBody); + + if (CommentState == LCS_InsideCComment) + skipLineStartingDecorations(); + + lexVerbatimBlockFirstLine(T); +} + +void Lexer::setupAndLexVerbatimLine(Token &T, const char *TextBegin) { + const StringRef Name(BufferPtr + 1, TextBegin - BufferPtr - 1); + formTokenWithChars(T, TextBegin, tok::verbatim_line_name); + T.setVerbatimLineName(Name); + + State = LS_VerbatimLineText; +} + +void Lexer::lexVerbatimLineText(Token &T) { + assert(State == LS_VerbatimLineText); + + // Extract current line. + const char *Newline = findNewline(BufferPtr, CommentEnd); + const StringRef Text(BufferPtr, Newline - BufferPtr); + formTokenWithChars(T, Newline, tok::verbatim_line_text); + T.setVerbatimLineText(Text); + + State = LS_Normal; +} + +void Lexer::lexHTMLCharacterReference(Token &T) { + const char *TokenPtr = BufferPtr; + assert(*TokenPtr == '&'); + TokenPtr++; + if (TokenPtr == CommentEnd) { + formTextToken(T, TokenPtr); + return; + } + const char *NamePtr; + bool isNamed = false; + bool isDecimal = false; + char C = *TokenPtr; + if (isHTMLNamedCharacterReferenceCharacter(C)) { + NamePtr = TokenPtr; + TokenPtr = skipNamedCharacterReference(TokenPtr, CommentEnd); + isNamed = true; + } else if (C == '#') { + TokenPtr++; + if (TokenPtr == CommentEnd) { + formTextToken(T, TokenPtr); + return; + } + C = *TokenPtr; + if (isHTMLDecimalCharacterReferenceCharacter(C)) { + NamePtr = TokenPtr; + TokenPtr = skipDecimalCharacterReference(TokenPtr, CommentEnd); + isDecimal = true; + } else if (C == 'x' || C == 'X') { + TokenPtr++; + NamePtr = TokenPtr; + TokenPtr = skipHexCharacterReference(TokenPtr, CommentEnd); + } else { + formTextToken(T, TokenPtr); + return; + } + } else { + formTextToken(T, TokenPtr); + return; + } + if (NamePtr == TokenPtr || TokenPtr == CommentEnd || + *TokenPtr != ';') { + formTextToken(T, TokenPtr); + return; + } + StringRef Name(NamePtr, TokenPtr - NamePtr); + TokenPtr++; // Skip semicolon. + StringRef Resolved; + if (isNamed) + Resolved = resolveHTMLNamedCharacterReference(Name); + else if (isDecimal) + Resolved = resolveHTMLDecimalCharacterReference(Name); + else + Resolved = resolveHTMLHexCharacterReference(Name); + + if (Resolved.empty()) { + formTextToken(T, TokenPtr); + return; + } + formTokenWithChars(T, TokenPtr, tok::text); + T.setText(Resolved); + return; +} + +void Lexer::setupAndLexHTMLStartTag(Token &T) { + assert(BufferPtr[0] == '<' && + isHTMLIdentifierStartingCharacter(BufferPtr[1])); + const char *TagNameEnd = skipHTMLIdentifier(BufferPtr + 2, CommentEnd); + + StringRef Name(BufferPtr + 1, TagNameEnd - (BufferPtr + 1)); + formTokenWithChars(T, TagNameEnd, tok::html_start_tag); + T.setHTMLTagStartName(Name); + + BufferPtr = skipWhitespace(BufferPtr, CommentEnd); + + const char C = *BufferPtr; + if (BufferPtr != CommentEnd && + (C == '>' || C == '/' || isHTMLIdentifierStartingCharacter(C))) + State = LS_HTMLStartTag; +} + +void Lexer::lexHTMLStartTag(Token &T) { + assert(State == LS_HTMLStartTag); + + const char *TokenPtr = BufferPtr; + char C = *TokenPtr; + if (isHTMLIdentifierCharacter(C)) { + TokenPtr = skipHTMLIdentifier(TokenPtr, CommentEnd); + StringRef Ident(BufferPtr, TokenPtr - BufferPtr); + formTokenWithChars(T, TokenPtr, tok::html_ident); + T.setHTMLIdent(Ident); + } else { + switch (C) { + case '=': + TokenPtr++; + formTokenWithChars(T, TokenPtr, tok::html_equals); + break; + case '\"': + case '\'': { + const char *OpenQuote = TokenPtr; + TokenPtr = skipHTMLQuotedString(TokenPtr, CommentEnd); + const char *ClosingQuote = TokenPtr; + if (TokenPtr != CommentEnd) // Skip closing quote. + TokenPtr++; + formTokenWithChars(T, TokenPtr, tok::html_quoted_string); + T.setHTMLQuotedString(StringRef(OpenQuote + 1, + ClosingQuote - (OpenQuote + 1))); + break; + } + case '>': + TokenPtr++; + formTokenWithChars(T, TokenPtr, tok::html_greater); + State = LS_Normal; + return; + case '/': + TokenPtr++; + if (TokenPtr != CommentEnd && *TokenPtr == '>') { + TokenPtr++; + formTokenWithChars(T, TokenPtr, tok::html_slash_greater); + } else + formTextToken(T, TokenPtr); + + State = LS_Normal; + return; + } + } + + // Now look ahead and return to normal state if we don't see any HTML tokens + // ahead. + BufferPtr = skipWhitespace(BufferPtr, CommentEnd); + if (BufferPtr == CommentEnd) { + State = LS_Normal; + return; + } + + C = *BufferPtr; + if (!isHTMLIdentifierStartingCharacter(C) && + C != '=' && C != '\"' && C != '\'' && C != '>') { + State = LS_Normal; + return; + } +} + +void Lexer::setupAndLexHTMLEndTag(Token &T) { + assert(BufferPtr[0] == '<' && BufferPtr[1] == '/'); + + const char *TagNameBegin = skipWhitespace(BufferPtr + 2, CommentEnd); + const char *TagNameEnd = skipHTMLIdentifier(TagNameBegin, CommentEnd); + + const char *End = skipWhitespace(TagNameEnd, CommentEnd); + + formTokenWithChars(T, End, tok::html_end_tag); + T.setHTMLTagEndName(StringRef(TagNameBegin, TagNameEnd - TagNameBegin)); + + if (BufferPtr != CommentEnd && *BufferPtr == '>') + State = LS_HTMLEndTag; +} + +void Lexer::lexHTMLEndTag(Token &T) { + assert(BufferPtr != CommentEnd && *BufferPtr == '>'); + + formTokenWithChars(T, BufferPtr + 1, tok::html_greater); + State = LS_Normal; +} + +Lexer::Lexer(llvm::BumpPtrAllocator &Allocator, const CommandTraits &Traits, + SourceLocation FileLoc, const CommentOptions &CommOpts, + const char *BufferStart, const char *BufferEnd): + Allocator(Allocator), Traits(Traits), + BufferStart(BufferStart), BufferEnd(BufferEnd), + FileLoc(FileLoc), CommOpts(CommOpts), BufferPtr(BufferStart), + CommentState(LCS_BeforeComment), State(LS_Normal) { +} + +void Lexer::lex(Token &T) { +again: + switch (CommentState) { + case LCS_BeforeComment: + if (BufferPtr == BufferEnd) { + formTokenWithChars(T, BufferPtr, tok::eof); + return; + } + + assert(*BufferPtr == '/'); + BufferPtr++; // Skip first slash. + switch(*BufferPtr) { + case '/': { // BCPL comment. + BufferPtr++; // Skip second slash. + + if (BufferPtr != BufferEnd) { + // Skip Doxygen magic marker, if it is present. + // It might be missing because of a typo //< or /*<, or because we + // merged this non-Doxygen comment into a bunch of Doxygen comments + // around it: /** ... */ /* ... */ /** ... */ + const char C = *BufferPtr; + if (C == '/' || C == '!') + BufferPtr++; + } + + // Skip less-than symbol that marks trailing comments. + // Skip it even if the comment is not a Doxygen one, because //< and /*< + // are frequent typos. + if (BufferPtr != BufferEnd && *BufferPtr == '<') + BufferPtr++; + + CommentState = LCS_InsideBCPLComment; + if (State != LS_VerbatimBlockBody && State != LS_VerbatimBlockFirstLine) + State = LS_Normal; + CommentEnd = findBCPLCommentEnd(BufferPtr, BufferEnd); + goto again; + } + case '*': { // C comment. + BufferPtr++; // Skip star. + + // Skip Doxygen magic marker. + const char C = *BufferPtr; + if ((C == '*' && *(BufferPtr + 1) != '/') || C == '!') + BufferPtr++; + + // Skip less-than symbol that marks trailing comments. + if (BufferPtr != BufferEnd && *BufferPtr == '<') + BufferPtr++; + + CommentState = LCS_InsideCComment; + State = LS_Normal; + CommentEnd = findCCommentEnd(BufferPtr, BufferEnd); + goto again; + } + default: + llvm_unreachable("second character of comment should be '/' or '*'"); + } + + case LCS_BetweenComments: { + // Consecutive comments are extracted only if there is only whitespace + // between them. So we can search for the start of the next comment. + const char *EndWhitespace = BufferPtr; + while(EndWhitespace != BufferEnd && *EndWhitespace != '/') + EndWhitespace++; + + // Turn any whitespace between comments (and there is only whitespace + // between them -- guaranteed by comment extraction) into a newline. We + // have two newlines between C comments in total (first one was synthesized + // after a comment). + formTokenWithChars(T, EndWhitespace, tok::newline); + + CommentState = LCS_BeforeComment; + break; + } + + case LCS_InsideBCPLComment: + case LCS_InsideCComment: + if (BufferPtr != CommentEnd) { + lexCommentText(T); + break; + } else { + // Skip C comment closing sequence. + if (CommentState == LCS_InsideCComment) { + assert(BufferPtr[0] == '*' && BufferPtr[1] == '/'); + BufferPtr += 2; + assert(BufferPtr <= BufferEnd); + + // Synthenize newline just after the C comment, regardless if there is + // actually a newline. + formTokenWithChars(T, BufferPtr, tok::newline); + + CommentState = LCS_BetweenComments; + break; + } else { + // Don't synthesized a newline after BCPL comment. + CommentState = LCS_BetweenComments; + goto again; + } + } + } +} + +StringRef Lexer::getSpelling(const Token &Tok, + const SourceManager &SourceMgr, + bool *Invalid) 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; + return StringRef(); + } + + const char *Begin = File.data() + LocInfo.second; + return StringRef(Begin, Tok.getLength()); +} + +} // end namespace comments +} // end namespace clang + diff --git a/lib/AST/CommentParser.cpp b/lib/AST/CommentParser.cpp new file mode 100644 index 000000000000..43abf6a476a8 --- /dev/null +++ b/lib/AST/CommentParser.cpp @@ -0,0 +1,722 @@ +//===--- CommentParser.cpp - Doxygen comment parser -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/CommentParser.h" +#include "clang/AST/CommentSema.h" +#include "clang/AST/CommentDiagnostic.h" +#include "clang/AST/CommentCommandTraits.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/ErrorHandling.h" + +namespace clang { +namespace comments { + +/// Re-lexes a sequence of tok::text tokens. +class TextTokenRetokenizer { + llvm::BumpPtrAllocator &Allocator; + Parser &P; + + /// This flag is set when there are no more tokens we can fetch from lexer. + bool NoMoreInterestingTokens; + + /// Token buffer: tokens we have processed and lookahead. + SmallVector<Token, 16> Toks; + + /// A position in \c Toks. + struct Position { + unsigned CurToken; + const char *BufferStart; + const char *BufferEnd; + const char *BufferPtr; + SourceLocation BufferStartLoc; + }; + + /// Current position in Toks. + Position Pos; + + bool isEnd() const { + return Pos.CurToken >= Toks.size(); + } + + /// Sets up the buffer pointers to point to current token. + void setupBuffer() { + assert(!isEnd()); + const Token &Tok = Toks[Pos.CurToken]; + + Pos.BufferStart = Tok.getText().begin(); + Pos.BufferEnd = Tok.getText().end(); + Pos.BufferPtr = Pos.BufferStart; + Pos.BufferStartLoc = Tok.getLocation(); + } + + SourceLocation getSourceLocation() const { + const unsigned CharNo = Pos.BufferPtr - Pos.BufferStart; + return Pos.BufferStartLoc.getLocWithOffset(CharNo); + } + + char peek() const { + assert(!isEnd()); + assert(Pos.BufferPtr != Pos.BufferEnd); + return *Pos.BufferPtr; + } + + void consumeChar() { + assert(!isEnd()); + assert(Pos.BufferPtr != Pos.BufferEnd); + Pos.BufferPtr++; + if (Pos.BufferPtr == Pos.BufferEnd) { + Pos.CurToken++; + if (isEnd() && !addToken()) + return; + + assert(!isEnd()); + setupBuffer(); + } + } + + /// Add a token. + /// Returns true on success, false if there are no interesting tokens to + /// fetch from lexer. + bool addToken() { + if (NoMoreInterestingTokens) + return false; + + if (P.Tok.is(tok::newline)) { + // If we see a single newline token between text tokens, skip it. + Token Newline = P.Tok; + P.consumeToken(); + if (P.Tok.isNot(tok::text)) { + P.putBack(Newline); + NoMoreInterestingTokens = true; + return false; + } + } + if (P.Tok.isNot(tok::text)) { + NoMoreInterestingTokens = true; + return false; + } + + Toks.push_back(P.Tok); + P.consumeToken(); + if (Toks.size() == 1) + setupBuffer(); + return true; + } + + static bool isWhitespace(char C) { + return C == ' ' || C == '\n' || C == '\r' || + C == '\t' || C == '\f' || C == '\v'; + } + + void consumeWhitespace() { + while (!isEnd()) { + if (isWhitespace(peek())) + consumeChar(); + else + break; + } + } + + void formTokenWithChars(Token &Result, + SourceLocation Loc, + const char *TokBegin, + unsigned TokLength, + StringRef Text) { + Result.setLocation(Loc); + Result.setKind(tok::text); + Result.setLength(TokLength); +#ifndef NDEBUG + Result.TextPtr1 = "<UNSET>"; + Result.TextLen1 = 7; +#endif + Result.setText(Text); + } + +public: + TextTokenRetokenizer(llvm::BumpPtrAllocator &Allocator, Parser &P): + Allocator(Allocator), P(P), NoMoreInterestingTokens(false) { + Pos.CurToken = 0; + addToken(); + } + + /// Extract a word -- sequence of non-whitespace characters. + bool lexWord(Token &Tok) { + if (isEnd()) + return false; + + Position SavedPos = Pos; + + consumeWhitespace(); + SmallString<32> WordText; + const char *WordBegin = Pos.BufferPtr; + SourceLocation Loc = getSourceLocation(); + while (!isEnd()) { + const char C = peek(); + if (!isWhitespace(C)) { + WordText.push_back(C); + consumeChar(); + } else + break; + } + const unsigned Length = WordText.size(); + if (Length == 0) { + Pos = SavedPos; + return false; + } + + char *TextPtr = Allocator.Allocate<char>(Length + 1); + + memcpy(TextPtr, WordText.c_str(), Length + 1); + StringRef Text = StringRef(TextPtr, Length); + + formTokenWithChars(Tok, Loc, WordBegin, + Pos.BufferPtr - WordBegin, Text); + return true; + } + + bool lexDelimitedSeq(Token &Tok, char OpenDelim, char CloseDelim) { + if (isEnd()) + return false; + + Position SavedPos = Pos; + + consumeWhitespace(); + SmallString<32> WordText; + const char *WordBegin = Pos.BufferPtr; + SourceLocation Loc = getSourceLocation(); + bool Error = false; + if (!isEnd()) { + const char C = peek(); + if (C == OpenDelim) { + WordText.push_back(C); + consumeChar(); + } else + Error = true; + } + char C = '\0'; + while (!Error && !isEnd()) { + C = peek(); + WordText.push_back(C); + consumeChar(); + if (C == CloseDelim) + break; + } + if (!Error && C != CloseDelim) + Error = true; + + if (Error) { + Pos = SavedPos; + return false; + } + + const unsigned Length = WordText.size(); + char *TextPtr = Allocator.Allocate<char>(Length + 1); + + memcpy(TextPtr, WordText.c_str(), Length + 1); + StringRef Text = StringRef(TextPtr, Length); + + formTokenWithChars(Tok, Loc, WordBegin, + Pos.BufferPtr - WordBegin, Text); + return true; + } + + /// Put back tokens that we didn't consume. + void putBackLeftoverTokens() { + if (isEnd()) + return; + + bool HavePartialTok = false; + Token PartialTok; + if (Pos.BufferPtr != Pos.BufferStart) { + formTokenWithChars(PartialTok, getSourceLocation(), + Pos.BufferPtr, Pos.BufferEnd - Pos.BufferPtr, + StringRef(Pos.BufferPtr, + Pos.BufferEnd - Pos.BufferPtr)); + HavePartialTok = true; + Pos.CurToken++; + } + + P.putBack(llvm::makeArrayRef(Toks.begin() + Pos.CurToken, Toks.end())); + Pos.CurToken = Toks.size(); + + if (HavePartialTok) + P.putBack(PartialTok); + } +}; + +Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator, + const SourceManager &SourceMgr, DiagnosticsEngine &Diags, + const CommandTraits &Traits): + L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), + Traits(Traits) { + consumeToken(); +} + +void Parser::parseParamCommandArgs(ParamCommandComment *PC, + TextTokenRetokenizer &Retokenizer) { + Token Arg; + // Check if argument looks like direction specification: [dir] + // e.g., [in], [out], [in,out] + if (Retokenizer.lexDelimitedSeq(Arg, '[', ']')) + S.actOnParamCommandDirectionArg(PC, + Arg.getLocation(), + Arg.getEndLocation(), + Arg.getText()); + + if (Retokenizer.lexWord(Arg)) + S.actOnParamCommandParamNameArg(PC, + Arg.getLocation(), + Arg.getEndLocation(), + Arg.getText()); +} + +void Parser::parseTParamCommandArgs(TParamCommandComment *TPC, + TextTokenRetokenizer &Retokenizer) { + Token Arg; + if (Retokenizer.lexWord(Arg)) + S.actOnTParamCommandParamNameArg(TPC, + Arg.getLocation(), + Arg.getEndLocation(), + Arg.getText()); +} + +void Parser::parseBlockCommandArgs(BlockCommandComment *BC, + TextTokenRetokenizer &Retokenizer, + unsigned NumArgs) { + typedef BlockCommandComment::Argument Argument; + Argument *Args = + new (Allocator.Allocate<Argument>(NumArgs)) Argument[NumArgs]; + unsigned ParsedArgs = 0; + Token Arg; + while (ParsedArgs < NumArgs && Retokenizer.lexWord(Arg)) { + Args[ParsedArgs] = Argument(SourceRange(Arg.getLocation(), + Arg.getEndLocation()), + Arg.getText()); + ParsedArgs++; + } + + S.actOnBlockCommandArgs(BC, llvm::makeArrayRef(Args, ParsedArgs)); +} + +BlockCommandComment *Parser::parseBlockCommand() { + assert(Tok.is(tok::command)); + + ParamCommandComment *PC; + TParamCommandComment *TPC; + BlockCommandComment *BC; + bool IsParam = false; + bool IsTParam = false; + unsigned NumArgs = 0; + if (Traits.isParamCommand(Tok.getCommandName())) { + IsParam = true; + PC = S.actOnParamCommandStart(Tok.getLocation(), + Tok.getEndLocation(), + Tok.getCommandName()); + } if (Traits.isTParamCommand(Tok.getCommandName())) { + IsTParam = true; + TPC = S.actOnTParamCommandStart(Tok.getLocation(), + Tok.getEndLocation(), + Tok.getCommandName()); + } else { + NumArgs = Traits.getBlockCommandNumArgs(Tok.getCommandName()); + BC = S.actOnBlockCommandStart(Tok.getLocation(), + Tok.getEndLocation(), + Tok.getCommandName()); + } + consumeToken(); + + if (Tok.is(tok::command) && Traits.isBlockCommand(Tok.getCommandName())) { + // Block command ahead. We can't nest block commands, so pretend that this + // command has an empty argument. + ParagraphComment *Paragraph = S.actOnParagraphComment( + ArrayRef<InlineContentComment *>()); + if (IsParam) { + S.actOnParamCommandFinish(PC, Paragraph); + return PC; + } else if (IsTParam) { + S.actOnTParamCommandFinish(TPC, Paragraph); + return TPC; + } else { + S.actOnBlockCommandFinish(BC, Paragraph); + return BC; + } + } + + if (IsParam || IsTParam || NumArgs > 0) { + // In order to parse command arguments we need to retokenize a few + // following text tokens. + TextTokenRetokenizer Retokenizer(Allocator, *this); + + if (IsParam) + parseParamCommandArgs(PC, Retokenizer); + else if (IsTParam) + parseTParamCommandArgs(TPC, Retokenizer); + else + parseBlockCommandArgs(BC, Retokenizer, NumArgs); + + Retokenizer.putBackLeftoverTokens(); + } + + BlockContentComment *Block = parseParagraphOrBlockCommand(); + // Since we have checked for a block command, we should have parsed a + // paragraph. + ParagraphComment *Paragraph = cast<ParagraphComment>(Block); + if (IsParam) { + S.actOnParamCommandFinish(PC, Paragraph); + return PC; + } else if (IsTParam) { + S.actOnTParamCommandFinish(TPC, Paragraph); + return TPC; + } else { + S.actOnBlockCommandFinish(BC, Paragraph); + return BC; + } +} + +InlineCommandComment *Parser::parseInlineCommand() { + assert(Tok.is(tok::command)); + + const Token CommandTok = Tok; + consumeToken(); + + TextTokenRetokenizer Retokenizer(Allocator, *this); + + Token ArgTok; + bool ArgTokValid = Retokenizer.lexWord(ArgTok); + + InlineCommandComment *IC; + if (ArgTokValid) { + IC = S.actOnInlineCommand(CommandTok.getLocation(), + CommandTok.getEndLocation(), + CommandTok.getCommandName(), + ArgTok.getLocation(), + ArgTok.getEndLocation(), + ArgTok.getText()); + } else { + IC = S.actOnInlineCommand(CommandTok.getLocation(), + CommandTok.getEndLocation(), + CommandTok.getCommandName()); + } + + Retokenizer.putBackLeftoverTokens(); + + return IC; +} + +HTMLStartTagComment *Parser::parseHTMLStartTag() { + assert(Tok.is(tok::html_start_tag)); + HTMLStartTagComment *HST = + S.actOnHTMLStartTagStart(Tok.getLocation(), + Tok.getHTMLTagStartName()); + consumeToken(); + + SmallVector<HTMLStartTagComment::Attribute, 2> Attrs; + while (true) { + switch (Tok.getKind()) { + case tok::html_ident: { + Token Ident = Tok; + consumeToken(); + if (Tok.isNot(tok::html_equals)) { + Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(), + Ident.getHTMLIdent())); + continue; + } + Token Equals = Tok; + consumeToken(); + if (Tok.isNot(tok::html_quoted_string)) { + Diag(Tok.getLocation(), + diag::warn_doc_html_start_tag_expected_quoted_string) + << SourceRange(Equals.getLocation()); + Attrs.push_back(HTMLStartTagComment::Attribute(Ident.getLocation(), + Ident.getHTMLIdent())); + while (Tok.is(tok::html_equals) || + Tok.is(tok::html_quoted_string)) + consumeToken(); + continue; + } + Attrs.push_back(HTMLStartTagComment::Attribute( + Ident.getLocation(), + Ident.getHTMLIdent(), + Equals.getLocation(), + SourceRange(Tok.getLocation(), + Tok.getEndLocation()), + Tok.getHTMLQuotedString())); + consumeToken(); + continue; + } + + case tok::html_greater: + S.actOnHTMLStartTagFinish(HST, + S.copyArray(llvm::makeArrayRef(Attrs)), + Tok.getLocation(), + /* IsSelfClosing = */ false); + consumeToken(); + return HST; + + case tok::html_slash_greater: + S.actOnHTMLStartTagFinish(HST, + S.copyArray(llvm::makeArrayRef(Attrs)), + Tok.getLocation(), + /* IsSelfClosing = */ true); + consumeToken(); + return HST; + + case tok::html_equals: + case tok::html_quoted_string: + Diag(Tok.getLocation(), + diag::warn_doc_html_start_tag_expected_ident_or_greater); + while (Tok.is(tok::html_equals) || + Tok.is(tok::html_quoted_string)) + consumeToken(); + if (Tok.is(tok::html_ident) || + Tok.is(tok::html_greater) || + Tok.is(tok::html_slash_greater)) + continue; + + S.actOnHTMLStartTagFinish(HST, + S.copyArray(llvm::makeArrayRef(Attrs)), + SourceLocation(), + /* IsSelfClosing = */ false); + return HST; + + default: + // Not a token from an HTML start tag. Thus HTML tag prematurely ended. + S.actOnHTMLStartTagFinish(HST, + S.copyArray(llvm::makeArrayRef(Attrs)), + SourceLocation(), + /* IsSelfClosing = */ false); + bool StartLineInvalid; + const unsigned StartLine = SourceMgr.getPresumedLineNumber( + HST->getLocation(), + &StartLineInvalid); + bool EndLineInvalid; + const unsigned EndLine = SourceMgr.getPresumedLineNumber( + Tok.getLocation(), + &EndLineInvalid); + if (StartLineInvalid || EndLineInvalid || StartLine == EndLine) + Diag(Tok.getLocation(), + diag::warn_doc_html_start_tag_expected_ident_or_greater) + << HST->getSourceRange(); + else { + Diag(Tok.getLocation(), + diag::warn_doc_html_start_tag_expected_ident_or_greater); + Diag(HST->getLocation(), diag::note_doc_html_tag_started_here) + << HST->getSourceRange(); + } + return HST; + } + } +} + +HTMLEndTagComment *Parser::parseHTMLEndTag() { + assert(Tok.is(tok::html_end_tag)); + Token TokEndTag = Tok; + consumeToken(); + SourceLocation Loc; + if (Tok.is(tok::html_greater)) { + Loc = Tok.getLocation(); + consumeToken(); + } + + return S.actOnHTMLEndTag(TokEndTag.getLocation(), + Loc, + TokEndTag.getHTMLTagEndName()); +} + +BlockContentComment *Parser::parseParagraphOrBlockCommand() { + SmallVector<InlineContentComment *, 8> Content; + + while (true) { + switch (Tok.getKind()) { + case tok::verbatim_block_begin: + case tok::verbatim_line_name: + case tok::eof: + assert(Content.size() != 0); + break; // Block content or EOF ahead, finish this parapgaph. + + case tok::command: + if (Traits.isBlockCommand(Tok.getCommandName())) { + if (Content.size() == 0) + return parseBlockCommand(); + break; // Block command ahead, finish this parapgaph. + } + if (Traits.isInlineCommand(Tok.getCommandName())) { + Content.push_back(parseInlineCommand()); + continue; + } + + // Not a block command, not an inline command ==> an unknown command. + Content.push_back(S.actOnUnknownCommand(Tok.getLocation(), + Tok.getEndLocation(), + Tok.getCommandName())); + consumeToken(); + continue; + + case tok::newline: { + consumeToken(); + if (Tok.is(tok::newline) || Tok.is(tok::eof)) { + consumeToken(); + break; // Two newlines -- end of paragraph. + } + if (Content.size() > 0) + Content.back()->addTrailingNewline(); + continue; + } + + // Don't deal with HTML tag soup now. + case tok::html_start_tag: + Content.push_back(parseHTMLStartTag()); + continue; + + case tok::html_end_tag: + Content.push_back(parseHTMLEndTag()); + continue; + + case tok::text: + Content.push_back(S.actOnText(Tok.getLocation(), + Tok.getEndLocation(), + Tok.getText())); + consumeToken(); + continue; + + case tok::verbatim_block_line: + case tok::verbatim_block_end: + case tok::verbatim_line_text: + case tok::html_ident: + case tok::html_equals: + case tok::html_quoted_string: + case tok::html_greater: + case tok::html_slash_greater: + llvm_unreachable("should not see this token"); + } + break; + } + + return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content))); +} + +VerbatimBlockComment *Parser::parseVerbatimBlock() { + assert(Tok.is(tok::verbatim_block_begin)); + + VerbatimBlockComment *VB = + S.actOnVerbatimBlockStart(Tok.getLocation(), + Tok.getVerbatimBlockName()); + consumeToken(); + + // Don't create an empty line if verbatim opening command is followed + // by a newline. + if (Tok.is(tok::newline)) + consumeToken(); + + SmallVector<VerbatimBlockLineComment *, 8> Lines; + while (Tok.is(tok::verbatim_block_line) || + Tok.is(tok::newline)) { + VerbatimBlockLineComment *Line; + if (Tok.is(tok::verbatim_block_line)) { + Line = S.actOnVerbatimBlockLine(Tok.getLocation(), + Tok.getVerbatimBlockText()); + consumeToken(); + if (Tok.is(tok::newline)) { + consumeToken(); + } + } else { + // Empty line, just a tok::newline. + Line = S.actOnVerbatimBlockLine(Tok.getLocation(), ""); + consumeToken(); + } + Lines.push_back(Line); + } + + if (Tok.is(tok::verbatim_block_end)) { + S.actOnVerbatimBlockFinish(VB, Tok.getLocation(), + Tok.getVerbatimBlockName(), + S.copyArray(llvm::makeArrayRef(Lines))); + consumeToken(); + } else { + // Unterminated \\verbatim block + S.actOnVerbatimBlockFinish(VB, SourceLocation(), "", + S.copyArray(llvm::makeArrayRef(Lines))); + } + + return VB; +} + +VerbatimLineComment *Parser::parseVerbatimLine() { + assert(Tok.is(tok::verbatim_line_name)); + + Token NameTok = Tok; + consumeToken(); + + SourceLocation TextBegin; + StringRef Text; + // Next token might not be a tok::verbatim_line_text if verbatim line + // starting command comes just before a newline or comment end. + if (Tok.is(tok::verbatim_line_text)) { + TextBegin = Tok.getLocation(); + Text = Tok.getVerbatimLineText(); + } else { + TextBegin = NameTok.getEndLocation(); + Text = ""; + } + + VerbatimLineComment *VL = S.actOnVerbatimLine(NameTok.getLocation(), + NameTok.getVerbatimLineName(), + TextBegin, + Text); + consumeToken(); + return VL; +} + +BlockContentComment *Parser::parseBlockContent() { + switch (Tok.getKind()) { + case tok::text: + case tok::command: + case tok::html_start_tag: + case tok::html_end_tag: + return parseParagraphOrBlockCommand(); + + case tok::verbatim_block_begin: + return parseVerbatimBlock(); + + case tok::verbatim_line_name: + return parseVerbatimLine(); + + case tok::eof: + case tok::newline: + case tok::verbatim_block_line: + case tok::verbatim_block_end: + case tok::verbatim_line_text: + case tok::html_ident: + case tok::html_equals: + case tok::html_quoted_string: + case tok::html_greater: + case tok::html_slash_greater: + llvm_unreachable("should not see this token"); + } + llvm_unreachable("bogus token kind"); +} + +FullComment *Parser::parseFullComment() { + // Skip newlines at the beginning of the comment. + while (Tok.is(tok::newline)) + consumeToken(); + + SmallVector<BlockContentComment *, 8> Blocks; + while (Tok.isNot(tok::eof)) { + Blocks.push_back(parseBlockContent()); + + // Skip extra newlines after paragraph end. + while (Tok.is(tok::newline)) + consumeToken(); + } + return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks))); +} + +} // end namespace comments +} // end namespace clang diff --git a/lib/AST/CommentSema.cpp b/lib/AST/CommentSema.cpp new file mode 100644 index 000000000000..c39ee573ef6f --- /dev/null +++ b/lib/AST/CommentSema.cpp @@ -0,0 +1,739 @@ +//===--- CommentSema.cpp - Doxygen comment semantic analysis --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/CommentSema.h" +#include "clang/AST/CommentDiagnostic.h" +#include "clang/AST/CommentCommandTraits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/StringSwitch.h" + +namespace clang { +namespace comments { + +Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr, + DiagnosticsEngine &Diags, const CommandTraits &Traits) : + Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits), + ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) { +} + +void Sema::setDecl(const Decl *D) { + if (!D) + return; + + ThisDeclInfo = new (Allocator) DeclInfo; + ThisDeclInfo->ThisDecl = D; + ThisDeclInfo->IsFilled = false; +} + +ParagraphComment *Sema::actOnParagraphComment( + ArrayRef<InlineContentComment *> Content) { + return new (Allocator) ParagraphComment(Content); +} + +BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin, + SourceLocation LocEnd, + StringRef Name) { + return new (Allocator) BlockCommandComment(LocBegin, LocEnd, Name); +} + +void Sema::actOnBlockCommandArgs(BlockCommandComment *Command, + ArrayRef<BlockCommandComment::Argument> Args) { + Command->setArgs(Args); +} + +void Sema::actOnBlockCommandFinish(BlockCommandComment *Command, + ParagraphComment *Paragraph) { + Command->setParagraph(Paragraph); + checkBlockCommandEmptyParagraph(Command); + checkBlockCommandDuplicate(Command); + checkReturnsCommand(Command); +} + +ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin, + SourceLocation LocEnd, + StringRef Name) { + ParamCommandComment *Command = + new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name); + + if (!isFunctionDecl()) + Diag(Command->getLocation(), + diag::warn_doc_param_not_attached_to_a_function_decl) + << Command->getCommandNameRange(); + + return Command; +} + +void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command, + SourceLocation ArgLocBegin, + SourceLocation ArgLocEnd, + StringRef Arg) { + ParamCommandComment::PassDirection Direction; + std::string ArgLower = Arg.lower(); + // TODO: optimize: lower Name first (need an API in SmallString for that), + // after that StringSwitch. + if (ArgLower == "[in]") + Direction = ParamCommandComment::In; + else if (ArgLower == "[out]") + Direction = ParamCommandComment::Out; + else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") + Direction = ParamCommandComment::InOut; + else { + // Remove spaces. + std::string::iterator O = ArgLower.begin(); + for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end(); + I != E; ++I) { + const char C = *I; + if (C != ' ' && C != '\n' && C != '\r' && + C != '\t' && C != '\v' && C != '\f') + *O++ = C; + } + ArgLower.resize(O - ArgLower.begin()); + + bool RemovingWhitespaceHelped = false; + if (ArgLower == "[in]") { + Direction = ParamCommandComment::In; + RemovingWhitespaceHelped = true; + } else if (ArgLower == "[out]") { + Direction = ParamCommandComment::Out; + RemovingWhitespaceHelped = true; + } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") { + Direction = ParamCommandComment::InOut; + RemovingWhitespaceHelped = true; + } else { + Direction = ParamCommandComment::In; + RemovingWhitespaceHelped = false; + } + + SourceRange ArgRange(ArgLocBegin, ArgLocEnd); + if (RemovingWhitespaceHelped) + Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction) + << ArgRange + << FixItHint::CreateReplacement( + ArgRange, + ParamCommandComment::getDirectionAsString(Direction)); + else + Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction) + << ArgRange; + } + Command->setDirection(Direction, /* Explicit = */ true); +} + +void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command, + SourceLocation ArgLocBegin, + SourceLocation ArgLocEnd, + StringRef Arg) { + // Parser will not feed us more arguments than needed. + assert(Command->getNumArgs() == 0); + + if (!Command->isDirectionExplicit()) { + // User didn't provide a direction argument. + Command->setDirection(ParamCommandComment::In, /* Explicit = */ false); + } + typedef BlockCommandComment::Argument Argument; + Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin, + ArgLocEnd), + Arg); + Command->setArgs(llvm::makeArrayRef(A, 1)); + + if (!isFunctionDecl()) { + // We already warned that this \\param is not attached to a function decl. + return; + } + + ArrayRef<const ParmVarDecl *> ParamVars = getParamVars(); + + // Check that referenced parameter name is in the function decl. + const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars); + if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) { + Command->setParamIndex(ResolvedParamIndex); + if (ParamVarDocs[ResolvedParamIndex]) { + SourceRange ArgRange(ArgLocBegin, ArgLocEnd); + Diag(ArgLocBegin, diag::warn_doc_param_duplicate) + << Arg << ArgRange; + ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex]; + Diag(PrevCommand->getLocation(), diag::note_doc_param_previous) + << PrevCommand->getParamNameRange(); + } + ParamVarDocs[ResolvedParamIndex] = Command; + return; + } + + SourceRange ArgRange(ArgLocBegin, ArgLocEnd); + Diag(ArgLocBegin, diag::warn_doc_param_not_found) + << Arg << ArgRange; + + // No parameters -- can't suggest a correction. + if (ParamVars.size() == 0) + return; + + unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex; + if (ParamVars.size() == 1) { + // If function has only one parameter then only that parameter + // can be documented. + CorrectedParamIndex = 0; + } else { + // Do typo correction. + CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars); + } + if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) { + const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex]; + if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier()) + Diag(ArgLocBegin, diag::note_doc_param_name_suggestion) + << CorrectedII->getName() + << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName()); + } + + return; +} + +void Sema::actOnParamCommandFinish(ParamCommandComment *Command, + ParagraphComment *Paragraph) { + Command->setParagraph(Paragraph); + checkBlockCommandEmptyParagraph(Command); +} + +TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin, + SourceLocation LocEnd, + StringRef Name) { + TParamCommandComment *Command = + new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name); + + if (!isTemplateOrSpecialization()) + Diag(Command->getLocation(), + diag::warn_doc_tparam_not_attached_to_a_template_decl) + << Command->getCommandNameRange(); + + return Command; +} + +void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command, + SourceLocation ArgLocBegin, + SourceLocation ArgLocEnd, + StringRef Arg) { + // Parser will not feed us more arguments than needed. + assert(Command->getNumArgs() == 0); + + typedef BlockCommandComment::Argument Argument; + Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin, + ArgLocEnd), + Arg); + Command->setArgs(llvm::makeArrayRef(A, 1)); + + if (!isTemplateOrSpecialization()) { + // We already warned that this \\tparam is not attached to a template decl. + return; + } + + const TemplateParameterList *TemplateParameters = + ThisDeclInfo->TemplateParameters; + SmallVector<unsigned, 2> Position; + if (resolveTParamReference(Arg, TemplateParameters, &Position)) { + Command->setPosition(copyArray(llvm::makeArrayRef(Position))); + llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt = + TemplateParameterDocs.find(Arg); + if (PrevCommandIt != TemplateParameterDocs.end()) { + SourceRange ArgRange(ArgLocBegin, ArgLocEnd); + Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate) + << Arg << ArgRange; + TParamCommandComment *PrevCommand = PrevCommandIt->second; + Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous) + << PrevCommand->getParamNameRange(); + } + TemplateParameterDocs[Arg] = Command; + return; + } + + SourceRange ArgRange(ArgLocBegin, ArgLocEnd); + Diag(ArgLocBegin, diag::warn_doc_tparam_not_found) + << Arg << ArgRange; + + if (!TemplateParameters || TemplateParameters->size() == 0) + return; + + StringRef CorrectedName; + if (TemplateParameters->size() == 1) { + const NamedDecl *Param = TemplateParameters->getParam(0); + const IdentifierInfo *II = Param->getIdentifier(); + if (II) + CorrectedName = II->getName(); + } else { + CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters); + } + + if (!CorrectedName.empty()) { + Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion) + << CorrectedName + << FixItHint::CreateReplacement(ArgRange, CorrectedName); + } + + return; +} + +void Sema::actOnTParamCommandFinish(TParamCommandComment *Command, + ParagraphComment *Paragraph) { + Command->setParagraph(Paragraph); + checkBlockCommandEmptyParagraph(Command); +} + +InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin, + SourceLocation CommandLocEnd, + StringRef CommandName) { + ArrayRef<InlineCommandComment::Argument> Args; + return new (Allocator) InlineCommandComment( + CommandLocBegin, + CommandLocEnd, + CommandName, + getInlineCommandRenderKind(CommandName), + Args); +} + +InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin, + SourceLocation CommandLocEnd, + StringRef CommandName, + SourceLocation ArgLocBegin, + SourceLocation ArgLocEnd, + StringRef Arg) { + typedef InlineCommandComment::Argument Argument; + Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin, + ArgLocEnd), + Arg); + + return new (Allocator) InlineCommandComment( + CommandLocBegin, + CommandLocEnd, + CommandName, + getInlineCommandRenderKind(CommandName), + llvm::makeArrayRef(A, 1)); +} + +InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin, + SourceLocation LocEnd, + StringRef Name) { + ArrayRef<InlineCommandComment::Argument> Args; + return new (Allocator) InlineCommandComment( + LocBegin, LocEnd, Name, + InlineCommandComment::RenderNormal, + Args); +} + +TextComment *Sema::actOnText(SourceLocation LocBegin, + SourceLocation LocEnd, + StringRef Text) { + return new (Allocator) TextComment(LocBegin, LocEnd, Text); +} + +VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc, + StringRef Name) { + return new (Allocator) VerbatimBlockComment( + Loc, + Loc.getLocWithOffset(1 + Name.size()), + Name); +} + +VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc, + StringRef Text) { + return new (Allocator) VerbatimBlockLineComment(Loc, Text); +} + +void Sema::actOnVerbatimBlockFinish( + VerbatimBlockComment *Block, + SourceLocation CloseNameLocBegin, + StringRef CloseName, + ArrayRef<VerbatimBlockLineComment *> Lines) { + Block->setCloseName(CloseName, CloseNameLocBegin); + Block->setLines(Lines); +} + +VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin, + StringRef Name, + SourceLocation TextBegin, + StringRef Text) { + return new (Allocator) VerbatimLineComment( + LocBegin, + TextBegin.getLocWithOffset(Text.size()), + Name, + TextBegin, + Text); +} + +HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin, + StringRef TagName) { + return new (Allocator) HTMLStartTagComment(LocBegin, TagName); +} + +void Sema::actOnHTMLStartTagFinish( + HTMLStartTagComment *Tag, + ArrayRef<HTMLStartTagComment::Attribute> Attrs, + SourceLocation GreaterLoc, + bool IsSelfClosing) { + Tag->setAttrs(Attrs); + Tag->setGreaterLoc(GreaterLoc); + if (IsSelfClosing) + Tag->setSelfClosing(); + else if (!isHTMLEndTagForbidden(Tag->getTagName())) + HTMLOpenTags.push_back(Tag); +} + +HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin, + SourceLocation LocEnd, + StringRef TagName) { + HTMLEndTagComment *HET = + new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName); + if (isHTMLEndTagForbidden(TagName)) { + Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden) + << TagName << HET->getSourceRange(); + return HET; + } + + bool FoundOpen = false; + for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator + I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend(); + I != E; ++I) { + if ((*I)->getTagName() == TagName) { + FoundOpen = true; + break; + } + } + if (!FoundOpen) { + Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced) + << HET->getSourceRange(); + return HET; + } + + while (!HTMLOpenTags.empty()) { + const HTMLStartTagComment *HST = HTMLOpenTags.back(); + HTMLOpenTags.pop_back(); + StringRef LastNotClosedTagName = HST->getTagName(); + if (LastNotClosedTagName == TagName) + break; + + if (isHTMLEndTagOptional(LastNotClosedTagName)) + continue; + + bool OpenLineInvalid; + const unsigned OpenLine = SourceMgr.getPresumedLineNumber( + HST->getLocation(), + &OpenLineInvalid); + bool CloseLineInvalid; + const unsigned CloseLine = SourceMgr.getPresumedLineNumber( + HET->getLocation(), + &CloseLineInvalid); + + if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine) + Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch) + << HST->getTagName() << HET->getTagName() + << HST->getSourceRange() << HET->getSourceRange(); + else { + Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch) + << HST->getTagName() << HET->getTagName() + << HST->getSourceRange(); + Diag(HET->getLocation(), diag::note_doc_html_end_tag) + << HET->getSourceRange(); + } + } + + return HET; +} + +FullComment *Sema::actOnFullComment( + ArrayRef<BlockContentComment *> Blocks) { + return new (Allocator) FullComment(Blocks, ThisDeclInfo); +} + +void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) { + ParagraphComment *Paragraph = Command->getParagraph(); + if (Paragraph->isWhitespace()) { + SourceLocation DiagLoc; + if (Command->getNumArgs() > 0) + DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd(); + if (!DiagLoc.isValid()) + DiagLoc = Command->getCommandNameRange().getEnd(); + Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph) + << Command->getCommandName() + << Command->getSourceRange(); + } +} + +void Sema::checkReturnsCommand(const BlockCommandComment *Command) { + if (!Traits.isReturnsCommand(Command->getCommandName())) + return; + if (isFunctionDecl()) { + if (ThisDeclInfo->ResultType->isVoidType()) { + unsigned DiagKind; + switch (ThisDeclInfo->ThisDecl->getKind()) { + default: + if (ThisDeclInfo->IsObjCMethod) + DiagKind = 3; + else + DiagKind = 0; + break; + case Decl::CXXConstructor: + DiagKind = 1; + break; + case Decl::CXXDestructor: + DiagKind = 2; + break; + } + Diag(Command->getLocation(), + diag::warn_doc_returns_attached_to_a_void_function) + << Command->getCommandName() + << DiagKind + << Command->getSourceRange(); + } + return; + } + Diag(Command->getLocation(), + diag::warn_doc_returns_not_attached_to_a_function_decl) + << Command->getCommandName() + << Command->getSourceRange(); +} + +void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) { + StringRef Name = Command->getCommandName(); + const BlockCommandComment *PrevCommand = NULL; + if (Traits.isBriefCommand(Name)) { + if (!BriefCommand) { + BriefCommand = Command; + return; + } + PrevCommand = BriefCommand; + } else if (Traits.isReturnsCommand(Name)) { + if (!ReturnsCommand) { + ReturnsCommand = Command; + return; + } + PrevCommand = ReturnsCommand; + } else { + // We don't want to check this command for duplicates. + return; + } + Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate) + << Name + << Command->getSourceRange(); + if (Name == PrevCommand->getCommandName()) + Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous) + << PrevCommand->getCommandName() + << Command->getSourceRange(); + else + Diag(PrevCommand->getLocation(), + diag::note_doc_block_command_previous_alias) + << PrevCommand->getCommandName() + << Name; +} + +bool Sema::isFunctionDecl() { + if (!ThisDeclInfo) + return false; + if (!ThisDeclInfo->IsFilled) + inspectThisDecl(); + return ThisDeclInfo->getKind() == DeclInfo::FunctionKind; +} + +bool Sema::isTemplateOrSpecialization() { + if (!ThisDeclInfo) + return false; + if (!ThisDeclInfo->IsFilled) + inspectThisDecl(); + return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate; +} + +ArrayRef<const ParmVarDecl *> Sema::getParamVars() { + if (!ThisDeclInfo->IsFilled) + inspectThisDecl(); + return ThisDeclInfo->ParamVars; +} + +void Sema::inspectThisDecl() { + ThisDeclInfo->fill(); + ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL); +} + +unsigned Sema::resolveParmVarReference(StringRef Name, + ArrayRef<const ParmVarDecl *> ParamVars) { + for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) { + const IdentifierInfo *II = ParamVars[i]->getIdentifier(); + if (II && II->getName() == Name) + return i; + } + return ParamCommandComment::InvalidParamIndex; +} + +namespace { +class SimpleTypoCorrector { + StringRef Typo; + const unsigned MaxEditDistance; + + const NamedDecl *BestDecl; + unsigned BestEditDistance; + unsigned BestIndex; + unsigned NextIndex; + +public: + SimpleTypoCorrector(StringRef Typo) : + Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3), + BestDecl(NULL), BestEditDistance(MaxEditDistance + 1), + BestIndex(0), NextIndex(0) + { } + + void addDecl(const NamedDecl *ND); + + const NamedDecl *getBestDecl() const { + if (BestEditDistance > MaxEditDistance) + return NULL; + + return BestDecl; + } + + unsigned getBestDeclIndex() const { + assert(getBestDecl()); + return BestIndex; + } +}; + +void SimpleTypoCorrector::addDecl(const NamedDecl *ND) { + unsigned CurrIndex = NextIndex++; + + const IdentifierInfo *II = ND->getIdentifier(); + if (!II) + return; + + StringRef Name = II->getName(); + unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size()); + if (MinPossibleEditDistance > 0 && + Typo.size() / MinPossibleEditDistance < 3) + return; + + unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance); + if (EditDistance < BestEditDistance) { + BestEditDistance = EditDistance; + BestDecl = ND; + BestIndex = CurrIndex; + } +} +} // unnamed namespace + +unsigned Sema::correctTypoInParmVarReference( + StringRef Typo, + ArrayRef<const ParmVarDecl *> ParamVars) { + SimpleTypoCorrector Corrector(Typo); + for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) + Corrector.addDecl(ParamVars[i]); + if (Corrector.getBestDecl()) + return Corrector.getBestDeclIndex(); + else + return ParamCommandComment::InvalidParamIndex;; +} + +namespace { +bool ResolveTParamReferenceHelper( + StringRef Name, + const TemplateParameterList *TemplateParameters, + SmallVectorImpl<unsigned> *Position) { + for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) { + const NamedDecl *Param = TemplateParameters->getParam(i); + const IdentifierInfo *II = Param->getIdentifier(); + if (II && II->getName() == Name) { + Position->push_back(i); + return true; + } + + if (const TemplateTemplateParmDecl *TTP = + dyn_cast<TemplateTemplateParmDecl>(Param)) { + Position->push_back(i); + if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(), + Position)) + return true; + Position->pop_back(); + } + } + return false; +} +} // unnamed namespace + +bool Sema::resolveTParamReference( + StringRef Name, + const TemplateParameterList *TemplateParameters, + SmallVectorImpl<unsigned> *Position) { + Position->clear(); + if (!TemplateParameters) + return false; + + return ResolveTParamReferenceHelper(Name, TemplateParameters, Position); +} + +namespace { +void CorrectTypoInTParamReferenceHelper( + const TemplateParameterList *TemplateParameters, + SimpleTypoCorrector &Corrector) { + for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) { + const NamedDecl *Param = TemplateParameters->getParam(i); + Corrector.addDecl(Param); + + if (const TemplateTemplateParmDecl *TTP = + dyn_cast<TemplateTemplateParmDecl>(Param)) + CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(), + Corrector); + } +} +} // unnamed namespace + +StringRef Sema::correctTypoInTParamReference( + StringRef Typo, + const TemplateParameterList *TemplateParameters) { + SimpleTypoCorrector Corrector(Typo); + CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector); + if (const NamedDecl *ND = Corrector.getBestDecl()) { + const IdentifierInfo *II = ND->getIdentifier(); + assert(II && "SimpleTypoCorrector should not return this decl"); + return II->getName(); + } + return StringRef(); +} + +InlineCommandComment::RenderKind +Sema::getInlineCommandRenderKind(StringRef Name) const { + assert(Traits.isInlineCommand(Name)); + + return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name) + .Case("b", InlineCommandComment::RenderBold) + .Cases("c", "p", InlineCommandComment::RenderMonospaced) + .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized) + .Default(InlineCommandComment::RenderNormal); +} + +bool Sema::isHTMLEndTagOptional(StringRef Name) { + return llvm::StringSwitch<bool>(Name) + .Case("p", true) + .Case("li", true) + .Case("dt", true) + .Case("dd", true) + .Case("tr", true) + .Case("th", true) + .Case("td", true) + .Case("thead", true) + .Case("tfoot", true) + .Case("tbody", true) + .Case("colgroup", true) + .Default(false); +} + +bool Sema::isHTMLEndTagForbidden(StringRef Name) { + return llvm::StringSwitch<bool>(Name) + .Case("br", true) + .Case("hr", true) + .Case("img", true) + .Case("col", true) + .Default(false); +} + +} // end namespace comments +} // end namespace clang + diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 53032bc64d61..d5b0be3ba4b1 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -66,32 +66,6 @@ static llvm::Optional<Visibility> getVisibilityOf(const Decl *D) { typedef NamedDecl::LinkageInfo LinkageInfo; -namespace { -/// Flags controlling the computation of linkage and visibility. -struct LVFlags { - const bool ConsiderGlobalVisibility; - const bool ConsiderVisibilityAttributes; - const bool ConsiderTemplateParameterTypes; - - LVFlags() : ConsiderGlobalVisibility(true), - ConsiderVisibilityAttributes(true), - ConsiderTemplateParameterTypes(true) { - } - - LVFlags(bool Global, bool Attributes, bool Parameters) : - ConsiderGlobalVisibility(Global), - ConsiderVisibilityAttributes(Attributes), - ConsiderTemplateParameterTypes(Parameters) { - } - - /// \brief Returns a set of flags that is only useful for computing the - /// linkage, not the visibility, of a declaration. - static LVFlags CreateOnlyDeclLinkage() { - return LVFlags(false, false, false); - } -}; -} // end anonymous namespace - static LinkageInfo getLVForType(QualType T) { std::pair<Linkage,Visibility> P = T->getLinkageAndVisibility(); return LinkageInfo(P.first, P.second, T->isVisibilityExplicit()); @@ -131,13 +105,13 @@ getLVForTemplateParameterList(const TemplateParameterList *Params) { } /// getLVForDecl - Get the linkage and visibility for the given declaration. -static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags F); +static LinkageInfo getLVForDecl(const NamedDecl *D, bool OnlyTemplate); /// \brief Get the most restrictive linkage for the types and /// declarations in the given template argument list. static LinkageInfo getLVForTemplateArgumentList(const TemplateArgument *Args, unsigned NumArgs, - LVFlags &F) { + bool OnlyTemplate) { LinkageInfo LV(ExternalLinkage, DefaultVisibility, false); for (unsigned I = 0; I != NumArgs; ++I) { @@ -148,7 +122,7 @@ static LinkageInfo getLVForTemplateArgumentList(const TemplateArgument *Args, break; case TemplateArgument::Type: - LV.merge(getLVForType(Args[I].getAsType())); + LV.mergeWithMin(getLVForType(Args[I].getAsType())); break; case TemplateArgument::Declaration: @@ -156,7 +130,7 @@ static LinkageInfo getLVForTemplateArgumentList(const TemplateArgument *Args, // arguments, valid only in C++0x. if (Decl *D = Args[I].getAsDecl()) { if (NamedDecl *ND = dyn_cast<NamedDecl>(D)) - LV = merge(LV, getLVForDecl(ND, F)); + LV.mergeWithMin(getLVForDecl(ND, OnlyTemplate)); } break; @@ -164,13 +138,13 @@ static LinkageInfo getLVForTemplateArgumentList(const TemplateArgument *Args, case TemplateArgument::TemplateExpansion: if (TemplateDecl *Template = Args[I].getAsTemplateOrTemplatePattern().getAsTemplateDecl()) - LV.merge(getLVForDecl(Template, F)); + LV.mergeWithMin(getLVForDecl(Template, OnlyTemplate)); break; case TemplateArgument::Pack: LV.mergeWithMin(getLVForTemplateArgumentList(Args[I].pack_begin(), Args[I].pack_size(), - F)); + OnlyTemplate)); break; } } @@ -180,21 +154,50 @@ static LinkageInfo getLVForTemplateArgumentList(const TemplateArgument *Args, static LinkageInfo getLVForTemplateArgumentList(const TemplateArgumentList &TArgs, - LVFlags &F) { - return getLVForTemplateArgumentList(TArgs.data(), TArgs.size(), F); + bool OnlyTemplate) { + return getLVForTemplateArgumentList(TArgs.data(), TArgs.size(), OnlyTemplate); } -static bool shouldConsiderTemplateLV(const FunctionDecl *fn, +static bool shouldConsiderTemplateVis(const FunctionDecl *fn, const FunctionTemplateSpecializationInfo *spec) { - return !(spec->isExplicitSpecialization() && - fn->hasAttr<VisibilityAttr>()); + return !fn->hasAttr<VisibilityAttr>() || spec->isExplicitSpecialization(); } -static bool shouldConsiderTemplateLV(const ClassTemplateSpecializationDecl *d) { - return !(d->isExplicitSpecialization() && d->hasAttr<VisibilityAttr>()); +static bool +shouldConsiderTemplateVis(const ClassTemplateSpecializationDecl *d) { + return !d->hasAttr<VisibilityAttr>() || d->isExplicitSpecialization(); } -static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { +static bool useInlineVisibilityHidden(const NamedDecl *D) { + // FIXME: we should warn if -fvisibility-inlines-hidden is used with c. + const LangOptions &Opts = D->getASTContext().getLangOpts(); + if (!Opts.CPlusPlus || !Opts.InlineVisibilityHidden) + return false; + + const FunctionDecl *FD = dyn_cast<FunctionDecl>(D); + if (!FD) + return false; + + TemplateSpecializationKind TSK = TSK_Undeclared; + if (FunctionTemplateSpecializationInfo *spec + = FD->getTemplateSpecializationInfo()) { + TSK = spec->getTemplateSpecializationKind(); + } else if (MemberSpecializationInfo *MSI = + FD->getMemberSpecializationInfo()) { + TSK = MSI->getTemplateSpecializationKind(); + } + + const FunctionDecl *Def = 0; + // InlineVisibilityHidden only applies to definitions, and + // isInlined() only gives meaningful answers on definitions + // anyway. + return TSK != TSK_ExplicitInstantiationDeclaration && + TSK != TSK_ExplicitInstantiationDefinition && + FD->hasBody(Def) && Def->isInlined(); +} + +static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, + bool OnlyTemplate) { assert(D->getDeclContext()->getRedeclContext()->isFileContext() && "Not a name having namespace scope"); ASTContext &Context = D->getASTContext(); @@ -271,11 +274,10 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { // scope and no storage-class specifier, its linkage is // external. LinkageInfo LV; - LV.mergeVisibility(Context.getLangOpts().getVisibilityMode()); - if (F.ConsiderVisibilityAttributes) { + if (!OnlyTemplate) { if (llvm::Optional<Visibility> Vis = D->getExplicitVisibility()) { - LV.setVisibility(*Vis, true); + LV.mergeVisibility(*Vis, true); } else { // If we're declared in a namespace with a visibility attribute, // use that namespace's visibility, but don't call it explicit. @@ -285,13 +287,21 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC); if (!ND) continue; if (llvm::Optional<Visibility> Vis = ND->getExplicitVisibility()) { - LV.setVisibility(*Vis, true); + LV.mergeVisibility(*Vis, true); break; } } } } + if (!OnlyTemplate) { + LV.mergeVisibility(Context.getLangOpts().getVisibilityMode()); + // If we're paying attention to global visibility, apply + // -finline-visibility-hidden if this is an inline method. + if (!LV.visibilityExplicit() && useInlineVisibilityHidden(D)) + LV.mergeVisibility(HiddenVisibility, true); + } + // C++ [basic.link]p4: // A name having namespace scope has external linkage if it is the @@ -325,11 +335,11 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { LinkageInfo TypeLV = getLVForType(Var->getType()); if (TypeLV.linkage() != ExternalLinkage) return LinkageInfo::uniqueExternal(); - LV.mergeVisibilityWithMin(TypeLV); + LV.mergeVisibility(TypeLV); } if (Var->getStorageClass() == SC_PrivateExtern) - LV.setVisibility(HiddenVisibility, true); + LV.mergeVisibility(HiddenVisibility, true); if (!Context.getLangOpts().CPlusPlus && (Var->getStorageClass() == SC_Extern || @@ -345,7 +355,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { // is visible, or if the prior declaration specifies no // linkage, then the identifier has external linkage. if (const VarDecl *PrevVar = Var->getPreviousDecl()) { - LinkageInfo PrevLV = getLVForDecl(PrevVar, F); + LinkageInfo PrevLV = getLVForDecl(PrevVar, OnlyTemplate); if (PrevLV.linkage()) LV.setLinkage(PrevLV.linkage()); LV.mergeVisibility(PrevLV); } @@ -359,7 +369,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { // just too painful to make work. if (Function->getStorageClass() == SC_PrivateExtern) - LV.setVisibility(HiddenVisibility, true); + LV.mergeVisibility(HiddenVisibility, true); // C99 6.2.2p5: // If the declaration of an identifier for a function has no @@ -380,7 +390,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { // is visible, or if the prior declaration specifies no // linkage, then the identifier has external linkage. if (const FunctionDecl *PrevFunc = Function->getPreviousDecl()) { - LinkageInfo PrevLV = getLVForDecl(PrevFunc, F); + LinkageInfo PrevLV = getLVForDecl(PrevFunc, OnlyTemplate); if (PrevLV.linkage()) LV.setLinkage(PrevLV.linkage()); LV.mergeVisibility(PrevLV); } @@ -399,11 +409,16 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { // this is an explicit specialization with a visibility attribute. if (FunctionTemplateSpecializationInfo *specInfo = Function->getTemplateSpecializationInfo()) { - if (shouldConsiderTemplateLV(Function, specInfo)) { - LV.merge(getLVForDecl(specInfo->getTemplate(), - LVFlags::CreateOnlyDeclLinkage())); - const TemplateArgumentList &templateArgs = *specInfo->TemplateArguments; - LV.mergeWithMin(getLVForTemplateArgumentList(templateArgs, F)); + LinkageInfo TempLV = getLVForDecl(specInfo->getTemplate(), true); + const TemplateArgumentList &templateArgs = *specInfo->TemplateArguments; + LinkageInfo ArgsLV = getLVForTemplateArgumentList(templateArgs, + OnlyTemplate); + if (shouldConsiderTemplateVis(Function, specInfo)) { + LV.mergeWithMin(TempLV); + LV.mergeWithMin(ArgsLV); + } else { + LV.mergeLinkage(TempLV); + LV.mergeLinkage(ArgsLV); } } @@ -422,20 +437,26 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { // linkage of the template and template arguments. if (const ClassTemplateSpecializationDecl *spec = dyn_cast<ClassTemplateSpecializationDecl>(Tag)) { - if (shouldConsiderTemplateLV(spec)) { - // From the template. - LV.merge(getLVForDecl(spec->getSpecializedTemplate(), - LVFlags::CreateOnlyDeclLinkage())); - - // The arguments at which the template was instantiated. - const TemplateArgumentList &TemplateArgs = spec->getTemplateArgs(); - LV.mergeWithMin(getLVForTemplateArgumentList(TemplateArgs, F)); + // From the template. + LinkageInfo TempLV = getLVForDecl(spec->getSpecializedTemplate(), true); + + // The arguments at which the template was instantiated. + const TemplateArgumentList &TemplateArgs = spec->getTemplateArgs(); + LinkageInfo ArgsLV = getLVForTemplateArgumentList(TemplateArgs, + OnlyTemplate); + if (shouldConsiderTemplateVis(spec)) { + LV.mergeWithMin(TempLV); + LV.mergeWithMin(ArgsLV); + } else { + LV.mergeLinkage(TempLV); + LV.mergeLinkage(ArgsLV); } } // - an enumerator belonging to an enumeration with external linkage; } else if (isa<EnumConstantDecl>(D)) { - LinkageInfo EnumLV = getLVForDecl(cast<NamedDecl>(D->getDeclContext()), F); + LinkageInfo EnumLV = getLVForDecl(cast<NamedDecl>(D->getDeclContext()), + OnlyTemplate); if (!isExternalLinkage(EnumLV.linkage())) return LinkageInfo::none(); LV.merge(EnumLV); @@ -443,9 +464,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { // - a template, unless it is a function template that has // internal linkage (Clause 14); } else if (const TemplateDecl *temp = dyn_cast<TemplateDecl>(D)) { - if (F.ConsiderTemplateParameterTypes) - LV.merge(getLVForTemplateParameterList(temp->getTemplateParameters())); - + LV.merge(getLVForTemplateParameterList(temp->getTemplateParameters())); // - a namespace (7.3), unless it is declared within an unnamed // namespace. } else if (isa<NamespaceDecl>(D) && !D->isInAnonymousNamespace()) { @@ -469,7 +488,7 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { return LV; } -static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) { +static LinkageInfo getLVForClassMember(const NamedDecl *D, bool OnlyTemplate) { // Only certain class members have linkage. Note that fields don't // really have linkage, but it's convenient to say they do for the // purposes of calculating linkage of pointer-to-data-member @@ -482,53 +501,32 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) { return LinkageInfo::none(); LinkageInfo LV; - LV.mergeVisibility(D->getASTContext().getLangOpts().getVisibilityMode()); - bool DHasExplicitVisibility = false; // If we have an explicit visibility attribute, merge that in. - if (F.ConsiderVisibilityAttributes) { - if (llvm::Optional<Visibility> Vis = D->getExplicitVisibility()) { + if (!OnlyTemplate) { + if (llvm::Optional<Visibility> Vis = D->getExplicitVisibility()) LV.mergeVisibility(*Vis, true); - - DHasExplicitVisibility = true; - } - } - // Ignore both global visibility and attributes when computing our - // parent's visibility if we already have an explicit one. - LVFlags ClassF = DHasExplicitVisibility ? - LVFlags::CreateOnlyDeclLinkage() : F; - - // If we're paying attention to global visibility, apply - // -finline-visibility-hidden if this is an inline method. - // - // Note that we do this before merging information about - // the class visibility. - if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { - TemplateSpecializationKind TSK = TSK_Undeclared; - if (FunctionTemplateSpecializationInfo *spec - = MD->getTemplateSpecializationInfo()) { - TSK = spec->getTemplateSpecializationKind(); - } else if (MemberSpecializationInfo *MSI = - MD->getMemberSpecializationInfo()) { - TSK = MSI->getTemplateSpecializationKind(); - } - - const FunctionDecl *Def = 0; - // InlineVisibilityHidden only applies to definitions, and - // isInlined() only gives meaningful answers on definitions - // anyway. - if (TSK != TSK_ExplicitInstantiationDeclaration && - TSK != TSK_ExplicitInstantiationDefinition && - F.ConsiderGlobalVisibility && - !LV.visibilityExplicit() && - MD->getASTContext().getLangOpts().InlineVisibilityHidden && - MD->hasBody(Def) && Def->isInlined()) + // If we're paying attention to global visibility, apply + // -finline-visibility-hidden if this is an inline method. + // + // Note that we do this before merging information about + // the class visibility. + if (!LV.visibilityExplicit() && useInlineVisibilityHidden(D)) LV.mergeVisibility(HiddenVisibility, true); } - // Class members only have linkage if their class has external - // linkage. - LV.merge(getLVForDecl(cast<RecordDecl>(D->getDeclContext()), ClassF)); + // If this class member has an explicit visibility attribute, the only + // thing that can change its visibility is the template arguments, so + // only look for them when processing the class. + bool ClassOnlyTemplate = LV.visibilityExplicit() ? true : OnlyTemplate; + + // If this member has an visibility attribute, ClassF will exclude + // attributes on the class or command line options, keeping only information + // about the template instantiation. If the member has no visibility + // attributes, mergeWithMin behaves like merge, so in both cases mergeWithMin + // produces the desired result. + LV.mergeWithMin(getLVForDecl(cast<RecordDecl>(D->getDeclContext()), + ClassOnlyTemplate)); if (!isExternalLinkage(LV.linkage())) return LinkageInfo::none(); @@ -536,6 +534,9 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) { if (LV.linkage() == UniqueExternalLinkage) return LinkageInfo::uniqueExternal(); + if (!OnlyTemplate) + LV.mergeVisibility(D->getASTContext().getLangOpts().getVisibilityMode()); + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { // If the type of the function uses a type with unique-external // linkage, it's not legally usable from outside this translation unit. @@ -546,12 +547,20 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) { // the template parameters and arguments. if (FunctionTemplateSpecializationInfo *spec = MD->getTemplateSpecializationInfo()) { - if (shouldConsiderTemplateLV(MD, spec)) { - LV.mergeWithMin(getLVForTemplateArgumentList(*spec->TemplateArguments, - F)); - if (F.ConsiderTemplateParameterTypes) - LV.merge(getLVForTemplateParameterList( - spec->getTemplate()->getTemplateParameters())); + const TemplateArgumentList &TemplateArgs = *spec->TemplateArguments; + LinkageInfo ArgsLV = getLVForTemplateArgumentList(TemplateArgs, + OnlyTemplate); + TemplateParameterList *TemplateParams = + spec->getTemplate()->getTemplateParameters(); + LinkageInfo ParamsLV = getLVForTemplateParameterList(TemplateParams); + if (shouldConsiderTemplateVis(MD, spec)) { + LV.mergeWithMin(ArgsLV); + if (!OnlyTemplate) + LV.mergeWithMin(ParamsLV); + } else { + LV.mergeLinkage(ArgsLV); + if (!OnlyTemplate) + LV.mergeLinkage(ParamsLV); } } @@ -561,14 +570,22 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) { } else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) { if (const ClassTemplateSpecializationDecl *spec = dyn_cast<ClassTemplateSpecializationDecl>(RD)) { - if (shouldConsiderTemplateLV(spec)) { - // Merge template argument/parameter information for member - // class template specializations. - LV.mergeWithMin(getLVForTemplateArgumentList(spec->getTemplateArgs(), - F)); - if (F.ConsiderTemplateParameterTypes) - LV.merge(getLVForTemplateParameterList( - spec->getSpecializedTemplate()->getTemplateParameters())); + // Merge template argument/parameter information for member + // class template specializations. + const TemplateArgumentList &TemplateArgs = spec->getTemplateArgs(); + LinkageInfo ArgsLV = getLVForTemplateArgumentList(TemplateArgs, + OnlyTemplate); + TemplateParameterList *TemplateParams = + spec->getSpecializedTemplate()->getTemplateParameters(); + LinkageInfo ParamsLV = getLVForTemplateParameterList(TemplateParams); + if (shouldConsiderTemplateVis(spec)) { + LV.mergeWithMin(ArgsLV); + if (!OnlyTemplate) + LV.mergeWithMin(ParamsLV); + } else { + LV.mergeLinkage(ArgsLV); + if (!OnlyTemplate) + LV.mergeLinkage(ParamsLV); } } @@ -579,8 +596,7 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) { LinkageInfo TypeLV = getLVForType(VD->getType()); if (TypeLV.linkage() != ExternalLinkage) LV.mergeLinkage(UniqueExternalLinkage); - if (!LV.visibilityExplicit()) - LV.mergeVisibility(TypeLV); + LV.mergeVisibility(TypeLV); } return LV; @@ -636,18 +652,17 @@ void NamedDecl::ClearLinkageCache() { Linkage NamedDecl::getLinkage() const { if (HasCachedLinkage) { assert(Linkage(CachedLinkage) == - getLVForDecl(this, LVFlags::CreateOnlyDeclLinkage()).linkage()); + getLVForDecl(this, true).linkage()); return Linkage(CachedLinkage); } - CachedLinkage = getLVForDecl(this, - LVFlags::CreateOnlyDeclLinkage()).linkage(); + CachedLinkage = getLVForDecl(this, true).linkage(); HasCachedLinkage = 1; return Linkage(CachedLinkage); } LinkageInfo NamedDecl::getLinkageAndVisibility() const { - LinkageInfo LI = getLVForDecl(this, LVFlags()); + LinkageInfo LI = getLVForDecl(this, false); assert(!HasCachedLinkage || Linkage(CachedLinkage) == LI.linkage()); HasCachedLinkage = 1; CachedLinkage = LI.linkage(); @@ -656,9 +671,19 @@ LinkageInfo NamedDecl::getLinkageAndVisibility() const { llvm::Optional<Visibility> NamedDecl::getExplicitVisibility() const { // Use the most recent declaration of a variable. - if (const VarDecl *var = dyn_cast<VarDecl>(this)) - return getVisibilityOf(var->getMostRecentDecl()); + if (const VarDecl *Var = dyn_cast<VarDecl>(this)) { + if (llvm::Optional<Visibility> V = + getVisibilityOf(Var->getMostRecentDecl())) + return V; + + if (Var->isStaticDataMember()) { + VarDecl *InstantiatedFrom = Var->getInstantiatedFromStaticDataMember(); + if (InstantiatedFrom) + return getVisibilityOf(InstantiatedFrom); + } + return llvm::Optional<Visibility>(); + } // Use the most recent declaration of a function, and also handle // function template specializations. if (const FunctionDecl *fn = dyn_cast<FunctionDecl>(this)) { @@ -685,6 +710,10 @@ llvm::Optional<Visibility> NamedDecl::getExplicitVisibility() const { if (llvm::Optional<Visibility> V = getVisibilityOf(this)) return V; + // The visibility of a template is stored in the templated decl. + if (const TemplateDecl *TD = dyn_cast<TemplateDecl>(this)) + return getVisibilityOf(TD->getTemplatedDecl()); + // If there wasn't explicit visibility there, and this is a // specialization of a class template, check for visibility // on the pattern. @@ -703,7 +732,7 @@ llvm::Optional<Visibility> NamedDecl::getExplicitVisibility() const { return llvm::Optional<Visibility>(); } -static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) { +static LinkageInfo getLVForDecl(const NamedDecl *D, bool OnlyTemplate) { // Objective-C: treat all Objective-C declarations as having external // linkage. switch (D->getKind()) { @@ -738,11 +767,12 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) { if (isa<ParmVarDecl>(ContextDecl)) DC = ContextDecl->getDeclContext()->getRedeclContext(); else - return getLVForDecl(cast<NamedDecl>(ContextDecl), Flags); + return getLVForDecl(cast<NamedDecl>(ContextDecl), + OnlyTemplate); } if (const NamedDecl *ND = dyn_cast<NamedDecl>(DC)) - return getLVForDecl(ND, Flags); + return getLVForDecl(ND, OnlyTemplate); return LinkageInfo::external(); } @@ -753,7 +783,7 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) { // Handle linkage for namespace-scope names. if (D->getDeclContext()->getRedeclContext()->isFileContext()) - return getLVForNamespaceScopeDecl(D, Flags); + return getLVForNamespaceScopeDecl(D, OnlyTemplate); // C++ [basic.link]p5: // In addition, a member function, static data member, a named @@ -763,7 +793,7 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) { // purposes (7.1.3), has external linkage if the name of the class // has external linkage. if (D->getDeclContext()->isRecord()) - return getLVForClassMember(D, Flags); + return getLVForClassMember(D, OnlyTemplate); // C++ [basic.link]p6: // The name of a function declared in block scope and the name of @@ -783,13 +813,13 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) { return LinkageInfo::uniqueExternal(); LinkageInfo LV; - if (Flags.ConsiderVisibilityAttributes) { + if (!OnlyTemplate) { if (llvm::Optional<Visibility> Vis = Function->getExplicitVisibility()) - LV.setVisibility(*Vis, true); + LV.mergeVisibility(*Vis, true); } if (const FunctionDecl *Prev = Function->getPreviousDecl()) { - LinkageInfo PrevLV = getLVForDecl(Prev, Flags); + LinkageInfo PrevLV = getLVForDecl(Prev, OnlyTemplate); if (PrevLV.linkage()) LV.setLinkage(PrevLV.linkage()); LV.mergeVisibility(PrevLV); } @@ -806,14 +836,14 @@ static LinkageInfo getLVForDecl(const NamedDecl *D, LVFlags Flags) { LinkageInfo LV; if (Var->getStorageClass() == SC_PrivateExtern) - LV.setVisibility(HiddenVisibility, true); - else if (Flags.ConsiderVisibilityAttributes) { + LV.mergeVisibility(HiddenVisibility, true); + else if (!OnlyTemplate) { if (llvm::Optional<Visibility> Vis = Var->getExplicitVisibility()) - LV.setVisibility(*Vis, true); + LV.mergeVisibility(*Vis, true); } if (const VarDecl *Prev = Var->getPreviousDecl()) { - LinkageInfo PrevLV = getLVForDecl(Prev, Flags); + LinkageInfo PrevLV = getLVForDecl(Prev, OnlyTemplate); if (PrevLV.linkage()) LV.setLinkage(PrevLV.linkage()); LV.mergeVisibility(PrevLV); } @@ -881,9 +911,7 @@ std::string NamedDecl::getQualifiedNameAsString(const PrintingPolicy &P) const { for (unsigned i = 0; i < NumParams; ++i) { if (i) OS << ", "; - std::string Param; - FD->getParamDecl(i)->getType().getAsStringInternal(Param, P); - OS << Param; + OS << FD->getParamDecl(i)->getType().stream(P); } if (FT->isVariadic()) { @@ -1672,6 +1700,13 @@ void FunctionDecl::setPure(bool P) { Parent->markedVirtualFunctionPure(); } +void FunctionDecl::setConstexpr(bool IC) { + IsConstexpr = IC; + CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(this); + if (IC && CD) + CD->getParent()->markedConstructorConstexpr(CD); +} + bool FunctionDecl::isMain() const { const TranslationUnitDecl *tunit = dyn_cast<TranslationUnitDecl>(getDeclContext()->getRedeclContext()); @@ -2446,15 +2481,15 @@ FieldDecl *FieldDecl::Create(const ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo, Expr *BW, bool Mutable, - bool HasInit) { + InClassInitStyle InitStyle) { return new (C) FieldDecl(Decl::Field, DC, StartLoc, IdLoc, Id, T, TInfo, - BW, Mutable, HasInit); + BW, Mutable, InitStyle); } FieldDecl *FieldDecl::CreateDeserialized(ASTContext &C, unsigned ID) { void *Mem = AllocateDeserializedDecl(C, ID, sizeof(FieldDecl)); return new (Mem) FieldDecl(Field, 0, SourceLocation(), SourceLocation(), - 0, QualType(), 0, 0, false, false); + 0, QualType(), 0, 0, false, ICIS_NoInit); } bool FieldDecl::isAnonymousStructOrUnion() const { @@ -2483,15 +2518,15 @@ unsigned FieldDecl::getFieldIndex() const { for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I, ++Index) { - (*I)->CachedFieldIndex = Index + 1; + I->CachedFieldIndex = Index + 1; if (IsMsStruct) { // Zero-length bitfields following non-bitfield members are ignored. - if (getASTContext().ZeroBitfieldFollowsNonBitfield((*I), LastFD)) { + if (getASTContext().ZeroBitfieldFollowsNonBitfield(*I, LastFD)) { --Index; continue; } - LastFD = (*I); + LastFD = *I; } } @@ -2505,11 +2540,16 @@ SourceRange FieldDecl::getSourceRange() const { return DeclaratorDecl::getSourceRange(); } +void FieldDecl::setBitWidth(Expr *Width) { + assert(!InitializerOrBitWidth.getPointer() && !hasInClassInitializer() && + "bit width or initializer already set"); + InitializerOrBitWidth.setPointer(Width); +} + void FieldDecl::setInClassInitializer(Expr *Init) { - assert(!InitializerOrBitWidth.getPointer() && + assert(!InitializerOrBitWidth.getPointer() && hasInClassInitializer() && "bit width or initializer already set"); InitializerOrBitWidth.setPointer(Init); - InitializerOrBitWidth.setInt(0); } //===----------------------------------------------------------------------===// diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 47a0d25093d5..f9ce46def5b9 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -411,23 +411,32 @@ AvailabilityResult Decl::getAvailability(std::string *Message) const { bool Decl::canBeWeakImported(bool &IsDefinition) const { IsDefinition = false; + + // Variables, if they aren't definitions. if (const VarDecl *Var = dyn_cast<VarDecl>(this)) { if (!Var->hasExternalStorage() || Var->getInit()) { IsDefinition = true; return false; } + return true; + + // Functions, if they aren't definitions. } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(this)) { if (FD->hasBody()) { IsDefinition = true; return false; } - } else if (isa<ObjCPropertyDecl>(this) || isa<ObjCMethodDecl>(this)) - return false; - else if (!(getASTContext().getLangOpts().ObjCNonFragileABI && - isa<ObjCInterfaceDecl>(this))) - return false; + return true; - return true; + // Objective-C classes, if this is the non-fragile runtime. + } else if (isa<ObjCInterfaceDecl>(this) && + getASTContext().getLangOpts().ObjCRuntime.hasWeakClassImport()) { + return true; + + // Nothing else. + } else { + return false; + } } bool Decl::isWeakImported() const { @@ -974,10 +983,6 @@ DeclContext::decl_iterator DeclContext::noload_decls_begin() const { return decl_iterator(FirstDecl); } -DeclContext::decl_iterator DeclContext::noload_decls_end() const { - return decl_iterator(); -} - DeclContext::decl_iterator DeclContext::decls_begin() const { if (hasExternalLexicalStorage()) LoadLexicalDeclsFromExternalStorage(); @@ -985,13 +990,6 @@ DeclContext::decl_iterator DeclContext::decls_begin() const { return decl_iterator(FirstDecl); } -DeclContext::decl_iterator DeclContext::decls_end() const { - if (hasExternalLexicalStorage()) - LoadLexicalDeclsFromExternalStorage(); - - return decl_iterator(); -} - bool DeclContext::decls_empty() const { if (hasExternalLexicalStorage()) LoadLexicalDeclsFromExternalStorage(); @@ -1192,34 +1190,31 @@ DeclContext::lookup(DeclarationName Name) { return I->second.getLookupResult(); } -DeclContext::lookup_const_result -DeclContext::lookup(DeclarationName Name) const { - return const_cast<DeclContext*>(this)->lookup(Name); -} - void DeclContext::localUncachedLookup(DeclarationName Name, llvm::SmallVectorImpl<NamedDecl *> &Results) { Results.clear(); // If there's no external storage, just perform a normal lookup and copy // the results. - if (!hasExternalVisibleStorage() && !hasExternalLexicalStorage()) { + if (!hasExternalVisibleStorage() && !hasExternalLexicalStorage() && Name) { lookup_result LookupResults = lookup(Name); Results.insert(Results.end(), LookupResults.first, LookupResults.second); return; } // If we have a lookup table, check there first. Maybe we'll get lucky. - if (StoredDeclsMap *Map = LookupPtr.getPointer()) { - StoredDeclsMap::iterator Pos = Map->find(Name); - if (Pos != Map->end()) { - Results.insert(Results.end(), - Pos->second.getLookupResult().first, - Pos->second.getLookupResult().second); - return; + if (Name) { + if (StoredDeclsMap *Map = LookupPtr.getPointer()) { + StoredDeclsMap::iterator Pos = Map->find(Name); + if (Pos != Map->end()) { + Results.insert(Results.end(), + Pos->second.getLookupResult().first, + Pos->second.getLookupResult().second); + return; + } } } - + // Slow case: grovel through the declarations in our chain looking for // matches. for (Decl *D = FirstDecl; D; D = D->getNextDeclInContext()) { diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 114322b1a8a6..eec2e9d3cf74 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -43,13 +43,11 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) Abstract(false), IsStandardLayout(true), HasNoNonEmptyBases(true), HasPrivateFields(false), HasProtectedFields(false), HasPublicFields(false), HasMutableFields(false), HasOnlyCMembers(true), + HasInClassInitializer(false), HasTrivialDefaultConstructor(true), HasConstexprNonCopyMoveConstructor(false), DefaultedDefaultConstructorIsConstexpr(true), - DefaultedCopyConstructorIsConstexpr(true), - DefaultedMoveConstructorIsConstexpr(true), - HasConstexprDefaultConstructor(false), HasConstexprCopyConstructor(false), - HasConstexprMoveConstructor(false), HasTrivialCopyConstructor(true), + HasConstexprDefaultConstructor(false), HasTrivialCopyConstructor(true), HasTrivialMoveConstructor(true), HasTrivialCopyAssignment(true), HasTrivialMoveAssignment(true), HasTrivialDestructor(true), HasIrrelevantDestructor(true), @@ -62,6 +60,14 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) NumVBases(0), Bases(), VBases(), Definition(D), FirstFriend(0) { } +CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const { + return Bases.get(Definition->getASTContext().getExternalSource()); +} + +CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getVBasesSlowCase() const { + return VBases.get(Definition->getASTContext().getExternalSource()); +} + CXXRecordDecl::CXXRecordDecl(Kind K, TagKind TK, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, CXXRecordDecl *PrevDecl) @@ -219,8 +225,6 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, // In the definition of a constexpr constructor [...] // -- the class shall not have any virtual base classes data().DefaultedDefaultConstructorIsConstexpr = false; - data().DefaultedCopyConstructorIsConstexpr = false; - data().DefaultedMoveConstructorIsConstexpr = false; } else { // C++ [class.ctor]p5: // A default constructor is trivial [...] if: @@ -259,25 +263,6 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, // default constructor is constexpr. if (!BaseClassDecl->hasConstexprDefaultConstructor()) data().DefaultedDefaultConstructorIsConstexpr = false; - - // C++11 [class.copy]p13: - // If the implicitly-defined constructor would satisfy the requirements - // of a constexpr constructor, the implicitly-defined constructor is - // constexpr. - // C++11 [dcl.constexpr]p4: - // -- every constructor involved in initializing [...] base class - // sub-objects shall be a constexpr constructor - if (!BaseClassDecl->hasConstexprCopyConstructor()) - data().DefaultedCopyConstructorIsConstexpr = false; - if (BaseClassDecl->hasDeclaredMoveConstructor() || - BaseClassDecl->needsImplicitMoveConstructor()) - // FIXME: If the implicit move constructor generated for the base class - // would be ill-formed, the implicit move constructor generated for the - // derived class calls the base class' copy constructor. - data().DefaultedMoveConstructorIsConstexpr &= - BaseClassDecl->hasConstexprMoveConstructor(); - else if (!BaseClassDecl->hasConstexprCopyConstructor()) - data().DefaultedMoveConstructorIsConstexpr = false; } // C++ [class.ctor]p3: @@ -359,8 +344,8 @@ GetBestOverloadCandidateSimple( if (Cands[Best].second.compatiblyIncludes(Cands[I].second)) Best = I; - for (unsigned I = 1; I != N; ++I) - if (Cands[Best].second.compatiblyIncludes(Cands[I].second)) + for (unsigned I = 0; I != N; ++I) + if (I != Best && Cands[Best].second.compatiblyIncludes(Cands[I].second)) return 0; return Cands[Best].first; @@ -469,6 +454,14 @@ void CXXRecordDecl::markedVirtualFunctionPure() { data().Abstract = true; } +void CXXRecordDecl::markedConstructorConstexpr(CXXConstructorDecl *CD) { + if (!CD->isCopyOrMoveConstructor()) + data().HasConstexprNonCopyMoveConstructor = true; + + if (CD->isDefaultConstructor()) + data().HasConstexprDefaultConstructor = true; +} + void CXXRecordDecl::addedMember(Decl *D) { if (!D->isImplicit() && !isa<FieldDecl>(D) && @@ -545,12 +538,8 @@ void CXXRecordDecl::addedMember(Decl *D) { } } else if (Constructor->isCopyConstructor()) { data().DeclaredCopyConstructor = true; - if (Constructor->isConstexpr()) - data().HasConstexprCopyConstructor = true; } else if (Constructor->isMoveConstructor()) { data().DeclaredMoveConstructor = true; - if (Constructor->isConstexpr()) - data().HasConstexprMoveConstructor = true; } else goto NotASpecialMember; return; @@ -607,9 +596,6 @@ NotASpecialMember:; // user-provided [...] if (UserProvided) data().HasTrivialCopyConstructor = false; - - if (Constructor->isConstexpr()) - data().HasConstexprCopyConstructor = true; } else if (Constructor->isMoveConstructor()) { data().UserDeclaredMoveConstructor = true; data().DeclaredMoveConstructor = true; @@ -619,9 +605,6 @@ NotASpecialMember:; // user-provided [...] if (UserProvided) data().HasTrivialMoveConstructor = false; - - if (Constructor->isConstexpr()) - data().HasConstexprMoveConstructor = true; } } if (Constructor->isConstexpr() && !Constructor->isCopyOrMoveConstructor()) { @@ -663,19 +646,9 @@ NotASpecialMember:; // C++11 [class.dtor]p5: // A destructor is trivial if it is not user-provided and if // -- the destructor is not virtual. - if (DD->isUserProvided() || DD->isVirtual()) { + if (DD->isUserProvided() || DD->isVirtual()) data().HasTrivialDestructor = false; - // C++11 [dcl.constexpr]p1: - // The constexpr specifier shall be applied only to [...] the - // declaration of a static data member of a literal type. - // C++11 [basic.types]p10: - // A type is a literal type if it is [...] a class type that [...] has - // a trivial destructor. - data().DefaultedDefaultConstructorIsConstexpr = false; - data().DefaultedCopyConstructorIsConstexpr = false; - data().DefaultedMoveConstructorIsConstexpr = false; - } - + return; } @@ -792,7 +765,7 @@ NotASpecialMember:; // that does not explicitly have no lifetime makes the class a non-POD. // However, we delay setting PlainOldData to false in this case so that // Sema has a chance to diagnostic causes where the same class will be - // non-POD with Automatic Reference Counting but a POD without Instant Objects. + // non-POD with Automatic Reference Counting but a POD without ARC. // In this case, the class will become a non-POD class when we complete // the definition. ASTContext &Context = getASTContext(); @@ -818,17 +791,19 @@ NotASpecialMember:; data().HasNonLiteralTypeFieldsOrBases = true; if (Field->hasInClassInitializer()) { - // C++0x [class]p5: + data().HasInClassInitializer = true; + + // C++11 [class]p5: // A default constructor is trivial if [...] no non-static data member // of its class has a brace-or-equal-initializer. data().HasTrivialDefaultConstructor = false; - // C++0x [dcl.init.aggr]p1: + // C++11 [dcl.init.aggr]p1: // An aggregate is a [...] class with [...] no // brace-or-equal-initializers for non-static data members. data().Aggregate = false; - // C++0x [class]p10: + // C++11 [class]p10: // A POD struct is [...] a trivial class. data().PlainOldData = false; } @@ -920,31 +895,15 @@ NotASpecialMember:; // -- every constructor involved in initializing non-static data // members [...] shall be a constexpr constructor if (!Field->hasInClassInitializer() && - !FieldRec->hasConstexprDefaultConstructor()) + !FieldRec->hasConstexprDefaultConstructor() && !isUnion()) // The standard requires any in-class initializer to be a constant // expression. We consider this to be a defect. data().DefaultedDefaultConstructorIsConstexpr = false; - - if (!FieldRec->hasConstexprCopyConstructor()) - data().DefaultedCopyConstructorIsConstexpr = false; - - if (FieldRec->hasDeclaredMoveConstructor() || - FieldRec->needsImplicitMoveConstructor()) - // FIXME: If the implicit move constructor generated for the member's - // class would be ill-formed, the implicit move constructor generated - // for this class calls the member's copy constructor. - data().DefaultedMoveConstructorIsConstexpr &= - FieldRec->hasConstexprMoveConstructor(); - else if (!FieldRec->hasConstexprCopyConstructor()) - data().DefaultedMoveConstructorIsConstexpr = false; } } else { // Base element type of field is a non-class type. - if (!T->isLiteralType()) { - data().DefaultedDefaultConstructorIsConstexpr = false; - data().DefaultedCopyConstructorIsConstexpr = false; - data().DefaultedMoveConstructorIsConstexpr = false; - } else if (!Field->hasInClassInitializer()) + if (!T->isLiteralType() || + (!Field->hasInClassInitializer() && !isUnion())) data().DefaultedDefaultConstructorIsConstexpr = false; } @@ -1018,7 +977,7 @@ static CanQualType GetConversionType(ASTContext &Context, NamedDecl *Conv) { /// Collect the visible conversions of a base class. /// -/// \param Base a base class of the class we're considering +/// \param Record a base class of the class we're considering /// \param InVirtual whether this base class is a virtual base (or a base /// of a virtual base) /// \param Access the access along the inheritance path to this base @@ -1050,8 +1009,10 @@ static void CollectVisibleConversions(ASTContext &Context, HiddenTypes = &HiddenTypesBuffer; for (UnresolvedSetIterator I = Cs.begin(), E = Cs.end(); I != E; ++I) { - bool Hidden = - !HiddenTypesBuffer.insert(GetConversionType(Context, I.getDecl())); + CanQualType ConvType(GetConversionType(Context, I.getDecl())); + bool Hidden = ParentHiddenTypes.count(ConvType); + if (!Hidden) + HiddenTypesBuffer.insert(ConvType); // If this conversion is hidden and we're in a virtual base, // remember that it's hidden along some inheritance path. @@ -1247,13 +1208,16 @@ void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) { // Objective-C Automatic Reference Counting: // If a class has a non-static data member of Objective-C pointer // type (or array thereof), it is a non-POD type and its - // default constructor (if any), copy constructor, copy assignment - // operator, and destructor are non-trivial. + // default constructor (if any), copy constructor, move constructor, + // copy assignment operator, move assignment operator, and destructor are + // non-trivial. struct DefinitionData &Data = data(); Data.PlainOldData = false; Data.HasTrivialDefaultConstructor = false; Data.HasTrivialCopyConstructor = false; + Data.HasTrivialMoveConstructor = false; Data.HasTrivialCopyAssignment = false; + Data.HasTrivialMoveAssignment = false; Data.HasTrivialDestructor = false; Data.HasIrrelevantDestructor = false; } @@ -1316,6 +1280,55 @@ bool CXXRecordDecl::mayBeAbstract() const { void CXXMethodDecl::anchor() { } +static bool recursivelyOverrides(const CXXMethodDecl *DerivedMD, + const CXXMethodDecl *BaseMD) { + for (CXXMethodDecl::method_iterator I = DerivedMD->begin_overridden_methods(), + E = DerivedMD->end_overridden_methods(); I != E; ++I) { + const CXXMethodDecl *MD = *I; + if (MD->getCanonicalDecl() == BaseMD->getCanonicalDecl()) + return true; + if (recursivelyOverrides(MD, BaseMD)) + return true; + } + return false; +} + +CXXMethodDecl * +CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD) { + if (this->getParent()->getCanonicalDecl() == RD->getCanonicalDecl()) + return this; + + // Lookup doesn't work for destructors, so handle them separately. + if (isa<CXXDestructorDecl>(this)) { + CXXMethodDecl *MD = RD->getDestructor(); + if (MD && recursivelyOverrides(MD, this)) + return MD; + return NULL; + } + + lookup_const_result Candidates = RD->lookup(getDeclName()); + for (NamedDecl * const * I = Candidates.first; I != Candidates.second; ++I) { + CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(*I); + if (!MD) + continue; + if (recursivelyOverrides(MD, this)) + return MD; + } + + for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), + E = RD->bases_end(); I != E; ++I) { + const RecordType *RT = I->getType()->getAs<RecordType>(); + if (!RT) + continue; + const CXXRecordDecl *Base = cast<CXXRecordDecl>(RT->getDecl()); + CXXMethodDecl *T = this->getCorrespondingMethodInClass(Base); + if (T) + return T; + } + + return NULL; +} + CXXMethodDecl * CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc, @@ -1690,7 +1703,9 @@ bool CXXConstructorDecl::isConvertingConstructor(bool AllowExplicit) const { return (getNumParams() == 0 && getType()->getAs<FunctionProtoType>()->isVariadic()) || (getNumParams() == 1) || - (getNumParams() > 1 && getParamDecl(1)->hasDefaultArg()); + (getNumParams() > 1 && + (getParamDecl(1)->hasDefaultArg() || + getParamDecl(1)->isParameterPack())); } bool CXXConstructorDecl::isSpecializationCopyingObject() const { @@ -1993,15 +2008,17 @@ StaticAssertDecl *StaticAssertDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation StaticAssertLoc, Expr *AssertExpr, StringLiteral *Message, - SourceLocation RParenLoc) { + SourceLocation RParenLoc, + bool Failed) { return new (C) StaticAssertDecl(DC, StaticAssertLoc, AssertExpr, Message, - RParenLoc); + RParenLoc, Failed); } StaticAssertDecl *StaticAssertDecl::CreateDeserialized(ASTContext &C, unsigned ID) { void *Mem = AllocateDeserializedDecl(C, ID, sizeof(StaticAssertDecl)); - return new (Mem) StaticAssertDecl(0, SourceLocation(), 0, 0,SourceLocation()); + return new (Mem) StaticAssertDecl(0, SourceLocation(), 0, 0, + SourceLocation(), false); } static const char *getAccessName(AccessSpecifier AS) { diff --git a/lib/AST/DeclFriend.cpp b/lib/AST/DeclFriend.cpp index 6e3bd8d4225b..553d170fc3d5 100644 --- a/lib/AST/DeclFriend.cpp +++ b/lib/AST/DeclFriend.cpp @@ -12,12 +12,18 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" #include "clang/AST/DeclFriend.h" #include "clang/AST/DeclTemplate.h" using namespace clang; void FriendDecl::anchor() { } +FriendDecl *FriendDecl::getNextFriendSlowCase() { + return cast_or_null<FriendDecl>( + NextFriend.get(getASTContext().getExternalSource())); +} + FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, FriendUnion Friend, diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index 2370d3c018f2..4d48ad8e4f5a 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -363,9 +363,12 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupMethod(Selector Sel, return NULL; } +// Will search "local" class/category implementations for a method decl. +// If failed, then we search in class's root for an instance method. +// Returns 0 if no method is found. ObjCMethodDecl *ObjCInterfaceDecl::lookupPrivateMethod( const Selector &Sel, - bool Instance) { + bool Instance) const { // FIXME: Should make sure no callers ever do this. if (!hasDefinition()) return 0; @@ -377,7 +380,23 @@ ObjCMethodDecl *ObjCInterfaceDecl::lookupPrivateMethod( if (ObjCImplementationDecl *ImpDecl = getImplementation()) Method = Instance ? ImpDecl->getInstanceMethod(Sel) : ImpDecl->getClassMethod(Sel); - + + // Look through local category implementations associated with the class. + if (!Method) + Method = Instance ? getCategoryInstanceMethod(Sel) + : getCategoryClassMethod(Sel); + + // Before we give up, check if the selector is an instance method. + // But only in the root. This matches gcc's behavior and what the + // runtime expects. + if (!Instance && !Method && !getSuperClass()) { + Method = lookupInstanceMethod(Sel); + // Look through local category implementations associated + // with the root class. + if (!Method) + Method = lookupPrivateMethod(Sel, true); + } + if (!Method && getSuperClass()) return getSuperClass()->lookupPrivateMethod(Sel, Instance); return Method; @@ -451,7 +470,8 @@ void ObjCMethodDecl::setMethodParams(ASTContext &C, if (isImplicit()) return setParamsAndSelLocs(C, Params, ArrayRef<SourceLocation>()); - SelLocsKind = hasStandardSelectorLocs(getSelector(), SelLocs, Params, EndLoc); + SelLocsKind = hasStandardSelectorLocs(getSelector(), SelLocs, Params, + DeclEndLoc); if (SelLocsKind != SelLoc_NonStandard) return setParamsAndSelLocs(C, Params, ArrayRef<SourceLocation>()); @@ -523,6 +543,12 @@ ObjCMethodDecl *ObjCMethodDecl::getCanonicalDecl() { return this; } +SourceLocation ObjCMethodDecl::getLocEnd() const { + if (Stmt *Body = getBody()) + return Body->getLocEnd(); + return DeclEndLoc; +} + ObjCMethodFamily ObjCMethodDecl::getMethodFamily() const { ObjCMethodFamily family = static_cast<ObjCMethodFamily>(Family); if (family != static_cast<unsigned>(InvalidObjCMethodFamily)) @@ -767,7 +793,7 @@ ObjCIvarDecl *ObjCInterfaceDecl::all_declared_ivar_begin() { ObjCIvarDecl *curIvar = 0; if (!ivar_empty()) { ObjCInterfaceDecl::ivar_iterator I = ivar_begin(), E = ivar_end(); - data().IvarList = (*I); ++I; + data().IvarList = *I; ++I; for (curIvar = data().IvarList; I != E; curIvar = *I, ++I) curIvar->setNextIvar(*I); } @@ -778,7 +804,7 @@ ObjCIvarDecl *ObjCInterfaceDecl::all_declared_ivar_begin() { ObjCCategoryDecl::ivar_iterator I = CDecl->ivar_begin(), E = CDecl->ivar_end(); if (!data().IvarList) { - data().IvarList = (*I); ++I; + data().IvarList = *I; ++I; curIvar = data().IvarList; } for ( ;I != E; curIvar = *I, ++I) @@ -791,7 +817,7 @@ ObjCIvarDecl *ObjCInterfaceDecl::all_declared_ivar_begin() { ObjCImplementationDecl::ivar_iterator I = ImplDecl->ivar_begin(), E = ImplDecl->ivar_end(); if (!data().IvarList) { - data().IvarList = (*I); ++I; + data().IvarList = *I; ++I; curIvar = data().IvarList; } for ( ;I != E; curIvar = *I, ++I) @@ -915,16 +941,10 @@ ObjCIvarDecl *ObjCIvarDecl::Create(ASTContext &C, ObjCContainerDecl *DC, // decl contexts, the previously built IvarList must be rebuilt. ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(DC); if (!ID) { - if (ObjCImplementationDecl *IM = dyn_cast<ObjCImplementationDecl>(DC)) { + if (ObjCImplementationDecl *IM = dyn_cast<ObjCImplementationDecl>(DC)) ID = IM->getClassInterface(); - if (BW) - IM->setHasSynthBitfield(true); - } else { - ObjCCategoryDecl *CD = cast<ObjCCategoryDecl>(DC); - ID = CD->getClassInterface(); - if (BW) - CD->setHasSynthBitfield(true); - } + else + ID = cast<ObjCCategoryDecl>(DC)->getClassInterface(); } ID->setIvarList(0); } @@ -1169,7 +1189,7 @@ void ObjCImplDecl::setClassInterface(ObjCInterfaceDecl *IFace) { } /// FindPropertyImplIvarDecl - This method lookup the ivar in the list of -/// properties implemented in this category @implementation block and returns +/// properties implemented in this category \@implementation block and returns /// the implemented property that uses it. /// ObjCPropertyImplDecl *ObjCImplDecl:: @@ -1184,8 +1204,8 @@ FindPropertyImplIvarDecl(IdentifierInfo *ivarId) const { } /// FindPropertyImplDecl - This method looks up a previous ObjCPropertyImplDecl -/// added to the list of those properties @synthesized/@dynamic in this -/// category @implementation block. +/// added to the list of those properties \@synthesized/\@dynamic in this +/// category \@implementation block. /// ObjCPropertyImplDecl *ObjCImplDecl:: FindPropertyImplDecl(IdentifierInfo *Id) const { diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index 74e1c1bb9df9..aad0ca1b536e 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -114,6 +114,8 @@ static QualType GetBaseType(QualType T) { BaseType = FTy->getResultType(); else if (const VectorType *VTy = BaseType->getAs<VectorType>()) BaseType = VTy->getElementType(); + else if (const ReferenceType *RTy = BaseType->getAs<ReferenceType>()) + BaseType = RTy->getPointeeType(); else llvm_unreachable("Unknown declarator!"); } @@ -173,8 +175,10 @@ void DeclContext::dumpDeclContext() const { Printer.VisitDeclContext(const_cast<DeclContext *>(this), /*Indent=*/false); } -void Decl::dump() const { - print(llvm::errs()); +void Decl::dump(raw_ostream &Out) const { + PrintingPolicy Policy = getASTContext().getPrintingPolicy(); + Policy.Dump = true; + print(Out, Policy, /*Indentation*/ 0, /*PrintInstantiation*/ true); } raw_ostream& DeclPrinter::Indent(unsigned Indentation) { @@ -322,15 +326,13 @@ void DeclPrinter::VisitTranslationUnitDecl(TranslationUnitDecl *D) { } void DeclPrinter::VisitTypedefDecl(TypedefDecl *D) { - std::string S = D->getNameAsString(); - D->getUnderlyingType().getAsStringInternal(S, Policy); if (!Policy.SuppressSpecifiers) { Out << "typedef "; if (D->isModulePrivate()) Out << "__module_private__ "; } - Out << S; + D->getUnderlyingType().print(Out, Policy, D->getName()); prettyPrintAttributes(D); } @@ -350,11 +352,8 @@ void DeclPrinter::VisitEnumDecl(EnumDecl *D) { } Out << *D; - if (D->isFixed()) { - std::string Underlying; - D->getIntegerType().getAsStringInternal(Underlying, Policy); - Out << " : " << Underlying; - } + if (D->isFixed()) + Out << " : " << D->getIntegerType().stream(Policy); if (D->isCompleteDefinition()) { Out << " {\n"; @@ -441,13 +440,12 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { Proto += ")"; - if (FT && FT->getTypeQuals()) { - unsigned TypeQuals = FT->getTypeQuals(); - if (TypeQuals & Qualifiers::Const) + if (FT) { + if (FT->isConst()) Proto += " const"; - if (TypeQuals & Qualifiers::Volatile) + if (FT->isVolatile()) Proto += " volatile"; - if (TypeQuals & Qualifiers::Restrict) + if (FT->isRestrict()) Proto += " restrict"; } @@ -460,9 +458,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { if (I) Proto += ", "; - std::string ExceptionType; - FT->getExceptionType(I).getAsStringInternal(ExceptionType, SubPolicy); - Proto += ExceptionType; + Proto += FT->getExceptionType(I).getAsString(SubPolicy);; } Proto += ")"; } else if (FT && isNoexceptExceptionSpec(FT->getExceptionSpecType())) { @@ -542,12 +538,11 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { } } else - AFT->getResultType().getAsStringInternal(Proto, Policy); + AFT->getResultType().print(Out, Policy, Proto); } else { - Ty.getAsStringInternal(Proto, Policy); + Ty.print(Out, Policy, Proto); } - Out << Proto; prettyPrintAttributes(D); if (D->isPure()) @@ -581,9 +576,7 @@ void DeclPrinter::VisitFieldDecl(FieldDecl *D) { if (!Policy.SuppressSpecifiers && D->isModulePrivate()) Out << "__module_private__ "; - std::string Name = D->getNameAsString(); - D->getType().getAsStringInternal(Name, Policy); - Out << Name; + Out << D->getType().stream(Policy, D->getName()); if (D->isBitField()) { Out << " : "; @@ -592,7 +585,10 @@ void DeclPrinter::VisitFieldDecl(FieldDecl *D) { Expr *Init = D->getInClassInitializer(); if (!Policy.SuppressInitializers && Init) { - Out << " = "; + if (D->getInClassInitStyle() == ICIS_ListInit) + Out << " "; + else + Out << " = "; Init->printPretty(Out, Context, 0, Policy, Indentation); } prettyPrintAttributes(D); @@ -613,12 +609,10 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { if (!Policy.SuppressSpecifiers && D->isModulePrivate()) Out << "__module_private__ "; - std::string Name = D->getNameAsString(); QualType T = D->getType(); if (ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) T = Parm->getOriginalType(); - T.getAsStringInternal(Name, Policy); - Out << Name; + T.print(Out, Policy, D->getName()); Expr *Init = D->getInit(); if (!Policy.SuppressInitializers && Init) { bool ImplicitInit = false; @@ -666,6 +660,8 @@ void DeclPrinter::VisitStaticAssertDecl(StaticAssertDecl *D) { // C++ declarations //---------------------------------------------------------------------------- void DeclPrinter::VisitNamespaceDecl(NamespaceDecl *D) { + if (D->isInline()) + Out << "inline "; Out << "namespace " << *D << " {\n"; VisitDeclContext(D); Indent() << "}"; @@ -923,7 +919,7 @@ void DeclPrinter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *OID) { Indentation += Policy.Indentation; for (ObjCInterfaceDecl::ivar_iterator I = OID->ivar_begin(), E = OID->ivar_end(); I != E; ++I) { - Indent() << (*I)->getType().getAsString(Policy) << ' ' << **I << ";\n"; + Indent() << I->getType().getAsString(Policy) << ' ' << **I << ";\n"; } Indentation -= Policy.Indentation; Out << "}\n"; diff --git a/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp index 4590195d6b31..5aebc2b764c0 100644 --- a/lib/AST/DeclTemplate.cpp +++ b/lib/AST/DeclTemplate.cpp @@ -145,7 +145,7 @@ RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() { template <class EntryType> typename RedeclarableTemplateDecl::SpecEntryTraits<EntryType>::DeclType* RedeclarableTemplateDecl::findSpecializationImpl( - llvm::FoldingSet<EntryType> &Specs, + llvm::FoldingSetVector<EntryType> &Specs, const TemplateArgument *Args, unsigned NumArgs, void *&InsertPos) { typedef SpecEntryTraits<EntryType> SETraits; @@ -298,13 +298,13 @@ void ClassTemplateDecl::LoadLazySpecializations() { } } -llvm::FoldingSet<ClassTemplateSpecializationDecl> & +llvm::FoldingSetVector<ClassTemplateSpecializationDecl> & ClassTemplateDecl::getSpecializations() { LoadLazySpecializations(); return getCommonPtr()->Specializations; } -llvm::FoldingSet<ClassTemplatePartialSpecializationDecl> & +llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> & ClassTemplateDecl::getPartialSpecializations() { LoadLazySpecializations(); return getCommonPtr()->PartialSpecializations; @@ -363,11 +363,11 @@ void ClassTemplateDecl::AddPartialSpecialization( void ClassTemplateDecl::getPartialSpecializations( SmallVectorImpl<ClassTemplatePartialSpecializationDecl *> &PS) { - llvm::FoldingSet<ClassTemplatePartialSpecializationDecl> &PartialSpecs + llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> &PartialSpecs = getPartialSpecializations(); PS.clear(); PS.resize(PartialSpecs.size()); - for (llvm::FoldingSet<ClassTemplatePartialSpecializationDecl>::iterator + for (llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl>::iterator P = PartialSpecs.begin(), PEnd = PartialSpecs.end(); P != PEnd; ++P) { assert(!PS[P->getSequenceNumber()]); @@ -378,7 +378,8 @@ void ClassTemplateDecl::getPartialSpecializations( ClassTemplatePartialSpecializationDecl * ClassTemplateDecl::findPartialSpecialization(QualType T) { ASTContext &Context = getASTContext(); - typedef llvm::FoldingSet<ClassTemplatePartialSpecializationDecl>::iterator + using llvm::FoldingSetVector; + typedef FoldingSetVector<ClassTemplatePartialSpecializationDecl>::iterator partial_spec_iterator; for (partial_spec_iterator P = getPartialSpecializations().begin(), PEnd = getPartialSpecializations().end(); @@ -394,7 +395,7 @@ ClassTemplatePartialSpecializationDecl * ClassTemplateDecl::findPartialSpecInstantiatedFromMember( ClassTemplatePartialSpecializationDecl *D) { Decl *DCanon = D->getCanonicalDecl(); - for (llvm::FoldingSet<ClassTemplatePartialSpecializationDecl>::iterator + for (llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl>::iterator P = getPartialSpecializations().begin(), PEnd = getPartialSpecializations().end(); P != PEnd; ++P) { @@ -868,5 +869,6 @@ ClassScopeFunctionSpecializationDecl::CreateDeserialized(ASTContext &C, unsigned ID) { void *Mem = AllocateDeserializedDecl(C, ID, sizeof(ClassScopeFunctionSpecializationDecl)); - return new (Mem) ClassScopeFunctionSpecializationDecl(0, SourceLocation(), 0); + return new (Mem) ClassScopeFunctionSpecializationDecl(0, SourceLocation(), 0, + false, TemplateArgumentListInfo()); } diff --git a/lib/AST/DeclarationName.cpp b/lib/AST/DeclarationName.cpp index 64924ad95095..28188d91c10a 100644 --- a/lib/AST/DeclarationName.cpp +++ b/lib/AST/DeclarationName.cpp @@ -135,33 +135,6 @@ int DeclarationName::compare(DeclarationName LHS, DeclarationName RHS) { } // end namespace clang -DeclarationName::DeclarationName(Selector Sel) { - if (!Sel.getAsOpaquePtr()) { - Ptr = 0; - return; - } - - switch (Sel.getNumArgs()) { - case 0: - Ptr = reinterpret_cast<uintptr_t>(Sel.getAsIdentifierInfo()); - assert((Ptr & PtrMask) == 0 && "Improperly aligned IdentifierInfo"); - Ptr |= StoredObjCZeroArgSelector; - break; - - case 1: - Ptr = reinterpret_cast<uintptr_t>(Sel.getAsIdentifierInfo()); - assert((Ptr & PtrMask) == 0 && "Improperly aligned IdentifierInfo"); - Ptr |= StoredObjCOneArgSelector; - break; - - default: - Ptr = Sel.InfoPtr & ~Selector::ArgFlags; - assert((Ptr & PtrMask) == 0 && "Improperly aligned MultiKeywordSelector"); - Ptr |= StoredDeclarationNameExtra; - break; - } -} - DeclarationName::NameKind DeclarationName::getNameKind() const { switch (getStoredNameKind()) { case StoredIdentifier: return Identifier; @@ -305,28 +278,10 @@ IdentifierInfo *DeclarationName::getCXXLiteralIdentifier() const { return 0; } -Selector DeclarationName::getObjCSelector() const { - switch (getNameKind()) { - case ObjCZeroArgSelector: - return Selector(reinterpret_cast<IdentifierInfo *>(Ptr & ~PtrMask), 0); - - case ObjCOneArgSelector: - return Selector(reinterpret_cast<IdentifierInfo *>(Ptr & ~PtrMask), 1); - - case ObjCMultiArgSelector: - return Selector(reinterpret_cast<MultiKeywordSelector *>(Ptr & ~PtrMask)); - - default: - break; - } - - return Selector(); -} - -void *DeclarationName::getFETokenInfoAsVoid() const { +void *DeclarationName::getFETokenInfoAsVoidSlow() const { switch (getNameKind()) { case Identifier: - return getAsIdentifierInfo()->getFETokenInfo<void>(); + llvm_unreachable("Handled by getFETokenInfo()"); case CXXConstructorName: case CXXDestructorName: @@ -481,12 +436,6 @@ DeclarationNameTable::getCXXLiteralOperatorName(IdentifierInfo *II) { return DeclarationName(LiteralName); } -unsigned -llvm::DenseMapInfo<clang::DeclarationName>:: -getHashValue(clang::DeclarationName N) { - return DenseMapInfo<void*>::getHashValue(N.getAsOpaquePtr()); -} - DeclarationNameLoc::DeclarationNameLoc(DeclarationName Name) { switch (Name.getNameKind()) { case DeclarationName::Identifier: diff --git a/lib/AST/DumpXML.cpp b/lib/AST/DumpXML.cpp index 4c7cd8a6793b..c1432b5720c7 100644 --- a/lib/AST/DumpXML.cpp +++ b/lib/AST/DumpXML.cpp @@ -326,7 +326,7 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>, } case TemplateArgument::Integral: { push("integer"); - setInteger("value", *A.getAsIntegral()); + setInteger("value", A.getAsIntegral()); completeAttrs(); pop(); break; @@ -778,7 +778,6 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>, // ObjCCategoryDecl void visitObjCCategoryDeclAttrs(ObjCCategoryDecl *D) { setFlag("extension", D->IsClassExtension()); - setFlag("synth_bitfield", D->hasSynthBitfield()); } void visitObjCCategoryDeclChildren(ObjCCategoryDecl *D) { visitDeclRef("interface", D->getClassInterface()); @@ -804,7 +803,6 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>, // ObjCImplementationDecl void visitObjCImplementationDeclAttrs(ObjCImplementationDecl *D) { - setFlag("synth_bitfield", D->hasSynthBitfield()); set("identifier", D->getName()); } void visitObjCImplementationDeclChildren(ObjCImplementationDecl *D) { @@ -973,9 +971,9 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>, } void visitFunctionProtoTypeAttrs(FunctionProtoType *T) { - setFlag("const", T->getTypeQuals() & Qualifiers::Const); - setFlag("volatile", T->getTypeQuals() & Qualifiers::Volatile); - setFlag("restrict", T->getTypeQuals() & Qualifiers::Restrict); + setFlag("const", T->isConst()); + setFlag("volatile", T->isVolatile()); + setFlag("restrict", T->isRestrict()); } void visitFunctionProtoTypeChildren(FunctionProtoType *T) { push("parameters"); @@ -1024,17 +1022,12 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>, }; } -void Decl::dumpXML() const { - dumpXML(llvm::errs()); -} - void Decl::dumpXML(raw_ostream &out) const { XMLDumper(out, getASTContext()).dispatch(const_cast<Decl*>(this)); } #else /* ifndef NDEBUG */ -void Decl::dumpXML() const {} void Decl::dumpXML(raw_ostream &out) const {} #endif diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index fcde5429b394..24361efafa6c 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -33,6 +33,21 @@ #include <cstring> using namespace clang; +const CXXRecordDecl *Expr::getBestDynamicClassType() const { + const Expr *E = ignoreParenBaseCasts(); + + QualType DerivedType = E->getType(); + if (const PointerType *PTy = DerivedType->getAs<PointerType>()) + DerivedType = PTy->getPointeeType(); + + if (DerivedType->isDependentType()) + return NULL; + + const RecordType *Ty = DerivedType->castAs<RecordType>(); + Decl *D = Ty->getDecl(); + return cast<CXXRecordDecl>(D); +} + /// isKnownToHaveBooleanValue - Return true if this is an integer expression /// that is known to return 0 or 1. This happens for _Bool/bool expressions /// but also int expressions which are produced by things like comparisons in @@ -196,7 +211,7 @@ static void computeDeclRefDependence(ASTContext &Ctx, NamedDecl *D, QualType T, if ((Ctx.getLangOpts().CPlusPlus0x ? Var->getType()->isLiteralType() : Var->getType()->isIntegralOrEnumerationType()) && - (Var->getType().getCVRQualifiers() == Qualifiers::Const || + (Var->getType().isConstQualified() || Var->getType()->isReferenceType())) { if (const Expr *Init = Var->getAnyInitializer()) if (Init->isValueDependent()) { @@ -414,9 +429,7 @@ std::string PredefinedExpr::ComputeName(IdentType IT, const Decl *CurrentDecl) { if (FT) { for (unsigned i = 0, e = Decl->getNumParams(); i != e; ++i) { if (i) POut << ", "; - std::string Param; - Decl->getParamDecl(i)->getType().getAsStringInternal(Param, Policy); - POut << Param; + POut << Decl->getParamDecl(i)->getType().stream(Policy); } if (FT->isVariadic()) { @@ -427,10 +440,10 @@ std::string PredefinedExpr::ComputeName(IdentType IT, const Decl *CurrentDecl) { POut << ")"; if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) { - Qualifiers ThisQuals = Qualifiers::fromCVRMask(MD->getTypeQualifiers()); - if (ThisQuals.hasConst()) + const FunctionType *FT = cast<FunctionType>(MD->getType().getTypePtr()); + if (FT->isConst()) POut << " const"; - if (ThisQuals.hasVolatile()) + if (FT->isVolatile()) POut << " volatile"; RefQualifierKind Ref = MD->getRefQualifier(); if (Ref == RQ_LValue) @@ -545,6 +558,17 @@ void APNumericStorage::setIntValue(ASTContext &C, const llvm::APInt &Val) { VAL = 0; } +IntegerLiteral::IntegerLiteral(ASTContext &C, const llvm::APInt &V, + QualType type, SourceLocation l) + : Expr(IntegerLiteralClass, type, VK_RValue, OK_Ordinary, false, false, + false, false), + Loc(l) { + assert(type->isIntegerType() && "Illegal type in IntegerLiteral"); + assert(V.getBitWidth() == C.getIntWidth(type) && + "Integer type is not the correct size for constant."); + setValue(C, V); +} + IntegerLiteral * IntegerLiteral::Create(ASTContext &C, const llvm::APInt &V, QualType type, SourceLocation l) { @@ -556,6 +580,23 @@ IntegerLiteral::Create(ASTContext &C, EmptyShell Empty) { return new (C) IntegerLiteral(Empty); } +FloatingLiteral::FloatingLiteral(ASTContext &C, const llvm::APFloat &V, + bool isexact, QualType Type, SourceLocation L) + : Expr(FloatingLiteralClass, Type, VK_RValue, OK_Ordinary, false, false, + false, false), Loc(L) { + FloatingLiteralBits.IsIEEE = + &C.getTargetInfo().getLongDoubleFormat() == &llvm::APFloat::IEEEquad; + FloatingLiteralBits.IsExact = isexact; + setValue(C, V); +} + +FloatingLiteral::FloatingLiteral(ASTContext &C, EmptyShell Empty) + : Expr(FloatingLiteralClass, Empty) { + FloatingLiteralBits.IsIEEE = + &C.getTargetInfo().getLongDoubleFormat() == &llvm::APFloat::IEEEquad; + FloatingLiteralBits.IsExact = false; +} + FloatingLiteral * FloatingLiteral::Create(ASTContext &C, const llvm::APFloat &V, bool isexact, QualType Type, SourceLocation L) { @@ -635,6 +676,99 @@ StringLiteral *StringLiteral::CreateEmpty(ASTContext &C, unsigned NumStrs) { return SL; } +void StringLiteral::outputString(raw_ostream &OS) { + switch (getKind()) { + case Ascii: break; // no prefix. + case Wide: OS << 'L'; break; + case UTF8: OS << "u8"; break; + case UTF16: OS << 'u'; break; + case UTF32: OS << 'U'; break; + } + OS << '"'; + static const char Hex[] = "0123456789ABCDEF"; + + unsigned LastSlashX = getLength(); + for (unsigned I = 0, N = getLength(); I != N; ++I) { + switch (uint32_t Char = getCodeUnit(I)) { + default: + // FIXME: Convert UTF-8 back to codepoints before rendering. + + // Convert UTF-16 surrogate pairs back to codepoints before rendering. + // Leave invalid surrogates alone; we'll use \x for those. + if (getKind() == UTF16 && I != N - 1 && Char >= 0xd800 && + Char <= 0xdbff) { + uint32_t Trail = getCodeUnit(I + 1); + if (Trail >= 0xdc00 && Trail <= 0xdfff) { + Char = 0x10000 + ((Char - 0xd800) << 10) + (Trail - 0xdc00); + ++I; + } + } + + if (Char > 0xff) { + // If this is a wide string, output characters over 0xff using \x + // escapes. Otherwise, this is a UTF-16 or UTF-32 string, and Char is a + // codepoint: use \x escapes for invalid codepoints. + if (getKind() == Wide || + (Char >= 0xd800 && Char <= 0xdfff) || Char >= 0x110000) { + // FIXME: Is this the best way to print wchar_t? + OS << "\\x"; + int Shift = 28; + while ((Char >> Shift) == 0) + Shift -= 4; + for (/**/; Shift >= 0; Shift -= 4) + OS << Hex[(Char >> Shift) & 15]; + LastSlashX = I; + break; + } + + if (Char > 0xffff) + OS << "\\U00" + << Hex[(Char >> 20) & 15] + << Hex[(Char >> 16) & 15]; + else + OS << "\\u"; + OS << Hex[(Char >> 12) & 15] + << Hex[(Char >> 8) & 15] + << Hex[(Char >> 4) & 15] + << Hex[(Char >> 0) & 15]; + break; + } + + // If we used \x... for the previous character, and this character is a + // hexadecimal digit, prevent it being slurped as part of the \x. + if (LastSlashX + 1 == I) { + switch (Char) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + OS << "\"\""; + } + } + + assert(Char <= 0xff && + "Characters above 0xff should already have been handled."); + + if (isprint(Char)) + OS << (char)Char; + else // Output anything hard as an octal escape. + OS << '\\' + << (char)('0' + ((Char >> 6) & 7)) + << (char)('0' + ((Char >> 3) & 7)) + << (char)('0' + ((Char >> 0) & 7)); + break; + // Handle some common non-printable cases to make dumps prettier. + case '\\': OS << "\\\\"; break; + case '"': OS << "\\\""; break; + case '\n': OS << "\\n"; break; + case '\t': OS << "\\t"; break; + case '\a': OS << "\\a"; break; + case '\b': OS << "\\b"; break; + } + } + OS << '"'; +} + void StringLiteral::setString(ASTContext &C, StringRef Str, StringKind Kind, bool IsPascal) { //FIXME: we assume that the string data comes from a target that uses the same @@ -681,7 +815,8 @@ void StringLiteral::setString(ASTContext &C, StringRef Str, SourceLocation StringLiteral:: getLocationOfByte(unsigned ByteNo, const SourceManager &SM, const LangOptions &Features, const TargetInfo &Target) const { - assert(Kind == StringLiteral::Ascii && "This only works for ASCII strings"); + assert((Kind == StringLiteral::Ascii || Kind == StringLiteral::UTF8) && + "Only narrow string literals are currently supported"); // Loop over all of the tokens in this string until we find the one that // contains the byte we're looking for. @@ -704,14 +839,9 @@ getLocationOfByte(unsigned ByteNo, const SourceManager &SM, const char *StrData = Buffer.data()+LocInfo.second; - // Create a langops struct and enable trigraphs. This is sufficient for - // relexing tokens. - LangOptions LangOpts; - LangOpts.Trigraphs = true; - // Create a lexer starting at the beginning of this token. - Lexer TheLexer(StrTokSpellingLoc, Features, Buffer.begin(), StrData, - Buffer.end()); + Lexer TheLexer(SM.getLocForStartOfFile(LocInfo.first), Features, + Buffer.begin(), StrData, Buffer.end()); Token TheTok; TheLexer.LexFromRawLexer(TheTok); @@ -1656,8 +1786,9 @@ Stmt *BlockExpr::getBody() { /// be warned about if the result is unused. If so, fill in Loc and Ranges /// with location to warn on and the source range[s] to report with the /// warning. -bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, - SourceRange &R2, ASTContext &Ctx) const { +bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, + SourceRange &R1, SourceRange &R2, + ASTContext &Ctx) const { // Don't warn if the expr is type dependent. The type could end up // instantiating to void. if (isTypeDependent()) @@ -1667,30 +1798,32 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, default: if (getType()->isVoidType()) return false; + WarnE = this; Loc = getExprLoc(); R1 = getSourceRange(); return true; case ParenExprClass: return cast<ParenExpr>(this)->getSubExpr()-> - isUnusedResultAWarning(Loc, R1, R2, Ctx); + isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); case GenericSelectionExprClass: return cast<GenericSelectionExpr>(this)->getResultExpr()-> - isUnusedResultAWarning(Loc, R1, R2, Ctx); + isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); case UnaryOperatorClass: { const UnaryOperator *UO = cast<UnaryOperator>(this); switch (UO->getOpcode()) { - default: break; + case UO_Plus: + case UO_Minus: + case UO_AddrOf: + case UO_Not: + case UO_LNot: + case UO_Deref: + break; case UO_PostInc: case UO_PostDec: case UO_PreInc: case UO_PreDec: // ++/-- return false; // Not a warning. - case UO_Deref: - // Dereferencing a volatile pointer is a side-effect. - if (Ctx.getCanonicalType(getType()).isVolatileQualified()) - return false; - break; case UO_Real: case UO_Imag: // accessing a piece of a volatile complex is a side-effect. @@ -1699,8 +1832,9 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, return false; break; case UO_Extension: - return UO->getSubExpr()->isUnusedResultAWarning(Loc, R1, R2, Ctx); + return UO->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); } + WarnE = this; Loc = UO->getOperatorLoc(); R1 = UO->getSubExpr()->getSourceRange(); return true; @@ -1719,17 +1853,18 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, dyn_cast<IntegerLiteral>(BO->getRHS()->IgnoreParens())) if (IE->getValue() == 0) return false; - return BO->getRHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx); + return BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); // Consider '||', '&&' to have side effects if the LHS or RHS does. case BO_LAnd: case BO_LOr: - if (!BO->getLHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx) || - !BO->getRHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx)) + if (!BO->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx) || + !BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)) return false; break; } if (BO->isAssignmentOp()) return false; + WarnE = this; Loc = BO->getOperatorLoc(); R1 = BO->getLHS()->getSourceRange(); R2 = BO->getRHS()->getSourceRange(); @@ -1745,28 +1880,22 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, // be being used for control flow. Only warn if both the LHS and // RHS are warnings. const ConditionalOperator *Exp = cast<ConditionalOperator>(this); - if (!Exp->getRHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx)) + if (!Exp->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)) return false; if (!Exp->getLHS()) return true; - return Exp->getLHS()->isUnusedResultAWarning(Loc, R1, R2, Ctx); + return Exp->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); } case MemberExprClass: - // If the base pointer or element is to a volatile pointer/field, accessing - // it is a side effect. - if (Ctx.getCanonicalType(getType()).isVolatileQualified()) - return false; + WarnE = this; Loc = cast<MemberExpr>(this)->getMemberLoc(); R1 = SourceRange(Loc, Loc); R2 = cast<MemberExpr>(this)->getBase()->getSourceRange(); return true; case ArraySubscriptExprClass: - // If the base pointer or element is to a volatile pointer/field, accessing - // it is a side effect. - if (Ctx.getCanonicalType(getType()).isVolatileQualified()) - return false; + WarnE = this; Loc = cast<ArraySubscriptExpr>(this)->getRBracketLoc(); R1 = cast<ArraySubscriptExpr>(this)->getLHS()->getSourceRange(); R2 = cast<ArraySubscriptExpr>(this)->getRHS()->getSourceRange(); @@ -1782,6 +1911,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, const CXXOperatorCallExpr *Op = cast<CXXOperatorCallExpr>(this); if (Op->getOperator() == OO_EqualEqual || Op->getOperator() == OO_ExclaimEqual) { + WarnE = this; Loc = Op->getOperatorLoc(); R1 = Op->getSourceRange(); return true; @@ -1802,6 +1932,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, // updated to match for QoI. if (FD->getAttr<WarnUnusedResultAttr>() || FD->getAttr<PureAttr>() || FD->getAttr<ConstAttr>()) { + WarnE = this; Loc = CE->getCallee()->getLocStart(); R1 = CE->getCallee()->getSourceRange(); @@ -1826,6 +1957,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, ME->getSelector().getIdentifierInfoForSlot(0) && ME->getSelector().getIdentifierInfoForSlot(0) ->getName().startswith("init")) { + WarnE = this; Loc = getExprLoc(); R1 = ME->getSourceRange(); return true; @@ -1833,6 +1965,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, const ObjCMethodDecl *MD = ME->getMethodDecl(); if (MD && MD->getAttr<WarnUnusedResultAttr>()) { + WarnE = this; Loc = getExprLoc(); return true; } @@ -1840,6 +1973,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, } case ObjCPropertyRefExprClass: + WarnE = this; Loc = getExprLoc(); R1 = getSourceRange(); return true; @@ -1852,6 +1986,7 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, isa<BinaryOperator>(PO->getSyntacticForm())) return false; + WarnE = this; Loc = getExprLoc(); R1 = getSourceRange(); return true; @@ -1866,50 +2001,67 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, const CompoundStmt *CS = cast<StmtExpr>(this)->getSubStmt(); if (!CS->body_empty()) { if (const Expr *E = dyn_cast<Expr>(CS->body_back())) - return E->isUnusedResultAWarning(Loc, R1, R2, Ctx); + return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); if (const LabelStmt *Label = dyn_cast<LabelStmt>(CS->body_back())) if (const Expr *E = dyn_cast<Expr>(Label->getSubStmt())) - return E->isUnusedResultAWarning(Loc, R1, R2, Ctx); + return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); } if (getType()->isVoidType()) return false; + WarnE = this; Loc = cast<StmtExpr>(this)->getLParenLoc(); R1 = getSourceRange(); return true; } - case CStyleCastExprClass: - // If this is an explicit cast to void, allow it. People do this when they - // think they know what they're doing :). - if (getType()->isVoidType()) - return false; - Loc = cast<CStyleCastExpr>(this)->getLParenLoc(); - R1 = cast<CStyleCastExpr>(this)->getSubExpr()->getSourceRange(); - return true; - case CXXFunctionalCastExprClass: { - if (getType()->isVoidType()) - return false; + case CStyleCastExprClass: { + // Ignore an explicit cast to void unless the operand is a non-trivial + // volatile lvalue. const CastExpr *CE = cast<CastExpr>(this); - - // If this is a cast to void or a constructor conversion, check the operand. + if (CE->getCastKind() == CK_ToVoid) { + if (CE->getSubExpr()->isGLValue() && + CE->getSubExpr()->getType().isVolatileQualified()) { + const DeclRefExpr *DRE = + dyn_cast<DeclRefExpr>(CE->getSubExpr()->IgnoreParens()); + if (!(DRE && isa<VarDecl>(DRE->getDecl()) && + cast<VarDecl>(DRE->getDecl())->hasLocalStorage())) { + return CE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, + R1, R2, Ctx); + } + } + return false; + } + + // If this is a cast to a constructor conversion, check the operand. // Otherwise, the result of the cast is unused. - if (CE->getCastKind() == CK_ToVoid || - CE->getCastKind() == CK_ConstructorConversion) - return (cast<CastExpr>(this)->getSubExpr() - ->isUnusedResultAWarning(Loc, R1, R2, Ctx)); - Loc = cast<CXXFunctionalCastExpr>(this)->getTypeBeginLoc(); - R1 = cast<CXXFunctionalCastExpr>(this)->getSubExpr()->getSourceRange(); + if (CE->getCastKind() == CK_ConstructorConversion) + return CE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + + WarnE = this; + if (const CXXFunctionalCastExpr *CXXCE = + dyn_cast<CXXFunctionalCastExpr>(this)) { + Loc = CXXCE->getTypeBeginLoc(); + R1 = CXXCE->getSubExpr()->getSourceRange(); + } else { + const CStyleCastExpr *CStyleCE = cast<CStyleCastExpr>(this); + Loc = CStyleCE->getLParenLoc(); + R1 = CStyleCE->getSubExpr()->getSourceRange(); + } return true; } + case ImplicitCastExprClass: { + const CastExpr *ICE = cast<ImplicitCastExpr>(this); - case ImplicitCastExprClass: - // Check the operand, since implicit casts are inserted by Sema - return (cast<ImplicitCastExpr>(this) - ->getSubExpr()->isUnusedResultAWarning(Loc, R1, R2, Ctx)); + // lvalue-to-rvalue conversion on a volatile lvalue is a side-effect. + if (ICE->getCastKind() == CK_LValueToRValue && + ICE->getSubExpr()->getType().isVolatileQualified()) + return false; + return ICE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + } case CXXDefaultArgExprClass: return (cast<CXXDefaultArgExpr>(this) - ->getExpr()->isUnusedResultAWarning(Loc, R1, R2, Ctx)); + ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); case CXXNewExprClass: // FIXME: In theory, there might be new expressions that don't have side @@ -1918,10 +2070,10 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, return false; case CXXBindTemporaryExprClass: return (cast<CXXBindTemporaryExpr>(this) - ->getSubExpr()->isUnusedResultAWarning(Loc, R1, R2, Ctx)); + ->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); case ExprWithCleanupsClass: return (cast<ExprWithCleanups>(this) - ->getSubExpr()->isUnusedResultAWarning(Loc, R1, R2, Ctx)); + ->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); } } @@ -2096,7 +2248,27 @@ Expr *Expr::IgnoreParenLValueCasts() { } return E; } - + +Expr *Expr::ignoreParenBaseCasts() { + Expr *E = this; + while (true) { + if (ParenExpr *P = dyn_cast<ParenExpr>(E)) { + E = P->getSubExpr(); + continue; + } + if (CastExpr *CE = dyn_cast<CastExpr>(E)) { + if (CE->getCastKind() == CK_DerivedToBase || + CE->getCastKind() == CK_UncheckedDerivedToBase || + CE->getCastKind() == CK_NoOp) { + E = CE->getSubExpr(); + continue; + } + } + + return E; + } +} + Expr *Expr::IgnoreParenImpCasts() { Expr *E = this; while (true) { @@ -2267,6 +2439,10 @@ bool Expr::isTemporaryObject(ASTContext &C, const CXXRecordDecl *TempTy) const { if (isa<MemberExpr>(E)) return false; + if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) + if (BO->isPtrMemOp()) + return false; + // - opaque values (all) if (isa<OpaqueValueExpr>(E)) return false; @@ -2446,6 +2622,207 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const { return isEvaluatable(Ctx); } +bool Expr::HasSideEffects(const ASTContext &Ctx) const { + if (isInstantiationDependent()) + return true; + + switch (getStmtClass()) { + case NoStmtClass: + #define ABSTRACT_STMT(Type) + #define STMT(Type, Base) case Type##Class: + #define EXPR(Type, Base) + #include "clang/AST/StmtNodes.inc" + llvm_unreachable("unexpected Expr kind"); + + case DependentScopeDeclRefExprClass: + case CXXUnresolvedConstructExprClass: + case CXXDependentScopeMemberExprClass: + case UnresolvedLookupExprClass: + case UnresolvedMemberExprClass: + case PackExpansionExprClass: + case SubstNonTypeTemplateParmPackExprClass: + llvm_unreachable("shouldn't see dependent / unresolved nodes here"); + + case DeclRefExprClass: + case ObjCIvarRefExprClass: + case PredefinedExprClass: + case IntegerLiteralClass: + case FloatingLiteralClass: + case ImaginaryLiteralClass: + case StringLiteralClass: + case CharacterLiteralClass: + case OffsetOfExprClass: + case ImplicitValueInitExprClass: + case UnaryExprOrTypeTraitExprClass: + case AddrLabelExprClass: + case GNUNullExprClass: + case CXXBoolLiteralExprClass: + case CXXNullPtrLiteralExprClass: + case CXXThisExprClass: + case CXXScalarValueInitExprClass: + case TypeTraitExprClass: + case UnaryTypeTraitExprClass: + case BinaryTypeTraitExprClass: + case ArrayTypeTraitExprClass: + case ExpressionTraitExprClass: + case CXXNoexceptExprClass: + case SizeOfPackExprClass: + case ObjCStringLiteralClass: + case ObjCEncodeExprClass: + case ObjCBoolLiteralExprClass: + case CXXUuidofExprClass: + case OpaqueValueExprClass: + // These never have a side-effect. + return false; + + case CallExprClass: + case CompoundAssignOperatorClass: + case VAArgExprClass: + case AtomicExprClass: + case StmtExprClass: + case CXXOperatorCallExprClass: + case CXXMemberCallExprClass: + case UserDefinedLiteralClass: + case CXXThrowExprClass: + case CXXNewExprClass: + case CXXDeleteExprClass: + case ExprWithCleanupsClass: + case CXXBindTemporaryExprClass: + case BlockExprClass: + case CUDAKernelCallExprClass: + // These always have a side-effect. + return true; + + case ParenExprClass: + case ArraySubscriptExprClass: + case MemberExprClass: + case ConditionalOperatorClass: + case BinaryConditionalOperatorClass: + case CompoundLiteralExprClass: + case ExtVectorElementExprClass: + case DesignatedInitExprClass: + case ParenListExprClass: + case CXXPseudoDestructorExprClass: + case SubstNonTypeTemplateParmExprClass: + case MaterializeTemporaryExprClass: + case ShuffleVectorExprClass: + case AsTypeExprClass: + // These have a side-effect if any subexpression does. + break; + + case UnaryOperatorClass: + if (cast<UnaryOperator>(this)->isIncrementDecrementOp()) + return true; + break; + + case BinaryOperatorClass: + if (cast<BinaryOperator>(this)->isAssignmentOp()) + return true; + break; + + case InitListExprClass: + // FIXME: The children for an InitListExpr doesn't include the array filler. + if (const Expr *E = cast<InitListExpr>(this)->getArrayFiller()) + if (E->HasSideEffects(Ctx)) + return true; + break; + + case GenericSelectionExprClass: + return cast<GenericSelectionExpr>(this)->getResultExpr()-> + HasSideEffects(Ctx); + + case ChooseExprClass: + return cast<ChooseExpr>(this)->getChosenSubExpr(Ctx)->HasSideEffects(Ctx); + + case CXXDefaultArgExprClass: + return cast<CXXDefaultArgExpr>(this)->getExpr()->HasSideEffects(Ctx); + + case CXXDynamicCastExprClass: { + // A dynamic_cast expression has side-effects if it can throw. + const CXXDynamicCastExpr *DCE = cast<CXXDynamicCastExpr>(this); + if (DCE->getTypeAsWritten()->isReferenceType() && + DCE->getCastKind() == CK_Dynamic) + return true; + } // Fall through. + case ImplicitCastExprClass: + case CStyleCastExprClass: + case CXXStaticCastExprClass: + case CXXReinterpretCastExprClass: + case CXXConstCastExprClass: + case CXXFunctionalCastExprClass: { + const CastExpr *CE = cast<CastExpr>(this); + if (CE->getCastKind() == CK_LValueToRValue && + CE->getSubExpr()->getType().isVolatileQualified()) + return true; + break; + } + + case CXXTypeidExprClass: + // typeid might throw if its subexpression is potentially-evaluated, so has + // side-effects in that case whether or not its subexpression does. + return cast<CXXTypeidExpr>(this)->isPotentiallyEvaluated(); + + case CXXConstructExprClass: + case CXXTemporaryObjectExprClass: { + const CXXConstructExpr *CE = cast<CXXConstructExpr>(this); + if (!CE->getConstructor()->isTrivial()) + return true; + // A trivial constructor does not add any side-effects of its own. Just look + // at its arguments. + break; + } + + case LambdaExprClass: { + const LambdaExpr *LE = cast<LambdaExpr>(this); + for (LambdaExpr::capture_iterator I = LE->capture_begin(), + E = LE->capture_end(); I != E; ++I) + if (I->getCaptureKind() == LCK_ByCopy) + // FIXME: Only has a side-effect if the variable is volatile or if + // the copy would invoke a non-trivial copy constructor. + return true; + return false; + } + + case PseudoObjectExprClass: { + // Only look for side-effects in the semantic form, and look past + // OpaqueValueExpr bindings in that form. + const PseudoObjectExpr *PO = cast<PseudoObjectExpr>(this); + for (PseudoObjectExpr::const_semantics_iterator I = PO->semantics_begin(), + E = PO->semantics_end(); + I != E; ++I) { + const Expr *Subexpr = *I; + if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(Subexpr)) + Subexpr = OVE->getSourceExpr(); + if (Subexpr->HasSideEffects(Ctx)) + return true; + } + return false; + } + + case ObjCBoxedExprClass: + case ObjCArrayLiteralClass: + case ObjCDictionaryLiteralClass: + case ObjCMessageExprClass: + case ObjCSelectorExprClass: + case ObjCProtocolExprClass: + case ObjCPropertyRefExprClass: + case ObjCIsaExprClass: + case ObjCIndirectCopyRestoreExprClass: + case ObjCSubscriptRefExprClass: + case ObjCBridgedCastExprClass: + // FIXME: Classify these cases better. + return true; + } + + // Recurse to children. + for (const_child_range SubStmts = children(); SubStmts; ++SubStmts) + if (const Stmt *S = *SubStmts) + if (cast<Expr>(S)->HasSideEffects(Ctx)) + return true; + + return false; +} + namespace { /// \brief Look for a call to a non-trivial function within an expression. class NonTrivialCallFinder : public EvaluatedExprVisitor<NonTrivialCallFinder> @@ -2514,7 +2891,7 @@ Expr::isNullPointerConstant(ASTContext &Ctx, llvm_unreachable("Unexpected value dependent expression!"); case NPC_ValueDependentIsNull: if (isTypeDependent() || getType()->isIntegralType(Ctx)) - return NPCK_ZeroInteger; + return NPCK_ZeroExpression; else return NPCK_NotNull; @@ -2588,7 +2965,12 @@ Expr::isNullPointerConstant(ASTContext &Ctx, return NPCK_NotNull; } - return (EvaluateKnownConstInt(Ctx) == 0) ? NPCK_ZeroInteger : NPCK_NotNull; + if (EvaluateKnownConstInt(Ctx) != 0) + return NPCK_NotNull; + + if (isa<IntegerLiteral>(this)) + return NPCK_ZeroLiteral; + return NPCK_ZeroExpression; } /// \brief If this expression is an l-value for an Objective C diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index 8cf519c93dc2..3fa49e0e18b9 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Basic/IdentifierTable.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/ExprCXX.h" @@ -23,6 +24,21 @@ using namespace clang; // Child Iterators for iterating over subexpressions/substatements //===----------------------------------------------------------------------===// +bool CXXTypeidExpr::isPotentiallyEvaluated() const { + if (isTypeOperand()) + return false; + + // C++11 [expr.typeid]p3: + // When typeid is applied to an expression other than a glvalue of + // polymorphic class type, [...] the expression is an unevaluated operand. + const Expr *E = getExprOperand(); + if (const CXXRecordDecl *RD = E->getType()->getAsCXXRecordDecl()) + if (RD->isPolymorphic() && E->isGLValue()) + return true; + + return false; +} + QualType CXXTypeidExpr::getTypeOperand() const { assert(isTypeOperand() && "Cannot call getTypeOperand for typeid(expr)"); return Operand.get<TypeSourceInfo *>()->getType().getNonReferenceType() @@ -126,13 +142,6 @@ SourceLocation CXXNewExpr::getEndLoc() const { // CXXDeleteExpr QualType CXXDeleteExpr::getDestroyedType() const { const Expr *Arg = getArgument(); - while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { - if (ICE->getCastKind() != CK_UserDefinedConversion && - ICE->getType()->isVoidPointerType()) - Arg = ICE->getSubExpr(); - else - break; - } // The type-to-delete may not be a pointer if it's a dependent type. const QualType ArgType = Arg->getType(); @@ -268,6 +277,7 @@ OverloadExpr::OverloadExpr(StmtClass K, ASTContext &C, isa<UnresolvedUsingValueDecl>(*I)) { ExprBits.TypeDependent = true; ExprBits.ValueDependent = true; + ExprBits.InstantiationDependent = true; } } @@ -415,37 +425,37 @@ SourceRange CXXConstructExpr::getSourceRange() const { return SourceRange(Loc, End); } -SourceRange CXXOperatorCallExpr::getSourceRange() const { +SourceRange CXXOperatorCallExpr::getSourceRangeImpl() const { OverloadedOperatorKind Kind = getOperator(); if (Kind == OO_PlusPlus || Kind == OO_MinusMinus) { if (getNumArgs() == 1) // Prefix operator - return SourceRange(getOperatorLoc(), - getArg(0)->getSourceRange().getEnd()); + return SourceRange(getOperatorLoc(), getArg(0)->getLocEnd()); else // Postfix operator - return SourceRange(getArg(0)->getSourceRange().getBegin(), - getOperatorLoc()); + return SourceRange(getArg(0)->getLocStart(), getOperatorLoc()); } else if (Kind == OO_Arrow) { return getArg(0)->getSourceRange(); } else if (Kind == OO_Call) { - return SourceRange(getArg(0)->getSourceRange().getBegin(), getRParenLoc()); + return SourceRange(getArg(0)->getLocStart(), getRParenLoc()); } else if (Kind == OO_Subscript) { - return SourceRange(getArg(0)->getSourceRange().getBegin(), getRParenLoc()); + return SourceRange(getArg(0)->getLocStart(), getRParenLoc()); } else if (getNumArgs() == 1) { - return SourceRange(getOperatorLoc(), getArg(0)->getSourceRange().getEnd()); + return SourceRange(getOperatorLoc(), getArg(0)->getLocEnd()); } else if (getNumArgs() == 2) { - return SourceRange(getArg(0)->getSourceRange().getBegin(), - getArg(1)->getSourceRange().getEnd()); + return SourceRange(getArg(0)->getLocStart(), getArg(1)->getLocEnd()); } else { - return SourceRange(); + return getOperatorLoc(); } } Expr *CXXMemberCallExpr::getImplicitObjectArgument() const { - if (const MemberExpr *MemExpr = - dyn_cast<MemberExpr>(getCallee()->IgnoreParens())) + const Expr *Callee = getCallee()->IgnoreParens(); + if (const MemberExpr *MemExpr = dyn_cast<MemberExpr>(Callee)) return MemExpr->getBase(); + if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(Callee)) + if (BO->getOpcode() == BO_PtrMemD || BO->getOpcode() == BO_PtrMemI) + return BO->getLHS(); // FIXME: Will eventually need to cope with member pointers. return 0; @@ -461,7 +471,7 @@ CXXMethodDecl *CXXMemberCallExpr::getMethodDecl() const { } -CXXRecordDecl *CXXMemberCallExpr::getRecordDecl() { +CXXRecordDecl *CXXMemberCallExpr::getRecordDecl() const { Expr* ThisArg = getImplicitObjectArgument(); if (!ThisArg) return 0; @@ -556,6 +566,9 @@ bool CXXDynamicCastExpr::isAlwaysNull() const DestType = DestType->castAs<PointerType>()->getPointeeType(); } + if (DestType->isVoidType()) + return false; + const CXXRecordDecl *SrcRD = cast<CXXRecordDecl>(SrcType->castAs<RecordType>()->getDecl()); @@ -796,10 +809,11 @@ LambdaExpr::LambdaExpr(QualType T, ArrayRef<Expr *> CaptureInits, ArrayRef<VarDecl *> ArrayIndexVars, ArrayRef<unsigned> ArrayIndexStarts, - SourceLocation ClosingBrace) + SourceLocation ClosingBrace, + bool ContainsUnexpandedParameterPack) : Expr(LambdaExprClass, T, VK_RValue, OK_Ordinary, T->isDependentType(), T->isDependentType(), T->isDependentType(), - /*ContainsUnexpandedParameterPack=*/false), + ContainsUnexpandedParameterPack), IntroducerRange(IntroducerRange), NumCaptures(Captures.size()), CaptureDefault(CaptureDefault), @@ -856,7 +870,8 @@ LambdaExpr *LambdaExpr::Create(ASTContext &Context, ArrayRef<Expr *> CaptureInits, ArrayRef<VarDecl *> ArrayIndexVars, ArrayRef<unsigned> ArrayIndexStarts, - SourceLocation ClosingBrace) { + SourceLocation ClosingBrace, + bool ContainsUnexpandedParameterPack) { // Determine the type of the expression (i.e., the type of the // function object we're creating). QualType T = Context.getTypeDeclType(Class); @@ -869,7 +884,7 @@ LambdaExpr *LambdaExpr::Create(ASTContext &Context, return new (Mem) LambdaExpr(T, IntroducerRange, CaptureDefault, Captures, ExplicitParams, ExplicitResultType, CaptureInits, ArrayIndexVars, ArrayIndexStarts, - ClosingBrace); + ClosingBrace, ContainsUnexpandedParameterPack); } LambdaExpr *LambdaExpr::CreateDeserialized(ASTContext &C, unsigned NumCaptures, @@ -944,7 +959,7 @@ CompoundStmt *LambdaExpr::getBody() const { } bool LambdaExpr::isMutable() const { - return (getCallOperator()->getTypeQualifiers() & Qualifiers::Const) == 0; + return !getCallOperator()->isConst(); } ExprWithCleanups::ExprWithCleanups(Expr *subexpr, diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp index b091e19a8a95..f16d70b5da1b 100644 --- a/lib/AST/ExprClassification.cpp +++ b/lib/AST/ExprClassification.cpp @@ -77,6 +77,7 @@ Cl Expr::ClassifyImpl(ASTContext &Ctx, SourceLocation *Loc) const { case Cl::CL_MemberFunction: case Cl::CL_SubObjCPropertySetting: case Cl::CL_ClassTemporary: + case Cl::CL_ArrayTemporary: case Cl::CL_ObjCMessageRValue: case Cl::CL_PRValue: assert(getValueKind() == VK_RValue); break; } @@ -87,6 +88,18 @@ Cl Expr::ClassifyImpl(ASTContext &Ctx, SourceLocation *Loc) const { return Classification(kind, modifiable); } +/// Classify an expression which creates a temporary, based on its type. +static Cl::Kinds ClassifyTemporary(QualType T) { + if (T->isRecordType()) + return Cl::CL_ClassTemporary; + if (T->isArrayType()) + return Cl::CL_ArrayTemporary; + + // No special classification: these don't behave differently from normal + // prvalues. + return Cl::CL_PRValue; +} + static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { // This function takes the first stab at classifying expressions. const LangOptions &Lang = Ctx.getLangOpts(); @@ -124,10 +137,10 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { return Cl::CL_LValue; // C99 6.5.2.5p5 says that compound literals are lvalues. - // In C++, they're class temporaries. + // In C++, they're prvalue temporaries. case Expr::CompoundLiteralExprClass: - return Ctx.getLangOpts().CPlusPlus? Cl::CL_ClassTemporary - : Cl::CL_LValue; + return Ctx.getLangOpts().CPlusPlus ? ClassifyTemporary(E->getType()) + : Cl::CL_LValue; // Expressions that are prvalues. case Expr::CXXBoolLiteralExprClass: @@ -158,7 +171,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::ObjCSelectorExprClass: case Expr::ObjCProtocolExprClass: case Expr::ObjCStringLiteralClass: - case Expr::ObjCNumericLiteralClass: + case Expr::ObjCBoxedExprClass: case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: case Expr::ObjCBoolLiteralExprClass: @@ -417,7 +430,7 @@ static Cl::Kinds ClassifyUnnamed(ASTContext &Ctx, QualType T) { return Cl::CL_LValue; const RValueReferenceType *RV = T->getAs<RValueReferenceType>(); if (!RV) // Could still be a class temporary, though. - return T->isRecordType() ? Cl::CL_ClassTemporary : Cl::CL_PRValue; + return ClassifyTemporary(T); return RV->getPointeeType()->isFunctionType() ? Cl::CL_LValue : Cl::CL_XValue; } @@ -602,6 +615,7 @@ Expr::LValueClassification Expr::ClassifyLValue(ASTContext &Ctx) const { case Cl::CL_MemberFunction: return LV_MemberFunction; case Cl::CL_SubObjCPropertySetting: return LV_SubObjCPropertySetting; case Cl::CL_ClassTemporary: return LV_ClassTemporary; + case Cl::CL_ArrayTemporary: return LV_ArrayTemporary; case Cl::CL_ObjCMessageRValue: return LV_InvalidMessageExpression; case Cl::CL_PRValue: return LV_InvalidExpression; } @@ -622,6 +636,7 @@ Expr::isModifiableLvalue(ASTContext &Ctx, SourceLocation *Loc) const { case Cl::CL_MemberFunction: return MLV_MemberFunction; case Cl::CL_SubObjCPropertySetting: return MLV_SubObjCPropertySetting; case Cl::CL_ClassTemporary: return MLV_ClassTemporary; + case Cl::CL_ArrayTemporary: return MLV_ArrayTemporary; case Cl::CL_ObjCMessageRValue: return MLV_InvalidMessageExpression; case Cl::CL_PRValue: return VC.getModifiable() == Cl::CM_LValueCast ? diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 66a88b065ce1..06c41a2e7ea2 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -287,7 +287,9 @@ namespace { /// parameters' function scope indices. const APValue *Arguments; - typedef llvm::DenseMap<const Expr*, APValue> MapTy; + // Note that we intentionally use std::map here so that references to + // values are stable. + typedef std::map<const Expr*, APValue> MapTy; typedef MapTy::const_iterator temp_iterator; /// Temporaries - Temporary lvalues materialized within this stack frame. MapTy Temporaries; @@ -361,11 +363,6 @@ namespace { /// NextCallIndex - The next call index to assign. unsigned NextCallIndex; - typedef llvm::DenseMap<const OpaqueValueExpr*, APValue> MapTy; - /// OpaqueValues - Values used as the common expression in a - /// BinaryConditionalOperator. - MapTy OpaqueValues; - /// BottomFrame - The frame in which evaluation started. This must be /// initialized after CurrentCall and CallStackDepth. CallStackFrame BottomFrame; @@ -394,12 +391,6 @@ namespace { EvaluatingDecl(0), EvaluatingDeclValue(0), HasActiveDiagnostic(false), CheckingPotentialConstantExpression(false) {} - const APValue *getOpaqueValue(const OpaqueValueExpr *e) const { - MapTy::const_iterator i = OpaqueValues.find(e); - if (i == OpaqueValues.end()) return 0; - return &i->second; - } - void setEvaluatingDecl(const VarDecl *VD, APValue &Value) { EvaluatingDecl = VD; EvaluatingDeclValue = &Value; @@ -1072,8 +1063,8 @@ static bool CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, } for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I) { - if (!CheckConstantExpression(Info, DiagLoc, (*I)->getType(), - Value.getStructField((*I)->getFieldIndex()))) + if (!CheckConstantExpression(Info, DiagLoc, I->getType(), + Value.getStructField(I->getFieldIndex()))) return false; } } @@ -1160,11 +1151,10 @@ static bool EvaluateAsBooleanCondition(const Expr *E, bool &Result, } template<typename T> -static bool HandleOverflow(EvalInfo &Info, const Expr *E, +static void HandleOverflow(EvalInfo &Info, const Expr *E, const T &SrcValue, QualType DestType) { - Info.Diag(E, diag::note_constexpr_overflow) + Info.CCEDiag(E, diag::note_constexpr_overflow) << SrcValue << DestType; - return false; } static bool HandleFloatToIntCast(EvalInfo &Info, const Expr *E, @@ -1178,7 +1168,7 @@ static bool HandleFloatToIntCast(EvalInfo &Info, const Expr *E, bool ignored; if (Value.convertToInteger(Result, llvm::APFloat::rmTowardZero, &ignored) & APFloat::opInvalidOp) - return HandleOverflow(Info, E, Value, DestType); + HandleOverflow(Info, E, Value, DestType); return true; } @@ -1190,7 +1180,7 @@ static bool HandleFloatToFloatCast(EvalInfo &Info, const Expr *E, if (Result.convert(Info.Ctx.getFloatTypeSemantics(DestType), APFloat::rmNearestTiesToEven, &ignored) & APFloat::opOverflow) - return HandleOverflow(Info, E, Value, DestType); + HandleOverflow(Info, E, Value, DestType); return true; } @@ -1213,7 +1203,7 @@ static bool HandleIntToFloatCast(EvalInfo &Info, const Expr *E, if (Result.convertFromAPInt(Value, Value.isSigned(), APFloat::rmNearestTiesToEven) & APFloat::opOverflow) - return HandleOverflow(Info, E, Value, DestType); + HandleOverflow(Info, E, Value, DestType); return true; } @@ -1282,6 +1272,7 @@ static bool CastToDerivedClass(EvalInfo &Info, const Expr *E, LValue &Result, // Truncate the path to the subobject, and remove any derived-to-base offsets. const RecordDecl *RD = TruncatedType; for (unsigned I = TruncatedElements, N = D.Entries.size(); I != N; ++I) { + if (RD->isInvalidDecl()) return false; const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); const CXXRecordDecl *Base = getAsBaseClass(D.Entries[I]); if (isVirtualBaseClass(D.Entries[I])) @@ -1294,13 +1285,18 @@ static bool CastToDerivedClass(EvalInfo &Info, const Expr *E, LValue &Result, return true; } -static void HandleLValueDirectBase(EvalInfo &Info, const Expr *E, LValue &Obj, +static bool HandleLValueDirectBase(EvalInfo &Info, const Expr *E, LValue &Obj, const CXXRecordDecl *Derived, const CXXRecordDecl *Base, const ASTRecordLayout *RL = 0) { - if (!RL) RL = &Info.Ctx.getASTRecordLayout(Derived); + if (!RL) { + if (Derived->isInvalidDecl()) return false; + RL = &Info.Ctx.getASTRecordLayout(Derived); + } + Obj.getLValueOffset() += RL->getBaseClassOffset(Base); Obj.addDecl(Info, E, Base, /*Virtual*/ false); + return true; } static bool HandleLValueBase(EvalInfo &Info, const Expr *E, LValue &Obj, @@ -1308,10 +1304,8 @@ static bool HandleLValueBase(EvalInfo &Info, const Expr *E, LValue &Obj, const CXXBaseSpecifier *Base) { const CXXRecordDecl *BaseDecl = Base->getType()->getAsCXXRecordDecl(); - if (!Base->isVirtual()) { - HandleLValueDirectBase(Info, E, Obj, DerivedDecl, BaseDecl); - return true; - } + if (!Base->isVirtual()) + return HandleLValueDirectBase(Info, E, Obj, DerivedDecl, BaseDecl); SubobjectDesignator &D = Obj.Designator; if (D.Invalid) @@ -1323,6 +1317,7 @@ static bool HandleLValueBase(EvalInfo &Info, const Expr *E, LValue &Obj, return false; // Find the virtual base class. + if (DerivedDecl->isInvalidDecl()) return false; const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(DerivedDecl); Obj.getLValueOffset() += Layout.getVBaseClassOffset(BaseDecl); Obj.addDecl(Info, E, BaseDecl, /*Virtual*/ true); @@ -1331,24 +1326,29 @@ static bool HandleLValueBase(EvalInfo &Info, const Expr *E, LValue &Obj, /// Update LVal to refer to the given field, which must be a member of the type /// currently described by LVal. -static void HandleLValueMember(EvalInfo &Info, const Expr *E, LValue &LVal, +static bool HandleLValueMember(EvalInfo &Info, const Expr *E, LValue &LVal, const FieldDecl *FD, const ASTRecordLayout *RL = 0) { - if (!RL) + if (!RL) { + if (FD->getParent()->isInvalidDecl()) return false; RL = &Info.Ctx.getASTRecordLayout(FD->getParent()); + } unsigned I = FD->getFieldIndex(); LVal.Offset += Info.Ctx.toCharUnitsFromBits(RL->getFieldOffset(I)); LVal.addDecl(Info, E, FD); + return true; } /// Update LVal to refer to the given indirect field. -static void HandleLValueIndirectMember(EvalInfo &Info, const Expr *E, +static bool HandleLValueIndirectMember(EvalInfo &Info, const Expr *E, LValue &LVal, const IndirectFieldDecl *IFD) { for (IndirectFieldDecl::chain_iterator C = IFD->chain_begin(), CE = IFD->chain_end(); C != CE; ++C) - HandleLValueMember(Info, E, LVal, cast<FieldDecl>(*C)); + if (!HandleLValueMember(Info, E, LVal, cast<FieldDecl>(*C))) + return false; + return true; } /// Get the size of the given type in char units. @@ -1952,22 +1952,27 @@ static const ValueDecl *HandleMemberPointerAccess(EvalInfo &Info, // The first class in the path is that of the lvalue. for (unsigned I = 1, N = MemPtr.Path.size(); I != N; ++I) { const CXXRecordDecl *Base = MemPtr.Path[N - I - 1]; - HandleLValueDirectBase(Info, BO, LV, RD, Base); + if (!HandleLValueDirectBase(Info, BO, LV, RD, Base)) + return 0; RD = Base; } // Finally cast to the class containing the member. - HandleLValueDirectBase(Info, BO, LV, RD, MemPtr.getContainingRecord()); + if (!HandleLValueDirectBase(Info, BO, LV, RD, MemPtr.getContainingRecord())) + return 0; } // Add the member. Note that we cannot build bound member functions here. if (IncludeMember) { - if (const FieldDecl *FD = dyn_cast<FieldDecl>(MemPtr.getDecl())) - HandleLValueMember(Info, BO, LV, FD); - else if (const IndirectFieldDecl *IFD = - dyn_cast<IndirectFieldDecl>(MemPtr.getDecl())) - HandleLValueIndirectMember(Info, BO, LV, IFD); - else + if (const FieldDecl *FD = dyn_cast<FieldDecl>(MemPtr.getDecl())) { + if (!HandleLValueMember(Info, BO, LV, FD)) + return 0; + } else if (const IndirectFieldDecl *IFD = + dyn_cast<IndirectFieldDecl>(MemPtr.getDecl())) { + if (!HandleLValueIndirectMember(Info, BO, LV, IFD)) + return 0; + } else { llvm_unreachable("can't construct reference to bound member function"); + } } return MemPtr.getDecl(); @@ -2189,6 +2194,7 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, Result = APValue(APValue::UninitStruct(), RD->getNumBases(), std::distance(RD->field_begin(), RD->field_end())); + if (RD->isInvalidDecl()) return false; const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); bool Success = true; @@ -2212,11 +2218,13 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, "base class initializers not in expected order"); ++BaseIt; #endif - HandleLValueDirectBase(Info, (*I)->getInit(), Subobject, RD, - BaseType->getAsCXXRecordDecl(), &Layout); + if (!HandleLValueDirectBase(Info, (*I)->getInit(), Subobject, RD, + BaseType->getAsCXXRecordDecl(), &Layout)) + return false; Value = &Result.getStructBase(BasesSeen++); } else if (FieldDecl *FD = (*I)->getMember()) { - HandleLValueMember(Info, (*I)->getInit(), Subobject, FD, &Layout); + if (!HandleLValueMember(Info, (*I)->getInit(), Subobject, FD, &Layout)) + return false; if (RD->isUnion()) { Result = APValue(FD); Value = &Result.getUnionValue(); @@ -2244,7 +2252,8 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, *Value = APValue(APValue::UninitStruct(), CD->getNumBases(), std::distance(CD->field_begin(), CD->field_end())); } - HandleLValueMember(Info, (*I)->getInit(), Subobject, FD); + if (!HandleLValueMember(Info, (*I)->getInit(), Subobject, FD)) + return false; if (CD->isUnion()) Value = &Value->getUnionValue(); else @@ -2268,107 +2277,6 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This, return Success; } -namespace { -class HasSideEffect - : public ConstStmtVisitor<HasSideEffect, bool> { - const ASTContext &Ctx; -public: - - HasSideEffect(const ASTContext &C) : Ctx(C) {} - - // Unhandled nodes conservatively default to having side effects. - bool VisitStmt(const Stmt *S) { - return true; - } - - bool VisitParenExpr(const ParenExpr *E) { return Visit(E->getSubExpr()); } - bool VisitGenericSelectionExpr(const GenericSelectionExpr *E) { - return Visit(E->getResultExpr()); - } - bool VisitDeclRefExpr(const DeclRefExpr *E) { - if (Ctx.getCanonicalType(E->getType()).isVolatileQualified()) - return true; - return false; - } - bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *E) { - if (Ctx.getCanonicalType(E->getType()).isVolatileQualified()) - return true; - return false; - } - - // We don't want to evaluate BlockExprs multiple times, as they generate - // a ton of code. - bool VisitBlockExpr(const BlockExpr *E) { return true; } - bool VisitPredefinedExpr(const PredefinedExpr *E) { return false; } - bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) - { return Visit(E->getInitializer()); } - bool VisitMemberExpr(const MemberExpr *E) { return Visit(E->getBase()); } - bool VisitIntegerLiteral(const IntegerLiteral *E) { return false; } - bool VisitFloatingLiteral(const FloatingLiteral *E) { return false; } - bool VisitStringLiteral(const StringLiteral *E) { return false; } - bool VisitCharacterLiteral(const CharacterLiteral *E) { return false; } - bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E) - { return false; } - bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E) - { return Visit(E->getLHS()) || Visit(E->getRHS()); } - bool VisitChooseExpr(const ChooseExpr *E) - { return Visit(E->getChosenSubExpr(Ctx)); } - bool VisitCastExpr(const CastExpr *E) { return Visit(E->getSubExpr()); } - bool VisitBinAssign(const BinaryOperator *E) { return true; } - bool VisitCompoundAssignOperator(const BinaryOperator *E) { return true; } - bool VisitBinaryOperator(const BinaryOperator *E) - { return Visit(E->getLHS()) || Visit(E->getRHS()); } - bool VisitUnaryPreInc(const UnaryOperator *E) { return true; } - bool VisitUnaryPostInc(const UnaryOperator *E) { return true; } - bool VisitUnaryPreDec(const UnaryOperator *E) { return true; } - bool VisitUnaryPostDec(const UnaryOperator *E) { return true; } - bool VisitUnaryDeref(const UnaryOperator *E) { - if (Ctx.getCanonicalType(E->getType()).isVolatileQualified()) - return true; - return Visit(E->getSubExpr()); - } - bool VisitUnaryOperator(const UnaryOperator *E) { return Visit(E->getSubExpr()); } - - // Has side effects if any element does. - bool VisitInitListExpr(const InitListExpr *E) { - for (unsigned i = 0, e = E->getNumInits(); i != e; ++i) - if (Visit(E->getInit(i))) return true; - if (const Expr *filler = E->getArrayFiller()) - return Visit(filler); - return false; - } - - bool VisitSizeOfPackExpr(const SizeOfPackExpr *) { return false; } -}; - -class OpaqueValueEvaluation { - EvalInfo &info; - OpaqueValueExpr *opaqueValue; - -public: - OpaqueValueEvaluation(EvalInfo &info, OpaqueValueExpr *opaqueValue, - Expr *value) - : info(info), opaqueValue(opaqueValue) { - - // If evaluation fails, fail immediately. - if (!Evaluate(info.OpaqueValues[opaqueValue], info, value)) { - this->opaqueValue = 0; - return; - } - } - - bool hasError() const { return opaqueValue == 0; } - - ~OpaqueValueEvaluation() { - // FIXME: For a recursive constexpr call, an outer stack frame might have - // been using this opaque value too, and will now have to re-evaluate the - // source expression. - if (opaqueValue) info.OpaqueValues.erase(opaqueValue); - } -}; - -} // end anonymous namespace - //===----------------------------------------------------------------------===// // Generic Evaluation //===----------------------------------------------------------------------===// @@ -2509,9 +2417,10 @@ public: } RetTy VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) { - // Cache the value of the common expression. - OpaqueValueEvaluation opaque(Info, E->getOpaqueValue(), E->getCommon()); - if (opaque.hasError()) + // 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->Temporaries[E->getOpaqueValue()], + Info, E->getCommon())) return false; return HandleConditionalOperator(E); @@ -2545,8 +2454,8 @@ public: } RetTy VisitOpaqueValueExpr(const OpaqueValueExpr *E) { - const APValue *Value = Info.getOpaqueValue(E); - if (!Value) { + APValue &Value = Info.CurrentCall->Temporaries[E]; + if (Value.isUninit()) { const Expr *Source = E->getSourceExpr(); if (!Source) return Error(E); @@ -2556,7 +2465,7 @@ public: } return StmtVisitorTy::Visit(Source); } - return DerivedSuccess(*Value, E); + return DerivedSuccess(Value, E); } RetTy VisitCallExpr(const CallExpr *E) { @@ -2773,9 +2682,11 @@ public: assert(BaseTy->getAs<RecordType>()->getDecl()->getCanonicalDecl() == FD->getParent()->getCanonicalDecl() && "record / field mismatch"); (void)BaseTy; - HandleLValueMember(this->Info, E, Result, FD); + if (!HandleLValueMember(this->Info, E, Result, FD)) + return false; } else if (const IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(MD)) { - HandleLValueIndirectMember(this->Info, E, Result, IFD); + if (!HandleLValueIndirectMember(this->Info, E, Result, IFD)) + return false; } else return this->Error(E); @@ -2970,6 +2881,9 @@ bool LValueExprEvaluator::VisitCXXTypeidExpr(const CXXTypeidExpr *E) { if (E->isTypeOperand()) return Success(E); CXXRecordDecl *RD = E->getExprOperand()->getType()->getAsCXXRecordDecl(); + // FIXME: The standard says "a typeid expression whose operand is of a + // polymorphic class type" is not a constant expression, but it probably + // means "a typeid expression whose operand is potentially evaluated". if (RD && RD->isPolymorphic()) { Info.Diag(E, diag::note_constexpr_typeid_polymorphic) << E->getExprOperand()->getType() @@ -3073,7 +2987,7 @@ public: bool VisitUnaryAddrOf(const UnaryOperator *E); bool VisitObjCStringLiteral(const ObjCStringLiteral *E) { return Success(E); } - bool VisitObjCNumericLiteral(const ObjCNumericLiteral *E) + bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E) { return Success(E); } bool VisitAddrLabelExpr(const AddrLabelExpr *E) { return Success(E); } @@ -3373,6 +3287,7 @@ static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E, Result = APValue(APValue::UninitStruct(), CD ? CD->getNumBases() : 0, std::distance(RD->field_begin(), RD->field_end())); + if (RD->isInvalidDecl()) return false; const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); if (CD) { @@ -3381,7 +3296,8 @@ static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E, End = CD->bases_end(); I != End; ++I, ++Index) { const CXXRecordDecl *Base = I->getType()->getAsCXXRecordDecl(); LValue Subobject = This; - HandleLValueDirectBase(Info, E, Subobject, CD, Base, &Layout); + if (!HandleLValueDirectBase(Info, E, Subobject, CD, Base, &Layout)) + return false; if (!HandleClassZeroInitialization(Info, E, Base, Subobject, Result.getStructBase(Index))) return false; @@ -3391,15 +3307,16 @@ static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E, for (RecordDecl::field_iterator I = RD->field_begin(), End = RD->field_end(); I != End; ++I) { // -- if T is a reference type, no initialization is performed. - if ((*I)->getType()->isReferenceType()) + if (I->getType()->isReferenceType()) continue; LValue Subobject = This; - HandleLValueMember(Info, E, Subobject, *I, &Layout); + if (!HandleLValueMember(Info, E, Subobject, *I, &Layout)) + return false; - ImplicitValueInitExpr VIE((*I)->getType()); + ImplicitValueInitExpr VIE(I->getType()); if (!EvaluateInPlace( - Result.getStructField((*I)->getFieldIndex()), Info, Subobject, &VIE)) + Result.getStructField(I->getFieldIndex()), Info, Subobject, &VIE)) return false; } @@ -3408,6 +3325,7 @@ static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E, bool RecordExprEvaluator::ZeroInitialization(const Expr *E) { const RecordDecl *RD = E->getType()->castAs<RecordType>()->getDecl(); + if (RD->isInvalidDecl()) return false; if (RD->isUnion()) { // C++11 [dcl.init]p5: If T is a (possibly cv-qualified) union type, the // object's first non-static named data member is zero-initialized @@ -3418,9 +3336,10 @@ bool RecordExprEvaluator::ZeroInitialization(const Expr *E) { } LValue Subobject = This; - HandleLValueMember(Info, E, Subobject, *I); + if (!HandleLValueMember(Info, E, Subobject, *I)) + return false; Result = APValue(*I); - ImplicitValueInitExpr VIE((*I)->getType()); + ImplicitValueInitExpr VIE(I->getType()); return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, &VIE); } @@ -3470,6 +3389,7 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { return false; const RecordDecl *RD = E->getType()->castAs<RecordType>()->getDecl(); + if (RD->isInvalidDecl()) return false; const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD); if (RD->isUnion()) { @@ -3484,7 +3404,8 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { const Expr *InitExpr = E->getNumInits() ? E->getInit(0) : &VIE; LValue Subobject = This; - HandleLValueMember(Info, InitExpr, Subobject, Field, &Layout); + if (!HandleLValueMember(Info, InitExpr, Subobject, Field, &Layout)) + return false; return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, InitExpr); } @@ -3507,15 +3428,16 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { // FIXME: Diagnostics here should point to the end of the initializer // list, not the start. - HandleLValueMember(Info, HaveInit ? E->getInit(ElementNo) : E, Subobject, - *Field, &Layout); + if (!HandleLValueMember(Info, HaveInit ? E->getInit(ElementNo) : E, + Subobject, *Field, &Layout)) + return false; // Perform an implicit value-initialization for members beyond the end of // the initializer list. ImplicitValueInitExpr VIE(HaveInit ? Info.Ctx.IntTy : Field->getType()); if (!EvaluateInPlace( - Result.getStructField((*Field)->getFieldIndex()), + Result.getStructField(Field->getFieldIndex()), Info, Subobject, HaveInit ? E->getInit(ElementNo++) : &VIE)) { if (!Info.keepEvaluatingAfterFailure()) return false; @@ -3528,6 +3450,8 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) { bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { const CXXConstructorDecl *FD = E->getConstructor(); + if (FD->isInvalidDecl() || FD->getParent()->isInvalidDecl()) return false; + bool ZeroInit = E->requiresZeroInitialization(); if (CheckTrivialDefaultConstructor(Info, E->getExprLoc(), FD, ZeroInit)) { // If we've already performed zero-initialization, we're already done. @@ -3870,8 +3794,24 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { bool Success = true; + assert((!Result.isArray() || Result.getArrayInitializedElts() == 0) && + "zero-initialized array shouldn't have any initialized elts"); + APValue Filler; + if (Result.isArray() && Result.hasArrayFiller()) + Filler = Result.getArrayFiller(); + Result = APValue(APValue::UninitArray(), E->getNumInits(), CAT->getSize().getZExtValue()); + + // If the array was previously zero-initialized, preserve the + // zero-initialized values. + if (!Filler.isUninit()) { + for (unsigned I = 0, E = Result.getArrayInitializedElts(); I != E; ++I) + Result.getArrayInitializedElt(I) = Filler; + if (Result.hasArrayFiller()) + Result.getArrayFiller() = Filler; + } + LValue Subobject = This; Subobject.addArray(Info, E, CAT); unsigned Index = 0; @@ -3898,15 +3838,29 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) { } bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { - const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(E->getType()); - if (!CAT) - return Error(E); + // FIXME: The Subobject here isn't necessarily right. This rarely matters, + // but sometimes does: + // struct S { constexpr S() : p(&p) {} void *p; }; + // S s[10]; + LValue Subobject = This; - bool HadZeroInit = !Result.isUninit(); - if (!HadZeroInit) - Result = APValue(APValue::UninitArray(), 0, CAT->getSize().getZExtValue()); - if (!Result.hasArrayFiller()) - return true; + APValue *Value = &Result; + bool HadZeroInit = true; + QualType ElemTy = E->getType(); + while (const ConstantArrayType *CAT = + Info.Ctx.getAsConstantArrayType(ElemTy)) { + Subobject.addArray(Info, E, CAT); + HadZeroInit &= !Value->isUninit(); + if (!HadZeroInit) + *Value = APValue(APValue::UninitArray(), 0, CAT->getSize().getZExtValue()); + if (!Value->hasArrayFiller()) + return true; + Value = &Value->getArrayFiller(); + ElemTy = CAT->getElementType(); + } + + if (!ElemTy->isRecordType()) + return Error(E); const CXXConstructorDecl *FD = E->getConstructor(); @@ -3916,17 +3870,15 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { return true; if (ZeroInit) { - LValue Subobject = This; - Subobject.addArray(Info, E, CAT); - ImplicitValueInitExpr VIE(CAT->getElementType()); - return EvaluateInPlace(Result.getArrayFiller(), Info, Subobject, &VIE); + ImplicitValueInitExpr VIE(ElemTy); + return EvaluateInPlace(*Value, Info, Subobject, &VIE); } const CXXRecordDecl *RD = FD->getParent(); if (RD->isUnion()) - Result.getArrayFiller() = APValue((FieldDecl*)0); + *Value = APValue((FieldDecl*)0); else - Result.getArrayFiller() = + *Value = APValue(APValue::UninitStruct(), RD->getNumBases(), std::distance(RD->field_begin(), RD->field_end())); return true; @@ -3938,23 +3890,16 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition)) return false; - // FIXME: The Subobject here isn't necessarily right. This rarely matters, - // but sometimes does: - // struct S { constexpr S() : p(&p) {} void *p; }; - // S s[10]; - LValue Subobject = This; - Subobject.addArray(Info, E, CAT); - if (ZeroInit && !HadZeroInit) { - ImplicitValueInitExpr VIE(CAT->getElementType()); - if (!EvaluateInPlace(Result.getArrayFiller(), Info, Subobject, &VIE)) + ImplicitValueInitExpr VIE(ElemTy); + if (!EvaluateInPlace(*Value, Info, Subobject, &VIE)) return false; } llvm::ArrayRef<const Expr*> Args(E->getArgs(), E->getNumArgs()); return HandleConstructorCall(E->getExprLoc(), Subobject, Args, cast<CXXConstructorDecl>(Definition), - Info, Result.getArrayFiller()); + Info, *Value); } //===----------------------------------------------------------------------===// @@ -4288,10 +4233,16 @@ QualType IntExprEvaluator::GetObjectType(APValue::LValueBase B) { } bool IntExprEvaluator::TryEvaluateBuiltinObjectSize(const CallExpr *E) { - // TODO: Perhaps we should let LLVM lower this? LValue Base; - if (!EvaluatePointer(E->getArg(0), Base, Info)) - return false; + + { + // The operand of __builtin_object_size is never evaluated for side-effects. + // If there are any, but we can determine the pointed-to object anyway, then + // ignore the side-effects. + SpeculativeEvaluationRAII SpeculativeEval(Info); + if (!EvaluatePointer(E->getArg(0), Base, Info)) + return false; + } // If we can prove the base is null, lower to zero now. if (!Base.getLValueBase()) return Success(0, E); @@ -4323,14 +4274,17 @@ bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) { if (TryEvaluateBuiltinObjectSize(E)) return true; - // If evaluating the argument has side-effects we can't determine - // the size of the object and lower it to unknown now. + // If evaluating the argument has side-effects, we can't determine the size + // of the object, and so we lower it to unknown now. CodeGen relies on us to + // handle all cases where the expression has side-effects. if (E->getArg(0)->HasSideEffects(Info.Ctx)) { if (E->getArg(1)->EvaluateKnownConstInt(Info.Ctx).getZExtValue() <= 1) return Success(-1ULL, E); return Success(0, E); } + // Expression had no side effects, but we couldn't statically determine the + // size of the referenced object. return Error(E); } @@ -5280,6 +5234,7 @@ bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *OOE) { if (!RT) return Error(OOE); RecordDecl *RD = RT->getDecl(); + if (RD->isInvalidDecl()) return false; const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD); unsigned i = MemberDecl->getFieldIndex(); assert(i < RL.getFieldCount() && "offsetof field in wrong type"); @@ -5301,6 +5256,7 @@ bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *OOE) { if (!RT) return Error(OOE); RecordDecl *RD = RT->getDecl(); + if (RD->isInvalidDecl()) return false; const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD); // Find the base class itself. @@ -6385,10 +6341,6 @@ bool Expr::isEvaluatable(const ASTContext &Ctx) const { return EvaluateAsRValue(Result, Ctx) && !Result.HasSideEffects; } -bool Expr::HasSideEffects(const ASTContext &Ctx) const { - return HasSideEffect(Ctx).Visit(this); -} - APSInt Expr::EvaluateKnownConstInt(const ASTContext &Ctx) const { EvalResult EvalResult; bool Result = EvaluateAsRValue(EvalResult, Ctx); @@ -6501,7 +6453,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { case Expr::CXXDependentScopeMemberExprClass: case Expr::UnresolvedMemberExprClass: case Expr::ObjCStringLiteralClass: - case Expr::ObjCNumericLiteralClass: + case Expr::ObjCBoxedExprClass: case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: case Expr::ObjCEncodeExprClass: diff --git a/lib/AST/ItaniumCXXABI.cpp b/lib/AST/ItaniumCXXABI.cpp index 0027dbf1915b..ce1244c54272 100644 --- a/lib/AST/ItaniumCXXABI.cpp +++ b/lib/AST/ItaniumCXXABI.cpp @@ -39,7 +39,7 @@ public: return 1; } - CallingConv getDefaultMethodCallConv() const { + CallingConv getDefaultMethodCallConv(bool isVariadic) const { return CC_C; } diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 0d405f1f57f2..7c7a5e5de387 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -657,7 +657,7 @@ void CXXNameMangler::mangleFloat(const llvm::APFloat &f) { // mistake; see the discussion on cxx-abi-dev beginning on // 2012-01-16. - // Our requirements here are just barely wierd enough to justify + // Our requirements here are just barely weird enough to justify // using a custom algorithm instead of post-processing APInt::toString(). llvm::APInt valueBits = f.bitcastToAPInt(); @@ -1032,17 +1032,14 @@ static const FieldDecl *FindFirstNamedDataMember(const RecordDecl *RD) { for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I) { - const FieldDecl *FD = *I; + if (I->getIdentifier()) + return *I; - if (FD->getIdentifier()) - return FD; - - if (const RecordType *RT = FD->getType()->getAs<RecordType>()) { + if (const RecordType *RT = I->getType()->getAs<RecordType>()) if (const FieldDecl *NamedDataMember = FindFirstNamedDataMember(RT->getDecl())) return NamedDataMember; } - } // We didn't find a named data member. return 0; @@ -1892,12 +1889,23 @@ void CXXNameMangler::mangleType(const BuiltinType *T) { } // <type> ::= <function-type> -// <function-type> ::= F [Y] <bare-function-type> E +// <function-type> ::= [<CV-qualifiers>] F [Y] +// <bare-function-type> [<ref-qualifier>] E +// (Proposal to cxx-abi-dev, 2012-05-11) void CXXNameMangler::mangleType(const FunctionProtoType *T) { + // Mangle CV-qualifiers, if present. These are 'this' qualifiers, + // e.g. "const" in "int (A::*)() const". + mangleQualifiers(Qualifiers::fromCVRMask(T->getTypeQuals())); + Out << 'F'; + // FIXME: We don't have enough information in the AST to produce the 'Y' // encoding for extern "C" function types. mangleBareFunctionType(T, /*MangleReturnType=*/true); + + // Mangle the ref-qualifier, if present. + mangleRefQualifier(T->getRefQualifier()); + Out << 'E'; } void CXXNameMangler::mangleType(const FunctionNoProtoType *T) { @@ -1990,8 +1998,6 @@ void CXXNameMangler::mangleType(const MemberPointerType *T) { mangleType(QualType(T->getClass(), 0)); QualType PointeeType = T->getPointeeType(); if (const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(PointeeType)) { - mangleQualifiers(Qualifiers::fromCVRMask(FPT->getTypeQuals())); - mangleRefQualifier(FPT->getRefQualifier()); mangleType(FPT); // Itanium C++ ABI 5.1.8: @@ -2005,9 +2011,11 @@ void CXXNameMangler::mangleType(const MemberPointerType *T) { // which the function is a member is considered part of the type of // function. + // Given that we already substitute member function pointers as a + // whole, the net effect of this rule is just to unconditionally + // suppress substitution on the function type in a member pointer. // We increment the SeqID here to emulate adding an entry to the - // substitution table. We can't actually add it because we don't want this - // particular function type to be substituted. + // substitution table. ++SeqID; } else mangleType(PointeeType); @@ -2390,7 +2398,7 @@ recurse: case Expr::ObjCProtocolExprClass: case Expr::ObjCSelectorExprClass: case Expr::ObjCStringLiteralClass: - case Expr::ObjCNumericLiteralClass: + case Expr::ObjCBoxedExprClass: case Expr::ObjCArrayLiteralClass: case Expr::ObjCDictionaryLiteralClass: case Expr::ObjCSubscriptRefExprClass: @@ -2981,7 +2989,7 @@ void CXXNameMangler::mangleFunctionParam(const ParmVarDecl *parm) { // Top-level qualifiers. We don't have to worry about arrays here, // because parameters declared as arrays should already have been - // tranformed to have pointer type. FIXME: apparently these don't + // transformed to have pointer type. FIXME: apparently these don't // get mangled if used as an rvalue of a known non-class type? assert(!parm->getType()->isArrayType() && "parameter's type is still an array type?"); @@ -3124,7 +3132,7 @@ void CXXNameMangler::mangleTemplateArg(const NamedDecl *P, break; } case TemplateArgument::Integral: - mangleIntegerLiteral(A.getIntegralType(), *A.getAsIntegral()); + mangleIntegerLiteral(A.getIntegralType(), A.getAsIntegral()); break; case TemplateArgument::Declaration: { assert(P && "Missing template parameter for declaration argument"); diff --git a/lib/AST/LambdaMangleContext.cpp b/lib/AST/LambdaMangleContext.cpp index f5272a7fdbce..6f4fe2d4b4eb 100644 --- a/lib/AST/LambdaMangleContext.cpp +++ b/lib/AST/LambdaMangleContext.cpp @@ -11,7 +11,9 @@ // the Itanium C++ ABI mangling numbers for lambda expressions. // //===----------------------------------------------------------------------===// + #include "clang/AST/LambdaMangleContext.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" using namespace clang; diff --git a/lib/AST/Mangle.cpp b/lib/AST/Mangle.cpp index 73c9f5778f41..d5f83719ec62 100644 --- a/lib/AST/Mangle.cpp +++ b/lib/AST/Mangle.cpp @@ -40,7 +40,11 @@ static void mangleFunctionBlock(MangleContext &Context, StringRef Outer, const BlockDecl *BD, raw_ostream &Out) { - Out << "__" << Outer << "_block_invoke_" << Context.getBlockId(BD, true); + unsigned discriminator = Context.getBlockId(BD, true); + if (discriminator == 0) + Out << "__" << Outer << "_block_invoke"; + else + Out << "__" << Outer << "_block_invoke_" << discriminator+1; } static void checkMangleDC(const DeclContext *DC, const BlockDecl *BD) { @@ -62,8 +66,20 @@ static void checkMangleDC(const DeclContext *DC, const BlockDecl *BD) { void MangleContext::anchor() { } void MangleContext::mangleGlobalBlock(const BlockDecl *BD, + const NamedDecl *ID, raw_ostream &Out) { - Out << "__block_global_" << getBlockId(BD, false); + unsigned discriminator = getBlockId(BD, false); + if (ID) { + if (shouldMangleDeclName(ID)) + mangleName(ID, Out); + else { + Out << ID->getIdentifier()->getName(); + } + } + if (discriminator == 0) + Out << "_block_invoke"; + else + Out << "_block_invoke_" << discriminator+1; } void MangleContext::mangleCtorBlock(const CXXConstructorDecl *CD, @@ -99,8 +115,8 @@ void MangleContext::mangleBlock(const DeclContext *DC, const BlockDecl *BD, mangleObjCMethodName(Method, Stream); } else { const NamedDecl *ND = cast<NamedDecl>(DC); - if (IdentifierInfo *II = ND->getIdentifier()) - Stream << II->getName(); + if (!shouldMangleDeclName(ND) && ND->getIdentifier()) + Stream << ND->getIdentifier()->getName(); else { // FIXME: We were doing a mangleUnqualifiedName() before, but that's // a private member of a class that will soon itself be private to the @@ -131,12 +147,13 @@ void MangleContext::mangleObjCMethodName(const ObjCMethodDecl *MD, } void MangleContext::mangleBlock(const BlockDecl *BD, - raw_ostream &Out) { + raw_ostream &Out, + const NamedDecl *ID) { const DeclContext *DC = BD->getDeclContext(); while (isa<BlockDecl>(DC) || isa<EnumDecl>(DC)) DC = DC->getParent(); if (DC->isFunctionOrMethod()) mangleBlock(DC, BD, Out); else - mangleGlobalBlock(BD, Out); + mangleGlobalBlock(BD, ID, Out); } diff --git a/lib/AST/MicrosoftCXXABI.cpp b/lib/AST/MicrosoftCXXABI.cpp index f33d6fe1f563..51308ea0c0f5 100644 --- a/lib/AST/MicrosoftCXXABI.cpp +++ b/lib/AST/MicrosoftCXXABI.cpp @@ -29,8 +29,8 @@ public: unsigned getMemberPointerSize(const MemberPointerType *MPT) const; - CallingConv getDefaultMethodCallConv() const { - if (Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86) + CallingConv getDefaultMethodCallConv(bool isVariadic) const { + if (!isVariadic && Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86) return CC_X86ThisCall; else return CC_C; diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index ba9856a8432f..e2cee7f52cfb 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -21,6 +21,8 @@ #include "clang/AST/ExprCXX.h" #include "clang/Basic/ABI.h" +#include <map> + using namespace clang; namespace { @@ -31,36 +33,59 @@ class MicrosoftCXXNameMangler { MangleContext &Context; raw_ostream &Out; + // FIXME: audit the performance of BackRefMap as it might do way too many + // copying of strings. + typedef std::map<std::string, unsigned> BackRefMap; + BackRefMap NameBackReferences; + bool UseNameBackReferences; + + typedef llvm::DenseMap<void*, unsigned> ArgBackRefMap; + ArgBackRefMap TypeBackReferences; + ASTContext &getASTContext() const { return Context.getASTContext(); } public: MicrosoftCXXNameMangler(MangleContext &C, raw_ostream &Out_) - : Context(C), Out(Out_) { } + : Context(C), Out(Out_), UseNameBackReferences(true) { } + + raw_ostream &getStream() const { return Out; } - void mangle(const NamedDecl *D, StringRef Prefix = "?"); + void mangle(const NamedDecl *D, StringRef Prefix = "\01?"); void mangleName(const NamedDecl *ND); void mangleFunctionEncoding(const FunctionDecl *FD); void mangleVariableEncoding(const VarDecl *VD); void mangleNumber(int64_t Number); - void mangleType(QualType T); + void mangleNumber(const llvm::APSInt &Value); + void mangleType(QualType T, SourceRange Range); private: + void disableBackReferences() { UseNameBackReferences = false; } void mangleUnqualifiedName(const NamedDecl *ND) { mangleUnqualifiedName(ND, ND->getDeclName()); } void mangleUnqualifiedName(const NamedDecl *ND, DeclarationName Name); void mangleSourceName(const IdentifierInfo *II); void manglePostfix(const DeclContext *DC, bool NoFunction=false); - void mangleOperatorName(OverloadedOperatorKind OO); + void mangleOperatorName(OverloadedOperatorKind OO, SourceLocation Loc); void mangleQualifiers(Qualifiers Quals, bool IsMember); + void mangleUnscopedTemplateName(const TemplateDecl *ND); + void mangleTemplateInstantiationName(const TemplateDecl *TD, + const SmallVectorImpl<TemplateArgumentLoc> &TemplateArgs); void mangleObjCMethodName(const ObjCMethodDecl *MD); + void mangleLocalName(const FunctionDecl *FD); + + void mangleTypeRepeated(QualType T, SourceRange Range); // Declare manglers for every type class. #define ABSTRACT_TYPE(CLASS, PARENT) #define NON_CANONICAL_TYPE(CLASS, PARENT) -#define TYPE(CLASS, PARENT) void mangleType(const CLASS##Type *T); +#define TYPE(CLASS, PARENT) void mangleType(const CLASS##Type *T, \ + SourceRange Range); #include "clang/AST/TypeNodes.def" +#undef ABSTRACT_TYPE +#undef NON_CANONICAL_TYPE +#undef TYPE void mangleType(const TagType*); void mangleType(const FunctionType *T, const FunctionDecl *D, @@ -69,8 +94,12 @@ private: void mangleExtraDimensions(QualType T); void mangleFunctionClass(const FunctionDecl *FD); void mangleCallingConvention(const FunctionType *T, bool IsInstMethod = false); + void mangleIntegerLiteral(QualType T, const llvm::APSInt &Number); void mangleThrowSpecification(const FunctionProtoType *T); + void mangleTemplateArgs( + const SmallVectorImpl<TemplateArgumentLoc> &TemplateArgs); + }; /// MicrosoftMangleContext - Overrides the default MangleContext for the @@ -157,15 +186,15 @@ void MicrosoftCXXNameMangler::mangle(const NamedDecl *D, StringRef Prefix) { // MSVC doesn't mangle C++ names the same way it mangles extern "C" names. // Therefore it's really important that we don't decorate the - // name with leading underscores or leading/trailing at signs. So, emit a - // asm marker at the start so we get the name right. - Out << '\01'; // LLVM IR Marker for __asm("foo") + // name with leading underscores or leading/trailing at signs. So, by + // default, we emit an asm marker at the start so we get the name right. + // Callers can override this with a custom prefix. // Any decl can be declared with __asm("foo") on it, and this takes precedence // over all other naming in the .o file. if (const AsmLabelAttr *ALA = D->getAttr<AsmLabelAttr>()) { // If we have an asm name, then we use it as the mangling. - Out << ALA->getLabel(); + Out << '\01' << ALA->getLabel(); return; } @@ -176,7 +205,15 @@ void MicrosoftCXXNameMangler::mangle(const NamedDecl *D, mangleFunctionEncoding(FD); else if (const VarDecl *VD = dyn_cast<VarDecl>(D)) mangleVariableEncoding(VD); - // TODO: Fields? Can MSVC even mangle them? + else { + // TODO: Fields? Can MSVC even mangle them? + // Issue a diagnostic for now. + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this declaration yet"); + Diags.Report(D->getLocation(), DiagID) + << D->getSourceRange(); + } } void MicrosoftCXXNameMangler::mangleFunctionEncoding(const FunctionDecl *FD) { @@ -188,7 +225,7 @@ void MicrosoftCXXNameMangler::mangleFunctionEncoding(const FunctionDecl *FD) { // We should never ever see a FunctionNoProtoType at this point. // We don't even know how to mangle their types anyway :). - const FunctionProtoType *FT = cast<FunctionProtoType>(FD->getType()); + const FunctionProtoType *FT = FD->getType()->castAs<FunctionProtoType>(); bool InStructor = false, InInstMethod = false; const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD); @@ -232,16 +269,17 @@ void MicrosoftCXXNameMangler::mangleVariableEncoding(const VarDecl *VD) { // ::= <type> A # pointers, references, arrays // Pointers and references are odd. The type of 'int * const foo;' gets // mangled as 'QAHA' instead of 'PAHB', for example. - QualType Ty = VD->getType(); + TypeLoc TL = VD->getTypeSourceInfo()->getTypeLoc(); + QualType Ty = TL.getType(); if (Ty->isPointerType() || Ty->isReferenceType()) { - mangleType(Ty); + mangleType(Ty, TL.getSourceRange()); Out << 'A'; - } else if (Ty->isArrayType()) { + } else if (const ArrayType *AT = getASTContext().getAsArrayType(Ty)) { // Global arrays are funny, too. - mangleType(cast<ArrayType>(Ty.getTypePtr()), true); + mangleType(AT, true); Out << 'A'; } else { - mangleType(Ty.getLocalUnqualifiedType()); + mangleType(Ty.getLocalUnqualifiedType(), TL.getSourceRange()); mangleQualifiers(Ty.getLocalQualifiers(), false); } } @@ -266,35 +304,156 @@ void MicrosoftCXXNameMangler::mangleName(const NamedDecl *ND) { } void MicrosoftCXXNameMangler::mangleNumber(int64_t Number) { - // <number> ::= [?] <decimal digit> # <= 9 - // ::= [?] <hex digit>+ @ # > 9; A = 0, B = 1, etc... + // <number> ::= [?] <decimal digit> # 1 <= Number <= 10 + // ::= [?] <hex digit>+ @ # 0 or > 9; A = 0, B = 1, etc... + // ::= [?] @ # 0 (alternate mangling, not emitted by VC) if (Number < 0) { Out << '?'; Number = -Number; } - if (Number >= 1 && Number <= 10) { + // There's a special shorter mangling for 0, but Microsoft + // chose not to use it. Instead, 0 gets mangled as "A@". Oh well... + if (Number >= 1 && Number <= 10) Out << Number-1; - } else { + else { // We have to build up the encoding in reverse order, so it will come // out right when we write it out. char Encoding[16]; char *EndPtr = Encoding+sizeof(Encoding); char *CurPtr = EndPtr; - while (Number) { + do { *--CurPtr = 'A' + (Number % 16); Number /= 16; + } while (Number); + Out.write(CurPtr, EndPtr-CurPtr); + Out << '@'; + } +} + +void MicrosoftCXXNameMangler::mangleNumber(const llvm::APSInt &Value) { + if (Value.isSigned() && Value.isNegative()) { + Out << '?'; + mangleNumber(llvm::APSInt(Value.abs())); + return; + } + llvm::APSInt Temp(Value); + if (Value.uge(1) && Value.ule(10)) { + --Temp; + Temp.print(Out, false); + } else { + // We have to build up the encoding in reverse order, so it will come + // out right when we write it out. + char Encoding[64]; + char *EndPtr = Encoding+sizeof(Encoding); + char *CurPtr = EndPtr; + llvm::APSInt NibbleMask(Value.getBitWidth(), Value.isUnsigned()); + NibbleMask = 0xf; + for (int i = 0, e = Value.getActiveBits() / 4; i != e; ++i) { + *--CurPtr = 'A' + Temp.And(NibbleMask).getLimitedValue(0xf); + Temp = Temp.lshr(4); } Out.write(CurPtr, EndPtr-CurPtr); Out << '@'; } } +static const TemplateDecl * +isTemplate(const NamedDecl *ND, + SmallVectorImpl<TemplateArgumentLoc> &TemplateArgs) { + // Check if we have a function template. + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)){ + if (const TemplateDecl *TD = FD->getPrimaryTemplate()) { + if (FD->getTemplateSpecializationArgsAsWritten()) { + const ASTTemplateArgumentListInfo *ArgList = + FD->getTemplateSpecializationArgsAsWritten(); + TemplateArgs.append(ArgList->getTemplateArgs(), + ArgList->getTemplateArgs() + + ArgList->NumTemplateArgs); + } else { + const TemplateArgumentList *ArgList = + FD->getTemplateSpecializationArgs(); + TemplateArgumentListInfo LI; + for (unsigned i = 0, e = ArgList->size(); i != e; ++i) + TemplateArgs.push_back(TemplateArgumentLoc(ArgList->get(i), + FD->getTypeSourceInfo())); + } + return TD; + } + } + + // Check if we have a class template. + if (const ClassTemplateSpecializationDecl *Spec = + dyn_cast<ClassTemplateSpecializationDecl>(ND)) { + TypeSourceInfo *TSI = Spec->getTypeAsWritten(); + if (TSI) { + TemplateSpecializationTypeLoc &TSTL = + cast<TemplateSpecializationTypeLoc>(TSI->getTypeLoc()); + TemplateArgumentListInfo LI(TSTL.getLAngleLoc(), TSTL.getRAngleLoc()); + for (unsigned i = 0, e = TSTL.getNumArgs(); i != e; ++i) + TemplateArgs.push_back(TSTL.getArgLoc(i)); + } else { + TemplateArgumentListInfo LI; + const TemplateArgumentList &ArgList = + Spec->getTemplateArgs(); + for (unsigned i = 0, e = ArgList.size(); i != e; ++i) + TemplateArgs.push_back(TemplateArgumentLoc(ArgList[i], + TemplateArgumentLocInfo())); + } + return Spec->getSpecializedTemplate(); + } + + return 0; +} + void MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, DeclarationName Name) { // <unqualified-name> ::= <operator-name> // ::= <ctor-dtor-name> // ::= <source-name> + // ::= <template-name> + SmallVector<TemplateArgumentLoc, 2> TemplateArgs; + // Check if we have a template. + if (const TemplateDecl *TD = isTemplate(ND, TemplateArgs)) { + // We have a template. + // Here comes the tricky thing: if we need to mangle something like + // void foo(A::X<Y>, B::X<Y>), + // the X<Y> part is aliased. However, if you need to mangle + // void foo(A::X<A::Y>, A::X<B::Y>), + // the A::X<> part is not aliased. + // That said, from the mangler's perspective we have a structure like this: + // namespace[s] -> type[ -> template-parameters] + // but from the Clang perspective we have + // type [ -> template-parameters] + // \-> namespace[s] + // What we do is we create a new mangler, mangle the same type (without + // a namespace suffix) using the extra mangler with back references + // disabled (to avoid infinite recursion) and then use the mangled type + // name as a key to check the mangling of different types for aliasing. + + std::string BackReferenceKey; + BackRefMap::iterator Found; + if (UseNameBackReferences) { + llvm::raw_string_ostream Stream(BackReferenceKey); + MicrosoftCXXNameMangler Extra(Context, Stream); + Extra.disableBackReferences(); + Extra.mangleUnqualifiedName(ND, Name); + Stream.flush(); + + Found = NameBackReferences.find(BackReferenceKey); + } + if (!UseNameBackReferences || Found == NameBackReferences.end()) { + mangleTemplateInstantiationName(TD, TemplateArgs); + if (UseNameBackReferences && NameBackReferences.size() < 10) { + size_t Size = NameBackReferences.size(); + NameBackReferences[BackReferenceKey] = Size; + } + } else { + Out << Found->second; + } + return; + } + switch (Name.getNameKind()) { case DeclarationName::Identifier: { if (const IdentifierInfo *II = Name.getAsIdentifierInfo()) { @@ -349,12 +508,17 @@ MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, break; case DeclarationName::CXXOperatorName: - mangleOperatorName(Name.getCXXOverloadedOperator()); + mangleOperatorName(Name.getCXXOverloadedOperator(), ND->getLocation()); break; - case DeclarationName::CXXLiteralOperatorName: + case DeclarationName::CXXLiteralOperatorName: { // FIXME: Was this added in VS2010? Does MS even know how to mangle this? - llvm_unreachable("Don't know how to mangle literal operators yet!"); + DiagnosticsEngine Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this literal operator yet"); + Diags.Report(ND->getLocation(), DiagID); + break; + } case DeclarationName::CXXUsingDirective: llvm_unreachable("Can't mangle a using directive name!"); @@ -364,8 +528,6 @@ MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, void MicrosoftCXXNameMangler::manglePostfix(const DeclContext *DC, bool NoFunction) { // <postfix> ::= <unqualified-name> [<postfix>] - // ::= <template-postfix> <template-args> [<postfix>] - // ::= <template-param> // ::= <substitution> [<postfix>] if (!DC) return; @@ -386,13 +548,16 @@ void MicrosoftCXXNameMangler::manglePostfix(const DeclContext *DC, return; else if (const ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(DC)) mangleObjCMethodName(Method); + else if (const FunctionDecl *Func = dyn_cast<FunctionDecl>(DC)) + mangleLocalName(Func); else { mangleUnqualifiedName(cast<NamedDecl>(DC)); manglePostfix(DC->getParent(), NoFunction); } } -void MicrosoftCXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO) { +void MicrosoftCXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO, + SourceLocation Loc) { switch (OO) { // ?0 # constructor // ?1 # destructor @@ -509,8 +674,13 @@ void MicrosoftCXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO) { // <operator-name> ::= ?_V # delete[] case OO_Array_Delete: Out << "?_V"; break; - case OO_Conditional: - llvm_unreachable("Don't know how to mangle ?:"); + case OO_Conditional: { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this conditional operator yet"); + Diags.Report(Loc, DiagID); + break; + } case OO_None: case NUM_OVERLOADED_OPERATORS: @@ -520,13 +690,141 @@ void MicrosoftCXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO) { void MicrosoftCXXNameMangler::mangleSourceName(const IdentifierInfo *II) { // <source name> ::= <identifier> @ - Out << II->getName() << '@'; + std::string key = II->getNameStart(); + BackRefMap::iterator Found; + if (UseNameBackReferences) + Found = NameBackReferences.find(key); + if (!UseNameBackReferences || Found == NameBackReferences.end()) { + Out << II->getName() << '@'; + if (UseNameBackReferences && NameBackReferences.size() < 10) { + size_t Size = NameBackReferences.size(); + NameBackReferences[key] = Size; + } + } else { + Out << Found->second; + } } void MicrosoftCXXNameMangler::mangleObjCMethodName(const ObjCMethodDecl *MD) { Context.mangleObjCMethodName(MD, Out); } +// Find out how many function decls live above this one and return an integer +// suitable for use as the number in a numbered anonymous scope. +// TODO: Memoize. +static unsigned getLocalNestingLevel(const FunctionDecl *FD) { + const DeclContext *DC = FD->getParent(); + int level = 1; + + while (DC && !DC->isTranslationUnit()) { + if (isa<FunctionDecl>(DC) || isa<ObjCMethodDecl>(DC)) level++; + DC = DC->getParent(); + } + + return 2*level; +} + +void MicrosoftCXXNameMangler::mangleLocalName(const FunctionDecl *FD) { + // <nested-name> ::= <numbered-anonymous-scope> ? <mangled-name> + // <numbered-anonymous-scope> ::= ? <number> + // Even though the name is rendered in reverse order (e.g. + // A::B::C is rendered as C@B@A), VC numbers the scopes from outermost to + // innermost. So a method bar in class C local to function foo gets mangled + // as something like: + // ?bar@C@?1??foo@@YAXXZ@QAEXXZ + // This is more apparent when you have a type nested inside a method of a + // type nested inside a function. A method baz in class D local to method + // bar of class C local to function foo gets mangled as: + // ?baz@D@?3??bar@C@?1??foo@@YAXXZ@QAEXXZ@QAEXXZ + // This scheme is general enough to support GCC-style nested + // functions. You could have a method baz of class C inside a function bar + // inside a function foo, like so: + // ?baz@C@?3??bar@?1??foo@@YAXXZ@YAXXZ@QAEXXZ + int NestLevel = getLocalNestingLevel(FD); + Out << '?'; + mangleNumber(NestLevel); + Out << '?'; + mangle(FD, "?"); +} + +void MicrosoftCXXNameMangler::mangleTemplateInstantiationName( + const TemplateDecl *TD, + const SmallVectorImpl<TemplateArgumentLoc> &TemplateArgs) { + // <template-name> ::= <unscoped-template-name> <template-args> + // ::= <substitution> + // Always start with the unqualified name. + + // Templates have their own context for back references. + BackRefMap TemplateContext; + NameBackReferences.swap(TemplateContext); + + mangleUnscopedTemplateName(TD); + mangleTemplateArgs(TemplateArgs); + + NameBackReferences.swap(TemplateContext); +} + +void +MicrosoftCXXNameMangler::mangleUnscopedTemplateName(const TemplateDecl *TD) { + // <unscoped-template-name> ::= ?$ <unqualified-name> + Out << "?$"; + mangleUnqualifiedName(TD); +} + +void +MicrosoftCXXNameMangler::mangleIntegerLiteral(QualType T, + const llvm::APSInt &Value) { + // <integer-literal> ::= $0 <number> + Out << "$0"; + // Make sure booleans are encoded as 0/1. + if (T->isBooleanType()) + Out << (Value.getBoolValue() ? "0" : "A@"); + else + mangleNumber(Value); +} + +void +MicrosoftCXXNameMangler::mangleTemplateArgs( + const SmallVectorImpl<TemplateArgumentLoc> &TemplateArgs) { + // <template-args> ::= {<type> | <integer-literal>}+ @ + unsigned NumTemplateArgs = TemplateArgs.size(); + for (unsigned i = 0; i < NumTemplateArgs; ++i) { + const TemplateArgumentLoc &TAL = TemplateArgs[i]; + const TemplateArgument &TA = TAL.getArgument(); + switch (TA.getKind()) { + case TemplateArgument::Null: + llvm_unreachable("Can't mangle null template arguments!"); + case TemplateArgument::Type: + mangleType(TA.getAsType(), TAL.getSourceRange()); + break; + case TemplateArgument::Integral: + mangleIntegerLiteral(TA.getIntegralType(), TA.getAsIntegral()); + break; + case TemplateArgument::Expression: { + // See if this is a constant expression. + Expr *TAE = TA.getAsExpr(); + llvm::APSInt Value; + if (TAE->isIntegerConstantExpr(Value, Context.getASTContext())) { + mangleIntegerLiteral(TAE->getType(), Value); + break; + } + /* fallthrough */ + } default: { + // Issue a diagnostic. + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this %select{ERROR|ERROR|pointer/reference|ERROR|" + "template|template pack expansion|expression|parameter pack}0 " + "template argument yet"); + Diags.Report(TAL.getLocation(), DiagID) + << TA.getKind() + << TAL.getSourceRange(); + } + } + } + Out << '@'; +} + void MicrosoftCXXNameMangler::mangleQualifiers(Qualifiers Quals, bool IsMember) { // <cvr-qualifiers> ::= [E] [F] [I] <base-cvr-qualifiers> @@ -610,7 +908,29 @@ void MicrosoftCXXNameMangler::mangleQualifiers(Qualifiers Quals, // FIXME: For now, just drop all extension qualifiers on the floor. } -void MicrosoftCXXNameMangler::mangleType(QualType T) { +void MicrosoftCXXNameMangler::mangleTypeRepeated(QualType T, SourceRange Range) { + void *TypePtr = getASTContext().getCanonicalType(T).getAsOpaquePtr(); + ArgBackRefMap::iterator Found = TypeBackReferences.find(TypePtr); + + if (Found == TypeBackReferences.end()) { + size_t OutSizeBefore = Out.GetNumBytesInBuffer(); + + mangleType(T,Range); + + // See if it's worth creating a back reference. + // Only types longer than 1 character are considered + // and only 10 back references slots are available: + bool LongerThanOneChar = (Out.GetNumBytesInBuffer() - OutSizeBefore > 1); + if (LongerThanOneChar && TypeBackReferences.size() < 10) { + size_t Size = TypeBackReferences.size(); + TypeBackReferences[TypePtr] = Size; + } + } else { + Out << Found->second; + } +} + +void MicrosoftCXXNameMangler::mangleType(QualType T, SourceRange Range) { // Only operate on the canonical type! T = getASTContext().getCanonicalType(T); @@ -644,18 +964,22 @@ void MicrosoftCXXNameMangler::mangleType(QualType T) { switch (T->getTypeClass()) { #define ABSTRACT_TYPE(CLASS, PARENT) #define NON_CANONICAL_TYPE(CLASS, PARENT) \ -case Type::CLASS: \ -llvm_unreachable("can't mangle non-canonical type " #CLASS "Type"); \ -return; + case Type::CLASS: \ + llvm_unreachable("can't mangle non-canonical type " #CLASS "Type"); \ + return; #define TYPE(CLASS, PARENT) \ -case Type::CLASS: \ -mangleType(static_cast<const CLASS##Type*>(T.getTypePtr())); \ -break; + case Type::CLASS: \ + mangleType(static_cast<const CLASS##Type*>(T.getTypePtr()), Range); \ + break; #include "clang/AST/TypeNodes.def" +#undef ABSTRACT_TYPE +#undef NON_CANONICAL_TYPE +#undef TYPE } } -void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T) { +void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T, + SourceRange Range) { // <type> ::= <builtin-type> // <builtin-type> ::= X # void // ::= C # signed char @@ -713,24 +1037,32 @@ void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T) { case BuiltinType::ObjCId: Out << "PAUobjc_object@@"; break; case BuiltinType::ObjCClass: Out << "PAUobjc_class@@"; break; case BuiltinType::ObjCSel: Out << "PAUobjc_selector@@"; break; + + case BuiltinType::NullPtr: Out << "$$T"; break; case BuiltinType::Char16: case BuiltinType::Char32: - case BuiltinType::Half: - case BuiltinType::NullPtr: - assert(0 && "Don't know how to mangle this type yet"); + case BuiltinType::Half: { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this built-in %0 type yet"); + Diags.Report(Range.getBegin(), DiagID) + << T->getName(Context.getASTContext().getPrintingPolicy()) + << Range; + break; + } } } // <type> ::= <function-type> -void MicrosoftCXXNameMangler::mangleType(const FunctionProtoType *T) { +void MicrosoftCXXNameMangler::mangleType(const FunctionProtoType *T, + SourceRange) { // Structors only appear in decls, so at this point we know it's not a // structor type. - // I'll probably have mangleType(MemberPointerType) call the mangleType() - // method directly. mangleType(T, NULL, false, false); } -void MicrosoftCXXNameMangler::mangleType(const FunctionNoProtoType *T) { +void MicrosoftCXXNameMangler::mangleType(const FunctionNoProtoType *T, + SourceRange) { llvm_unreachable("Can't mangle K&R function prototypes"); } @@ -753,8 +1085,23 @@ void MicrosoftCXXNameMangler::mangleType(const FunctionType *T, // ::= @ # structors (they have no declared return type) if (IsStructor) Out << '@'; - else - mangleType(Proto->getResultType()); + else { + QualType Result = Proto->getResultType(); + const Type* RT = Result.getTypePtr(); + if (!RT->isAnyPointerType() && !RT->isReferenceType()) { + if (Result.hasQualifiers() || !RT->isBuiltinType()) + Out << '?'; + if (!RT->isBuiltinType() && !Result.hasQualifiers()) { + // Lack of qualifiers for user types is mangled as 'A'. + Out << 'A'; + } + } + + // FIXME: Get the source range for the result type. Or, better yet, + // implement the unimplemented stuff so we don't need accurate source + // location info anymore :). + mangleType(Result, SourceRange()); + } // <argument-list> ::= X # void // ::= <type>+ @ @@ -763,17 +1110,21 @@ void MicrosoftCXXNameMangler::mangleType(const FunctionType *T, Out << 'X'; } else { if (D) { - // If we got a decl, use the "types-as-written" to make sure arrays - // get mangled right. + // If we got a decl, use the type-as-written to make sure arrays + // get mangled right. Note that we can't rely on the TSI + // existing if (for example) the parameter was synthesized. for (FunctionDecl::param_const_iterator Parm = D->param_begin(), - ParmEnd = D->param_end(); - Parm != ParmEnd; ++Parm) - mangleType((*Parm)->getTypeSourceInfo()->getType()); + ParmEnd = D->param_end(); Parm != ParmEnd; ++Parm) { + TypeSourceInfo *TSI = (*Parm)->getTypeSourceInfo(); + QualType Type = TSI ? TSI->getType() : (*Parm)->getType(); + mangleTypeRepeated(Type, (*Parm)->getSourceRange()); + } } else { + // Happens for function pointer type arguments for example. for (FunctionProtoType::arg_type_iterator Arg = Proto->arg_type_begin(), ArgEnd = Proto->arg_type_end(); Arg != ArgEnd; ++Arg) - mangleType(*Arg); + mangleTypeRepeated(*Arg, SourceRange()); } // <builtin-type> ::= Z # ellipsis if (Proto->isVariadic()) @@ -860,8 +1211,16 @@ void MicrosoftCXXNameMangler::mangleCallingConvention(const FunctionType *T, // that they could be in a DLL and somebody from another module could call // them.) CallingConv CC = T->getCallConv(); - if (CC == CC_Default) - CC = IsInstMethod ? getASTContext().getDefaultMethodCallConv() : CC_C; + if (CC == CC_Default) { + if (IsInstMethod) { + const FunctionProtoType *FPT = + T->getCanonicalTypeUnqualified().getAs<FunctionProtoType>(); + bool isVariadic = FPT->isVariadic(); + CC = getASTContext().getDefaultCXXMethodCallConv(isVariadic); + } else { + CC = CC_C; + } + } switch (CC) { default: llvm_unreachable("Unsupported CC for mangling"); @@ -884,8 +1243,15 @@ void MicrosoftCXXNameMangler::mangleThrowSpecification( Out << 'Z'; } -void MicrosoftCXXNameMangler::mangleType(const UnresolvedUsingType *T) { - llvm_unreachable("Don't know how to mangle UnresolvedUsingTypes yet!"); +void MicrosoftCXXNameMangler::mangleType(const UnresolvedUsingType *T, + SourceRange Range) { + // Probably should be mangled as a template instantiation; need to see what + // VC does first. + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this unresolved dependent type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; } // <type> ::= <union-type> | <struct-type> | <class-type> | <enum-type> @@ -893,10 +1259,10 @@ void MicrosoftCXXNameMangler::mangleType(const UnresolvedUsingType *T) { // <struct-type> ::= U <name> // <class-type> ::= V <name> // <enum-type> ::= W <size> <name> -void MicrosoftCXXNameMangler::mangleType(const EnumType *T) { +void MicrosoftCXXNameMangler::mangleType(const EnumType *T, SourceRange) { mangleType(static_cast<const TagType*>(T)); } -void MicrosoftCXXNameMangler::mangleType(const RecordType *T) { +void MicrosoftCXXNameMangler::mangleType(const RecordType *T, SourceRange) { mangleType(static_cast<const TagType*>(T)); } void MicrosoftCXXNameMangler::mangleType(const TagType *T) { @@ -936,31 +1302,48 @@ void MicrosoftCXXNameMangler::mangleType(const ArrayType *T, bool IsGlobal) { Out << 'Q'; mangleExtraDimensions(T->getElementType()); } -void MicrosoftCXXNameMangler::mangleType(const ConstantArrayType *T) { +void MicrosoftCXXNameMangler::mangleType(const ConstantArrayType *T, + SourceRange) { mangleType(static_cast<const ArrayType *>(T), false); } -void MicrosoftCXXNameMangler::mangleType(const VariableArrayType *T) { +void MicrosoftCXXNameMangler::mangleType(const VariableArrayType *T, + SourceRange) { mangleType(static_cast<const ArrayType *>(T), false); } -void MicrosoftCXXNameMangler::mangleType(const DependentSizedArrayType *T) { +void MicrosoftCXXNameMangler::mangleType(const DependentSizedArrayType *T, + SourceRange) { mangleType(static_cast<const ArrayType *>(T), false); } -void MicrosoftCXXNameMangler::mangleType(const IncompleteArrayType *T) { +void MicrosoftCXXNameMangler::mangleType(const IncompleteArrayType *T, + SourceRange) { mangleType(static_cast<const ArrayType *>(T), false); } void MicrosoftCXXNameMangler::mangleExtraDimensions(QualType ElementTy) { SmallVector<llvm::APInt, 3> Dimensions; for (;;) { - if (ElementTy->isConstantArrayType()) { - const ConstantArrayType *CAT = - static_cast<const ConstantArrayType *>(ElementTy.getTypePtr()); + if (const ConstantArrayType *CAT = + getASTContext().getAsConstantArrayType(ElementTy)) { Dimensions.push_back(CAT->getSize()); ElementTy = CAT->getElementType(); } else if (ElementTy->isVariableArrayType()) { - llvm_unreachable("Don't know how to mangle VLAs!"); + const VariableArrayType *VAT = + getASTContext().getAsVariableArrayType(ElementTy); + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this variable-length array yet"); + Diags.Report(VAT->getSizeExpr()->getExprLoc(), DiagID) + << VAT->getBracketsRange(); + return; } else if (ElementTy->isDependentSizedArrayType()) { // The dependent expression has to be folded into a constant (TODO). - llvm_unreachable("Don't know how to mangle dependent-sized arrays!"); + const DependentSizedArrayType *DSAT = + getASTContext().getAsDependentSizedArrayType(ElementTy); + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this dependent-length array yet"); + Diags.Report(DSAT->getSizeExpr()->getExprLoc(), DiagID) + << DSAT->getBracketsRange(); + return; } else if (ElementTy->isIncompleteArrayType()) continue; else break; } @@ -974,151 +1357,246 @@ void MicrosoftCXXNameMangler::mangleExtraDimensions(QualType ElementTy) { mangleNumber(Dimensions[Dim].getLimitedValue()); } } - mangleType(ElementTy.getLocalUnqualifiedType()); + mangleType(ElementTy.getLocalUnqualifiedType(), SourceRange()); } // <type> ::= <pointer-to-member-type> // <pointer-to-member-type> ::= <pointer-cvr-qualifiers> <cvr-qualifiers> // <class name> <type> -void MicrosoftCXXNameMangler::mangleType(const MemberPointerType *T) { +void MicrosoftCXXNameMangler::mangleType(const MemberPointerType *T, + SourceRange Range) { QualType PointeeType = T->getPointeeType(); - if (const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(PointeeType)) { + if (const FunctionProtoType *FPT = PointeeType->getAs<FunctionProtoType>()) { Out << '8'; - mangleName(cast<RecordType>(T->getClass())->getDecl()); + mangleName(T->getClass()->castAs<RecordType>()->getDecl()); mangleType(FPT, NULL, false, true); } else { mangleQualifiers(PointeeType.getQualifiers(), true); - mangleName(cast<RecordType>(T->getClass())->getDecl()); - mangleType(PointeeType.getLocalUnqualifiedType()); + mangleName(T->getClass()->castAs<RecordType>()->getDecl()); + mangleType(PointeeType.getLocalUnqualifiedType(), Range); } } -void MicrosoftCXXNameMangler::mangleType(const TemplateTypeParmType *T) { - llvm_unreachable("Don't know how to mangle TemplateTypeParmTypes yet!"); +void MicrosoftCXXNameMangler::mangleType(const TemplateTypeParmType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this template type parameter type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; } void MicrosoftCXXNameMangler::mangleType( - const SubstTemplateTypeParmPackType *T) { - llvm_unreachable( - "Don't know how to mangle SubstTemplateTypeParmPackTypes yet!"); + const SubstTemplateTypeParmPackType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this substituted parameter pack yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; } // <type> ::= <pointer-type> // <pointer-type> ::= <pointer-cvr-qualifiers> <cvr-qualifiers> <type> -void MicrosoftCXXNameMangler::mangleType(const PointerType *T) { +void MicrosoftCXXNameMangler::mangleType(const PointerType *T, + SourceRange Range) { QualType PointeeTy = T->getPointeeType(); if (PointeeTy->isArrayType()) { // Pointers to arrays are mangled like arrays. - mangleExtraDimensions(T->getPointeeType()); - } else if (PointeeTy->isFunctionType()) { + mangleExtraDimensions(PointeeTy); + } else if (const FunctionType *FT = PointeeTy->getAs<FunctionType>()) { // Function pointers are special. Out << '6'; - mangleType(static_cast<const FunctionType *>(PointeeTy.getTypePtr()), - NULL, false, false); + mangleType(FT, NULL, false, false); } else { if (!PointeeTy.hasQualifiers()) // Lack of qualifiers is mangled as 'A'. Out << 'A'; - mangleType(PointeeTy); + mangleType(PointeeTy, Range); } } -void MicrosoftCXXNameMangler::mangleType(const ObjCObjectPointerType *T) { +void MicrosoftCXXNameMangler::mangleType(const ObjCObjectPointerType *T, + SourceRange Range) { // Object pointers never have qualifiers. Out << 'A'; - mangleType(T->getPointeeType()); + mangleType(T->getPointeeType(), Range); } // <type> ::= <reference-type> // <reference-type> ::= A <cvr-qualifiers> <type> -void MicrosoftCXXNameMangler::mangleType(const LValueReferenceType *T) { +void MicrosoftCXXNameMangler::mangleType(const LValueReferenceType *T, + SourceRange Range) { Out << 'A'; QualType PointeeTy = T->getPointeeType(); if (!PointeeTy.hasQualifiers()) // Lack of qualifiers is mangled as 'A'. Out << 'A'; - mangleType(PointeeTy); -} - -void MicrosoftCXXNameMangler::mangleType(const RValueReferenceType *T) { - llvm_unreachable("Don't know how to mangle RValueReferenceTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const ComplexType *T) { - llvm_unreachable("Don't know how to mangle ComplexTypes yet!"); + mangleType(PointeeTy, Range); } -void MicrosoftCXXNameMangler::mangleType(const VectorType *T) { - llvm_unreachable("Don't know how to mangle VectorTypes yet!"); -} -void MicrosoftCXXNameMangler::mangleType(const ExtVectorType *T) { - llvm_unreachable("Don't know how to mangle ExtVectorTypes yet!"); -} -void MicrosoftCXXNameMangler::mangleType(const DependentSizedExtVectorType *T) { - llvm_unreachable( - "Don't know how to mangle DependentSizedExtVectorTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const ObjCInterfaceType *T) { +// <type> ::= <r-value-reference-type> +// <r-value-reference-type> ::= $$Q <cvr-qualifiers> <type> +void MicrosoftCXXNameMangler::mangleType(const RValueReferenceType *T, + SourceRange Range) { + Out << "$$Q"; + QualType PointeeTy = T->getPointeeType(); + if (!PointeeTy.hasQualifiers()) + // Lack of qualifiers is mangled as 'A'. + Out << 'A'; + mangleType(PointeeTy, Range); +} + +void MicrosoftCXXNameMangler::mangleType(const ComplexType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this complex number type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const VectorType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this vector type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} +void MicrosoftCXXNameMangler::mangleType(const ExtVectorType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this extended vector type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} +void MicrosoftCXXNameMangler::mangleType(const DependentSizedExtVectorType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this dependent-sized extended vector type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const ObjCInterfaceType *T, + SourceRange) { // ObjC interfaces have structs underlying them. Out << 'U'; mangleName(T->getDecl()); } -void MicrosoftCXXNameMangler::mangleType(const ObjCObjectType *T) { +void MicrosoftCXXNameMangler::mangleType(const ObjCObjectType *T, + SourceRange Range) { // We don't allow overloading by different protocol qualification, // so mangling them isn't necessary. - mangleType(T->getBaseType()); + mangleType(T->getBaseType(), Range); } -void MicrosoftCXXNameMangler::mangleType(const BlockPointerType *T) { +void MicrosoftCXXNameMangler::mangleType(const BlockPointerType *T, + SourceRange Range) { Out << "_E"; - mangleType(T->getPointeeType()); + mangleType(T->getPointeeType(), Range); } -void MicrosoftCXXNameMangler::mangleType(const InjectedClassNameType *T) { - llvm_unreachable("Don't know how to mangle InjectedClassNameTypes yet!"); +void MicrosoftCXXNameMangler::mangleType(const InjectedClassNameType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this injected class name type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; } -void MicrosoftCXXNameMangler::mangleType(const TemplateSpecializationType *T) { - llvm_unreachable("Don't know how to mangle TemplateSpecializationTypes yet!"); +void MicrosoftCXXNameMangler::mangleType(const TemplateSpecializationType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this template specialization type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; } -void MicrosoftCXXNameMangler::mangleType(const DependentNameType *T) { - llvm_unreachable("Don't know how to mangle DependentNameTypes yet!"); +void MicrosoftCXXNameMangler::mangleType(const DependentNameType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this dependent name type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; } void MicrosoftCXXNameMangler::mangleType( - const DependentTemplateSpecializationType *T) { - llvm_unreachable( - "Don't know how to mangle DependentTemplateSpecializationTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const PackExpansionType *T) { - llvm_unreachable("Don't know how to mangle PackExpansionTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const TypeOfType *T) { - llvm_unreachable("Don't know how to mangle TypeOfTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const TypeOfExprType *T) { - llvm_unreachable("Don't know how to mangle TypeOfExprTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const DecltypeType *T) { - llvm_unreachable("Don't know how to mangle DecltypeTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const UnaryTransformType *T) { - llvm_unreachable("Don't know how to mangle UnaryTransformationTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const AutoType *T) { - llvm_unreachable("Don't know how to mangle AutoTypes yet!"); -} - -void MicrosoftCXXNameMangler::mangleType(const AtomicType *T) { - llvm_unreachable("Don't know how to mangle AtomicTypes yet!"); + const DependentTemplateSpecializationType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this dependent template specialization type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const PackExpansionType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this pack expansion yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const TypeOfType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this typeof(type) yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const TypeOfExprType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this typeof(expression) yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const DecltypeType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this decltype() yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const UnaryTransformType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this unary transform type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const AutoType *T, SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this 'auto' type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + +void MicrosoftCXXNameMangler::mangleType(const AtomicType *T, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this C11 atomic type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; } void MicrosoftMangleContext::mangleName(const NamedDecl *D, @@ -1138,17 +1616,35 @@ void MicrosoftMangleContext::mangleName(const NamedDecl *D, void MicrosoftMangleContext::mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk, raw_ostream &) { - llvm_unreachable("Can't yet mangle thunks!"); + unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle thunk for this method yet"); + getDiags().Report(MD->getLocation(), DiagID); } void MicrosoftMangleContext::mangleCXXDtorThunk(const CXXDestructorDecl *DD, CXXDtorType Type, const ThisAdjustment &, raw_ostream &) { - llvm_unreachable("Can't yet mangle destructor thunks!"); + unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle thunk for this destructor yet"); + getDiags().Report(DD->getLocation(), DiagID); } void MicrosoftMangleContext::mangleCXXVTable(const CXXRecordDecl *RD, - raw_ostream &) { - llvm_unreachable("Can't yet mangle virtual tables!"); + raw_ostream &Out) { + // <mangled-name> ::= ? <operator-name> <class-name> <storage-class> + // <cvr-qualifiers> [<name>] @ + // <operator-name> ::= _7 # vftable + // ::= _8 # vbtable + // NOTE: <cvr-qualifiers> here is always 'B' (const). <storage-class> + // is always '6' for vftables and '7' for vbtables. (The difference is + // beyond me.) + // TODO: vbtables. + MicrosoftCXXNameMangler Mangler(*this, Out); + Mangler.getStream() << "\01??_7"; + Mangler.mangleName(RD); + Mangler.getStream() << "6B"; + // TODO: If the class has more than one vtable, mangle in the class it came + // from. + Mangler.getStream() << '@'; } void MicrosoftMangleContext::mangleCXXVTT(const CXXRecordDecl *RD, raw_ostream &) { @@ -1162,11 +1658,19 @@ void MicrosoftMangleContext::mangleCXXCtorVTable(const CXXRecordDecl *RD, } void MicrosoftMangleContext::mangleCXXRTTI(QualType T, raw_ostream &) { - llvm_unreachable("Can't yet mangle RTTI!"); + // FIXME: Give a location... + unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle RTTI descriptors for type %0 yet"); + getDiags().Report(DiagID) + << T.getBaseTypeIdentifier(); } void MicrosoftMangleContext::mangleCXXRTTIName(QualType T, raw_ostream &) { - llvm_unreachable("Can't yet mangle RTTI names!"); + // FIXME: Give a location... + unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle the name of type %0 into RTTI descriptors yet"); + getDiags().Report(DiagID) + << T.getBaseTypeIdentifier(); } void MicrosoftMangleContext::mangleCXXCtor(const CXXConstructorDecl *D, CXXCtorType Type, @@ -1180,9 +1684,11 @@ void MicrosoftMangleContext::mangleCXXDtor(const CXXDestructorDecl *D, MicrosoftCXXNameMangler mangler(*this, Out); mangler.mangle(D); } -void MicrosoftMangleContext::mangleReferenceTemporary(const clang::VarDecl *, +void MicrosoftMangleContext::mangleReferenceTemporary(const clang::VarDecl *VD, raw_ostream &) { - llvm_unreachable("Can't yet mangle reference temporaries!"); + unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this reference temporary yet"); + getDiags().Report(VD->getLocation(), DiagID); } MangleContext *clang::createMicrosoftMangleContext(ASTContext &Context, diff --git a/lib/AST/NSAPI.cpp b/lib/AST/NSAPI.cpp index f5ea2c54ee2a..39077d1766e8 100644 --- a/lib/AST/NSAPI.cpp +++ b/lib/AST/NSAPI.cpp @@ -9,11 +9,13 @@ #include "clang/AST/NSAPI.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" using namespace clang; NSAPI::NSAPI(ASTContext &ctx) - : Ctx(ctx), ClassIds() { + : Ctx(ctx), ClassIds(), BOOLId(0), NSIntegerId(0), NSUIntegerId(0), + NSASCIIStringEncodingId(0), NSUTF8StringEncodingId(0) { } IdentifierInfo *NSAPI::getNSClassId(NSClassIdKindKind K) const { @@ -40,6 +42,21 @@ Selector NSAPI::getNSStringSelector(NSStringMethodKind MK) const { case NSStr_stringWithString: Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("stringWithString")); break; + case NSStr_stringWithUTF8String: + Sel = Ctx.Selectors.getUnarySelector( + &Ctx.Idents.get("stringWithUTF8String")); + break; + case NSStr_stringWithCStringEncoding: { + IdentifierInfo *KeyIdents[] = { + &Ctx.Idents.get("stringWithCString"), + &Ctx.Idents.get("encoding") + }; + Sel = Ctx.Selectors.getSelector(2, KeyIdents); + break; + } + case NSStr_stringWithCString: + Sel= Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("stringWithCString")); + break; case NSStr_initWithString: Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("initWithString")); break; @@ -50,6 +67,17 @@ Selector NSAPI::getNSStringSelector(NSStringMethodKind MK) const { return NSStringSelectors[MK]; } +llvm::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 llvm::Optional<NSStringMethodKind>(); +} + Selector NSAPI::getNSArraySelector(NSArrayMethodKind MK) const { if (NSArraySelectors[MK].isNull()) { Selector Sel; @@ -251,11 +279,22 @@ NSAPI::getNSNumberLiteralMethodKind(Selector Sel) const { } llvm::Optional<NSAPI::NSNumberLiteralMethodKind> -NSAPI::getNSNumberFactoryMethodKind(QualType T) { +NSAPI::getNSNumberFactoryMethodKind(QualType T) const { const BuiltinType *BT = T->getAs<BuiltinType>(); if (!BT) return llvm::Optional<NSAPI::NSNumberLiteralMethodKind>(); - + + const TypedefType *TDT = T->getAs<TypedefType>(); + if (TDT) { + QualType TDTTy = QualType(TDT, 0); + if (isObjCBOOLType(TDTTy)) + return NSAPI::NSNumberWithBool; + if (isObjCNSIntegerType(TDTTy)) + return NSAPI::NSNumberWithInteger; + if (isObjCNSUIntegerType(TDTTy)) + return NSAPI::NSNumberWithUnsignedInteger; + } + switch (BT->getKind()) { case BuiltinType::Char_S: case BuiltinType::SChar: @@ -310,3 +349,65 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) { return llvm::Optional<NSAPI::NSNumberLiteralMethodKind>(); } + +/// \brief Returns true if \param T is a typedef of "BOOL" in objective-c. +bool NSAPI::isObjCBOOLType(QualType T) const { + return isObjCTypedef(T, "BOOL", BOOLId); +} +/// \brief Returns true if \param T is a typedef of "NSInteger" in objective-c. +bool NSAPI::isObjCNSIntegerType(QualType T) const { + return isObjCTypedef(T, "NSInteger", NSIntegerId); +} +/// \brief Returns true if \param T is a typedef of "NSUInteger" in objective-c. +bool NSAPI::isObjCNSUIntegerType(QualType T) const { + return isObjCTypedef(T, "NSUInteger", NSUIntegerId); +} + +bool NSAPI::isObjCTypedef(QualType T, + StringRef name, IdentifierInfo *&II) const { + if (!Ctx.getLangOpts().ObjC1) + return false; + if (T.isNull()) + return false; + + if (!II) + II = &Ctx.Idents.get(name); + + while (const TypedefType *TDT = T->getAs<TypedefType>()) { + if (TDT->getDecl()->getDeclName().getAsIdentifierInfo() == II) + return true; + T = TDT->desugar(); + } + + return false; +} + +bool NSAPI::isObjCEnumerator(const Expr *E, + StringRef name, IdentifierInfo *&II) const { + if (!Ctx.getLangOpts().ObjC1) + return false; + if (!E) + return false; + + if (!II) + II = &Ctx.Idents.get(name); + + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) + if (const EnumConstantDecl * + EnumD = dyn_cast_or_null<EnumConstantDecl>(DRE->getDecl())) + return EnumD->getIdentifier() == II; + + return false; +} + +Selector NSAPI::getOrInitSelector(ArrayRef<StringRef> Ids, + Selector &Sel) const { + if (Sel.isNull()) { + SmallVector<IdentifierInfo *, 4> Idents; + for (ArrayRef<StringRef>::const_iterator + I = Ids.begin(), E = Ids.end(); I != E; ++I) + Idents.push_back(&Ctx.Idents.get(*I)); + Sel = Ctx.Selectors.getSelector(Idents.size(), Idents.data()); + } + return Sel; +} diff --git a/lib/AST/ParentMap.cpp b/lib/AST/ParentMap.cpp index 64016d9cdd6d..fa87afd0fa22 100644 --- a/lib/AST/ParentMap.cpp +++ b/lib/AST/ParentMap.cpp @@ -23,13 +23,20 @@ typedef llvm::DenseMap<Stmt*, Stmt*> MapTy; static void BuildParentMap(MapTy& M, Stmt* S) { for (Stmt::child_range I = S->children(); I; ++I) if (*I) { - M[*I] = S; - BuildParentMap(M, *I); + // Prefer the first time we see this statement in the traversal. + // This is important for PseudoObjectExprs. + Stmt *&Parent = M[*I]; + if (!Parent) { + Parent = S; + BuildParentMap(M, *I); + } } // Also include the source expr tree of an OpaqueValueExpr in the map. - if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) + if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) { + M[OVE->getSourceExpr()] = S; BuildParentMap(M, OVE->getSourceExpr()); + } } ParentMap::ParentMap(Stmt* S) : Impl(0) { diff --git a/lib/AST/RawCommentList.cpp b/lib/AST/RawCommentList.cpp new file mode 100644 index 000000000000..c704cabe69f7 --- /dev/null +++ b/lib/AST/RawCommentList.cpp @@ -0,0 +1,271 @@ +//===--- RawCommentList.cpp - Processing raw comments -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/RawCommentList.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Comment.h" +#include "clang/AST/CommentLexer.h" +#include "clang/AST/CommentBriefParser.h" +#include "clang/AST/CommentSema.h" +#include "clang/AST/CommentParser.h" +#include "clang/AST/CommentCommandTraits.h" +#include "llvm/ADT/STLExtras.h" + +using namespace clang; + +namespace { +/// Get comment kind and bool describing if it is a trailing comment. +std::pair<RawComment::CommentKind, bool> getCommentKind(StringRef Comment) { + if (Comment.size() < 3 || Comment[0] != '/') + return std::make_pair(RawComment::RCK_Invalid, false); + + RawComment::CommentKind K; + if (Comment[1] == '/') { + if (Comment.size() < 3) + return std::make_pair(RawComment::RCK_OrdinaryBCPL, false); + + if (Comment[2] == '/') + K = RawComment::RCK_BCPLSlash; + else if (Comment[2] == '!') + K = RawComment::RCK_BCPLExcl; + else + return std::make_pair(RawComment::RCK_OrdinaryBCPL, false); + } else { + assert(Comment.size() >= 4); + + // Comment lexer does not understand escapes in comment markers, so pretend + // that this is not a comment. + if (Comment[1] != '*' || + Comment[Comment.size() - 2] != '*' || + Comment[Comment.size() - 1] != '/') + return std::make_pair(RawComment::RCK_Invalid, false); + + if (Comment[2] == '*') + K = RawComment::RCK_JavaDoc; + else if (Comment[2] == '!') + K = RawComment::RCK_Qt; + else + return std::make_pair(RawComment::RCK_OrdinaryC, false); + } + const bool TrailingComment = (Comment.size() > 3) && (Comment[3] == '<'); + return std::make_pair(K, TrailingComment); +} + +bool mergedCommentIsTrailingComment(StringRef Comment) { + return (Comment.size() > 3) && (Comment[3] == '<'); +} +} // unnamed namespace + +RawComment::RawComment(const SourceManager &SourceMgr, SourceRange SR, + bool Merged) : + Range(SR), RawTextValid(false), BriefTextValid(false), + IsAlmostTrailingComment(false), + BeginLineValid(false), EndLineValid(false) { + // Extract raw comment text, if possible. + if (SR.getBegin() == SR.getEnd() || getRawText(SourceMgr).empty()) { + Kind = RCK_Invalid; + return; + } + + if (!Merged) { + // Guess comment kind. + std::pair<CommentKind, bool> K = getCommentKind(RawText); + Kind = K.first; + IsTrailingComment = K.second; + + IsAlmostTrailingComment = RawText.startswith("//<") || + RawText.startswith("/*<"); + } else { + Kind = RCK_Merged; + IsTrailingComment = mergedCommentIsTrailingComment(RawText); + } +} + +const Decl *RawComment::getDecl() const { + if (DeclOrParsedComment.isNull()) + return NULL; + + if (const Decl *D = DeclOrParsedComment.dyn_cast<const Decl *>()) + return D; + + return DeclOrParsedComment.get<comments::FullComment *>()->getDecl(); +} + +unsigned RawComment::getBeginLine(const SourceManager &SM) const { + if (BeginLineValid) + return BeginLine; + + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Range.getBegin()); + BeginLine = SM.getLineNumber(LocInfo.first, LocInfo.second); + BeginLineValid = true; + return BeginLine; +} + +unsigned RawComment::getEndLine(const SourceManager &SM) const { + if (EndLineValid) + return EndLine; + + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Range.getEnd()); + EndLine = SM.getLineNumber(LocInfo.first, LocInfo.second); + EndLineValid = true; + return EndLine; +} + +StringRef RawComment::getRawTextSlow(const SourceManager &SourceMgr) const { + FileID BeginFileID; + FileID EndFileID; + unsigned BeginOffset; + unsigned EndOffset; + + llvm::tie(BeginFileID, BeginOffset) = + SourceMgr.getDecomposedLoc(Range.getBegin()); + llvm::tie(EndFileID, EndOffset) = + SourceMgr.getDecomposedLoc(Range.getEnd()); + + const unsigned Length = EndOffset - BeginOffset; + if (Length < 2) + return StringRef(); + + // The comment can't begin in one file and end in another. + assert(BeginFileID == EndFileID); + + bool Invalid = false; + const char *BufferStart = SourceMgr.getBufferData(BeginFileID, + &Invalid).data(); + if (Invalid) + return StringRef(); + + return StringRef(BufferStart + BeginOffset, Length); +} + +const char *RawComment::extractBriefText(const ASTContext &Context) const { + // Make sure that RawText is valid. + getRawText(Context.getSourceManager()); + + // Since we will be copying the resulting text, all allocations made during + // parsing are garbage after resulting string is formed. Thus we can use + // a separate allocator for all temporary stuff. + llvm::BumpPtrAllocator Allocator; + + comments::CommandTraits Traits; + comments::Lexer L(Allocator, Traits, + Range.getBegin(), comments::CommentOptions(), + RawText.begin(), RawText.end()); + comments::BriefParser P(L, Traits); + + const std::string Result = P.Parse(); + const unsigned BriefTextLength = Result.size(); + char *BriefTextPtr = new (Context) char[BriefTextLength + 1]; + memcpy(BriefTextPtr, Result.c_str(), BriefTextLength + 1); + BriefText = BriefTextPtr; + BriefTextValid = true; + + return BriefTextPtr; +} + +comments::FullComment *RawComment::parse(const ASTContext &Context) const { + // Make sure that RawText is valid. + getRawText(Context.getSourceManager()); + + comments::CommandTraits Traits; + comments::Lexer L(Context.getAllocator(), Traits, + getSourceRange().getBegin(), comments::CommentOptions(), + RawText.begin(), RawText.end()); + comments::Sema S(Context.getAllocator(), Context.getSourceManager(), + Context.getDiagnostics(), Traits); + S.setDecl(getDecl()); + comments::Parser P(L, S, Context.getAllocator(), Context.getSourceManager(), + Context.getDiagnostics(), Traits); + + comments::FullComment *FC = P.parseFullComment(); + DeclOrParsedComment = FC; + return FC; +} + +namespace { +bool containsOnlyWhitespace(StringRef Str) { + return Str.find_first_not_of(" \t\f\v\r\n") == StringRef::npos; +} + +bool onlyWhitespaceBetweenComments(SourceManager &SM, + const RawComment &C1, const RawComment &C2) { + std::pair<FileID, unsigned> C1EndLocInfo = SM.getDecomposedLoc( + C1.getSourceRange().getEnd()); + std::pair<FileID, unsigned> C2BeginLocInfo = SM.getDecomposedLoc( + C2.getSourceRange().getBegin()); + + // Question does not make sense if comments are located in different files. + if (C1EndLocInfo.first != C2BeginLocInfo.first) + return false; + + bool Invalid = false; + const char *Buffer = SM.getBufferData(C1EndLocInfo.first, &Invalid).data(); + if (Invalid) + return false; + + StringRef TextBetweenComments(Buffer + C1EndLocInfo.second, + C2BeginLocInfo.second - C1EndLocInfo.second); + + return containsOnlyWhitespace(TextBetweenComments); +} +} // unnamed namespace + +void RawCommentList::addComment(const RawComment &RC, + llvm::BumpPtrAllocator &Allocator) { + if (RC.isInvalid()) + return; + + // Check if the comments are not in source order. + while (!Comments.empty() && + !SourceMgr.isBeforeInTranslationUnit( + Comments.back()->getSourceRange().getBegin(), + RC.getSourceRange().getBegin())) { + // If they are, just pop a few last comments that don't fit. + // This happens if an \#include directive contains comments. + Comments.pop_back(); + } + + if (OnlyWhitespaceSeen) { + if (!onlyWhitespaceBetweenComments(SourceMgr, LastComment, RC)) + OnlyWhitespaceSeen = false; + } + + LastComment = RC; + + // Ordinary comments are not interesting for us. + if (RC.isOrdinary()) + return; + + // 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)); + OnlyWhitespaceSeen = true; + return; + } + + const RawComment &C1 = *Comments.back(); + const RawComment &C2 = RC; + + // Merge comments only if there is only whitespace between them. + // Can't merge trailing and non-trailing comments. + // Merge trailing comments if they are on same or consecutive lines. + if (OnlyWhitespaceSeen && + (C1.isTrailingComment() == C2.isTrailingComment()) && + (!C1.isTrailingComment() || + C1.getEndLine(SourceMgr) + 1 >= C2.getBeginLine(SourceMgr))) { + SourceRange MergedRange(C1.getSourceRange().getBegin(), + C2.getSourceRange().getEnd()); + *Comments.back() = RawComment(SourceMgr, MergedRange, true); + } else + Comments.push_back(new (Allocator) RawComment(RC)); + + OnlyWhitespaceSeen = true; +} + diff --git a/lib/AST/RecordLayout.cpp b/lib/AST/RecordLayout.cpp index 0114eba06db7..2ae0aab19f69 100644 --- a/lib/AST/RecordLayout.cpp +++ b/lib/AST/RecordLayout.cpp @@ -32,7 +32,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment, CharUnits datasize, const uint64_t *fieldoffsets, unsigned fieldcount) - : Size(size), DataSize(datasize), FieldOffsets(0), Alignment(alignment), + : Size(size), DataSize(datasize), Alignment(alignment), FieldOffsets(0), FieldCount(fieldcount), CXXInfo(0) { if (FieldCount > 0) { FieldOffsets = new (Ctx) uint64_t[FieldCount]; @@ -43,7 +43,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size, // Constructor for C++ records. ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size, CharUnits alignment, - CharUnits vfptroffset, CharUnits vbptroffset, + bool hasOwnVFPtr, CharUnits vbptroffset, CharUnits datasize, const uint64_t *fieldoffsets, unsigned fieldcount, @@ -53,8 +53,8 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, const CXXRecordDecl *PrimaryBase, bool IsPrimaryBaseVirtual, const BaseOffsetsMapTy& BaseOffsets, - const BaseOffsetsMapTy& VBaseOffsets) - : Size(size), DataSize(datasize), FieldOffsets(0), Alignment(alignment), + const VBaseOffsetsMapTy& VBaseOffsets) + : Size(size), DataSize(datasize), Alignment(alignment), FieldOffsets(0), FieldCount(fieldcount), CXXInfo(new (Ctx) CXXRecordLayoutInfo) { if (FieldCount > 0) { @@ -69,7 +69,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CXXInfo->SizeOfLargestEmptySubobject = SizeOfLargestEmptySubobject; CXXInfo->BaseOffsets = BaseOffsets; CXXInfo->VBaseOffsets = VBaseOffsets; - CXXInfo->VFPtrOffset = vfptroffset; + CXXInfo->HasOwnVFPtr = hasOwnVFPtr; CXXInfo->VBPtrOffset = vbptroffset; #ifndef NDEBUG @@ -81,7 +81,7 @@ ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, "Primary virtual base must be at offset 0!"); } } else { - assert(getBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(getBaseClassOffset(PrimaryBase).isZero() && "Primary base must be at offset 0!"); } } diff --git a/lib/AST/RecordLayoutBuilder.cpp b/lib/AST/RecordLayoutBuilder.cpp index c2d9294a007e..d5df63f7d151 100644 --- a/lib/AST/RecordLayoutBuilder.cpp +++ b/lib/AST/RecordLayoutBuilder.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/Decl.h" @@ -161,10 +162,9 @@ void EmptySubobjectMap::ComputeEmptySubobjectSizes() { // Check the fields. for (CXXRecordDecl::field_iterator I = Class->field_begin(), E = Class->field_end(); I != E; ++I) { - const FieldDecl *FD = *I; const RecordType *RT = - Context.getBaseElementType(FD->getType())->getAs<RecordType>(); + Context.getBaseElementType(I->getType())->getAs<RecordType>(); // We only care about record types. if (!RT) @@ -261,12 +261,11 @@ EmptySubobjectMap::CanPlaceBaseSubobjectAtOffset(const BaseSubobjectInfo *Info, unsigned FieldNo = 0; for (CXXRecordDecl::field_iterator I = Info->Class->field_begin(), E = Info->Class->field_end(); I != E; ++I, ++FieldNo) { - const FieldDecl *FD = *I; - if (FD->isBitField()) + if (I->isBitField()) continue; CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo); - if (!CanPlaceFieldSubobjectAtOffset(FD, FieldOffset)) + if (!CanPlaceFieldSubobjectAtOffset(*I, FieldOffset)) return false; } @@ -310,12 +309,11 @@ void EmptySubobjectMap::UpdateEmptyBaseSubobjects(const BaseSubobjectInfo *Info, unsigned FieldNo = 0; for (CXXRecordDecl::field_iterator I = Info->Class->field_begin(), E = Info->Class->field_end(); I != E; ++I, ++FieldNo) { - const FieldDecl *FD = *I; - if (FD->isBitField()) + if (I->isBitField()) continue; CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo); - UpdateEmptyFieldSubobjects(FD, FieldOffset); + UpdateEmptyFieldSubobjects(*I, FieldOffset); } } @@ -380,13 +378,12 @@ EmptySubobjectMap::CanPlaceFieldSubobjectAtOffset(const CXXRecordDecl *RD, unsigned FieldNo = 0; for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I, ++FieldNo) { - const FieldDecl *FD = *I; - if (FD->isBitField()) + if (I->isBitField()) continue; CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo); - if (!CanPlaceFieldSubobjectAtOffset(FD, FieldOffset)) + if (!CanPlaceFieldSubobjectAtOffset(*I, FieldOffset)) return false; } @@ -491,13 +488,12 @@ void EmptySubobjectMap::UpdateEmptyFieldSubobjects(const CXXRecordDecl *RD, unsigned FieldNo = 0; for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I, ++FieldNo) { - const FieldDecl *FD = *I; - if (FD->isBitField()) + if (I->isBitField()) continue; CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo); - UpdateEmptyFieldSubobjects(FD, FieldOffset); + UpdateEmptyFieldSubobjects(*I, FieldOffset); } } @@ -538,6 +534,8 @@ void EmptySubobjectMap::UpdateEmptyFieldSubobjects(const FieldDecl *FD, } } +typedef llvm::SmallPtrSet<const CXXRecordDecl*, 4> ClassSetTy; + class RecordLayoutBuilder { protected: // FIXME: Remove this and make the appropriate fields public. @@ -600,8 +598,9 @@ protected: /// out is virtual. bool PrimaryBaseIsVirtual; - /// VFPtrOffset - Virtual function table offset. Only for MS layout. - CharUnits VFPtrOffset; + /// HasOwnVFPtr - Whether the class provides its own vtable/vftbl + /// pointer, as opposed to inheriting one from a primary base class. + bool HasOwnVFPtr; /// VBPtrOffset - Virtual base table offset. Only for MS layout. CharUnits VBPtrOffset; @@ -612,7 +611,7 @@ protected: BaseOffsetsMapTy Bases; // VBases - virtual base classes and their offsets in the record. - BaseOffsetsMapTy VBases; + ASTRecordLayout::VBaseOffsetsMapTy VBases; /// IndirectPrimaryBases - Virtual base classes, direct or indirect, that are /// primary base classes for some other direct or indirect base class. @@ -652,7 +651,7 @@ protected: NonVirtualAlignment(CharUnits::One()), ZeroLengthBitfield(0), PrimaryBase(0), PrimaryBaseIsVirtual(false), - VFPtrOffset(CharUnits::fromQuantity(-1)), + HasOwnVFPtr(false), VBPtrOffset(CharUnits::fromQuantity(-1)), FirstNearlyEmptyVBase(0) { } @@ -725,15 +724,20 @@ protected: CharUnits Offset); bool needsVFTable(const CXXRecordDecl *RD) const; - bool hasNewVirtualFunction(const CXXRecordDecl *RD) const; + bool hasNewVirtualFunction(const CXXRecordDecl *RD, + bool IgnoreDestructor = false) const; bool isPossiblePrimaryBase(const CXXRecordDecl *Base) const; + void computeVtordisps(const CXXRecordDecl *RD, + ClassSetTy &VtordispVBases); + /// LayoutVirtualBases - Lays out all the virtual bases. void LayoutVirtualBases(const CXXRecordDecl *RD, const CXXRecordDecl *MostDerivedClass); /// LayoutVirtualBase - Lays out a single virtual base. - void LayoutVirtualBase(const BaseSubobjectInfo *Base); + void LayoutVirtualBase(const BaseSubobjectInfo *Base, + bool IsVtordispNeed = false); /// LayoutBase - Will lay out a base and return the offset where it was /// placed, in chars. @@ -1044,8 +1048,7 @@ RecordLayoutBuilder::LayoutNonVirtualBases(const CXXRecordDecl *RD) { CharUnits PtrAlign = Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerAlign(0)); EnsureVTablePointerAlignment(PtrAlign); - if (isMicrosoftCXXABI()) - VFPtrOffset = getSize(); + HasOwnVFPtr = true; setSize(getSize() + PtrWidth); setDataSize(getSize()); } @@ -1142,7 +1145,7 @@ RecordLayoutBuilder::AddPrimaryVirtualBaseOffsets(const BaseSubobjectInfo *Info, assert(!VBases.count(Info->PrimaryVirtualBaseInfo->Class) && "primary vbase offset already exists!"); VBases.insert(std::make_pair(Info->PrimaryVirtualBaseInfo->Class, - Offset)); + ASTRecordLayout::VBaseInfo(Offset, false))); // Traverse the primary virtual base. AddPrimaryVirtualBaseOffsets(Info->PrimaryVirtualBaseInfo, Offset); @@ -1193,19 +1196,177 @@ bool RecordLayoutBuilder::needsVFTable(const CXXRecordDecl *RD) const { return hasNewVirtualFunction(RD); } +/// Does the given class inherit non-virtually from any of the classes +/// in the given set? +static bool hasNonVirtualBaseInSet(const CXXRecordDecl *RD, + const ClassSetTy &set) { + for (CXXRecordDecl::base_class_const_iterator + I = RD->bases_begin(), E = RD->bases_end(); I != E; ++I) { + // Ignore virtual links. + if (I->isVirtual()) continue; + + // Check whether the set contains the base. + const CXXRecordDecl *base = I->getType()->getAsCXXRecordDecl(); + if (set.count(base)) + return true; + + // Otherwise, recurse and propagate. + if (hasNonVirtualBaseInSet(base, set)) + return true; + } + + return false; +} + +/// Does the given method (B::foo()) already override a method (A::foo()) +/// such that A requires a vtordisp in B? If so, we don't need to add a +/// new vtordisp for B in a yet-more-derived class C providing C::foo(). +static bool overridesMethodRequiringVtorDisp(const ASTContext &Context, + const CXXMethodDecl *M) { + CXXMethodDecl::method_iterator + I = M->begin_overridden_methods(), E = M->end_overridden_methods(); + if (I == E) return false; + + const ASTRecordLayout::VBaseOffsetsMapTy &offsets = + Context.getASTRecordLayout(M->getParent()).getVBaseOffsetsMap(); + do { + const CXXMethodDecl *overridden = *I; + + // If the overridden method's class isn't recognized as a virtual + // base in the derived class, ignore it. + ASTRecordLayout::VBaseOffsetsMapTy::const_iterator + it = offsets.find(overridden->getParent()); + if (it == offsets.end()) continue; + + // Otherwise, check if the overridden method's class needs a vtordisp. + if (it->second.hasVtorDisp()) return true; + + } while (++I != E); + return false; +} + +/// In the Microsoft ABI, decide which of the virtual bases require a +/// vtordisp field. +void RecordLayoutBuilder::computeVtordisps(const CXXRecordDecl *RD, + ClassSetTy &vtordispVBases) { + // Bail out if we have no virtual bases. + assert(RD->getNumVBases()); + + // Build up the set of virtual bases that we haven't decided yet. + ClassSetTy undecidedVBases; + for (CXXRecordDecl::base_class_const_iterator + I = RD->vbases_begin(), E = RD->vbases_end(); I != E; ++I) { + const CXXRecordDecl *vbase = I->getType()->getAsCXXRecordDecl(); + undecidedVBases.insert(vbase); + } + assert(!undecidedVBases.empty()); + + // A virtual base requires a vtordisp field in a derived class if it + // requires a vtordisp field in a base class. Walk all the direct + // bases and collect this information. + for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), + E = RD->bases_end(); I != E; ++I) { + const CXXRecordDecl *base = I->getType()->getAsCXXRecordDecl(); + const ASTRecordLayout &baseLayout = Context.getASTRecordLayout(base); + + // Iterate over the set of virtual bases provided by this class. + for (ASTRecordLayout::VBaseOffsetsMapTy::const_iterator + VI = baseLayout.getVBaseOffsetsMap().begin(), + VE = baseLayout.getVBaseOffsetsMap().end(); VI != VE; ++VI) { + // If it doesn't need a vtordisp in this base, ignore it. + if (!VI->second.hasVtorDisp()) continue; + + // If we've already seen it and decided it needs a vtordisp, ignore it. + if (!undecidedVBases.erase(VI->first)) + continue; + + // Add it. + vtordispVBases.insert(VI->first); + + // Quit as soon as we've decided everything. + if (undecidedVBases.empty()) + return; + } + } + + // Okay, we have virtual bases that we haven't yet decided about. A + // virtual base requires a vtordisp if any the non-destructor + // virtual methods declared in this class directly override a method + // provided by that virtual base. (If so, we need to emit a thunk + // for that method, to be used in the construction vftable, which + // applies an additional 'vtordisp' this-adjustment.) + + // Collect the set of bases directly overridden by any method in this class. + // It's possible that some of these classes won't be virtual bases, or won't be + // provided by virtual bases, or won't be virtual bases in the overridden + // instance but are virtual bases elsewhere. Only the last matters for what + // we're doing, and we can ignore those: if we don't directly override + // a method provided by a virtual copy of a base class, but we do directly + // override a method provided by a non-virtual copy of that base class, + // then we must indirectly override the method provided by the virtual base, + // and so we should already have collected it in the loop above. + ClassSetTy overriddenBases; + for (CXXRecordDecl::method_iterator + M = RD->method_begin(), E = RD->method_end(); M != E; ++M) { + // Ignore non-virtual methods and destructors. + if (isa<CXXDestructorDecl>(*M) || !M->isVirtual()) + continue; + + for (CXXMethodDecl::method_iterator I = M->begin_overridden_methods(), + E = M->end_overridden_methods(); I != E; ++I) { + const CXXMethodDecl *overriddenMethod = (*I); + + // Ignore methods that override methods from vbases that require + // require vtordisps. + if (overridesMethodRequiringVtorDisp(Context, overriddenMethod)) + continue; + + // As an optimization, check immediately whether we're overriding + // something from the undecided set. + const CXXRecordDecl *overriddenBase = overriddenMethod->getParent(); + if (undecidedVBases.erase(overriddenBase)) { + vtordispVBases.insert(overriddenBase); + if (undecidedVBases.empty()) return; + + // We can't 'continue;' here because one of our undecided + // vbases might non-virtually inherit from this base. + // Consider: + // struct A { virtual void foo(); }; + // struct B : A {}; + // struct C : virtual A, virtual B { virtual void foo(); }; + // We need a vtordisp for B here. + } + + // Otherwise, just collect it. + overriddenBases.insert(overriddenBase); + } + } + + // Walk the undecided v-bases and check whether they (non-virtually) + // provide any of the overridden bases. We don't need to consider + // virtual links because the vtordisp inheres to the layout + // subobject containing the base. + for (ClassSetTy::const_iterator + I = undecidedVBases.begin(), E = undecidedVBases.end(); I != E; ++I) { + if (hasNonVirtualBaseInSet(*I, overriddenBases)) + vtordispVBases.insert(*I); + } +} + /// hasNewVirtualFunction - Does the given polymorphic class declare a /// virtual function that does not override a method from any of its /// base classes? bool -RecordLayoutBuilder::hasNewVirtualFunction(const CXXRecordDecl *RD) const { - assert(RD->isPolymorphic()); +RecordLayoutBuilder::hasNewVirtualFunction(const CXXRecordDecl *RD, + bool IgnoreDestructor) const { if (!RD->getNumBases()) return true; for (CXXRecordDecl::method_iterator method = RD->method_begin(); method != RD->method_end(); ++method) { - if (method->isVirtual() && !method->size_overridden_methods()) { + if (method->isVirtual() && !method->size_overridden_methods() && + !(IgnoreDestructor && method->getKind() == Decl::CXXDestructor)) { return true; } } @@ -1215,11 +1376,11 @@ RecordLayoutBuilder::hasNewVirtualFunction(const CXXRecordDecl *RD) const { /// isPossiblePrimaryBase - Is the given base class an acceptable /// primary base class? bool -RecordLayoutBuilder::isPossiblePrimaryBase(const CXXRecordDecl *Base) const { +RecordLayoutBuilder::isPossiblePrimaryBase(const CXXRecordDecl *base) const { // In the Itanium ABI, a class can be a primary base class if it has // a vtable for any reason. if (!isMicrosoftCXXABI()) - return Base->isDynamicClass(); + return base->isDynamicClass(); // In the MS ABI, a class can only be a primary base class if it // provides a vf-table at a static offset. That means it has to be @@ -1228,14 +1389,22 @@ RecordLayoutBuilder::isPossiblePrimaryBase(const CXXRecordDecl *Base) const { // base, which we have to guard against. // First off, it has to have virtual functions. - if (!Base->isPolymorphic()) return false; + if (!base->isPolymorphic()) return false; + + // If it has no virtual bases, then the vfptr must be at a static offset. + if (!base->getNumVBases()) return true; + + // Otherwise, the necessary information is cached in the layout. + const ASTRecordLayout &layout = Context.getASTRecordLayout(base); + + // If the base has its own vfptr, it can be a primary base. + if (layout.hasOwnVFPtr()) return true; - // If it has no virtual bases, then everything is at a static offset. - if (!Base->getNumVBases()) return true; + // If the base has a primary base class, then it can be a primary base. + if (layout.getPrimaryBase()) return true; - // Okay, just ask the base class's layout. - return (Context.getASTRecordLayout(Base).getVFPtrOffset() - != CharUnits::fromQuantity(-1)); + // Otherwise it can't. + return false; } void @@ -1288,10 +1457,12 @@ RecordLayoutBuilder::LayoutVirtualBases(const CXXRecordDecl *RD, } void RecordLayoutBuilder::MSLayoutVirtualBases(const CXXRecordDecl *RD) { - if (!RD->getNumVBases()) return; + ClassSetTy VtordispVBases; + computeVtordisps(RD, VtordispVBases); + // This is substantially simplified because there are no virtual // primary bases. for (CXXRecordDecl::base_class_const_iterator I = RD->vbases_begin(), @@ -1299,12 +1470,25 @@ void RecordLayoutBuilder::MSLayoutVirtualBases(const CXXRecordDecl *RD) { const CXXRecordDecl *BaseDecl = I->getType()->getAsCXXRecordDecl(); const BaseSubobjectInfo *BaseInfo = VirtualBaseInfo.lookup(BaseDecl); assert(BaseInfo && "Did not find virtual base info!"); - - LayoutVirtualBase(BaseInfo); + + // If this base requires a vtordisp, add enough space for an int field. + // This is apparently always 32-bits, even on x64. + bool vtordispNeeded = false; + if (VtordispVBases.count(BaseDecl)) { + CharUnits IntSize = + CharUnits::fromQuantity(Context.getTargetInfo().getIntWidth() / 8); + + setSize(getSize() + IntSize); + setDataSize(getSize()); + vtordispNeeded = true; + } + + LayoutVirtualBase(BaseInfo, vtordispNeeded); } } -void RecordLayoutBuilder::LayoutVirtualBase(const BaseSubobjectInfo *Base) { +void RecordLayoutBuilder::LayoutVirtualBase(const BaseSubobjectInfo *Base, + bool IsVtordispNeed) { assert(!Base->Derived && "Trying to lay out a primary virtual base!"); // Layout the base. @@ -1312,9 +1496,11 @@ void RecordLayoutBuilder::LayoutVirtualBase(const BaseSubobjectInfo *Base) { // Add its base class offset. assert(!VBases.count(Base->Class) && "vbase offset already exists!"); - VBases.insert(std::make_pair(Base->Class, Offset)); - - AddPrimaryVirtualBaseOffsets(Base, Offset); + VBases.insert(std::make_pair(Base->Class, + ASTRecordLayout::VBaseInfo(Offset, IsVtordispNeed))); + + if (!isMicrosoftCXXABI()) + AddPrimaryVirtualBaseOffsets(Base, Offset); } CharUnits RecordLayoutBuilder::LayoutBase(const BaseSubobjectInfo *Base) { @@ -1461,8 +1647,8 @@ void RecordLayoutBuilder::Layout(const CXXRecordDecl *RD) { Context.getTargetInfo().getCharAlign())); NonVirtualAlignment = Alignment; - if (isMicrosoftCXXABI() && - NonVirtualSize != NonVirtualSize.RoundUpToAlignment(Alignment)) { + if (isMicrosoftCXXABI()) { + if (NonVirtualSize != NonVirtualSize.RoundUpToAlignment(Alignment)) { CharUnits AlignMember = NonVirtualSize.RoundUpToAlignment(Alignment) - NonVirtualSize; @@ -1472,9 +1658,9 @@ void RecordLayoutBuilder::Layout(const CXXRecordDecl *RD) { NonVirtualSize = Context.toCharUnitsFromBits( llvm::RoundUpToAlignment(getSizeInBits(), Context.getTargetInfo().getCharAlign())); + } MSLayoutVirtualBases(RD); - } else { // Lay out the virtual bases and add the primary virtual base offsets. LayoutVirtualBases(RD, RD); @@ -1540,7 +1726,7 @@ void RecordLayoutBuilder::LayoutFields(const RecordDecl *D) { for (RecordDecl::field_iterator Field = D->field_begin(), FieldEnd = D->field_end(); Field != FieldEnd; ++Field) { if (IsMsStruct) { - FieldDecl *FD = (*Field); + FieldDecl *FD = *Field; if (Context.ZeroBitfieldFollowsBitfield(FD, LastFD)) ZeroLengthBitfield = FD; // Zero-length bitfields following non-bitfield members are @@ -1635,9 +1821,8 @@ void RecordLayoutBuilder::LayoutFields(const RecordDecl *D) { } else if (!Context.getTargetInfo().useBitFieldTypeAlignment() && Context.getTargetInfo().useZeroLengthBitfieldAlignment()) { - FieldDecl *FD = (*Field); - if (FD->isBitField() && FD->getBitWidthValue(Context) == 0) - ZeroLengthBitfield = FD; + if (Field->isBitField() && Field->getBitWidthValue(Context) == 0) + ZeroLengthBitfield = *Field; } LayoutField(*Field); } @@ -2166,6 +2351,10 @@ RecordLayoutBuilder::ComputeKeyFunction(const CXXRecordDecl *RD) { if (MD->hasInlineBody()) continue; + // Ignore inline deleted or defaulted functions. + if (!MD->isUserProvided()) + continue; + // We found it. return MD; } @@ -2238,7 +2427,7 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const { NewEntry = new (*this) ASTRecordLayout(*this, Builder.getSize(), Builder.Alignment, - Builder.VFPtrOffset, + Builder.HasOwnVFPtr, Builder.VBPtrOffset, DataSize, Builder.FieldOffsets.data(), @@ -2375,7 +2564,7 @@ static void DumpCXXRecordLayout(raw_ostream &OS, IndentLevel++; const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase(); - bool HasVfptr = Layout.getVFPtrOffset() != CharUnits::fromQuantity(-1); + bool HasVfptr = Layout.hasOwnVFPtr(); bool HasVbptr = Layout.getVBPtrOffset() != CharUnits::fromQuantity(-1); // Vtable pointer. @@ -2405,7 +2594,7 @@ static void DumpCXXRecordLayout(raw_ostream &OS, // vfptr and vbptr (for Microsoft C++ ABI) if (HasVfptr) { - PrintOffset(OS, Offset + Layout.getVFPtrOffset(), IndentLevel); + PrintOffset(OS, Offset, IndentLevel); OS << '(' << *RD << " vftable pointer)\n"; } if (HasVbptr) { @@ -2417,27 +2606,29 @@ static void DumpCXXRecordLayout(raw_ostream &OS, uint64_t FieldNo = 0; for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); I != E; ++I, ++FieldNo) { - const FieldDecl *Field = *I; + const FieldDecl &Field = **I; CharUnits FieldOffset = Offset + C.toCharUnitsFromBits(Layout.getFieldOffset(FieldNo)); - if (const RecordType *RT = Field->getType()->getAs<RecordType>()) { + if (const RecordType *RT = Field.getType()->getAs<RecordType>()) { if (const CXXRecordDecl *D = dyn_cast<CXXRecordDecl>(RT->getDecl())) { DumpCXXRecordLayout(OS, D, C, FieldOffset, IndentLevel, - Field->getName().data(), + Field.getName().data(), /*IncludeVirtualBases=*/true); continue; } } PrintOffset(OS, FieldOffset, IndentLevel); - OS << Field->getType().getAsString() << ' ' << *Field << '\n'; + OS << Field.getType().getAsString() << ' ' << Field << '\n'; } if (!IncludeVirtualBases) return; // Dump virtual bases. + const ASTRecordLayout::VBaseOffsetsMapTy &vtordisps = + Layout.getVBaseOffsetsMap(); for (CXXRecordDecl::base_class_const_iterator I = RD->vbases_begin(), E = RD->vbases_end(); I != E; ++I) { assert(I->isVirtual() && "Found non-virtual class!"); @@ -2445,6 +2636,12 @@ static void DumpCXXRecordLayout(raw_ostream &OS, cast<CXXRecordDecl>(I->getType()->getAs<RecordType>()->getDecl()); CharUnits VBaseOffset = Offset + Layout.getVBaseClassOffset(VBase); + + if (vtordisps.find(VBase)->second.hasVtorDisp()) { + PrintOffset(OS, VBaseOffset - CharUnits::fromQuantity(4), IndentLevel); + OS << "(vtordisp for vbase " << *VBase << ")\n"; + } + DumpCXXRecordLayout(OS, VBase, C, VBaseOffset, IndentLevel, VBase == PrimaryBase ? "(primary virtual base)" : "(virtual base)", diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index e4d9f0a1ef42..d877c3fab762 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -244,6 +244,22 @@ SourceLocation Stmt::getLocEnd() const { llvm_unreachable("unknown statement kind"); } +CompoundStmt::CompoundStmt(ASTContext &C, Stmt **StmtStart, unsigned NumStmts, + SourceLocation LB, SourceLocation RB) + : Stmt(CompoundStmtClass), LBracLoc(LB), RBracLoc(RB) { + CompoundStmtBits.NumStmts = NumStmts; + assert(CompoundStmtBits.NumStmts == NumStmts && + "NumStmts doesn't fit in bits of CompoundStmtBits.NumStmts!"); + + if (NumStmts == 0) { + Body = 0; + return; + } + + Body = new (C) Stmt*[NumStmts]; + memcpy(Body, StmtStart, NumStmts * sizeof(*Body)); +} + void CompoundStmt::setStmts(ASTContext &C, Stmt **Stmts, unsigned NumStmts) { if (this->Body) C.Deallocate(Body); @@ -257,6 +273,23 @@ const char *LabelStmt::getName() const { return getDecl()->getIdentifier()->getNameStart(); } +AttributedStmt *AttributedStmt::Create(ASTContext &C, SourceLocation Loc, + ArrayRef<const Attr*> Attrs, + Stmt *SubStmt) { + void *Mem = C.Allocate(sizeof(AttributedStmt) + + sizeof(Attr*) * (Attrs.size() - 1), + llvm::alignOf<AttributedStmt>()); + return new (Mem) AttributedStmt(Loc, Attrs, SubStmt); +} + +AttributedStmt *AttributedStmt::CreateEmpty(ASTContext &C, unsigned NumAttrs) { + assert(NumAttrs > 0 && "NumAttrs should be greater than zero"); + void *Mem = C.Allocate(sizeof(AttributedStmt) + + sizeof(Attr*) * (NumAttrs - 1), + llvm::alignOf<AttributedStmt>()); + return new (Mem) AttributedStmt(EmptyShell(), NumAttrs); +} + // This is defined here to avoid polluting Stmt.h with importing Expr.h SourceRange ReturnStmt::getSourceRange() const { if (RetExpr) @@ -328,7 +361,7 @@ void AsmStmt::setOutputsAndInputsAndClobbers(ASTContext &C, StringLiteral **Constraints, Stmt **Exprs, unsigned NumOutputs, - unsigned NumInputs, + unsigned NumInputs, StringLiteral **Clobbers, unsigned NumClobbers) { this->NumOutputs = NumOutputs; @@ -336,19 +369,19 @@ void AsmStmt::setOutputsAndInputsAndClobbers(ASTContext &C, this->NumClobbers = NumClobbers; unsigned NumExprs = NumOutputs + NumInputs; - + C.Deallocate(this->Names); this->Names = new (C) IdentifierInfo*[NumExprs]; std::copy(Names, Names + NumExprs, this->Names); - + C.Deallocate(this->Exprs); this->Exprs = new (C) Stmt*[NumExprs]; std::copy(Exprs, Exprs + NumExprs, this->Exprs); - + C.Deallocate(this->Constraints); this->Constraints = new (C) StringLiteral*[NumExprs]; std::copy(Constraints, Constraints + NumExprs, this->Constraints); - + C.Deallocate(this->Clobbers); this->Clobbers = new (C) StringLiteral*[NumClobbers]; std::copy(Clobbers, Clobbers + NumClobbers, this->Clobbers); @@ -407,7 +440,7 @@ unsigned AsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces, std::string CurStringPiece; bool HasVariants = !C.getTargetInfo().hasNoAsmVariants(); - + while (1) { // Done with the string? if (CurPtr == StrEnd) { @@ -428,7 +461,7 @@ unsigned AsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces, CurStringPiece += CurChar; continue; } - + // Escaped "%" character in asm string. if (CurPtr == StrEnd) { // % at end of string is invalid (no escape). @@ -525,8 +558,8 @@ QualType CXXCatchStmt::getCaughtType() const { // Constructors //===----------------------------------------------------------------------===// -AsmStmt::AsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple, - bool isvolatile, bool msasm, +AsmStmt::AsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple, + bool isvolatile, bool msasm, unsigned numoutputs, unsigned numinputs, IdentifierInfo **names, StringLiteral **constraints, Expr **exprs, StringLiteral *asmstr, unsigned numclobbers, @@ -535,8 +568,8 @@ AsmStmt::AsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple, , IsSimple(issimple), IsVolatile(isvolatile), MSAsm(msasm) , NumOutputs(numoutputs), NumInputs(numinputs), NumClobbers(numclobbers) { - unsigned NumExprs = NumOutputs +NumInputs; - + unsigned NumExprs = NumOutputs + NumInputs; + Names = new (C) IdentifierInfo*[NumExprs]; std::copy(names, names + NumExprs, Names); @@ -550,6 +583,33 @@ AsmStmt::AsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple, std::copy(clobbers, clobbers + NumClobbers, Clobbers); } +MSAsmStmt::MSAsmStmt(ASTContext &C, SourceLocation asmloc, + bool issimple, bool isvolatile, ArrayRef<Token> asmtoks, + ArrayRef<unsigned> lineends, StringRef asmstr, + ArrayRef<StringRef> clobbers, SourceLocation endloc) + : Stmt(MSAsmStmtClass), AsmLoc(asmloc), EndLoc(endloc), + AsmStr(asmstr.str()), IsSimple(issimple), IsVolatile(isvolatile), + NumAsmToks(asmtoks.size()), NumLineEnds(lineends.size()), + NumClobbers(clobbers.size()) { + + AsmToks = new (C) Token[NumAsmToks]; + for (unsigned i = 0, e = NumAsmToks; i != e; ++i) + AsmToks[i] = asmtoks[i]; + + LineEnds = new (C) unsigned[NumLineEnds]; + for (unsigned i = 0, e = NumLineEnds; i != e; ++i) + LineEnds[i] = lineends[i]; + + Clobbers = new (C) StringRef[NumClobbers]; + for (unsigned i = 0, e = NumClobbers; i != e; ++i) { + // FIXME: Avoid the allocation/copy if at all possible. + size_t size = clobbers[i].size(); + char *dest = new (C) char[size]; + std::strncpy(dest, clobbers[i].data(), size); + Clobbers[i] = StringRef(dest, size); + } +} + ObjCForCollectionStmt::ObjCForCollectionStmt(Stmt *Elem, Expr *Collect, Stmt *Body, SourceLocation FCL, SourceLocation RPL) @@ -571,31 +631,31 @@ ObjCAtTryStmt::ObjCAtTryStmt(SourceLocation atTryLoc, Stmt *atTryStmt, Stmts[0] = atTryStmt; for (unsigned I = 0; I != NumCatchStmts; ++I) Stmts[I + 1] = CatchStmts[I]; - + if (HasFinally) Stmts[NumCatchStmts + 1] = atFinallyStmt; } -ObjCAtTryStmt *ObjCAtTryStmt::Create(ASTContext &Context, - SourceLocation atTryLoc, +ObjCAtTryStmt *ObjCAtTryStmt::Create(ASTContext &Context, + SourceLocation atTryLoc, Stmt *atTryStmt, - Stmt **CatchStmts, + Stmt **CatchStmts, unsigned NumCatchStmts, Stmt *atFinallyStmt) { - unsigned Size = sizeof(ObjCAtTryStmt) + + unsigned Size = sizeof(ObjCAtTryStmt) + (1 + NumCatchStmts + (atFinallyStmt != 0)) * sizeof(Stmt *); void *Mem = Context.Allocate(Size, llvm::alignOf<ObjCAtTryStmt>()); return new (Mem) ObjCAtTryStmt(atTryLoc, atTryStmt, CatchStmts, NumCatchStmts, atFinallyStmt); } -ObjCAtTryStmt *ObjCAtTryStmt::CreateEmpty(ASTContext &Context, +ObjCAtTryStmt *ObjCAtTryStmt::CreateEmpty(ASTContext &Context, unsigned NumCatchStmts, bool HasFinally) { - unsigned Size = sizeof(ObjCAtTryStmt) + + unsigned Size = sizeof(ObjCAtTryStmt) + (1 + NumCatchStmts + HasFinally) * sizeof(Stmt *); void *Mem = Context.Allocate(Size, llvm::alignOf<ObjCAtTryStmt>()); - return new (Mem) ObjCAtTryStmt(EmptyShell(), NumCatchStmts, HasFinally); + return new (Mem) ObjCAtTryStmt(EmptyShell(), NumCatchStmts, HasFinally); } SourceRange ObjCAtTryStmt::getSourceRange() const { @@ -606,12 +666,12 @@ SourceRange ObjCAtTryStmt::getSourceRange() const { EndLoc = getCatchStmt(NumCatchStmts - 1)->getLocEnd(); else EndLoc = getTryBody()->getLocEnd(); - + return SourceRange(AtTryLoc, EndLoc); } CXXTryStmt *CXXTryStmt::Create(ASTContext &C, SourceLocation tryLoc, - Stmt *tryBlock, Stmt **handlers, + Stmt *tryBlock, Stmt **handlers, unsigned numHandlers) { std::size_t Size = sizeof(CXXTryStmt); Size += ((numHandlers + 1) * sizeof(Stmt)); @@ -671,20 +731,20 @@ const VarDecl *CXXForRangeStmt::getLoopVariable() const { return const_cast<CXXForRangeStmt*>(this)->getLoopVariable(); } -IfStmt::IfStmt(ASTContext &C, SourceLocation IL, VarDecl *var, Expr *cond, +IfStmt::IfStmt(ASTContext &C, SourceLocation IL, VarDecl *var, Expr *cond, Stmt *then, SourceLocation EL, Stmt *elsev) : Stmt(IfStmtClass), IfLoc(IL), ElseLoc(EL) { setConditionVariable(C, var); SubExprs[COND] = reinterpret_cast<Stmt*>(cond); SubExprs[THEN] = then; - SubExprs[ELSE] = elsev; + SubExprs[ELSE] = elsev; } VarDecl *IfStmt::getConditionVariable() const { if (!SubExprs[VAR]) return 0; - + DeclStmt *DS = cast<DeclStmt>(SubExprs[VAR]); return cast<VarDecl>(DS->getSingleDecl()); } @@ -694,16 +754,16 @@ void IfStmt::setConditionVariable(ASTContext &C, VarDecl *V) { SubExprs[VAR] = 0; return; } - + SourceRange VarRange = V->getSourceRange(); SubExprs[VAR] = new (C) DeclStmt(DeclGroupRef(V), VarRange.getBegin(), VarRange.getEnd()); } -ForStmt::ForStmt(ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar, - Expr *Inc, Stmt *Body, SourceLocation FL, SourceLocation LP, +ForStmt::ForStmt(ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar, + Expr *Inc, Stmt *Body, SourceLocation FL, SourceLocation LP, SourceLocation RP) - : Stmt(ForStmtClass), ForLoc(FL), LParenLoc(LP), RParenLoc(RP) + : Stmt(ForStmtClass), ForLoc(FL), LParenLoc(LP), RParenLoc(RP) { SubExprs[INIT] = Init; setConditionVariable(C, condVar); @@ -715,7 +775,7 @@ ForStmt::ForStmt(ASTContext &C, Stmt *Init, Expr *Cond, VarDecl *condVar, VarDecl *ForStmt::getConditionVariable() const { if (!SubExprs[CONDVAR]) return 0; - + DeclStmt *DS = cast<DeclStmt>(SubExprs[CONDVAR]); return cast<VarDecl>(DS->getSingleDecl()); } @@ -725,14 +785,14 @@ void ForStmt::setConditionVariable(ASTContext &C, VarDecl *V) { SubExprs[CONDVAR] = 0; return; } - + SourceRange VarRange = V->getSourceRange(); SubExprs[CONDVAR] = new (C) DeclStmt(DeclGroupRef(V), VarRange.getBegin(), VarRange.getEnd()); } -SwitchStmt::SwitchStmt(ASTContext &C, VarDecl *Var, Expr *cond) - : Stmt(SwitchStmtClass), FirstCase(0), AllEnumCasesCovered(0) +SwitchStmt::SwitchStmt(ASTContext &C, VarDecl *Var, Expr *cond) + : Stmt(SwitchStmtClass), FirstCase(0), AllEnumCasesCovered(0) { setConditionVariable(C, Var); SubExprs[COND] = reinterpret_cast<Stmt*>(cond); @@ -742,7 +802,7 @@ SwitchStmt::SwitchStmt(ASTContext &C, VarDecl *Var, Expr *cond) VarDecl *SwitchStmt::getConditionVariable() const { if (!SubExprs[VAR]) return 0; - + DeclStmt *DS = cast<DeclStmt>(SubExprs[VAR]); return cast<VarDecl>(DS->getSingleDecl()); } @@ -752,7 +812,7 @@ void SwitchStmt::setConditionVariable(ASTContext &C, VarDecl *V) { SubExprs[VAR] = 0; return; } - + SourceRange VarRange = V->getSourceRange(); SubExprs[VAR] = new (C) DeclStmt(DeclGroupRef(V), VarRange.getBegin(), VarRange.getEnd()); @@ -764,7 +824,7 @@ Stmt *SwitchCase::getSubStmt() { return cast<DefaultStmt>(this)->getSubStmt(); } -WhileStmt::WhileStmt(ASTContext &C, VarDecl *Var, Expr *cond, Stmt *body, +WhileStmt::WhileStmt(ASTContext &C, VarDecl *Var, Expr *cond, Stmt *body, SourceLocation WL) : Stmt(WhileStmtClass) { setConditionVariable(C, Var); @@ -776,7 +836,7 @@ WhileStmt::WhileStmt(ASTContext &C, VarDecl *Var, Expr *cond, Stmt *body, VarDecl *WhileStmt::getConditionVariable() const { if (!SubExprs[VAR]) return 0; - + DeclStmt *DS = cast<DeclStmt>(SubExprs[VAR]); return cast<VarDecl>(DS->getSingleDecl()); } diff --git a/lib/AST/StmtDumper.cpp b/lib/AST/StmtDumper.cpp index b5e298c0c94c..962e35269c18 100644 --- a/lib/AST/StmtDumper.cpp +++ b/lib/AST/StmtDumper.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/StmtVisitor.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/PrettyPrinter.h" @@ -166,6 +167,7 @@ namespace { void VisitObjCAtCatchStmt(ObjCAtCatchStmt *Node); void VisitObjCEncodeExpr(ObjCEncodeExpr *Node); void VisitObjCMessageExpr(ObjCMessageExpr* Node); + void VisitObjCBoxedExpr(ObjCBoxedExpr* Node); void VisitObjCSelectorExpr(ObjCSelectorExpr *Node); void VisitObjCProtocolExpr(ObjCProtocolExpr *Node); void VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *Node); @@ -423,6 +425,7 @@ void StmtDumper::VisitPredefinedExpr(PredefinedExpr *Node) { default: llvm_unreachable("unknown case"); case PredefinedExpr::Func: OS << " __func__"; break; case PredefinedExpr::Function: OS << " __FUNCTION__"; break; + case PredefinedExpr::LFunction: OS << " L__FUNCTION__"; break; case PredefinedExpr::PrettyFunction: OS << " __PRETTY_FUNCTION__";break; } } @@ -445,18 +448,8 @@ void StmtDumper::VisitFloatingLiteral(FloatingLiteral *Node) { void StmtDumper::VisitStringLiteral(StringLiteral *Str) { DumpExpr(Str); - // FIXME: this doesn't print wstrings right. OS << " "; - switch (Str->getKind()) { - case StringLiteral::Ascii: break; // No prefix - case StringLiteral::Wide: OS << 'L'; break; - case StringLiteral::UTF8: OS << "u8"; break; - case StringLiteral::UTF16: OS << 'u'; break; - case StringLiteral::UTF32: OS << 'U'; break; - } - OS << '"'; - OS.write_escaped(Str->getString()); - OS << '"'; + Str->outputString(OS); } void StmtDumper::VisitUnaryOperator(UnaryOperator *Node) { @@ -471,7 +464,7 @@ void StmtDumper::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) { OS << " sizeof "; break; case UETT_AlignOf: - OS << " __alignof "; + OS << " alignof "; break; case UETT_VecStep: OS << " vec_step "; @@ -637,6 +630,11 @@ void StmtDumper::VisitObjCMessageExpr(ObjCMessageExpr* Node) { } } +void StmtDumper::VisitObjCBoxedExpr(ObjCBoxedExpr* Node) { + DumpExpr(Node); + OS << " selector=" << Node->getBoxingMethod()->getSelector().getAsString(); +} + void StmtDumper::VisitObjCAtCatchStmt(ObjCAtCatchStmt *Node) { DumpStmt(Node); if (VarDecl *CatchParam = Node->getCatchParamDecl()) { diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 0d1066b7e322..2f7cb55c7cc1 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -172,9 +173,9 @@ void StmtPrinter::VisitLabelStmt(LabelStmt *Node) { void StmtPrinter::VisitAttributedStmt(AttributedStmt *Node) { OS << "[["; bool first = true; - for (AttrVec::const_iterator it = Node->getAttrs().begin(), - end = Node->getAttrs().end(); - it != end; ++it) { + for (ArrayRef<const Attr*>::iterator it = Node->getAttrs().begin(), + end = Node->getAttrs().end(); + it != end; ++it) { if (!first) { OS << ", "; first = false; @@ -429,6 +430,11 @@ void StmtPrinter::VisitAsmStmt(AsmStmt *Node) { OS << ");\n"; } +void StmtPrinter::VisitMSAsmStmt(MSAsmStmt *Node) { + // FIXME: Implement MS style inline asm statement printer. + Indent() << "asm ()"; +} + void StmtPrinter::VisitObjCAtTryStmt(ObjCAtTryStmt *Node) { Indent() << "@try"; if (CompoundStmt *TS = dyn_cast<CompoundStmt>(Node->getTryBody())) { @@ -638,6 +644,9 @@ void StmtPrinter::VisitPredefinedExpr(PredefinedExpr *Node) { case PredefinedExpr::Function: OS << "__FUNCTION__"; break; + case PredefinedExpr::LFunction: + OS << "L__FUNCTION__"; + break; case PredefinedExpr::PrettyFunction: OS << "__PRETTY_FUNCTION__"; break; @@ -734,93 +743,7 @@ void StmtPrinter::VisitImaginaryLiteral(ImaginaryLiteral *Node) { } void StmtPrinter::VisitStringLiteral(StringLiteral *Str) { - switch (Str->getKind()) { - case StringLiteral::Ascii: break; // no prefix. - case StringLiteral::Wide: OS << 'L'; break; - case StringLiteral::UTF8: OS << "u8"; break; - case StringLiteral::UTF16: OS << 'u'; break; - case StringLiteral::UTF32: OS << 'U'; break; - } - OS << '"'; - static char Hex[] = "0123456789ABCDEF"; - - unsigned LastSlashX = Str->getLength(); - for (unsigned I = 0, N = Str->getLength(); I != N; ++I) { - switch (uint32_t Char = Str->getCodeUnit(I)) { - default: - // FIXME: Convert UTF-8 back to codepoints before rendering. - - // Convert UTF-16 surrogate pairs back to codepoints before rendering. - // Leave invalid surrogates alone; we'll use \x for those. - if (Str->getKind() == StringLiteral::UTF16 && I != N - 1 && - Char >= 0xd800 && Char <= 0xdbff) { - uint32_t Trail = Str->getCodeUnit(I + 1); - if (Trail >= 0xdc00 && Trail <= 0xdfff) { - Char = 0x10000 + ((Char - 0xd800) << 10) + (Trail - 0xdc00); - ++I; - } - } - - if (Char > 0xff) { - // If this is a wide string, output characters over 0xff using \x - // escapes. Otherwise, this is a UTF-16 or UTF-32 string, and Char is a - // codepoint: use \x escapes for invalid codepoints. - if (Str->getKind() == StringLiteral::Wide || - (Char >= 0xd800 && Char <= 0xdfff) || Char >= 0x110000) { - // FIXME: Is this the best way to print wchar_t? - OS << "\\x"; - int Shift = 28; - while ((Char >> Shift) == 0) - Shift -= 4; - for (/**/; Shift >= 0; Shift -= 4) - OS << Hex[(Char >> Shift) & 15]; - LastSlashX = I; - break; - } - - if (Char > 0xffff) - OS << "\\U00" - << Hex[(Char >> 20) & 15] - << Hex[(Char >> 16) & 15]; - else - OS << "\\u"; - OS << Hex[(Char >> 12) & 15] - << Hex[(Char >> 8) & 15] - << Hex[(Char >> 4) & 15] - << Hex[(Char >> 0) & 15]; - break; - } - - // If we used \x... for the previous character, and this character is a - // hexadecimal digit, prevent it being slurped as part of the \x. - if (LastSlashX + 1 == I) { - switch (Char) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': - OS << "\"\""; - } - } - - if (Char <= 0xff && isprint(Char)) - OS << (char)Char; - else // Output anything hard as an octal escape. - OS << '\\' - << (char)('0' + ((Char >> 6) & 7)) - << (char)('0' + ((Char >> 3) & 7)) - << (char)('0' + ((Char >> 0) & 7)); - break; - // Handle some common non-printable cases to make dumps prettier. - case '\\': OS << "\\\\"; break; - case '"': OS << "\\\""; break; - case '\n': OS << "\\n"; break; - case '\t': OS << "\\t"; break; - case '\a': OS << "\\a"; break; - case '\b': OS << "\\b"; break; - } - } - OS << '"'; + Str->outputString(OS); } void StmtPrinter::VisitParenExpr(ParenExpr *Node) { OS << "("; @@ -892,7 +815,12 @@ void StmtPrinter::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node){ OS << "sizeof"; break; case UETT_AlignOf: - OS << "__alignof"; + if (Policy.LangOpts.CPlusPlus) + OS << "alignof"; + else if (Policy.LangOpts.C11) + OS << "_Alignof"; + else + OS << "__alignof"; break; case UETT_VecStep: OS << "vec_step"; @@ -1275,7 +1203,7 @@ void StmtPrinter::VisitUserDefinedLiteral(UserDefinedLiteral *Node) { const TemplateArgument &Pack = Args->get(0); for (TemplateArgument::pack_iterator I = Pack.pack_begin(), E = Pack.pack_end(); I != E; ++I) { - char C = (char)I->getAsIntegral()->getZExtValue(); + char C = (char)I->getAsIntegral().getZExtValue(); OS << C; } break; @@ -1727,9 +1655,9 @@ void StmtPrinter::VisitObjCStringLiteral(ObjCStringLiteral *Node) { VisitStringLiteral(Node->getString()); } -void StmtPrinter::VisitObjCNumericLiteral(ObjCNumericLiteral *E) { +void StmtPrinter::VisitObjCBoxedExpr(ObjCBoxedExpr *E) { OS << "@"; - Visit(E->getNumber()); + Visit(E->getSubExpr()); } void StmtPrinter::VisitObjCArrayLiteral(ObjCArrayLiteral *E) { diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index e50523ae860b..2168b64d5b23 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -178,6 +178,11 @@ void StmtProfiler::VisitAsmStmt(const AsmStmt *S) { VisitStringLiteral(S->getClobber(I)); } +void StmtProfiler::VisitMSAsmStmt(const MSAsmStmt *S) { + // FIXME: Implement MS style inline asm statement profiler. + VisitStmt(S); +} + void StmtProfiler::VisitCXXCatchStmt(const CXXCatchStmt *S) { VisitStmt(S); VisitType(S->getCaughtType()); @@ -981,7 +986,7 @@ void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) { VisitExpr(S); } -void StmtProfiler::VisitObjCNumericLiteral(const ObjCNumericLiteral *E) { +void StmtProfiler::VisitObjCBoxedExpr(const ObjCBoxedExpr *E) { VisitExpr(E); } @@ -1161,7 +1166,7 @@ void StmtProfiler::VisitTemplateArgument(const TemplateArgument &Arg) { break; case TemplateArgument::Integral: - Arg.getAsIntegral()->Profile(ID); + Arg.getAsIntegral().Profile(ID); VisitType(Arg.getIntegralType()); break; diff --git a/lib/AST/TemplateBase.cpp b/lib/AST/TemplateBase.cpp index 531e03e302b2..f8dd396d92df 100644 --- a/lib/AST/TemplateBase.cpp +++ b/lib/AST/TemplateBase.cpp @@ -36,17 +36,17 @@ using namespace clang; static void printIntegral(const TemplateArgument &TemplArg, raw_ostream &Out) { const ::clang::Type *T = TemplArg.getIntegralType().getTypePtr(); - const llvm::APSInt *Val = TemplArg.getAsIntegral(); + const llvm::APSInt &Val = TemplArg.getAsIntegral(); if (T->isBooleanType()) { - Out << (Val->getBoolValue() ? "true" : "false"); + Out << (Val.getBoolValue() ? "true" : "false"); } else if (T->isCharType()) { - const char Ch = Val->getZExtValue(); + const char Ch = Val.getZExtValue(); Out << ((Ch == '\'') ? "'\\" : "'"); Out.write_escaped(StringRef(&Ch, 1), /*UseHexEscapes=*/ true); Out << "'"; } else { - Out << Val->toString(10); + Out << Val; } } @@ -54,6 +54,25 @@ static void printIntegral(const TemplateArgument &TemplArg, // TemplateArgument Implementation //===----------------------------------------------------------------------===// +TemplateArgument::TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, + QualType Type) + : Kind(Integral) { + // Copy the APSInt value into our decomposed form. + Integer.BitWidth = Value.getBitWidth(); + Integer.IsUnsigned = Value.isUnsigned(); + // If the value is large, we have to get additional memory from the ASTContext + unsigned NumWords = Value.getNumWords(); + if (NumWords > 1) { + void *Mem = Ctx.Allocate(NumWords * sizeof(uint64_t)); + std::memcpy(Mem, Value.getRawData(), NumWords * sizeof(uint64_t)); + Integer.pVal = static_cast<uint64_t *>(Mem); + } else { + Integer.VAL = Value.getZExtValue(); + } + + Integer.Type = Type.getAsOpaquePtr(); +} + TemplateArgument TemplateArgument::CreatePackCopy(ASTContext &Context, const TemplateArgument *Args, unsigned NumArgs) { @@ -246,7 +265,7 @@ void TemplateArgument::Profile(llvm::FoldingSetNodeID &ID, } case Integral: - getAsIntegral()->Profile(ID); + getAsIntegral().Profile(ID); getIntegralType().Profile(ID); break; @@ -275,7 +294,7 @@ bool TemplateArgument::structurallyEquals(const TemplateArgument &Other) const { case Integral: return getIntegralType() == Other.getIntegralType() && - *getAsIntegral() == *Other.getAsIntegral(); + getAsIntegral() == Other.getAsIntegral(); case Pack: if (Args.NumArgs != Other.Args.NumArgs) return false; @@ -498,7 +517,7 @@ const DiagnosticBuilder &clang::operator<<(const DiagnosticBuilder &DB, return DB << "nullptr"; case TemplateArgument::Integral: - return DB << Arg.getAsIntegral()->toString(10); + return DB << Arg.getAsIntegral().toString(10); case TemplateArgument::Template: return DB << Arg.getAsTemplate(); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 3f6a09457d1e..abefae44b2ba 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -288,6 +288,28 @@ QualType QualType::IgnoreParens(QualType T) { return T; } +/// \brief This will check for a TypedefType by removing any existing sugar +/// until it reaches a TypedefType or a non-sugared type. +template <> const TypedefType *Type::getAs() const { + const Type *Cur = this; + + while (true) { + if (const TypedefType *TDT = dyn_cast<TypedefType>(Cur)) + return TDT; + switch (Cur->getTypeClass()) { +#define ABSTRACT_TYPE(Class, Parent) +#define TYPE(Class, Parent) \ + case Class: { \ + const Class##Type *Ty = cast<Class##Type>(Cur); \ + if (!Ty->isSugared()) return 0; \ + Cur = Ty->desugar().getTypePtr(); \ + break; \ + } +#include "clang/AST/TypeNodes.def" + } + } +} + /// getUnqualifiedDesugaredType - Pull any qualifiers and syntactic /// sugar off the given type. This should produce an object of the /// same dynamic type as the canonical type. @@ -895,6 +917,14 @@ bool Type::isIncompleteType(NamedDecl **Def) const { } bool QualType::isPODType(ASTContext &Context) const { + // C++11 has a more relaxed definition of POD. + if (Context.getLangOpts().CPlusPlus0x) + return isCXX11PODType(Context); + + return isCXX98PODType(Context); +} + +bool QualType::isCXX98PODType(ASTContext &Context) const { // The compiler shouldn't query this for incomplete types, but the user might. // We return false for that case. Except for incomplete arrays of PODs, which // are PODs according to the standard. @@ -902,7 +932,7 @@ bool QualType::isPODType(ASTContext &Context) const { return 0; if ((*this)->isIncompleteArrayType()) - return Context.getBaseElementType(*this).isPODType(Context); + return Context.getBaseElementType(*this).isCXX98PODType(Context); if ((*this)->isIncompleteType()) return false; @@ -929,7 +959,7 @@ bool QualType::isPODType(ASTContext &Context) const { case Type::VariableArray: case Type::ConstantArray: // IncompleteArray is handled above. - return Context.getBaseElementType(*this).isPODType(Context); + return Context.getBaseElementType(*this).isCXX98PODType(Context); case Type::ObjCObjectPointer: case Type::BlockPointer: @@ -1417,7 +1447,7 @@ const char *Type::getTypeClassName() const { llvm_unreachable("Invalid type class."); } -const char *BuiltinType::getName(const PrintingPolicy &Policy) const { +StringRef BuiltinType::getName(const PrintingPolicy &Policy) const { switch (getKind()) { case Void: return "void"; case Bool: return Policy.Bool ? "bool" : "_Bool"; @@ -1554,6 +1584,11 @@ FunctionProtoType::FunctionProtoType(QualType result, const QualType *args, slot[1] = epi.ExceptionSpecTemplate; // This exception specification doesn't make the type dependent, because // it's not instantiated as part of instantiating the type. + } else if (getExceptionSpecType() == EST_Unevaluated) { + // Store the function decl from which we will resolve our + // exception specification. + FunctionDecl **slot = reinterpret_cast<FunctionDecl**>(argSlot + numArgs); + slot[0] = epi.ExceptionSpecDecl; } if (epi.ConsumedArguments) { @@ -1637,7 +1672,8 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, ID.AddPointer(epi.Exceptions[i].getAsOpaquePtr()); } else if (epi.ExceptionSpecType == EST_ComputedNoexcept && epi.NoexceptExpr){ epi.NoexceptExpr->Profile(ID, Context, false); - } else if (epi.ExceptionSpecType == EST_Uninstantiated) { + } else if (epi.ExceptionSpecType == EST_Uninstantiated || + epi.ExceptionSpecType == EST_Unevaluated) { ID.AddPointer(epi.ExceptionSpecDecl->getCanonicalDecl()); } if (epi.ConsumedArguments) { @@ -1832,8 +1868,7 @@ TemplateSpecializationType(TemplateName T, Canon.isNull()? T.isDependent() : Canon->isInstantiationDependentType(), false, - Canon.isNull()? T.containsUnexpandedParameterPack() - : Canon->containsUnexpandedParameterPack()), + T.containsUnexpandedParameterPack()), Template(T), NumArgs(NumArgs), TypeAlias(!AliasedType.isNull()) { assert(!T.getAsDependentTemplateName() && "Use DependentTemplateSpecializationType for dependent template-name"); @@ -1858,6 +1893,8 @@ TemplateSpecializationType(TemplateName T, // arguments is. Given: // template<typename T> using U = int; // U<T> is always non-dependent, irrespective of the type T. + // However, U<Ts> contains an unexpanded parameter pack, even though + // its expansion (and thus its desugared type) doesn't. if (Canon.isNull() && Args[Arg].isDependent()) setDependent(); else if (Args[Arg].isInstantiationDependent()) @@ -1866,7 +1903,7 @@ TemplateSpecializationType(TemplateName T, if (Args[Arg].getKind() == TemplateArgument::Type && Args[Arg].getAsType()->isVariablyModifiedType()) setVariablyModified(); - if (Canon.isNull() && Args[Arg].containsUnexpandedParameterPack()) + if (Args[Arg].containsUnexpandedParameterPack()) setContainsUnexpandedParameterPack(); new (&TemplateArgs[Arg]) TemplateArgument(Args[Arg]); diff --git a/lib/AST/TypeLoc.cpp b/lib/AST/TypeLoc.cpp index caa19b19df54..c7bb7da4d17b 100644 --- a/lib/AST/TypeLoc.cpp +++ b/lib/AST/TypeLoc.cpp @@ -13,6 +13,7 @@ #include "llvm/Support/raw_ostream.h" #include "clang/AST/TypeLocVisitor.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "llvm/Support/ErrorHandling.h" using namespace clang; diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 3bf80e797244..c42117c82bf5 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" @@ -19,8 +20,10 @@ #include "clang/AST/PrettyPrinter.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/SaveAndRestore.h" using namespace clang; namespace { @@ -40,62 +43,124 @@ namespace { Policy.SuppressStrongLifetime = Old; } }; + + class ParamPolicyRAII { + PrintingPolicy &Policy; + bool Old; + + public: + explicit ParamPolicyRAII(PrintingPolicy &Policy) + : Policy(Policy), Old(Policy.SuppressSpecifiers) { + Policy.SuppressSpecifiers = false; + } + + ~ParamPolicyRAII() { + Policy.SuppressSpecifiers = Old; + } + }; + + class ElaboratedTypePolicyRAII { + PrintingPolicy &Policy; + bool SuppressTagKeyword; + bool SuppressScope; + + public: + explicit ElaboratedTypePolicyRAII(PrintingPolicy &Policy) : Policy(Policy) { + SuppressTagKeyword = Policy.SuppressTagKeyword; + SuppressScope = Policy.SuppressScope; + Policy.SuppressTagKeyword = true; + Policy.SuppressScope = true; + } + + ~ElaboratedTypePolicyRAII() { + Policy.SuppressTagKeyword = SuppressTagKeyword; + Policy.SuppressScope = SuppressScope; + } + }; class TypePrinter { PrintingPolicy Policy; + bool HasEmptyPlaceHolder; public: - explicit TypePrinter(const PrintingPolicy &Policy) : Policy(Policy) { } - - void print(const Type *ty, Qualifiers qs, std::string &buffer); - void print(QualType T, std::string &S); - void AppendScope(DeclContext *DC, std::string &S); - void printTag(TagDecl *T, std::string &S); + explicit TypePrinter(const PrintingPolicy &Policy) + : Policy(Policy), HasEmptyPlaceHolder(false) { } + + void print(const Type *ty, Qualifiers qs, raw_ostream &OS, + StringRef PlaceHolder); + void print(QualType T, raw_ostream &OS, StringRef PlaceHolder); + + static bool canPrefixQualifiers(const Type *T, bool &NeedARCStrongQualifier); + void spaceBeforePlaceHolder(raw_ostream &OS); + void printTypeSpec(const NamedDecl *D, raw_ostream &OS); + + void printBefore(const Type *ty, Qualifiers qs, raw_ostream &OS); + void printBefore(QualType T, raw_ostream &OS); + void printAfter(const Type *ty, Qualifiers qs, raw_ostream &OS); + void printAfter(QualType T, raw_ostream &OS); + void AppendScope(DeclContext *DC, raw_ostream &OS); + void printTag(TagDecl *T, raw_ostream &OS); #define ABSTRACT_TYPE(CLASS, PARENT) #define TYPE(CLASS, PARENT) \ - void print##CLASS(const CLASS##Type *T, std::string &S); + 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" }; } -static void AppendTypeQualList(std::string &S, unsigned TypeQuals) { +static void AppendTypeQualList(raw_ostream &OS, unsigned TypeQuals) { + bool appendSpace = false; if (TypeQuals & Qualifiers::Const) { - if (!S.empty()) S += ' '; - S += "const"; + OS << "const"; + appendSpace = true; } if (TypeQuals & Qualifiers::Volatile) { - if (!S.empty()) S += ' '; - S += "volatile"; + if (appendSpace) OS << ' '; + OS << "volatile"; + appendSpace = true; } if (TypeQuals & Qualifiers::Restrict) { - if (!S.empty()) S += ' '; - S += "restrict"; + if (appendSpace) OS << ' '; + OS << "restrict"; } } -void TypePrinter::print(QualType t, std::string &buffer) { +void TypePrinter::spaceBeforePlaceHolder(raw_ostream &OS) { + if (!HasEmptyPlaceHolder) + OS << ' '; +} + +void TypePrinter::print(QualType t, raw_ostream &OS, StringRef PlaceHolder) { SplitQualType split = t.split(); - print(split.Ty, split.Quals, buffer); + print(split.Ty, split.Quals, OS, PlaceHolder); } -void TypePrinter::print(const Type *T, Qualifiers Quals, std::string &buffer) { +void TypePrinter::print(const Type *T, Qualifiers Quals, raw_ostream &OS, + StringRef PlaceHolder) { if (!T) { - buffer += "NULL TYPE"; + OS << "NULL TYPE"; return; } if (Policy.SuppressSpecifiers && T->isSpecifierType()) return; - - // Print qualifiers as appropriate. - + + SaveAndRestore<bool> PHVal(HasEmptyPlaceHolder, PlaceHolder.empty()); + + printBefore(T, Quals, OS); + OS << PlaceHolder; + printAfter(T, Quals, OS); +} + +bool TypePrinter::canPrefixQualifiers(const Type *T, + bool &NeedARCStrongQualifier) { // CanPrefixQualifiers - We prefer to print type qualifiers before the type, // so that we get "const int" instead of "int const", but we can't do this if // the type is complex. For example if the type is "int*", we *must* print // "int * const", printing "const int *" is different. Only do this when the // type expands to a simple string. bool CanPrefixQualifiers = false; - bool NeedARCStrongQualifier = false; + NeedARCStrongQualifier = false; Type::TypeClass TC = T->getTypeClass(); if (const AutoType *AT = dyn_cast<AutoType>(T)) TC = AT->desugar()->getTypeClass(); @@ -157,493 +222,616 @@ void TypePrinter::print(const Type *T, Qualifiers Quals, std::string &buffer) { CanPrefixQualifiers = false; break; } - - if (!CanPrefixQualifiers && !Quals.empty()) { - std::string qualsBuffer; + + return CanPrefixQualifiers; +} + +void TypePrinter::printBefore(QualType T, raw_ostream &OS) { + SplitQualType Split = T.split(); + + // If we have cv1 T, where T is substituted for cv2 U, only print cv1 - cv2 + // at this level. + Qualifiers Quals = Split.Quals; + if (const SubstTemplateTypeParmType *Subst = + dyn_cast<SubstTemplateTypeParmType>(Split.Ty)) + Quals -= QualType(Subst, 0).getQualifiers(); + + printBefore(Split.Ty, Quals, OS); +} + +/// \brief Prints the part of the type string before an identifier, e.g. for +/// "int foo[10]" it prints "int ". +void TypePrinter::printBefore(const Type *T,Qualifiers Quals, raw_ostream &OS) { + if (Policy.SuppressSpecifiers && T->isSpecifierType()) + return; + + SaveAndRestore<bool> PrevPHIsEmpty(HasEmptyPlaceHolder); + + // Print qualifiers as appropriate. + + bool CanPrefixQualifiers = false; + bool NeedARCStrongQualifier = false; + CanPrefixQualifiers = canPrefixQualifiers(T, NeedARCStrongQualifier); + + if (CanPrefixQualifiers && !Quals.empty()) { if (NeedARCStrongQualifier) { IncludeStrongLifetimeRAII Strong(Policy); - Quals.getAsStringInternal(qualsBuffer, Policy); + Quals.print(OS, Policy, /*appendSpaceIfNonEmpty=*/true); } else { - Quals.getAsStringInternal(qualsBuffer, Policy); - } - - if (!qualsBuffer.empty()) { - if (!buffer.empty()) { - qualsBuffer += ' '; - qualsBuffer += buffer; - } - std::swap(buffer, qualsBuffer); + Quals.print(OS, Policy, /*appendSpaceIfNonEmpty=*/true); } } - + + bool hasAfterQuals = false; + if (!CanPrefixQualifiers && !Quals.empty()) { + hasAfterQuals = !Quals.isEmptyWhenPrinted(Policy); + if (hasAfterQuals) + HasEmptyPlaceHolder = false; + } + switch (T->getTypeClass()) { #define ABSTRACT_TYPE(CLASS, PARENT) #define TYPE(CLASS, PARENT) case Type::CLASS: \ - print##CLASS(cast<CLASS##Type>(T), buffer); \ + print##CLASS##Before(cast<CLASS##Type>(T), OS); \ break; #include "clang/AST/TypeNodes.def" } - - // If we're adding the qualifiers as a prefix, do it now. - if (CanPrefixQualifiers && !Quals.empty()) { - std::string qualsBuffer; + + if (hasAfterQuals) { if (NeedARCStrongQualifier) { IncludeStrongLifetimeRAII Strong(Policy); - Quals.getAsStringInternal(qualsBuffer, Policy); + Quals.print(OS, Policy, /*appendSpaceIfNonEmpty=*/!PrevPHIsEmpty.get()); } else { - Quals.getAsStringInternal(qualsBuffer, Policy); - } - - if (!qualsBuffer.empty()) { - if (!buffer.empty()) { - qualsBuffer += ' '; - qualsBuffer += buffer; - } - std::swap(buffer, qualsBuffer); + Quals.print(OS, Policy, /*appendSpaceIfNonEmpty=*/!PrevPHIsEmpty.get()); } } } -void TypePrinter::printBuiltin(const BuiltinType *T, std::string &S) { - if (S.empty()) { - S = T->getName(Policy); - } else { - // Prefix the basic type, e.g. 'int X'. - S = ' ' + S; - S = T->getName(Policy) + S; +void TypePrinter::printAfter(QualType t, raw_ostream &OS) { + SplitQualType split = t.split(); + printAfter(split.Ty, split.Quals, OS); +} + +/// \brief Prints the part of the type string after an identifier, e.g. for +/// "int foo[10]" it prints "[10]". +void TypePrinter::printAfter(const Type *T, Qualifiers Quals, raw_ostream &OS) { + switch (T->getTypeClass()) { +#define ABSTRACT_TYPE(CLASS, PARENT) +#define TYPE(CLASS, PARENT) case Type::CLASS: \ + print##CLASS##After(cast<CLASS##Type>(T), OS); \ + break; +#include "clang/AST/TypeNodes.def" } } -void TypePrinter::printComplex(const ComplexType *T, std::string &S) { - print(T->getElementType(), S); - S = "_Complex " + S; +void TypePrinter::printBuiltinBefore(const BuiltinType *T, raw_ostream &OS) { + OS << T->getName(Policy); + spaceBeforePlaceHolder(OS); } +void TypePrinter::printBuiltinAfter(const BuiltinType *T, raw_ostream &OS) { } -void TypePrinter::printPointer(const PointerType *T, std::string &S) { - S = '*' + S; - +void TypePrinter::printComplexBefore(const ComplexType *T, raw_ostream &OS) { + OS << "_Complex "; + printBefore(T->getElementType(), OS); +} +void TypePrinter::printComplexAfter(const ComplexType *T, raw_ostream &OS) { + printAfter(T->getElementType(), OS); +} + +void TypePrinter::printPointerBefore(const PointerType *T, raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getPointeeType(), OS); // Handle things like 'int (*A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. if (isa<ArrayType>(T->getPointeeType())) - S = '(' + S + ')'; - + OS << '('; + OS << '*'; +} +void TypePrinter::printPointerAfter(const PointerType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getPointeeType(), S); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + // Handle things like 'int (*A)[4];' correctly. + // FIXME: this should include vectors, but vectors use attributes I guess. + if (isa<ArrayType>(T->getPointeeType())) + OS << ')'; + printAfter(T->getPointeeType(), OS); } -void TypePrinter::printBlockPointer(const BlockPointerType *T, std::string &S) { - S = '^' + S; - print(T->getPointeeType(), S); +void TypePrinter::printBlockPointerBefore(const BlockPointerType *T, + raw_ostream &OS) { + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getPointeeType(), OS); + OS << '^'; +} +void TypePrinter::printBlockPointerAfter(const BlockPointerType *T, + raw_ostream &OS) { + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printAfter(T->getPointeeType(), OS); } -void TypePrinter::printLValueReference(const LValueReferenceType *T, - std::string &S) { - S = '&' + S; - +void TypePrinter::printLValueReferenceBefore(const LValueReferenceType *T, + raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getPointeeTypeAsWritten(), OS); // Handle things like 'int (&A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. if (isa<ArrayType>(T->getPointeeTypeAsWritten())) - S = '(' + S + ')'; - + OS << '('; + OS << '&'; +} +void TypePrinter::printLValueReferenceAfter(const LValueReferenceType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getPointeeTypeAsWritten(), S); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + // Handle things like 'int (&A)[4];' correctly. + // FIXME: this should include vectors, but vectors use attributes I guess. + if (isa<ArrayType>(T->getPointeeTypeAsWritten())) + OS << ')'; + printAfter(T->getPointeeTypeAsWritten(), OS); } -void TypePrinter::printRValueReference(const RValueReferenceType *T, - std::string &S) { - S = "&&" + S; - +void TypePrinter::printRValueReferenceBefore(const RValueReferenceType *T, + raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getPointeeTypeAsWritten(), OS); // Handle things like 'int (&&A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. if (isa<ArrayType>(T->getPointeeTypeAsWritten())) - S = '(' + S + ')'; - + OS << '('; + OS << "&&"; +} +void TypePrinter::printRValueReferenceAfter(const RValueReferenceType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getPointeeTypeAsWritten(), S); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + // Handle things like 'int (&&A)[4];' correctly. + // FIXME: this should include vectors, but vectors use attributes I guess. + if (isa<ArrayType>(T->getPointeeTypeAsWritten())) + OS << ')'; + printAfter(T->getPointeeTypeAsWritten(), OS); } -void TypePrinter::printMemberPointer(const MemberPointerType *T, - std::string &S) { - PrintingPolicy InnerPolicy(Policy); - Policy.SuppressTag = true; - std::string C = QualType(T->getClass(), 0).getAsString(InnerPolicy); - C += "::*"; - S = C + S; - +void TypePrinter::printMemberPointerBefore(const MemberPointerType *T, + raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getPointeeType(), OS); // Handle things like 'int (Cls::*A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. if (isa<ArrayType>(T->getPointeeType())) - S = '(' + S + ')'; - + OS << '('; + + PrintingPolicy InnerPolicy(Policy); + InnerPolicy.SuppressTag = false; + TypePrinter(InnerPolicy).print(QualType(T->getClass(), 0), OS, StringRef()); + + OS << "::*"; +} +void TypePrinter::printMemberPointerAfter(const MemberPointerType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getPointeeType(), S); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + // Handle things like 'int (Cls::*A)[4];' correctly. + // FIXME: this should include vectors, but vectors use attributes I guess. + if (isa<ArrayType>(T->getPointeeType())) + OS << ')'; + printAfter(T->getPointeeType(), OS); } -void TypePrinter::printConstantArray(const ConstantArrayType *T, - std::string &S) { - S += '['; - S += llvm::utostr(T->getSize().getZExtValue()); - S += ']'; - +void TypePrinter::printConstantArrayBefore(const ConstantArrayType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getElementType(), S); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getElementType(), OS); +} +void TypePrinter::printConstantArrayAfter(const ConstantArrayType *T, + raw_ostream &OS) { + OS << '[' << T->getSize().getZExtValue() << ']'; + printAfter(T->getElementType(), OS); } -void TypePrinter::printIncompleteArray(const IncompleteArrayType *T, - std::string &S) { - S += "[]"; +void TypePrinter::printIncompleteArrayBefore(const IncompleteArrayType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getElementType(), S); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getElementType(), OS); +} +void TypePrinter::printIncompleteArrayAfter(const IncompleteArrayType *T, + raw_ostream &OS) { + OS << "[]"; + printAfter(T->getElementType(), OS); } -void TypePrinter::printVariableArray(const VariableArrayType *T, - std::string &S) { - S += '['; - +void TypePrinter::printVariableArrayBefore(const VariableArrayType *T, + raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getElementType(), OS); +} +void TypePrinter::printVariableArrayAfter(const VariableArrayType *T, + raw_ostream &OS) { + OS << '['; if (T->getIndexTypeQualifiers().hasQualifiers()) { - AppendTypeQualList(S, T->getIndexTypeCVRQualifiers()); - S += ' '; + AppendTypeQualList(OS, T->getIndexTypeCVRQualifiers()); + OS << ' '; } - + if (T->getSizeModifier() == VariableArrayType::Static) - S += "static"; + OS << "static"; else if (T->getSizeModifier() == VariableArrayType::Star) - S += '*'; - - if (T->getSizeExpr()) { - std::string SStr; - llvm::raw_string_ostream s(SStr); - T->getSizeExpr()->printPretty(s, 0, Policy); - S += s.str(); - } - S += ']'; - - IncludeStrongLifetimeRAII Strong(Policy); - print(T->getElementType(), S); + OS << '*'; + + if (T->getSizeExpr()) + T->getSizeExpr()->printPretty(OS, 0, Policy); + OS << ']'; + + printAfter(T->getElementType(), OS); } -void TypePrinter::printDependentSizedArray(const DependentSizedArrayType *T, - std::string &S) { - S += '['; - - if (T->getSizeExpr()) { - std::string SStr; - llvm::raw_string_ostream s(SStr); - T->getSizeExpr()->printPretty(s, 0, Policy); - S += s.str(); - } - S += ']'; - +void TypePrinter::printDependentSizedArrayBefore( + const DependentSizedArrayType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getElementType(), S); + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + printBefore(T->getElementType(), OS); +} +void TypePrinter::printDependentSizedArrayAfter( + const DependentSizedArrayType *T, + raw_ostream &OS) { + OS << '['; + if (T->getSizeExpr()) + T->getSizeExpr()->printPretty(OS, 0, Policy); + OS << ']'; + printAfter(T->getElementType(), OS); } -void TypePrinter::printDependentSizedExtVector( +void TypePrinter::printDependentSizedExtVectorBefore( const DependentSizedExtVectorType *T, - std::string &S) { - print(T->getElementType(), S); - - S += " __attribute__((ext_vector_type("; - if (T->getSizeExpr()) { - std::string SStr; - llvm::raw_string_ostream s(SStr); - T->getSizeExpr()->printPretty(s, 0, Policy); - S += s.str(); - } - S += ")))"; + raw_ostream &OS) { + printBefore(T->getElementType(), OS); +} +void TypePrinter::printDependentSizedExtVectorAfter( + const DependentSizedExtVectorType *T, + raw_ostream &OS) { + OS << " __attribute__((ext_vector_type("; + if (T->getSizeExpr()) + T->getSizeExpr()->printPretty(OS, 0, Policy); + OS << ")))"; + printAfter(T->getElementType(), OS); } -void TypePrinter::printVector(const VectorType *T, std::string &S) { +void TypePrinter::printVectorBefore(const VectorType *T, raw_ostream &OS) { switch (T->getVectorKind()) { case VectorType::AltiVecPixel: - S = "__vector __pixel " + S; + OS << "__vector __pixel "; break; case VectorType::AltiVecBool: - print(T->getElementType(), S); - S = "__vector __bool " + S; + OS << "__vector __bool "; + printBefore(T->getElementType(), OS); break; case VectorType::AltiVecVector: - print(T->getElementType(), S); - S = "__vector " + S; + OS << "__vector "; + printBefore(T->getElementType(), OS); break; case VectorType::NeonVector: - print(T->getElementType(), S); - S = ("__attribute__((neon_vector_type(" + - llvm::utostr_32(T->getNumElements()) + "))) " + S); + OS << "__attribute__((neon_vector_type(" + << T->getNumElements() << "))) "; + printBefore(T->getElementType(), OS); break; case VectorType::NeonPolyVector: - print(T->getElementType(), S); - S = ("__attribute__((neon_polyvector_type(" + - llvm::utostr_32(T->getNumElements()) + "))) " + S); + OS << "__attribute__((neon_polyvector_type(" << + T->getNumElements() << "))) "; + printBefore(T->getElementType(), OS); break; case VectorType::GenericVector: { // FIXME: We prefer to print the size directly here, but have no way // to get the size of the type. - print(T->getElementType(), S); - std::string V = "__attribute__((__vector_size__("; - V += llvm::utostr_32(T->getNumElements()); // convert back to bytes. - std::string ET; - print(T->getElementType(), ET); - V += " * sizeof(" + ET + ")))) "; - S = V + S; + OS << "__attribute__((__vector_size__(" + << T->getNumElements() + << " * sizeof("; + print(T->getElementType(), OS, StringRef()); + OS << ")))) "; + printBefore(T->getElementType(), OS); break; } } } +void TypePrinter::printVectorAfter(const VectorType *T, raw_ostream &OS) { + printAfter(T->getElementType(), OS); +} -void TypePrinter::printExtVector(const ExtVectorType *T, std::string &S) { - S += " __attribute__((ext_vector_type("; - S += llvm::utostr_32(T->getNumElements()); - S += ")))"; - print(T->getElementType(), S); +void TypePrinter::printExtVectorBefore(const ExtVectorType *T, + raw_ostream &OS) { + printBefore(T->getElementType(), OS); +} +void TypePrinter::printExtVectorAfter(const ExtVectorType *T, raw_ostream &OS) { + printAfter(T->getElementType(), OS); + OS << " __attribute__((ext_vector_type("; + OS << T->getNumElements(); + OS << ")))"; } void -FunctionProtoType::printExceptionSpecification(std::string &S, +FunctionProtoType::printExceptionSpecification(raw_ostream &OS, PrintingPolicy Policy) const { if (hasDynamicExceptionSpec()) { - S += " throw("; + OS << " throw("; if (getExceptionSpecType() == EST_MSAny) - S += "..."; + OS << "..."; else for (unsigned I = 0, N = getNumExceptions(); I != N; ++I) { if (I) - S += ", "; + OS << ", "; - S += getExceptionType(I).getAsString(Policy); + OS << getExceptionType(I).stream(Policy); } - S += ")"; + OS << ')'; } else if (isNoexceptExceptionSpec(getExceptionSpecType())) { - S += " noexcept"; + OS << " noexcept"; if (getExceptionSpecType() == EST_ComputedNoexcept) { - S += "("; - llvm::raw_string_ostream EOut(S); - getNoexceptExpr()->printPretty(EOut, 0, Policy); - EOut.flush(); - S += EOut.str(); - S += ")"; + OS << '('; + getNoexceptExpr()->printPretty(OS, 0, Policy); + OS << ')'; } } } -void TypePrinter::printFunctionProto(const FunctionProtoType *T, - std::string &S) { +void TypePrinter::printFunctionProtoBefore(const FunctionProtoType *T, + raw_ostream &OS) { + if (T->hasTrailingReturn()) { + OS << "auto "; + if (!HasEmptyPlaceHolder) + OS << '('; + } else { + // If needed for precedence reasons, wrap the inner part in grouping parens. + SaveAndRestore<bool> PrevPHIsEmpty(HasEmptyPlaceHolder, false); + printBefore(T->getResultType(), OS); + if (!PrevPHIsEmpty.get()) + OS << '('; + } +} + +void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, + raw_ostream &OS) { // If needed for precedence reasons, wrap the inner part in grouping parens. - if (!S.empty()) - S = "(" + S + ")"; - - S += "("; - std::string Tmp; - PrintingPolicy ParamPolicy(Policy); - ParamPolicy.SuppressSpecifiers = false; - for (unsigned i = 0, e = T->getNumArgs(); i != e; ++i) { - if (i) S += ", "; - print(T->getArgType(i), Tmp); - S += Tmp; - Tmp.clear(); + if (!HasEmptyPlaceHolder) + OS << ')'; + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); + + OS << '('; + { + ParamPolicyRAII ParamPolicy(Policy); + for (unsigned i = 0, e = T->getNumArgs(); i != e; ++i) { + if (i) OS << ", "; + print(T->getArgType(i), OS, StringRef()); + } } if (T->isVariadic()) { if (T->getNumArgs()) - S += ", "; - S += "..."; + OS << ", "; + OS << "..."; } else if (T->getNumArgs() == 0 && !Policy.LangOpts.CPlusPlus) { // Do not emit int() if we have a proto, emit 'int(void)'. - S += "void"; + OS << "void"; } - S += ")"; + OS << ')'; FunctionType::ExtInfo Info = T->getExtInfo(); switch(Info.getCC()) { case CC_Default: break; case CC_C: - S += " __attribute__((cdecl))"; + OS << " __attribute__((cdecl))"; break; case CC_X86StdCall: - S += " __attribute__((stdcall))"; + OS << " __attribute__((stdcall))"; break; case CC_X86FastCall: - S += " __attribute__((fastcall))"; + OS << " __attribute__((fastcall))"; break; case CC_X86ThisCall: - S += " __attribute__((thiscall))"; + OS << " __attribute__((thiscall))"; break; case CC_X86Pascal: - S += " __attribute__((pascal))"; + OS << " __attribute__((pascal))"; break; case CC_AAPCS: - S += " __attribute__((pcs(\"aapcs\")))"; + OS << " __attribute__((pcs(\"aapcs\")))"; break; case CC_AAPCS_VFP: - S += " __attribute__((pcs(\"aapcs-vfp\")))"; + OS << " __attribute__((pcs(\"aapcs-vfp\")))"; break; } if (Info.getNoReturn()) - S += " __attribute__((noreturn))"; + OS << " __attribute__((noreturn))"; if (Info.getRegParm()) - S += " __attribute__((regparm (" + - llvm::utostr_32(Info.getRegParm()) + ")))"; - - AppendTypeQualList(S, T->getTypeQuals()); + OS << " __attribute__((regparm (" + << Info.getRegParm() << ")))"; + + if (unsigned quals = T->getTypeQuals()) { + OS << ' '; + AppendTypeQualList(OS, quals); + } switch (T->getRefQualifier()) { case RQ_None: break; case RQ_LValue: - S += " &"; + OS << " &"; break; case RQ_RValue: - S += " &&"; + OS << " &&"; break; } - T->printExceptionSpecification(S, Policy); + T->printExceptionSpecification(OS, Policy); + if (T->hasTrailingReturn()) { - std::string ResultS; - print(T->getResultType(), ResultS); - S = "auto " + S + " -> " + ResultS; + OS << " -> "; + print(T->getResultType(), OS, StringRef()); } else - print(T->getResultType(), S); + printAfter(T->getResultType(), OS); } -void TypePrinter::printFunctionNoProto(const FunctionNoProtoType *T, - std::string &S) { +void TypePrinter::printFunctionNoProtoBefore(const FunctionNoProtoType *T, + raw_ostream &OS) { + // If needed for precedence reasons, wrap the inner part in grouping parens. + SaveAndRestore<bool> PrevPHIsEmpty(HasEmptyPlaceHolder, false); + printBefore(T->getResultType(), OS); + if (!PrevPHIsEmpty.get()) + OS << '('; +} +void TypePrinter::printFunctionNoProtoAfter(const FunctionNoProtoType *T, + raw_ostream &OS) { // If needed for precedence reasons, wrap the inner part in grouping parens. - if (!S.empty()) - S = "(" + S + ")"; + if (!HasEmptyPlaceHolder) + OS << ')'; + SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false); - S += "()"; + OS << "()"; if (T->getNoReturnAttr()) - S += " __attribute__((noreturn))"; - print(T->getResultType(), S); + OS << " __attribute__((noreturn))"; + printAfter(T->getResultType(), OS); } -static void printTypeSpec(const NamedDecl *D, std::string &S) { +void TypePrinter::printTypeSpec(const NamedDecl *D, raw_ostream &OS) { IdentifierInfo *II = D->getIdentifier(); - if (S.empty()) - S = II->getName().str(); - else - S = II->getName().str() + ' ' + S; + OS << II->getName(); + spaceBeforePlaceHolder(OS); } -void TypePrinter::printUnresolvedUsing(const UnresolvedUsingType *T, - std::string &S) { - printTypeSpec(T->getDecl(), S); +void TypePrinter::printUnresolvedUsingBefore(const UnresolvedUsingType *T, + raw_ostream &OS) { + printTypeSpec(T->getDecl(), OS); } +void TypePrinter::printUnresolvedUsingAfter(const UnresolvedUsingType *T, + raw_ostream &OS) { } -void TypePrinter::printTypedef(const TypedefType *T, std::string &S) { - printTypeSpec(T->getDecl(), S); +void TypePrinter::printTypedefBefore(const TypedefType *T, raw_ostream &OS) { + printTypeSpec(T->getDecl(), OS); } +void TypePrinter::printTypedefAfter(const TypedefType *T, raw_ostream &OS) { } -void TypePrinter::printTypeOfExpr(const TypeOfExprType *T, std::string &S) { - if (!S.empty()) // Prefix the basic type, e.g. 'typeof(e) X'. - S = ' ' + S; - std::string Str; - llvm::raw_string_ostream s(Str); - T->getUnderlyingExpr()->printPretty(s, 0, Policy); - S = "typeof " + s.str() + S; +void TypePrinter::printTypeOfExprBefore(const TypeOfExprType *T, + raw_ostream &OS) { + OS << "typeof "; + T->getUnderlyingExpr()->printPretty(OS, 0, Policy); + spaceBeforePlaceHolder(OS); } - -void TypePrinter::printTypeOf(const TypeOfType *T, std::string &S) { - if (!S.empty()) // Prefix the basic type, e.g. 'typeof(t) X'. - S = ' ' + S; - std::string Tmp; - print(T->getUnderlyingType(), Tmp); - S = "typeof(" + Tmp + ")" + S; +void TypePrinter::printTypeOfExprAfter(const TypeOfExprType *T, + raw_ostream &OS) { } + +void TypePrinter::printTypeOfBefore(const TypeOfType *T, raw_ostream &OS) { + OS << "typeof("; + print(T->getUnderlyingType(), OS, StringRef()); + OS << ')'; + spaceBeforePlaceHolder(OS); } +void TypePrinter::printTypeOfAfter(const TypeOfType *T, raw_ostream &OS) { } -void TypePrinter::printDecltype(const DecltypeType *T, std::string &S) { - if (!S.empty()) // Prefix the basic type, e.g. 'decltype(t) X'. - S = ' ' + S; - std::string Str; - llvm::raw_string_ostream s(Str); - T->getUnderlyingExpr()->printPretty(s, 0, Policy); - S = "decltype(" + s.str() + ")" + S; +void TypePrinter::printDecltypeBefore(const DecltypeType *T, raw_ostream &OS) { + OS << "decltype("; + T->getUnderlyingExpr()->printPretty(OS, 0, Policy); + OS << ')'; + spaceBeforePlaceHolder(OS); } +void TypePrinter::printDecltypeAfter(const DecltypeType *T, raw_ostream &OS) { } -void TypePrinter::printUnaryTransform(const UnaryTransformType *T, - std::string &S) { - if (!S.empty()) - S = ' ' + S; - std::string Str; +void TypePrinter::printUnaryTransformBefore(const UnaryTransformType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getBaseType(), Str); switch (T->getUTTKind()) { case UnaryTransformType::EnumUnderlyingType: - S = "__underlying_type(" + Str + ")" + S; - break; + OS << "__underlying_type("; + print(T->getBaseType(), OS, StringRef()); + OS << ')'; + spaceBeforePlaceHolder(OS); + return; } + + printBefore(T->getBaseType(), OS); +} +void TypePrinter::printUnaryTransformAfter(const UnaryTransformType *T, + raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + + switch (T->getUTTKind()) { + case UnaryTransformType::EnumUnderlyingType: + return; + } + + printAfter(T->getBaseType(), OS); } -void TypePrinter::printAuto(const AutoType *T, std::string &S) { +void TypePrinter::printAutoBefore(const AutoType *T, raw_ostream &OS) { // If the type has been deduced, do not print 'auto'. if (T->isDeduced()) { - print(T->getDeducedType(), S); + printBefore(T->getDeducedType(), OS); } else { - if (!S.empty()) // Prefix the basic type, e.g. 'auto X'. - S = ' ' + S; - S = "auto" + S; + OS << "auto"; + spaceBeforePlaceHolder(OS); } } +void TypePrinter::printAutoAfter(const AutoType *T, raw_ostream &OS) { + // If the type has been deduced, do not print 'auto'. + if (T->isDeduced()) + printAfter(T->getDeducedType(), OS); +} -void TypePrinter::printAtomic(const AtomicType *T, std::string &S) { - if (!S.empty()) - S = ' ' + S; - std::string Str; +void TypePrinter::printAtomicBefore(const AtomicType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getValueType(), Str); - S = "_Atomic(" + Str + ")" + S; + OS << "_Atomic("; + print(T->getValueType(), OS, StringRef()); + OS << ')'; + spaceBeforePlaceHolder(OS); } +void TypePrinter::printAtomicAfter(const AtomicType *T, raw_ostream &OS) { } /// Appends the given scope to the end of a string. -void TypePrinter::AppendScope(DeclContext *DC, std::string &Buffer) { +void TypePrinter::AppendScope(DeclContext *DC, raw_ostream &OS) { if (DC->isTranslationUnit()) return; - AppendScope(DC->getParent(), Buffer); - - unsigned OldSize = Buffer.size(); + AppendScope(DC->getParent(), OS); if (NamespaceDecl *NS = dyn_cast<NamespaceDecl>(DC)) { if (Policy.SuppressUnwrittenScope && (NS->isAnonymousNamespace() || NS->isInline())) return; if (NS->getIdentifier()) - Buffer += NS->getNameAsString(); + OS << NS->getName() << "::"; else - Buffer += "<anonymous>"; + OS << "<anonymous>::"; } else if (ClassTemplateSpecializationDecl *Spec = dyn_cast<ClassTemplateSpecializationDecl>(DC)) { IncludeStrongLifetimeRAII Strong(Policy); + OS << Spec->getIdentifier()->getName(); const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs(); - std::string TemplateArgsStr - = TemplateSpecializationType::PrintTemplateArgumentList( + TemplateSpecializationType::PrintTemplateArgumentList(OS, TemplateArgs.data(), TemplateArgs.size(), Policy); - Buffer += Spec->getIdentifier()->getName(); - Buffer += TemplateArgsStr; + OS << "::"; } else if (TagDecl *Tag = dyn_cast<TagDecl>(DC)) { if (TypedefNameDecl *Typedef = Tag->getTypedefNameForAnonDecl()) - Buffer += Typedef->getIdentifier()->getName(); + OS << Typedef->getIdentifier()->getName() << "::"; else if (Tag->getIdentifier()) - Buffer += Tag->getIdentifier()->getName(); + OS << Tag->getIdentifier()->getName() << "::"; else return; } - - if (Buffer.size() != OldSize) - Buffer += "::"; } -void TypePrinter::printTag(TagDecl *D, std::string &InnerString) { +void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) { if (Policy.SuppressTag) return; - std::string Buffer; bool HasKindDecoration = false; // bool SuppressTagKeyword @@ -654,25 +842,24 @@ void TypePrinter::printTag(TagDecl *D, std::string &InnerString) { if (!(Policy.LangOpts.CPlusPlus || Policy.SuppressTagKeyword || D->getTypedefNameForAnonDecl())) { HasKindDecoration = true; - Buffer += D->getKindName(); - Buffer += ' '; + OS << D->getKindName(); + OS << ' '; } // Compute the full nested-name-specifier for this type. // In C, this will always be empty except when the type // being printed is anonymous within other Record. if (!Policy.SuppressScope) - AppendScope(D->getDeclContext(), Buffer); + AppendScope(D->getDeclContext(), OS); if (const IdentifierInfo *II = D->getIdentifier()) - Buffer += II->getNameStart(); + OS << II->getName(); else if (TypedefNameDecl *Typedef = D->getTypedefNameForAnonDecl()) { assert(Typedef->getIdentifier() && "Typedef without identifier?"); - Buffer += Typedef->getIdentifier()->getNameStart(); + OS << Typedef->getIdentifier()->getName(); } else { // Make an unambiguous representation for anonymous types, e.g. // <anonymous enum at /usr/include/string.h:120:9> - llvm::raw_string_ostream OS(Buffer); if (isa<CXXRecordDecl>(D) && cast<CXXRecordDecl>(D)->isLambda()) { OS << "<lambda"; @@ -717,219 +904,223 @@ void TypePrinter::printTag(TagDecl *D, std::string &InnerString) { NumArgs = TemplateArgs.size(); } IncludeStrongLifetimeRAII Strong(Policy); - Buffer += TemplateSpecializationType::PrintTemplateArgumentList(Args, - NumArgs, - Policy); - } - - if (!InnerString.empty()) { - Buffer += ' '; - Buffer += InnerString; + TemplateSpecializationType::PrintTemplateArgumentList(OS, + Args, NumArgs, + Policy); } - std::swap(Buffer, InnerString); + spaceBeforePlaceHolder(OS); } -void TypePrinter::printRecord(const RecordType *T, std::string &S) { - printTag(T->getDecl(), S); +void TypePrinter::printRecordBefore(const RecordType *T, raw_ostream &OS) { + printTag(T->getDecl(), OS); } +void TypePrinter::printRecordAfter(const RecordType *T, raw_ostream &OS) { } -void TypePrinter::printEnum(const EnumType *T, std::string &S) { - printTag(T->getDecl(), S); +void TypePrinter::printEnumBefore(const EnumType *T, raw_ostream &OS) { + printTag(T->getDecl(), OS); } +void TypePrinter::printEnumAfter(const EnumType *T, raw_ostream &OS) { } -void TypePrinter::printTemplateTypeParm(const TemplateTypeParmType *T, - std::string &S) { - if (!S.empty()) // Prefix the basic type, e.g. 'parmname X'. - S = ' ' + S; - +void TypePrinter::printTemplateTypeParmBefore(const TemplateTypeParmType *T, + raw_ostream &OS) { if (IdentifierInfo *Id = T->getIdentifier()) - S = Id->getName().str() + S; + OS << Id->getName(); else - S = "type-parameter-" + llvm::utostr_32(T->getDepth()) + '-' + - llvm::utostr_32(T->getIndex()) + S; + OS << "type-parameter-" << T->getDepth() << '-' << T->getIndex(); + spaceBeforePlaceHolder(OS); } +void TypePrinter::printTemplateTypeParmAfter(const TemplateTypeParmType *T, + raw_ostream &OS) { } -void TypePrinter::printSubstTemplateTypeParm(const SubstTemplateTypeParmType *T, - std::string &S) { +void TypePrinter::printSubstTemplateTypeParmBefore( + const SubstTemplateTypeParmType *T, + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - print(T->getReplacementType(), S); + printBefore(T->getReplacementType(), OS); +} +void TypePrinter::printSubstTemplateTypeParmAfter( + const SubstTemplateTypeParmType *T, + raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + printAfter(T->getReplacementType(), OS); } -void TypePrinter::printSubstTemplateTypeParmPack( +void TypePrinter::printSubstTemplateTypeParmPackBefore( const SubstTemplateTypeParmPackType *T, - std::string &S) { + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - printTemplateTypeParm(T->getReplacedParameter(), S); + printTemplateTypeParmBefore(T->getReplacedParameter(), OS); +} +void TypePrinter::printSubstTemplateTypeParmPackAfter( + const SubstTemplateTypeParmPackType *T, + raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + printTemplateTypeParmAfter(T->getReplacedParameter(), OS); } -void TypePrinter::printTemplateSpecialization( +void TypePrinter::printTemplateSpecializationBefore( const TemplateSpecializationType *T, - std::string &S) { + raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - std::string SpecString; + T->getTemplateName().print(OS, Policy); - { - llvm::raw_string_ostream OS(SpecString); - T->getTemplateName().print(OS, Policy); - } - - SpecString += TemplateSpecializationType::PrintTemplateArgumentList( - T->getArgs(), - T->getNumArgs(), - Policy); - if (S.empty()) - S.swap(SpecString); - else - S = SpecString + ' ' + S; + TemplateSpecializationType::PrintTemplateArgumentList(OS, + T->getArgs(), + T->getNumArgs(), + Policy); + spaceBeforePlaceHolder(OS); } +void TypePrinter::printTemplateSpecializationAfter( + const TemplateSpecializationType *T, + raw_ostream &OS) { } -void TypePrinter::printInjectedClassName(const InjectedClassNameType *T, - std::string &S) { - printTemplateSpecialization(T->getInjectedTST(), S); +void TypePrinter::printInjectedClassNameBefore(const InjectedClassNameType *T, + raw_ostream &OS) { + printTemplateSpecializationBefore(T->getInjectedTST(), OS); } - -void TypePrinter::printElaborated(const ElaboratedType *T, std::string &S) { - std::string MyString; - - { - llvm::raw_string_ostream OS(MyString); - OS << TypeWithKeyword::getKeywordName(T->getKeyword()); - if (T->getKeyword() != ETK_None) - OS << " "; - NestedNameSpecifier* Qualifier = T->getQualifier(); - if (Qualifier) - Qualifier->print(OS, Policy); - } - - std::string TypeStr; - PrintingPolicy InnerPolicy(Policy); - InnerPolicy.SuppressTagKeyword = true; - InnerPolicy.SuppressScope = true; - TypePrinter(InnerPolicy).print(T->getNamedType(), TypeStr); +void TypePrinter::printInjectedClassNameAfter(const InjectedClassNameType *T, + raw_ostream &OS) { } + +void TypePrinter::printElaboratedBefore(const ElaboratedType *T, + raw_ostream &OS) { + OS << TypeWithKeyword::getKeywordName(T->getKeyword()); + if (T->getKeyword() != ETK_None) + OS << " "; + NestedNameSpecifier* Qualifier = T->getQualifier(); + if (Qualifier) + Qualifier->print(OS, Policy); - MyString += TypeStr; - if (S.empty()) - S.swap(MyString); - else - S = MyString + ' ' + S; + ElaboratedTypePolicyRAII PolicyRAII(Policy); + printBefore(T->getNamedType(), OS); +} +void TypePrinter::printElaboratedAfter(const ElaboratedType *T, + raw_ostream &OS) { + ElaboratedTypePolicyRAII PolicyRAII(Policy); + printAfter(T->getNamedType(), OS); } -void TypePrinter::printParen(const ParenType *T, std::string &S) { - if (!S.empty() && !isa<FunctionType>(T->getInnerType())) - S = '(' + S + ')'; - print(T->getInnerType(), S); +void TypePrinter::printParenBefore(const ParenType *T, raw_ostream &OS) { + if (!HasEmptyPlaceHolder && !isa<FunctionType>(T->getInnerType())) { + printBefore(T->getInnerType(), OS); + OS << '('; + } else + printBefore(T->getInnerType(), OS); +} +void TypePrinter::printParenAfter(const ParenType *T, raw_ostream &OS) { + if (!HasEmptyPlaceHolder && !isa<FunctionType>(T->getInnerType())) { + OS << ')'; + printAfter(T->getInnerType(), OS); + } else + printAfter(T->getInnerType(), OS); } -void TypePrinter::printDependentName(const DependentNameType *T, std::string &S) { - std::string MyString; +void TypePrinter::printDependentNameBefore(const DependentNameType *T, + raw_ostream &OS) { + OS << TypeWithKeyword::getKeywordName(T->getKeyword()); + if (T->getKeyword() != ETK_None) + OS << " "; - { - llvm::raw_string_ostream OS(MyString); - OS << TypeWithKeyword::getKeywordName(T->getKeyword()); - if (T->getKeyword() != ETK_None) - OS << " "; - - T->getQualifier()->print(OS, Policy); - - OS << T->getIdentifier()->getName(); - } + T->getQualifier()->print(OS, Policy); - if (S.empty()) - S.swap(MyString); - else - S = MyString + ' ' + S; + OS << T->getIdentifier()->getName(); + spaceBeforePlaceHolder(OS); } +void TypePrinter::printDependentNameAfter(const DependentNameType *T, + raw_ostream &OS) { } -void TypePrinter::printDependentTemplateSpecialization( - const DependentTemplateSpecializationType *T, std::string &S) { +void TypePrinter::printDependentTemplateSpecializationBefore( + const DependentTemplateSpecializationType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); - std::string MyString; - { - llvm::raw_string_ostream OS(MyString); - OS << TypeWithKeyword::getKeywordName(T->getKeyword()); - if (T->getKeyword() != ETK_None) - OS << " "; - - if (T->getQualifier()) - T->getQualifier()->print(OS, Policy); - OS << T->getIdentifier()->getName(); - OS << TemplateSpecializationType::PrintTemplateArgumentList( - T->getArgs(), - T->getNumArgs(), - Policy); - } + OS << TypeWithKeyword::getKeywordName(T->getKeyword()); + if (T->getKeyword() != ETK_None) + OS << " "; - if (S.empty()) - S.swap(MyString); - else - S = MyString + ' ' + S; + if (T->getQualifier()) + T->getQualifier()->print(OS, Policy); + OS << T->getIdentifier()->getName(); + TemplateSpecializationType::PrintTemplateArgumentList(OS, + T->getArgs(), + T->getNumArgs(), + Policy); + spaceBeforePlaceHolder(OS); } +void TypePrinter::printDependentTemplateSpecializationAfter( + const DependentTemplateSpecializationType *T, raw_ostream &OS) { } -void TypePrinter::printPackExpansion(const PackExpansionType *T, - std::string &S) { - print(T->getPattern(), S); - S += "..."; +void TypePrinter::printPackExpansionBefore(const PackExpansionType *T, + raw_ostream &OS) { + printBefore(T->getPattern(), OS); +} +void TypePrinter::printPackExpansionAfter(const PackExpansionType *T, + raw_ostream &OS) { + printAfter(T->getPattern(), OS); + OS << "..."; } -void TypePrinter::printAttributed(const AttributedType *T, - std::string &S) { +void TypePrinter::printAttributedBefore(const AttributedType *T, + raw_ostream &OS) { // Prefer the macro forms of the GC and ownership qualifiers. if (T->getAttrKind() == AttributedType::attr_objc_gc || T->getAttrKind() == AttributedType::attr_objc_ownership) - return print(T->getEquivalentType(), S); + return printBefore(T->getEquivalentType(), OS); + + printBefore(T->getModifiedType(), OS); +} - print(T->getModifiedType(), S); +void TypePrinter::printAttributedAfter(const AttributedType *T, + raw_ostream &OS) { + // Prefer the macro forms of the GC and ownership qualifiers. + if (T->getAttrKind() == AttributedType::attr_objc_gc || + T->getAttrKind() == AttributedType::attr_objc_ownership) + return printAfter(T->getEquivalentType(), OS); // TODO: not all attributes are GCC-style attributes. - S += " __attribute__(("; + OS << " __attribute__(("; switch (T->getAttrKind()) { case AttributedType::attr_address_space: - S += "address_space("; - S += T->getEquivalentType().getAddressSpace(); - S += ")"; + OS << "address_space("; + OS << T->getEquivalentType().getAddressSpace(); + OS << ')'; break; case AttributedType::attr_vector_size: { - S += "__vector_size__("; + OS << "__vector_size__("; if (const VectorType *vector =T->getEquivalentType()->getAs<VectorType>()) { - S += vector->getNumElements(); - S += " * sizeof("; - - std::string tmp; - print(vector->getElementType(), tmp); - S += tmp; - S += ")"; + OS << vector->getNumElements(); + OS << " * sizeof("; + print(vector->getElementType(), OS, StringRef()); + OS << ')'; } - S += ")"; + OS << ')'; break; } case AttributedType::attr_neon_vector_type: case AttributedType::attr_neon_polyvector_type: { if (T->getAttrKind() == AttributedType::attr_neon_vector_type) - S += "neon_vector_type("; + OS << "neon_vector_type("; else - S += "neon_polyvector_type("; + OS << "neon_polyvector_type("; const VectorType *vector = T->getEquivalentType()->getAs<VectorType>(); - S += llvm::utostr_32(vector->getNumElements()); - S += ")"; + OS << vector->getNumElements(); + OS << ')'; break; } case AttributedType::attr_regparm: { - S += "regparm("; + OS << "regparm("; QualType t = T->getEquivalentType(); while (!t->isFunctionType()) t = t->getPointeeType(); - S += t->getAs<FunctionType>()->getRegParmType(); - S += ")"; + OS << t->getAs<FunctionType>()->getRegParmType(); + OS << ')'; break; } case AttributedType::attr_objc_gc: { - S += "objc_gc("; + OS << "objc_gc("; QualType tmp = T->getEquivalentType(); while (tmp.getObjCGCAttr() == Qualifiers::GCNone) { @@ -939,116 +1130,244 @@ void TypePrinter::printAttributed(const AttributedType *T, } if (tmp.isObjCGCWeak()) - S += "weak"; + OS << "weak"; else - S += "strong"; - S += ")"; + OS << "strong"; + OS << ')'; break; } case AttributedType::attr_objc_ownership: - S += "objc_ownership("; + OS << "objc_ownership("; switch (T->getEquivalentType().getObjCLifetime()) { case Qualifiers::OCL_None: llvm_unreachable("no ownership!"); - case Qualifiers::OCL_ExplicitNone: S += "none"; break; - case Qualifiers::OCL_Strong: S += "strong"; break; - case Qualifiers::OCL_Weak: S += "weak"; break; - case Qualifiers::OCL_Autoreleasing: S += "autoreleasing"; break; + case Qualifiers::OCL_ExplicitNone: OS << "none"; break; + case Qualifiers::OCL_Strong: OS << "strong"; break; + case Qualifiers::OCL_Weak: OS << "weak"; break; + case Qualifiers::OCL_Autoreleasing: OS << "autoreleasing"; break; } - S += ")"; + OS << ')'; break; - case AttributedType::attr_noreturn: S += "noreturn"; break; - case AttributedType::attr_cdecl: S += "cdecl"; break; - case AttributedType::attr_fastcall: S += "fastcall"; break; - case AttributedType::attr_stdcall: S += "stdcall"; break; - case AttributedType::attr_thiscall: S += "thiscall"; break; - case AttributedType::attr_pascal: S += "pascal"; break; + case AttributedType::attr_noreturn: OS << "noreturn"; break; + case AttributedType::attr_cdecl: OS << "cdecl"; break; + case AttributedType::attr_fastcall: OS << "fastcall"; break; + case AttributedType::attr_stdcall: OS << "stdcall"; break; + case AttributedType::attr_thiscall: OS << "thiscall"; break; + case AttributedType::attr_pascal: OS << "pascal"; break; case AttributedType::attr_pcs: { - S += "pcs("; + OS << "pcs("; QualType t = T->getEquivalentType(); while (!t->isFunctionType()) t = t->getPointeeType(); - S += (t->getAs<FunctionType>()->getCallConv() == CC_AAPCS ? + OS << (t->getAs<FunctionType>()->getCallConv() == CC_AAPCS ? "\"aapcs\"" : "\"aapcs-vfp\""); - S += ")"; + OS << ')'; break; } } - S += "))"; + OS << "))"; } -void TypePrinter::printObjCInterface(const ObjCInterfaceType *T, - std::string &S) { - if (!S.empty()) // Prefix the basic type, e.g. 'typedefname X'. - S = ' ' + S; - - std::string ObjCQIString = T->getDecl()->getNameAsString(); - S = ObjCQIString + S; +void TypePrinter::printObjCInterfaceBefore(const ObjCInterfaceType *T, + raw_ostream &OS) { + OS << T->getDecl()->getName(); + spaceBeforePlaceHolder(OS); } +void TypePrinter::printObjCInterfaceAfter(const ObjCInterfaceType *T, + raw_ostream &OS) { } -void TypePrinter::printObjCObject(const ObjCObjectType *T, - std::string &S) { +void TypePrinter::printObjCObjectBefore(const ObjCObjectType *T, + raw_ostream &OS) { if (T->qual_empty()) - return print(T->getBaseType(), S); + return printBefore(T->getBaseType(), OS); - std::string tmp; - print(T->getBaseType(), tmp); - tmp += '<'; + print(T->getBaseType(), OS, StringRef()); + OS << '<'; bool isFirst = true; for (ObjCObjectType::qual_iterator I = T->qual_begin(), E = T->qual_end(); I != E; ++I) { if (isFirst) isFirst = false; else - tmp += ','; - tmp += (*I)->getNameAsString(); + OS << ','; + OS << (*I)->getName(); } - tmp += '>'; - - if (!S.empty()) { - tmp += ' '; - tmp += S; - } - std::swap(tmp, S); + OS << '>'; + spaceBeforePlaceHolder(OS); +} +void TypePrinter::printObjCObjectAfter(const ObjCObjectType *T, + raw_ostream &OS) { + if (T->qual_empty()) + return printAfter(T->getBaseType(), OS); } -void TypePrinter::printObjCObjectPointer(const ObjCObjectPointerType *T, - std::string &S) { - std::string ObjCQIString; - - T->getPointeeType().getLocalQualifiers().getAsStringInternal(ObjCQIString, - Policy); - if (!ObjCQIString.empty()) - ObjCQIString += ' '; - +void TypePrinter::printObjCObjectPointerBefore(const ObjCObjectPointerType *T, + raw_ostream &OS) { + T->getPointeeType().getLocalQualifiers().print(OS, Policy, + /*appendSpaceIfNonEmpty=*/true); + if (T->isObjCIdType() || T->isObjCQualifiedIdType()) - ObjCQIString += "id"; + OS << "id"; else if (T->isObjCClassType() || T->isObjCQualifiedClassType()) - ObjCQIString += "Class"; + OS << "Class"; else if (T->isObjCSelType()) - ObjCQIString += "SEL"; + OS << "SEL"; else - ObjCQIString += T->getInterfaceDecl()->getNameAsString(); + OS << T->getInterfaceDecl()->getName(); if (!T->qual_empty()) { - ObjCQIString += '<'; + OS << '<'; for (ObjCObjectPointerType::qual_iterator I = T->qual_begin(), E = T->qual_end(); I != E; ++I) { - ObjCQIString += (*I)->getNameAsString(); + OS << (*I)->getName(); if (I+1 != E) - ObjCQIString += ','; + OS << ','; + } + OS << '>'; + } + + if (!T->isObjCIdType() && !T->isObjCQualifiedIdType()) { + OS << " *"; // Don't forget the implicit pointer. + } else { + spaceBeforePlaceHolder(OS); + } +} +void TypePrinter::printObjCObjectPointerAfter(const ObjCObjectPointerType *T, + raw_ostream &OS) { } + +void TemplateSpecializationType:: + PrintTemplateArgumentList(raw_ostream &OS, + const TemplateArgumentListInfo &Args, + const PrintingPolicy &Policy) { + return PrintTemplateArgumentList(OS, + Args.getArgumentArray(), + Args.size(), + Policy); +} + +void +TemplateSpecializationType::PrintTemplateArgumentList( + raw_ostream &OS, + const TemplateArgument *Args, + unsigned NumArgs, + const PrintingPolicy &Policy, + bool SkipBrackets) { + if (!SkipBrackets) + OS << '<'; + + bool needSpace = false; + for (unsigned Arg = 0; Arg < NumArgs; ++Arg) { + if (Arg > 0) + OS << ", "; + + // Print the argument into a string. + SmallString<128> Buf; + llvm::raw_svector_ostream ArgOS(Buf); + if (Args[Arg].getKind() == TemplateArgument::Pack) { + PrintTemplateArgumentList(ArgOS, + Args[Arg].pack_begin(), + Args[Arg].pack_size(), + Policy, true); + } else { + Args[Arg].print(Policy, ArgOS); + } + StringRef ArgString = ArgOS.str(); + + // If this is the first argument and its string representation + // begins with the global scope specifier ('::foo'), add a space + // to avoid printing the diagraph '<:'. + if (!Arg && !ArgString.empty() && ArgString[0] == ':') + OS << ' '; + + OS << ArgString; + + needSpace = (!ArgString.empty() && ArgString.back() == '>'); + } + + // If the last character of our string is '>', add another space to + // keep the two '>''s separate tokens. We don't *have* to do this in + // C++0x, but it's still good hygiene. + if (needSpace) + OS << ' '; + + if (!SkipBrackets) + OS << '>'; +} + +// Sadly, repeat all that with TemplateArgLoc. +void TemplateSpecializationType:: +PrintTemplateArgumentList(raw_ostream &OS, + const TemplateArgumentLoc *Args, unsigned NumArgs, + const PrintingPolicy &Policy) { + OS << '<'; + + bool needSpace = false; + for (unsigned Arg = 0; Arg < NumArgs; ++Arg) { + if (Arg > 0) + OS << ", "; + + // Print the argument into a string. + SmallString<128> Buf; + llvm::raw_svector_ostream ArgOS(Buf); + if (Args[Arg].getArgument().getKind() == TemplateArgument::Pack) { + PrintTemplateArgumentList(ArgOS, + Args[Arg].getArgument().pack_begin(), + Args[Arg].getArgument().pack_size(), + Policy, true); + } else { + Args[Arg].getArgument().print(Policy, ArgOS); } - ObjCQIString += '>'; + StringRef ArgString = ArgOS.str(); + + // If this is the first argument and its string representation + // begins with the global scope specifier ('::foo'), add a space + // to avoid printing the diagraph '<:'. + if (!Arg && !ArgString.empty() && ArgString[0] == ':') + OS << ' '; + + OS << ArgString; + + needSpace = (!ArgString.empty() && ArgString.back() == '>'); } - if (!T->isObjCIdType() && !T->isObjCQualifiedIdType()) - ObjCQIString += " *"; // Don't forget the implicit pointer. - else if (!S.empty()) // Prefix the basic type, e.g. 'typedefname X'. - S = ' ' + S; + // If the last character of our string is '>', add another space to + // keep the two '>''s separate tokens. We don't *have* to do this in + // C++0x, but it's still good hygiene. + if (needSpace) + OS << ' '; + + OS << '>'; +} + +void +FunctionProtoType::printExceptionSpecification(std::string &S, + PrintingPolicy Policy) const { - S = ObjCQIString + S; + if (hasDynamicExceptionSpec()) { + S += " throw("; + if (getExceptionSpecType() == EST_MSAny) + S += "..."; + else + for (unsigned I = 0, N = getNumExceptions(); I != N; ++I) { + if (I) + S += ", "; + + S += getExceptionType(I).getAsString(Policy); + } + S += ")"; + } else if (isNoexceptExceptionSpec(getExceptionSpecType())) { + S += " noexcept"; + if (getExceptionSpecType() == EST_ComputedNoexcept) { + S += "("; + llvm::raw_string_ostream EOut(S); + getNoexceptExpr()->printPretty(EOut, 0, Policy); + EOut.flush(); + S += EOut.str(); + S += ")"; + } + } } std::string TemplateSpecializationType:: @@ -1148,15 +1467,14 @@ PrintTemplateArgumentList(const TemplateArgumentLoc *Args, unsigned NumArgs, } void QualType::dump(const char *msg) const { - std::string R = "identifier"; - LangOptions LO; - getAsStringInternal(R, PrintingPolicy(LO)); if (msg) llvm::errs() << msg << ": "; - llvm::errs() << R << "\n"; + LangOptions LO; + print(llvm::errs(), PrintingPolicy(LO), "identifier"); + llvm::errs() << '\n'; } void QualType::dump() const { - dump(""); + dump(0); } void Type::dump() const { @@ -1171,51 +1489,99 @@ std::string Qualifiers::getAsString() const { // Appends qualifiers to the given string, separated by spaces. Will // prefix a space if the string is non-empty. Will not append a final // space. -void Qualifiers::getAsStringInternal(std::string &S, - const PrintingPolicy& Policy) const { - AppendTypeQualList(S, getCVRQualifiers()); +std::string Qualifiers::getAsString(const PrintingPolicy &Policy) const { + SmallString<64> Buf; + llvm::raw_svector_ostream StrOS(Buf); + print(StrOS, Policy); + return StrOS.str(); +} + +bool Qualifiers::isEmptyWhenPrinted(const PrintingPolicy &Policy) const { + if (getCVRQualifiers()) + return false; + + if (getAddressSpace()) + return false; + + if (getObjCGCAttr()) + return false; + + if (Qualifiers::ObjCLifetime lifetime = getObjCLifetime()) + if (!(lifetime == Qualifiers::OCL_Strong && Policy.SuppressStrongLifetime)) + return false; + + return true; +} + +// Appends qualifiers to the given string, separated by spaces. Will +// prefix a space if the string is non-empty. Will not append a final +// space. +void Qualifiers::print(raw_ostream &OS, const PrintingPolicy& Policy, + bool appendSpaceIfNonEmpty) const { + bool addSpace = false; + + unsigned quals = getCVRQualifiers(); + if (quals) { + AppendTypeQualList(OS, quals); + addSpace = true; + } if (unsigned addrspace = getAddressSpace()) { - if (!S.empty()) S += ' '; + if (addSpace) + OS << ' '; + addSpace = true; switch (addrspace) { case LangAS::opencl_global: - S += "__global"; + OS << "__global"; break; case LangAS::opencl_local: - S += "__local"; + OS << "__local"; break; case LangAS::opencl_constant: - S += "__constant"; + OS << "__constant"; break; default: - S += "__attribute__((address_space("; - S += llvm::utostr_32(addrspace); - S += ")))"; + OS << "__attribute__((address_space("; + OS << addrspace; + OS << ")))"; } } if (Qualifiers::GC gc = getObjCGCAttr()) { - if (!S.empty()) S += ' '; + if (addSpace) + OS << ' '; + addSpace = true; if (gc == Qualifiers::Weak) - S += "__weak"; + OS << "__weak"; else - S += "__strong"; + OS << "__strong"; } if (Qualifiers::ObjCLifetime lifetime = getObjCLifetime()) { - if (!S.empty() && - !(lifetime == Qualifiers::OCL_Strong && Policy.SuppressStrongLifetime)) - S += ' '; - + if (!(lifetime == Qualifiers::OCL_Strong && Policy.SuppressStrongLifetime)){ + if (addSpace) + OS << ' '; + addSpace = true; + } + switch (lifetime) { case Qualifiers::OCL_None: llvm_unreachable("none but true"); - case Qualifiers::OCL_ExplicitNone: S += "__unsafe_unretained"; break; + case Qualifiers::OCL_ExplicitNone: OS << "__unsafe_unretained"; break; case Qualifiers::OCL_Strong: if (!Policy.SuppressStrongLifetime) - S += "__strong"; + OS << "__strong"; break; - case Qualifiers::OCL_Weak: S += "__weak"; break; - case Qualifiers::OCL_Autoreleasing: S += "__autoreleasing"; break; + case Qualifiers::OCL_Weak: OS << "__weak"; break; + case Qualifiers::OCL_Autoreleasing: OS << "__autoreleasing"; break; } } + + if (appendSpaceIfNonEmpty && addSpace) + OS << ' '; +} + +std::string QualType::getAsString(const PrintingPolicy &Policy) const { + std::string S; + getAsStringInternal(S, Policy); + return S; } std::string QualType::getAsString(const Type *ty, Qualifiers qs) { @@ -1225,8 +1591,25 @@ std::string QualType::getAsString(const Type *ty, Qualifiers qs) { return buffer; } +void QualType::print(const Type *ty, Qualifiers qs, + raw_ostream &OS, const PrintingPolicy &policy, + const Twine &PlaceHolder) { + SmallString<128> PHBuf; + StringRef PH; + if (PlaceHolder.isSingleStringRef()) + PH = PlaceHolder.getSingleStringRef(); + else + PH = PlaceHolder.toStringRef(PHBuf); + + TypePrinter(policy).print(ty, qs, OS, PH); +} + void QualType::getAsStringInternal(const Type *ty, Qualifiers qs, std::string &buffer, const PrintingPolicy &policy) { - TypePrinter(policy).print(ty, qs, buffer); + SmallString<256> Buf; + llvm::raw_svector_ostream StrOS(Buf); + TypePrinter(policy).print(ty, qs, StrOS, buffer); + std::string str = StrOS.str(); + buffer.swap(str); } diff --git a/lib/AST/VTTBuilder.cpp b/lib/AST/VTTBuilder.cpp index f5ff624cf069..5ca4e862ef7b 100644 --- a/lib/AST/VTTBuilder.cpp +++ b/lib/AST/VTTBuilder.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/VTTBuilder.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/RecordLayout.h" #include "clang/Basic/TargetInfo.h" diff --git a/lib/AST/VTableBuilder.cpp b/lib/AST/VTableBuilder.cpp index 107d9fb78c50..104530fc1305 100644 --- a/lib/AST/VTableBuilder.cpp +++ b/lib/AST/VTableBuilder.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/VTableBuilder.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/RecordLayout.h" #include "clang/Basic/TargetInfo.h" @@ -164,7 +165,7 @@ FinalOverriders::FinalOverriders(const CXXRecordDecl *MostDerivedClass, SubobjectOffsets, SubobjectLayoutClassOffsets, SubobjectCounts); - // Get the the final overriders. + // Get the final overriders. CXXFinalOverriderMap FinalOverriders; MostDerivedClass->getFinalOverriders(FinalOverriders); @@ -630,7 +631,7 @@ VCallAndVBaseOffsetBuilder::AddVCallAndVBaseOffsets(BaseSubobject Base, // Get the base offset of the primary base. if (PrimaryBaseIsVirtual) { - assert(Layout.getVBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(Layout.getVBaseClassOffset(PrimaryBase).isZero() && "Primary vbase should have a zero offset!"); const ASTRecordLayout &MostDerivedClassLayout = @@ -639,7 +640,7 @@ VCallAndVBaseOffsetBuilder::AddVCallAndVBaseOffsets(BaseSubobject Base, PrimaryBaseOffset = MostDerivedClassLayout.getVBaseClassOffset(PrimaryBase); } else { - assert(Layout.getBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(Layout.getBaseClassOffset(PrimaryBase).isZero() && "Primary base should have a zero offset!"); PrimaryBaseOffset = Base.getBaseOffset(); @@ -682,7 +683,7 @@ void VCallAndVBaseOffsetBuilder::AddVCallOffsets(BaseSubobject Base, // primary base will have its vcall and vbase offsets emitted already. if (PrimaryBase && !Layout.isPrimaryBaseVirtual()) { // Get the base offset of the primary base. - assert(Layout.getBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(Layout.getBaseClassOffset(PrimaryBase).isZero() && "Primary base should have a zero offset!"); AddVCallOffsets(BaseSubobject(PrimaryBase, Base.getBaseOffset()), @@ -1370,7 +1371,7 @@ VTableBuilder::IsOverriderUsed(const CXXMethodDecl *Overrider, break; if (Layout.isPrimaryBaseVirtual()) { - assert(Layout.getVBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(Layout.getVBaseClassOffset(PrimaryBase).isZero() && "Primary base should always be at offset 0!"); const ASTRecordLayout &LayoutClassLayout = @@ -1384,7 +1385,7 @@ VTableBuilder::IsOverriderUsed(const CXXMethodDecl *Overrider, break; } } else { - assert(Layout.getBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(Layout.getBaseClassOffset(PrimaryBase).isZero() && "Primary base should always be at offset 0!"); } @@ -1436,7 +1437,7 @@ VTableBuilder::AddMethods(BaseSubobject Base, CharUnits BaseOffsetInLayoutClass, CharUnits PrimaryBaseOffset; CharUnits PrimaryBaseOffsetInLayoutClass; if (Layout.isPrimaryBaseVirtual()) { - assert(Layout.getVBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(Layout.getVBaseClassOffset(PrimaryBase).isZero() && "Primary vbase should have a zero offset!"); const ASTRecordLayout &MostDerivedClassLayout = @@ -1451,7 +1452,7 @@ VTableBuilder::AddMethods(BaseSubobject Base, CharUnits BaseOffsetInLayoutClass, PrimaryBaseOffsetInLayoutClass = LayoutClassLayout.getVBaseClassOffset(PrimaryBase); } else { - assert(Layout.getBaseClassOffsetInBits(PrimaryBase) == 0 && + assert(Layout.getBaseClassOffset(PrimaryBase).isZero() && "Primary base should have a zero offset!"); PrimaryBaseOffset = Base.getBaseOffset(); |