From b926dd8427655ec16e1c5a49094e8f4f47f43952 Mon Sep 17 00:00:00 2001 From: Aleksei Sidorin <a.sidorin@samsung.com> Date: Fri, 26 Jan 2018 11:36:54 +0000 Subject: [PATCH] [ASTImporter] Support LambdaExprs and improve template support Also, a number of style and bug fixes was done: * ASTImporterTest: added sanity check for source node * ExternalASTMerger: better lookup for template specializations * ASTImporter: don't add templated declarations into DeclContext * ASTImporter: introduce a helper, ImportTemplateArgumentListInfo getting SourceLocations * ASTImporter: proper set ParmVarDecls for imported FunctionProtoTypeLoc Differential Revision: https://reviews.llvm.org/D42301 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@323519 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ASTImporter.cpp | 311 +++++++++++++++--- lib/AST/ASTStructuralEquivalence.cpp | 48 ++- lib/AST/ExternalASTMerger.cpp | 36 +- .../class-template/Inputs/class-template1.cpp | 5 +- .../class-template/Inputs/class-template2.cpp | 4 +- test/ASTMerge/class-template/test.cpp | 36 +- test/ASTMerge/exprs-cpp/Inputs/exprs3.cpp | 17 + test/ASTMerge/exprs-cpp/test.cpp | 3 + .../function-cpp/Inputs/function-1.cpp | 8 + test/ASTMerge/function-cpp/test.cpp | 10 + .../template-specialization/Inputs/T.cpp | 4 + test/Import/template-specialization/test.cpp | 5 +- unittests/AST/ASTImporterTest.cpp | 35 +- 13 files changed, 435 insertions(+), 87 deletions(-) create mode 100644 test/ASTMerge/function-cpp/Inputs/function-1.cpp create mode 100644 test/ASTMerge/function-cpp/test.cpp diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 27b6ff1c51..aea044c8c4 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -97,6 +97,8 @@ namespace clang { typedef DesignatedInitExpr::Designator Designator; Designator ImportDesignator(const Designator &D); + Optional<LambdaCapture> ImportLambdaCapture(const LambdaCapture &From); + /// \brief What we should import from the definition. enum ImportDefinitionKind { @@ -127,16 +129,26 @@ namespace clang { bool ImportDefinition(ObjCProtocolDecl *From, ObjCProtocolDecl *To, ImportDefinitionKind Kind = IDK_Default); TemplateParameterList *ImportTemplateParameterList( - TemplateParameterList *Params); + TemplateParameterList *Params); TemplateArgument ImportTemplateArgument(const TemplateArgument &From); Optional<TemplateArgumentLoc> ImportTemplateArgumentLoc( const TemplateArgumentLoc &TALoc); bool ImportTemplateArguments(const TemplateArgument *FromArgs, unsigned NumFromArgs, - SmallVectorImpl<TemplateArgument> &ToArgs); + SmallVectorImpl<TemplateArgument> &ToArgs); + template <typename InContainerTy> bool ImportTemplateArgumentListInfo(const InContainerTy &Container, TemplateArgumentListInfo &ToTAInfo); + + template<typename InContainerTy> + bool ImportTemplateArgumentListInfo(SourceLocation FromLAngleLoc, + SourceLocation FromRAngleLoc, + const InContainerTy &Container, + TemplateArgumentListInfo &Result); + + bool ImportTemplateInformation(FunctionDecl *FromFD, FunctionDecl *ToFD); + bool IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord, bool Complain = true); bool IsStructuralMatch(VarDecl *FromVar, VarDecl *ToVar, @@ -295,6 +307,7 @@ namespace clang { Expr *VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr *E); Expr *VisitMemberExpr(MemberExpr *E); Expr *VisitCallExpr(CallExpr *E); + Expr *VisitLambdaExpr(LambdaExpr *LE); Expr *VisitInitListExpr(InitListExpr *E); Expr *VisitArrayInitLoopExpr(ArrayInitLoopExpr *E); Expr *VisitArrayInitIndexExpr(ArrayInitIndexExpr *E); @@ -1045,7 +1058,6 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To, = FromData.HasDeclaredCopyConstructorWithConstParam; ToData.HasDeclaredCopyAssignmentWithConstParam = FromData.HasDeclaredCopyAssignmentWithConstParam; - ToData.IsLambda = FromData.IsLambda; SmallVector<CXXBaseSpecifier *, 4> Bases; for (const auto &Base1 : FromCXX->bases()) { @@ -1256,6 +1268,9 @@ bool ASTNodeImporter::ImportTemplateArguments(const TemplateArgument *FromArgs, return false; } +// We cannot use Optional<> pattern here and below because +// TemplateArgumentListInfo's operator new is declared as deleted so it cannot +// be stored in Optional. template <typename InContainerTy> bool ASTNodeImporter::ImportTemplateArgumentListInfo( const InContainerTy &Container, TemplateArgumentListInfo &ToTAInfo) { @@ -1268,6 +1283,18 @@ bool ASTNodeImporter::ImportTemplateArgumentListInfo( return false; } +template <typename InContainerTy> +bool ASTNodeImporter::ImportTemplateArgumentListInfo( + SourceLocation FromLAngleLoc, SourceLocation FromRAngleLoc, + const InContainerTy &Container, TemplateArgumentListInfo &Result) { + TemplateArgumentListInfo ToTAInfo(Importer.Import(FromLAngleLoc), + Importer.Import(FromRAngleLoc)); + if (ImportTemplateArgumentListInfo(Container, ToTAInfo)) + return true; + Result = ToTAInfo; + return false; +} + bool ASTNodeImporter::IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord, bool Complain) { // Eliminate a potential failure point where we attempt to re-import @@ -1918,16 +1945,16 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { if (DCXX->getLambdaContextDecl() && !CDecl) return nullptr; D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), CDecl); - } else if (DCXX->isInjectedClassName()) { - // We have to be careful to do a similar dance to the one in - // Sema::ActOnStartCXXMemberDeclarations - CXXRecordDecl *const PrevDecl = nullptr; - const bool DelayTypeCreation = true; - D2CXX = CXXRecordDecl::Create( - Importer.getToContext(), D->getTagKind(), DC, StartLoc, Loc, - Name.getAsIdentifierInfo(), PrevDecl, DelayTypeCreation); - Importer.getToContext().getTypeDeclType( - D2CXX, llvm::dyn_cast<CXXRecordDecl>(DC)); + } else if (DCXX->isInjectedClassName()) { + // We have to be careful to do a similar dance to the one in + // Sema::ActOnStartCXXMemberDeclarations + CXXRecordDecl *const PrevDecl = nullptr; + const bool DelayTypeCreation = true; + D2CXX = CXXRecordDecl::Create( + Importer.getToContext(), D->getTagKind(), DC, StartLoc, Loc, + Name.getAsIdentifierInfo(), PrevDecl, DelayTypeCreation); + Importer.getToContext().getTypeDeclType( + D2CXX, llvm::dyn_cast<CXXRecordDecl>(DC)); } else { D2CXX = CXXRecordDecl::Create(Importer.getToContext(), D->getTagKind(), @@ -1936,6 +1963,9 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { } D2 = D2CXX; D2->setAccess(D->getAccess()); + D2->setLexicalDeclContext(LexicalDC); + if (!DCXX->getDescribedClassTemplate()) + LexicalDC->addDeclInternal(D2); Importer.Imported(D, D2); @@ -1964,11 +1994,11 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { } else { D2 = RecordDecl::Create(Importer.getToContext(), D->getTagKind(), DC, StartLoc, Loc, Name.getAsIdentifierInfo()); + D2->setLexicalDeclContext(LexicalDC); + LexicalDC->addDeclInternal(D2); } D2->setQualifierInfo(Importer.Import(D->getQualifierLoc())); - D2->setLexicalDeclContext(LexicalDC); - LexicalDC->addDeclInternal(D2); if (D->isAnonymousStructOrUnion()) D2->setAnonymousStructOrUnion(true); if (PrevDecl) { @@ -2044,6 +2074,94 @@ Decl *ASTNodeImporter::VisitEnumConstantDecl(EnumConstantDecl *D) { return ToEnumerator; } +bool ASTNodeImporter::ImportTemplateInformation(FunctionDecl *FromFD, + FunctionDecl *ToFD) { + switch (FromFD->getTemplatedKind()) { + case FunctionDecl::TK_NonTemplate: + case FunctionDecl::TK_FunctionTemplate: + break; + + case FunctionDecl::TK_MemberSpecialization: { + auto *InstFD = cast_or_null<FunctionDecl>( + Importer.Import(FromFD->getInstantiatedFromMemberFunction())); + if (!InstFD) + return true; + + TemplateSpecializationKind TSK = FromFD->getTemplateSpecializationKind(); + SourceLocation POI = Importer.Import( + FromFD->getMemberSpecializationInfo()->getPointOfInstantiation()); + ToFD->setInstantiationOfMemberFunction(InstFD, TSK); + ToFD->getMemberSpecializationInfo()->setPointOfInstantiation(POI); + break; + } + + case FunctionDecl::TK_FunctionTemplateSpecialization: { + auto *FTSInfo = FromFD->getTemplateSpecializationInfo(); + auto *Template = cast_or_null<FunctionTemplateDecl>( + Importer.Import(FTSInfo->getTemplate())); + if (!Template) + return true; + TemplateSpecializationKind TSK = FTSInfo->getTemplateSpecializationKind(); + + // Import template arguments. + auto TemplArgs = FTSInfo->TemplateArguments->asArray(); + SmallVector<TemplateArgument, 8> ToTemplArgs; + if (ImportTemplateArguments(TemplArgs.data(), TemplArgs.size(), + ToTemplArgs)) + return true; + + TemplateArgumentList *ToTAList = TemplateArgumentList::CreateCopy( + Importer.getToContext(), ToTemplArgs); + + TemplateArgumentListInfo ToTAInfo; + const auto *FromTAArgsAsWritten = FTSInfo->TemplateArgumentsAsWritten; + if (FromTAArgsAsWritten) { + if (ImportTemplateArgumentListInfo( + FromTAArgsAsWritten->LAngleLoc, FromTAArgsAsWritten->RAngleLoc, + FromTAArgsAsWritten->arguments(), ToTAInfo)) + return true; + } + + SourceLocation POI = Importer.Import(FTSInfo->getPointOfInstantiation()); + + ToFD->setFunctionTemplateSpecialization( + Template, ToTAList, /* InsertPos= */ nullptr, + TSK, FromTAArgsAsWritten ? &ToTAInfo : nullptr, POI); + break; + } + + case FunctionDecl::TK_DependentFunctionTemplateSpecialization: { + auto *FromInfo = FromFD->getDependentSpecializationInfo(); + UnresolvedSet<8> TemplDecls; + unsigned NumTemplates = FromInfo->getNumTemplates(); + for (unsigned I = 0; I < NumTemplates; I++) { + if (auto *ToFTD = cast_or_null<FunctionTemplateDecl>( + Importer.Import(FromInfo->getTemplate(I)))) + TemplDecls.addDecl(ToFTD); + else + return true; + } + + // Import TemplateArgumentListInfo. + TemplateArgumentListInfo ToTAInfo; + if (ImportTemplateArgumentListInfo( + FromInfo->getLAngleLoc(), FromInfo->getRAngleLoc(), + llvm::makeArrayRef(FromInfo->getTemplateArgs(), + FromInfo->getNumTemplateArgs()), + ToTAInfo)) + return true; + + ToFD->setDependentTemplateSpecialization(Importer.getToContext(), + TemplDecls, ToTAInfo); + break; + } + default: + llvm_unreachable("All cases should be covered!"); + } + + return false; +} + Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { // Import the major distinguishing characteristics of this function. DeclContext *DC, *LexicalDC; @@ -2151,15 +2269,18 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { Parameters.push_back(ToP); } - // Create the imported function. TypeSourceInfo *TInfo = Importer.Import(D->getTypeSourceInfo()); + if (D->getTypeSourceInfo() && !TInfo) + return nullptr; + + // Create the imported function. FunctionDecl *ToFunction = nullptr; SourceLocation InnerLocStart = Importer.Import(D->getInnerLocStart()); if (CXXConstructorDecl *FromConstructor = dyn_cast<CXXConstructorDecl>(D)) { ToFunction = CXXConstructorDecl::Create(Importer.getToContext(), cast<CXXRecordDecl>(DC), InnerLocStart, - NameInfo, T, TInfo, + NameInfo, T, TInfo, FromConstructor->isExplicit(), D->isInlineSpecified(), D->isImplicit(), @@ -2225,9 +2346,9 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { Importer.Imported(D, ToFunction); // Set the parameters. - for (unsigned I = 0, N = Parameters.size(); I != N; ++I) { - Parameters[I]->setOwningFunction(ToFunction); - ToFunction->addDeclInternal(Parameters[I]); + for (ParmVarDecl *Param : Parameters) { + Param->setOwningFunction(ToFunction); + ToFunction->addDeclInternal(Param); } ToFunction->setParams(Parameters); @@ -2237,6 +2358,16 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { ToFunction->setPreviousDecl(Recent); } + // We need to complete creation of FunctionProtoTypeLoc manually with setting + // params it refers to. + if (TInfo) { + if (auto ProtoLoc = + TInfo->getTypeLoc().IgnoreParens().getAs<FunctionProtoTypeLoc>()) { + for (unsigned I = 0, N = Parameters.size(); I != N; ++I) + ProtoLoc.setParam(I, Parameters[I]); + } + } + if (usedDifferentExceptionSpec) { // Update FunctionProtoType::ExtProtoInfo. QualType T = Importer.Import(D->getType()); @@ -2254,8 +2385,17 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { // FIXME: Other bits to merge? + // If it is a template, import all related things. + if (ImportTemplateInformation(D, ToFunction)) + return nullptr; + // Add this function to the lexical context. - LexicalDC->addDeclInternal(ToFunction); + // NOTE: If the function is templated declaration, it should be not added into + // LexicalDC. But described template is imported during import of + // FunctionTemplateDecl (it happens later). So, we use source declaration + // to determine if we should add the result function. + if (!D->getDescribedFunctionTemplate()) + LexicalDC->addDeclInternal(ToFunction); if (auto *FromCXXMethod = dyn_cast<CXXMethodDecl>(D)) ImportOverrides(cast<CXXMethodDecl>(ToFunction), FromCXXMethod); @@ -2749,6 +2889,14 @@ Decl *ASTNodeImporter::VisitParmVarDecl(ParmVarDecl *D) { if (FromDefArg && !ToDefArg) return nullptr; + if (D->isObjCMethodParameter()) { + ToParm->setObjCMethodScopeInfo(D->getFunctionScopeIndex()); + ToParm->setObjCDeclQualifier(D->getObjCDeclQualifier()); + } else { + ToParm->setScopeInfo(D->getFunctionScopeDepth(), + D->getFunctionScopeIndex()); + } + if (D->isUsed()) ToParm->setIsUsed(); @@ -3850,12 +3998,12 @@ Decl *ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) { return nullptr; } - CXXRecordDecl *DTemplated = D->getTemplatedDecl(); - + CXXRecordDecl *FromTemplated = D->getTemplatedDecl(); + // Create the declaration that is being templated. - CXXRecordDecl *D2Templated = cast_or_null<CXXRecordDecl>( - Importer.Import(DTemplated)); - if (!D2Templated) + CXXRecordDecl *ToTemplated = cast_or_null<CXXRecordDecl>( + Importer.Import(FromTemplated)); + if (!ToTemplated) return nullptr; // Resolve possible cyclic import. @@ -3863,15 +4011,15 @@ Decl *ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) { return AlreadyImported; // Create the class template declaration itself. - TemplateParameterList *TemplateParams - = ImportTemplateParameterList(D->getTemplateParameters()); + TemplateParameterList *TemplateParams = + ImportTemplateParameterList(D->getTemplateParameters()); if (!TemplateParams) return nullptr; ClassTemplateDecl *D2 = ClassTemplateDecl::Create(Importer.getToContext(), DC, Loc, Name, TemplateParams, - D2Templated); - D2Templated->setDescribedClassTemplate(D2); + ToTemplated); + ToTemplated->setDescribedClassTemplate(D2); D2->setAccess(D->getAccess()); D2->setLexicalDeclContext(LexicalDC); @@ -3879,10 +4027,10 @@ Decl *ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) { // Note the relationship between the class templates. Importer.Imported(D, D2); - Importer.Imported(DTemplated, D2Templated); + Importer.Imported(FromTemplated, ToTemplated); - if (DTemplated->isCompleteDefinition() && - !D2Templated->isCompleteDefinition()) { + if (FromTemplated->isCompleteDefinition() && + !ToTemplated->isCompleteDefinition()) { // FIXME: Import definition! } @@ -3958,12 +4106,8 @@ Decl *ASTNodeImporter::VisitClassTemplateSpecializationDecl( // Import TemplateArgumentListInfo TemplateArgumentListInfo ToTAInfo; auto &ASTTemplateArgs = *PartialSpec->getTemplateArgsAsWritten(); - for (unsigned I = 0, E = ASTTemplateArgs.NumTemplateArgs; I < E; ++I) { - if (auto ToLoc = ImportTemplateArgumentLoc(ASTTemplateArgs[I])) - ToTAInfo.addArgument(*ToLoc); - else - return nullptr; - } + if (ImportTemplateArgumentListInfo(ASTTemplateArgs.arguments(), ToTAInfo)) + return nullptr; QualType CanonInjType = Importer.Import( PartialSpec->getInjectedSpecializationType()); @@ -4901,12 +5045,8 @@ Expr *ASTNodeImporter::VisitDeclRefExpr(DeclRefExpr *E) { TemplateArgumentListInfo ToTAInfo; TemplateArgumentListInfo *ResInfo = nullptr; if (E->hasExplicitTemplateArgs()) { - for (const auto &FromLoc : E->template_arguments()) { - if (auto ToTALoc = ImportTemplateArgumentLoc(FromLoc)) - ToTAInfo.addArgument(*ToTALoc); - else - return nullptr; - } + if (ImportTemplateArgumentListInfo(E->template_arguments(), ToTAInfo)) + return nullptr; ResInfo = &ToTAInfo; } @@ -5861,11 +6001,10 @@ Expr *ASTNodeImporter::VisitCXXDependentScopeMemberExpr( if (BaseType.isNull()) return nullptr; - TemplateArgumentListInfo ToTAInfo(Importer.Import(E->getLAngleLoc()), - Importer.Import(E->getRAngleLoc())); - TemplateArgumentListInfo *ResInfo = nullptr; + TemplateArgumentListInfo ToTAInfo, *ResInfo = nullptr; if (E->hasExplicitTemplateArgs()) { - if (ImportTemplateArgumentListInfo(E->template_arguments(), ToTAInfo)) + if (ImportTemplateArgumentListInfo(E->getLAngleLoc(), E->getRAngleLoc(), + E->template_arguments(), ToTAInfo)) return nullptr; ResInfo = &ToTAInfo; } @@ -5926,11 +6065,10 @@ Expr *ASTNodeImporter::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E) { return nullptr; } - TemplateArgumentListInfo ToTAInfo(Importer.Import(E->getLAngleLoc()), - Importer.Import(E->getRAngleLoc())); - TemplateArgumentListInfo *ResInfo = nullptr; + TemplateArgumentListInfo ToTAInfo, *ResInfo = nullptr; if (E->hasExplicitTemplateArgs()) { - if (ImportTemplateArgumentListInfo(E->template_arguments(), ToTAInfo)) + if (ImportTemplateArgumentListInfo(E->getLAngleLoc(), E->getRAngleLoc(), + E->template_arguments(), ToTAInfo)) return nullptr; ResInfo = &ToTAInfo; } @@ -5981,6 +6119,73 @@ Expr *ASTNodeImporter::VisitCallExpr(CallExpr *E) { Importer.Import(E->getRParenLoc())); } +Optional<LambdaCapture> +ASTNodeImporter::ImportLambdaCapture(const LambdaCapture &From) { + VarDecl *Var = nullptr; + if (From.capturesVariable()) { + Var = cast_or_null<VarDecl>(Importer.Import(From.getCapturedVar())); + if (!Var) + return None; + } + + return LambdaCapture(Importer.Import(From.getLocation()), From.isImplicit(), + From.getCaptureKind(), Var, + From.isPackExpansion() + ? Importer.Import(From.getEllipsisLoc()) + : SourceLocation()); +} + +Expr *ASTNodeImporter::VisitLambdaExpr(LambdaExpr *LE) { + CXXRecordDecl *FromClass = LE->getLambdaClass(); + auto *ToClass = dyn_cast_or_null<CXXRecordDecl>(Importer.Import(FromClass)); + if (!ToClass) + return nullptr; + + // NOTE: lambda classes are created with BeingDefined flag set up. + // It means that ImportDefinition doesn't work for them and we should fill it + // manually. + if (ToClass->isBeingDefined()) { + for (auto FromField : FromClass->fields()) { + auto *ToField = cast_or_null<FieldDecl>(Importer.Import(FromField)); + if (!ToField) + return nullptr; + } + } + + auto *ToCallOp = dyn_cast_or_null<CXXMethodDecl>( + Importer.Import(LE->getCallOperator())); + if (!ToCallOp) + return nullptr; + + ToClass->completeDefinition(); + + unsigned NumCaptures = LE->capture_size(); + SmallVector<LambdaCapture, 8> Captures; + Captures.reserve(NumCaptures); + for (const auto &FromCapture : LE->captures()) { + if (auto ToCapture = ImportLambdaCapture(FromCapture)) + Captures.push_back(*ToCapture); + else + return nullptr; + } + + SmallVector<Expr *, 8> InitCaptures(NumCaptures); + if (ImportContainerChecked(LE->capture_inits(), InitCaptures)) + return nullptr; + + return LambdaExpr::Create(Importer.getToContext(), ToClass, + Importer.Import(LE->getIntroducerRange()), + LE->getCaptureDefault(), + Importer.Import(LE->getCaptureDefaultLoc()), + Captures, + LE->hasExplicitParameters(), + LE->hasExplicitResultType(), + InitCaptures, + Importer.Import(LE->getLocEnd()), + LE->containsUnexpandedParameterPack()); +} + + Expr *ASTNodeImporter::VisitInitListExpr(InitListExpr *ILE) { QualType T = Importer.Import(ILE->getType()); if (T.isNull()) diff --git a/lib/AST/ASTStructuralEquivalence.cpp b/lib/AST/ASTStructuralEquivalence.cpp index 0df8e5653f..a563c7e2fa 100644 --- a/lib/AST/ASTStructuralEquivalence.cpp +++ b/lib/AST/ASTStructuralEquivalence.cpp @@ -1153,12 +1153,22 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, D2->getTemplateParameters()); } +static bool IsTemplateDeclCommonStructurallyEquivalent( + StructuralEquivalenceContext &Ctx, TemplateDecl *D1, TemplateDecl *D2) { + if (!IsStructurallyEquivalent(D1->getIdentifier(), D2->getIdentifier())) + return false; + if (!D1->getIdentifier()) // Special name + if (D1->getNameAsString() != D2->getNameAsString()) + return false; + return IsStructurallyEquivalent(Ctx, D1->getTemplateParameters(), + D2->getTemplateParameters()); +} + static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, ClassTemplateDecl *D1, ClassTemplateDecl *D2) { // Check template parameters. - if (!IsStructurallyEquivalent(Context, D1->getTemplateParameters(), - D2->getTemplateParameters())) + if (!IsTemplateDeclCommonStructurallyEquivalent(Context, D1, D2)) return false; // Check the templated declaration. @@ -1166,6 +1176,18 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, D2->getTemplatedDecl()); } +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + FunctionTemplateDecl *D1, + FunctionTemplateDecl *D2) { + // Check template parameters. + if (!IsTemplateDeclCommonStructurallyEquivalent(Context, D1, D2)) + return false; + + // Check the templated declaration. + return Context.IsStructurallyEquivalent(D1->getTemplatedDecl()->getType(), + D2->getTemplatedDecl()->getType()); +} + /// Determine structural equivalence of two declarations. static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, Decl *D1, Decl *D2) { @@ -1293,6 +1315,7 @@ bool StructuralEquivalenceContext::Finish() { // Record/non-record mismatch. Equivalent = false; } + } else if (EnumDecl *Enum1 = dyn_cast<EnumDecl>(D1)) { if (EnumDecl *Enum2 = dyn_cast<EnumDecl>(D2)) { // Check for equivalent enum names. @@ -1309,6 +1332,7 @@ bool StructuralEquivalenceContext::Finish() { // Enum/non-enum mismatch Equivalent = false; } + } else if (TypedefNameDecl *Typedef1 = dyn_cast<TypedefNameDecl>(D1)) { if (TypedefNameDecl *Typedef2 = dyn_cast<TypedefNameDecl>(D2)) { if (!::IsStructurallyEquivalent(Typedef1->getIdentifier(), @@ -1320,17 +1344,30 @@ bool StructuralEquivalenceContext::Finish() { // Typedef/non-typedef mismatch. Equivalent = false; } + } else if (ClassTemplateDecl *ClassTemplate1 = dyn_cast<ClassTemplateDecl>(D1)) { if (ClassTemplateDecl *ClassTemplate2 = dyn_cast<ClassTemplateDecl>(D2)) { - if (!::IsStructurallyEquivalent(ClassTemplate1->getIdentifier(), - ClassTemplate2->getIdentifier()) || - !::IsStructurallyEquivalent(*this, ClassTemplate1, ClassTemplate2)) + if (!::IsStructurallyEquivalent(*this, ClassTemplate1, + ClassTemplate2)) Equivalent = false; } else { // Class template/non-class-template mismatch. Equivalent = false; } + + } else if (FunctionTemplateDecl *FunctionTemplate1 = + dyn_cast<FunctionTemplateDecl>(D1)) { + if (FunctionTemplateDecl *FunctionTemplate2 = + dyn_cast<FunctionTemplateDecl>(D2)) { + if (!::IsStructurallyEquivalent(*this, FunctionTemplate1, + FunctionTemplate2)) + Equivalent = false; + } else { + // Class template/non-class-template mismatch. + Equivalent = false; + } + } else if (TemplateTypeParmDecl *TTP1 = dyn_cast<TemplateTypeParmDecl>(D1)) { if (TemplateTypeParmDecl *TTP2 = dyn_cast<TemplateTypeParmDecl>(D2)) { @@ -1350,6 +1387,7 @@ bool StructuralEquivalenceContext::Finish() { // Kind mismatch. Equivalent = false; } + } else if (TemplateTemplateParmDecl *TTP1 = dyn_cast<TemplateTemplateParmDecl>(D1)) { if (TemplateTemplateParmDecl *TTP2 = diff --git a/lib/AST/ExternalASTMerger.cpp b/lib/AST/ExternalASTMerger.cpp index 6b75c51c64..91de8dbca0 100644 --- a/lib/AST/ExternalASTMerger.cpp +++ b/lib/AST/ExternalASTMerger.cpp @@ -16,6 +16,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/ExternalASTMerger.h" using namespace clang; @@ -351,6 +352,27 @@ void ExternalASTMerger::RemoveSources(llvm::ArrayRef<ImporterSource> Sources) { } } +template <typename DeclTy> +static bool importSpecializations(DeclTy *D, ASTImporter *Importer) { + for (auto *Spec : D->specializations()) + if (!Importer->Import(Spec)) + return true; + return false; +} + +/// Imports specializations from template declarations that can be specialized. +static bool importSpecializationsIfNeeded(Decl *D, ASTImporter *Importer) { + if (!isa<TemplateDecl>(D)) + return false; + if (auto *FunctionTD = dyn_cast<FunctionTemplateDecl>(D)) + return importSpecializations(FunctionTD, Importer); + else if (auto *ClassTD = dyn_cast<ClassTemplateDecl>(D)) + return importSpecializations(ClassTD, Importer); + else if (auto *VarTD = dyn_cast<VarTemplateDecl>(D)) + return importSpecializations(VarTD, Importer); + return false; +} + bool ExternalASTMerger::FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) { llvm::SmallVector<NamedDecl *, 1> Decls; @@ -376,9 +398,17 @@ bool ExternalASTMerger::FindExternalVisibleDeclsByName(const DeclContext *DC, Decls.reserve(Candidates.size()); for (const Candidate &C : Candidates) { - NamedDecl *d = cast<NamedDecl>(C.second->Import(C.first.get())); - assert(d); - Decls.push_back(d); + Decl *LookupRes = C.first.get(); + ASTImporter *Importer = C.second; + NamedDecl *ND = cast_or_null<NamedDecl>(Importer->Import(LookupRes)); + assert(ND); + // If we don't import specialization, they are not available via lookup + // because the lookup result is imported TemplateDecl and it does not + // reference its specializations until they are imported explicitly. + bool IsSpecImportFailed = + importSpecializationsIfNeeded(LookupRes, Importer); + assert(!IsSpecImportFailed); + Decls.push_back(ND); } SetExternalVisibleDeclsForName(DC, Name, Decls); return true; diff --git a/test/ASTMerge/class-template/Inputs/class-template1.cpp b/test/ASTMerge/class-template/Inputs/class-template1.cpp index 440b5abfc8..fb5b229e0a 100644 --- a/test/ASTMerge/class-template/Inputs/class-template1.cpp +++ b/test/ASTMerge/class-template/Inputs/class-template1.cpp @@ -1,5 +1,7 @@ template<typename T> -struct X0; +struct X0 { + T getValue(T arg) { return arg; } +}; template<int I> struct X1; @@ -26,6 +28,7 @@ extern X0<float> *x0r; template<> struct X0<char> { int member; + char getValue(char ch) { return static_cast<char>(member); } }; template<> diff --git a/test/ASTMerge/class-template/Inputs/class-template2.cpp b/test/ASTMerge/class-template/Inputs/class-template2.cpp index 6300301a4f..b5d0add13f 100644 --- a/test/ASTMerge/class-template/Inputs/class-template2.cpp +++ b/test/ASTMerge/class-template/Inputs/class-template2.cpp @@ -1,5 +1,7 @@ template<class T> -struct X0; +struct X0 { + T getValue(T arg); +}; template<int I> struct X1; diff --git a/test/ASTMerge/class-template/test.cpp b/test/ASTMerge/class-template/test.cpp index 0ab5443db7..7e25c5d6cc 100644 --- a/test/ASTMerge/class-template/test.cpp +++ b/test/ASTMerge/class-template/test.cpp @@ -1,24 +1,28 @@ -// RUN: %clang_cc1 -emit-pch -o %t.1.ast %S/Inputs/class-template1.cpp -// RUN: %clang_cc1 -emit-pch -o %t.2.ast %S/Inputs/class-template2.cpp -// RUN: not %clang_cc1 -ast-merge %t.1.ast -ast-merge %t.2.ast -fsyntax-only %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++1z -emit-pch -o %t.1.ast %S/Inputs/class-template1.cpp +// RUN: %clang_cc1 -std=c++1z -emit-pch -o %t.2.ast %S/Inputs/class-template2.cpp +// RUN: not %clang_cc1 -std=c++1z -ast-merge %t.1.ast -ast-merge %t.2.ast -fsyntax-only %s 2>&1 | FileCheck %s -// CHECK: class-template1.cpp:7:14: error: non-type template parameter declared with incompatible types in different translation units ('int' vs. 'long') -// CHECK: class-template2.cpp:7:15: note: declared here with type 'long' +static_assert(sizeof(X0<char>().getValue(1)) == sizeof(char)); +static_assert(sizeof(X0<int>().getValue(1)) == sizeof(int)); -// CHECK: class-template1.cpp:10:14: error: template parameter has different kinds in different translation units -// CHECK: class-template2.cpp:10:10: note: template parameter declared here +// CHECK: class-template1.cpp:9:14: error: non-type template parameter declared with incompatible types in different translation units ('int' vs. 'long') +// CHECK: class-template2.cpp:9:15: note: declared here with type 'long' -// CHECK: class-template1.cpp:16:23: error: non-type template parameter declared with incompatible types in different translation units ('long' vs. 'int') -// CHECK: class-template2.cpp:16:23: note: declared here with type 'int' +// CHECK: class-template1.cpp:12:14: error: template parameter has different kinds in different translation units +// CHECK: class-template2.cpp:12:10: note: template parameter declared here -// CHECK: class-template1.cpp:19:10: error: template parameter has different kinds in different translation units -// CHECK: class-template2.cpp:19:10: note: template parameter declared here +// CHECK: class-template1.cpp:18:23: error: non-type template parameter declared with incompatible types in different translation units ('long' vs. 'int') +// CHECK: class-template2.cpp:18:23: note: declared here with type 'int' -// CHECK: class-template2.cpp:25:20: error: external variable 'x0r' declared with incompatible types in different translation units ('X0<double> *' vs. 'X0<float> *') -// CHECK: class-template1.cpp:24:19: note: declared here with type 'X0<float> *' +// CHECK: class-template1.cpp:21:10: error: template parameter has different kinds in different translation units +// CHECK: class-template2.cpp:21:10: note: template parameter declared here -// CHECK: class-template1.cpp:32:8: warning: type 'X0<wchar_t>' has incompatible definitions in different translation units -// CHECK: class-template1.cpp:33:7: note: field 'member' has type 'int' here -// CHECK: class-template2.cpp:34:9: note: field 'member' has type 'float' here +// CHECK: class-template2.cpp:27:20: error: external variable 'x0r' declared with incompatible types in different translation units ('X0<double> *' vs. 'X0<float> *') +// CHECK: class-template1.cpp:26:19: note: declared here with type 'X0<float> *' + +// CHECK: class-template1.cpp:35:8: warning: type 'X0<wchar_t>' has incompatible definitions in different translation units +// CHECK: class-template1.cpp:36:7: note: field 'member' has type 'int' here +// CHECK: class-template2.cpp:36:9: note: field 'member' has type 'float' here // CHECK: 1 warning and 5 errors generated. +// CHECK-NOT: static_assert diff --git a/test/ASTMerge/exprs-cpp/Inputs/exprs3.cpp b/test/ASTMerge/exprs-cpp/Inputs/exprs3.cpp index 2a33c35d9e..6fdc33fb39 100644 --- a/test/ASTMerge/exprs-cpp/Inputs/exprs3.cpp +++ b/test/ASTMerge/exprs-cpp/Inputs/exprs3.cpp @@ -122,3 +122,20 @@ void useTemplateType() { const bool ExpressionTrait = __is_lvalue_expr(1); const unsigned ArrayRank = __array_rank(int[10][20]); const unsigned ArrayExtent = __array_extent(int[10][20], 1); + +constexpr int testLambdaAdd(int toAdd) { + const int Captured1 = 1, Captured2 = 2; + constexpr auto LambdaAdd = [Captured1, Captured2](int k) -> int { + return Captured1 + Captured2 + k; + }; + return LambdaAdd(toAdd); +} + +template<typename T> +struct TestLambdaTemplate { + T i, j; + TestLambdaTemplate(T i, const T &j) : i(i), j(j) {} + T testLambda(T k) { + return [this](T k) -> decltype(auto) { return i + j + k; }(k); + } +}; diff --git a/test/ASTMerge/exprs-cpp/test.cpp b/test/ASTMerge/exprs-cpp/test.cpp index 0535aa8533..c0b282ec02 100644 --- a/test/ASTMerge/exprs-cpp/test.cpp +++ b/test/ASTMerge/exprs-cpp/test.cpp @@ -30,6 +30,8 @@ static_assert(ExpressionTrait == false); static_assert(ArrayRank == 2); static_assert(ArrayExtent == 20); +static_assert(testLambdaAdd(3) == 6); + void testImport(int *x, const S1 &cs1, S1 &s1) { testNewThrowDelete(); testArrayElement(nullptr, 12); @@ -44,4 +46,5 @@ void testImport(int *x, const S1 &cs1, S1 &s1) { testDefaultArg(); testDefaultArgExpr(); useTemplateType(); + TestLambdaTemplate<int>(1, 2).testLambda(3); } diff --git a/test/ASTMerge/function-cpp/Inputs/function-1.cpp b/test/ASTMerge/function-cpp/Inputs/function-1.cpp new file mode 100644 index 0000000000..ee97a1a8a5 --- /dev/null +++ b/test/ASTMerge/function-cpp/Inputs/function-1.cpp @@ -0,0 +1,8 @@ + +template<typename T> constexpr T add(T arg1, T arg2) { + return arg1 + arg2; +} + +template<> constexpr int add(int arg1, int arg2) { + return arg1 + arg2 + 2; +} diff --git a/test/ASTMerge/function-cpp/test.cpp b/test/ASTMerge/function-cpp/test.cpp new file mode 100644 index 0000000000..304ce3c634 --- /dev/null +++ b/test/ASTMerge/function-cpp/test.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -std=c++1z -emit-pch -o %t.1.ast %S/Inputs/function-1.cpp +// RUN: %clang_cc1 -std=c++1z -ast-merge %t.1.ast -fsyntax-only %s 2>&1 | FileCheck %s +// XFAIL: * + +static_assert(add(1, 2) == 5); + +// FIXME: support of templated function overload is still not implemented. +static_assert(add('\1', '\2') == 3); + +// CHECK-NOT: static_assert diff --git a/test/Import/template-specialization/Inputs/T.cpp b/test/Import/template-specialization/Inputs/T.cpp index b31e2439ef..7eea958290 100644 --- a/test/Import/template-specialization/Inputs/T.cpp +++ b/test/Import/template-specialization/Inputs/T.cpp @@ -12,3 +12,7 @@ template <> struct A<bool> { int g; }; }; + + +template <typename T> constexpr int f() { return 0; } +template <> constexpr int f<int>() { return 4; } diff --git a/test/Import/template-specialization/test.cpp b/test/Import/template-specialization/test.cpp index 43996c53a7..30df818760 100644 --- a/test/Import/template-specialization/test.cpp +++ b/test/Import/template-specialization/test.cpp @@ -1,7 +1,10 @@ // RUN: clang-import-test -import %S/Inputs/T.cpp -expression %s -// XFAIL: * + void expr() { A<int>::B b1; A<bool>::B b2; b1.f + b2.g; } + +static_assert(f<char>() == 0, ""); +static_assert(f<int>() == 4, ""); diff --git a/unittests/AST/ASTImporterTest.cpp b/unittests/AST/ASTImporterTest.cpp index 5b87876989..277d16fa3b 100644 --- a/unittests/AST/ASTImporterTest.cpp +++ b/unittests/AST/ASTImporterTest.cpp @@ -11,9 +11,9 @@ // //===----------------------------------------------------------------------===// +#include "MatchVerifier.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTImporter.h" -#include "MatchVerifier.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Tooling/Tooling.h" @@ -99,7 +99,11 @@ testImport(const std::string &FromCode, const ArgVector &FromArgs, if (FoundDecls.size() != 1) return testing::AssertionFailure() << "Multiple declarations were found!"; - auto Imported = Importer.Import(*FoundDecls.begin()); + // Sanity check: the node being imported should match in the same way as + // the result node. + EXPECT_TRUE(Verifier.match(FoundDecls.front(), AMatcher)); + + auto Imported = Importer.Import(FoundDecls.front()); if (!Imported) return testing::AssertionFailure() << "Import failed, nullptr returned!"; @@ -624,7 +628,7 @@ TEST(ImportExpr, ImportCXXPseudoDestructorExpr) { TEST(ImportDecl, ImportUsingDecl) { MatchVerifier<Decl> Verifier; testImport("namespace foo { int bar; }" - "int declToImport(){ using foo::bar; }", + "void declToImport() { using foo::bar; }", Lang_CXX, "", Lang_CXX, Verifier, functionDecl( has( @@ -665,13 +669,13 @@ TEST(ImportExpr, ImportUnresolvedLookupExpr) { "}" "void instantiate() { declToImport<int>(); }", Lang_CXX, "", Lang_CXX, Verifier, - functionTemplateDecl(has(functionDecl(has( - compoundStmt(has(unresolvedLookupExpr()))))))); + functionTemplateDecl(has(functionDecl( + has(compoundStmt(has(unresolvedLookupExpr()))))))); } TEST(ImportExpr, ImportCXXUnresolvedConstructExpr) { MatchVerifier<Decl> Verifier; - testImport("template <typename T> class C { T t; };" + testImport("template <typename T> struct C { T t; };" "template <typename T> void declToImport() {" " C<T> d;" " d.t = T();" @@ -680,7 +684,7 @@ TEST(ImportExpr, ImportCXXUnresolvedConstructExpr) { Lang_CXX, "", Lang_CXX, Verifier, functionTemplateDecl(has(functionDecl(has(compoundStmt(has( binaryOperator(has(cxxUnresolvedConstructExpr()))))))))); - testImport("template <typename T> class C { T t; };" + testImport("template <typename T> struct C { T t; };" "template <typename T> void declToImport() {" " C<T> d;" " (&d)->t = T();" @@ -691,5 +695,22 @@ TEST(ImportExpr, ImportCXXUnresolvedConstructExpr) { binaryOperator(has(cxxUnresolvedConstructExpr()))))))))); } +/// Check that function "declToImport()" (which is the templated function +/// for corresponding FunctionTemplateDecl) is not added into DeclContext. +/// Same for class template declarations. +TEST(ImportDecl, ImportTemplatedDeclForTemplate) { + MatchVerifier<Decl> Verifier; + testImport("template <typename T> void declToImport() { T a = 1; }" + "void instantiate() { declToImport<int>(); }", + Lang_CXX, "", Lang_CXX, Verifier, + functionTemplateDecl(hasAncestor(translationUnitDecl( + unless(has(functionDecl(hasName("declToImport")))))))); + testImport("template <typename T> struct declToImport { T t; };" + "void instantiate() { declToImport<int>(); }", + Lang_CXX, "", Lang_CXX, Verifier, + classTemplateDecl(hasAncestor(translationUnitDecl( + unless(has(cxxRecordDecl(hasName("declToImport")))))))); +} + } // end namespace ast_matchers } // end namespace clang -- 2.40.0