From dd59d27a1f8aa993187f8476cccf98bcc9f24ec2 Mon Sep 17 00:00:00 2001 From: Gabor Marton Date: Tue, 19 Mar 2019 14:04:50 +0000 Subject: [PATCH] [ASTImporter] Fix redecl failures of FunctionTemplateSpec Summary: Redecl chains of function template specializations are not handled well currently. We want to handle them similarly to functions, i.e. try to keep the structure of the original AST as much as possible. The aim is to not squash a prototype with a definition, rather we create both and put them in a redecl chain. Reviewers: a_sidorin, shafik, a.sidorin Subscribers: rnkovacs, dkrupp, Szelethus, gamesh411, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D58668 llvm-svn: 356455 --- clang/lib/AST/ASTImporter.cpp | 21 +++-- clang/unittests/AST/ASTImporterTest.cpp | 137 ++++++++++++++------------------ 2 files changed, 74 insertions(+), 84 deletions(-) diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index b845492..aee36e0 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -290,6 +290,16 @@ namespace clang { ToD->setImplicit(); } + // Check if we have found an existing definition. Returns with that + // definition if yes, otherwise returns null. + Decl *FindAndMapDefinition(FunctionDecl *D, FunctionDecl *FoundFunction) { + const FunctionDecl *Definition = nullptr; + if (D->doesThisDeclarationHaveABody() && + FoundFunction->hasBody(Definition)) + return Importer.MapImported(D, const_cast(Definition)); + return nullptr; + } + public: explicit ASTNodeImporter(ASTImporter &Importer) : Importer(Importer) {} @@ -2973,8 +2983,8 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { if (!FoundFunctionOrErr) return FoundFunctionOrErr.takeError(); if (FunctionDecl *FoundFunction = *FoundFunctionOrErr) { - if (D->doesThisDeclarationHaveABody() && FoundFunction->hasBody()) - return Importer.MapImported(D, FoundFunction); + if (Decl *Def = FindAndMapDefinition(D, FoundFunction)) + return Def; FoundByLookup = FoundFunction; } } @@ -2993,11 +3003,8 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { continue; if (IsStructuralMatch(D, FoundFunction)) { - const FunctionDecl *Definition = nullptr; - if (D->doesThisDeclarationHaveABody() && - FoundFunction->hasBody(Definition)) - return Importer.MapImported(D, - const_cast(Definition)); + if (Decl *Def = FindAndMapDefinition(D, FoundFunction)) + return Def; FoundByLookup = FoundFunction; break; } diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index 497d220..d6bd134 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -3978,6 +3978,45 @@ struct RedeclChain : ASTImporterOptionSpecificTestBase { std::string getDefinition() { return TypeParam::Definition; } BindableMatcher getPattern() const { return TypeParam().getPattern(); } + void CheckPreviousDecl(Decl *Prev, Decl *Current) { + ASSERT_NE(Prev, Current); + ASSERT_EQ(&Prev->getASTContext(), &Current->getASTContext()); + EXPECT_EQ(Prev->getCanonicalDecl(), Current->getCanonicalDecl()); + + // Templates. + if (auto *PrevT = dyn_cast(Prev)) { + EXPECT_EQ(Current->getPreviousDecl(), Prev); + auto *CurrentT = cast(Current); + ASSERT_TRUE(PrevT->getTemplatedDecl()); + ASSERT_TRUE(CurrentT->getTemplatedDecl()); + EXPECT_EQ(CurrentT->getTemplatedDecl()->getPreviousDecl(), + PrevT->getTemplatedDecl()); + return; + } + + // Specializations. + if (auto *PrevF = dyn_cast(Prev)) { + if (PrevF->getTemplatedKind() == + FunctionDecl::TK_FunctionTemplateSpecialization) { + // There may be a hidden fwd spec decl before a spec decl. + // In that case the previous visible decl can be reached through that + // invisible one. + EXPECT_THAT(Prev, testing::AnyOf( + Current->getPreviousDecl(), + Current->getPreviousDecl()->getPreviousDecl())); + auto *ToTU = Prev->getTranslationUnitDecl(); + auto *TemplateD = FirstDeclMatcher().match( + ToTU, functionTemplateDecl()); + auto *FirstSpecD = *(TemplateD->spec_begin()); + EXPECT_EQ(FirstSpecD->getCanonicalDecl(), PrevF->getCanonicalDecl()); + return; + } + } + + // The rest: Classes, Functions, etc. + EXPECT_EQ(Current->getPreviousDecl(), Prev); + } + void TypedTest_PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition() { Decl *FromTU = getTuDecl(getPrototype(), Lang_CXX); @@ -4030,14 +4069,8 @@ struct RedeclChain : ASTImporterOptionSpecificTestBase { EXPECT_TRUE(Imported1 == To1); EXPECT_FALSE(To0->isThisDeclarationADefinition()); EXPECT_FALSE(To1->isThisDeclarationADefinition()); - EXPECT_EQ(To1->getPreviousDecl(), To0); - if (auto *ToT0 = dyn_cast(To0)) { - auto *ToT1 = cast(To1); - ASSERT_TRUE(ToT0->getTemplatedDecl()); - ASSERT_TRUE(ToT1->getTemplatedDecl()); - EXPECT_EQ(ToT1->getTemplatedDecl()->getPreviousDecl(), - ToT0->getTemplatedDecl()); - } + + CheckPreviousDecl(To0, To1); } void TypedTest_ImportDefinitionAfterImportedPrototype() { @@ -4059,14 +4092,8 @@ struct RedeclChain : ASTImporterOptionSpecificTestBase { EXPECT_TRUE(ImportedDef == ToDef); EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); - EXPECT_EQ(ToDef->getPreviousDecl(), ToProto); - if (auto *ToProtoT = dyn_cast(ToProto)) { - auto *ToDefT = cast(ToDef); - ASSERT_TRUE(ToProtoT->getTemplatedDecl()); - ASSERT_TRUE(ToDefT->getTemplatedDecl()); - EXPECT_EQ(ToDefT->getTemplatedDecl()->getPreviousDecl(), - ToProtoT->getTemplatedDecl()); - } + + CheckPreviousDecl(ToProto, ToDef); } void TypedTest_ImportPrototypeAfterImportedDefinition() { @@ -4088,14 +4115,8 @@ struct RedeclChain : ASTImporterOptionSpecificTestBase { EXPECT_TRUE(ImportedProto == ToProto); EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); - EXPECT_EQ(ToProto->getPreviousDecl(), ToDef); - if (auto *ToDefT = dyn_cast(ToDef)) { - auto *ToProtoT = cast(ToProto); - ASSERT_TRUE(ToDefT->getTemplatedDecl()); - ASSERT_TRUE(ToProtoT->getTemplatedDecl()); - EXPECT_EQ(ToProtoT->getTemplatedDecl()->getPreviousDecl(), - ToDefT->getTemplatedDecl()); - } + + CheckPreviousDecl(ToDef, ToProto); } void TypedTest_ImportPrototypes() { @@ -4117,27 +4138,8 @@ struct RedeclChain : ASTImporterOptionSpecificTestBase { EXPECT_TRUE(Imported1 == To1); EXPECT_FALSE(To0->isThisDeclarationADefinition()); EXPECT_FALSE(To1->isThisDeclarationADefinition()); - EXPECT_EQ(To1->getPreviousDecl(), To0); - if (auto *ToT0 = dyn_cast(To0)) { - auto *ToT1 = cast(To1); - ASSERT_TRUE(ToT0->getTemplatedDecl()); - ASSERT_TRUE(ToT1->getTemplatedDecl()); - EXPECT_EQ(ToT1->getTemplatedDecl()->getPreviousDecl(), - ToT0->getTemplatedDecl()); - } - // Extra check for specializations. - // FIXME Add this check to other tests too (possibly factor out into a - // function), when they start to pass. - if (auto *From0F = dyn_cast(From0)) { - auto *To0F = cast(To0); - if (From0F->getTemplatedKind() == - FunctionDecl::TK_FunctionTemplateSpecialization) { - auto *TemplateD = FirstDeclMatcher().match( - ToTU, functionTemplateDecl()); - auto *FirstSpecD = *(TemplateD->spec_begin()); - EXPECT_EQ(FirstSpecD->getCanonicalDecl(), To0F->getCanonicalDecl()); - } - } + + CheckPreviousDecl(To0, To1); } void TypedTest_ImportDefinitions() { @@ -4182,14 +4184,8 @@ struct RedeclChain : ASTImporterOptionSpecificTestBase { EXPECT_TRUE(ImportedProto == ToProto); EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); - EXPECT_EQ(ToProto->getPreviousDecl(), ToDef); - if (auto *ToDefT = dyn_cast(ToDef)) { - auto *ToProtoT = cast(ToProto); - ASSERT_TRUE(ToDefT->getTemplatedDecl()); - ASSERT_TRUE(ToProtoT->getTemplatedDecl()); - EXPECT_EQ(ToProtoT->getTemplatedDecl()->getPreviousDecl(), - ToDefT->getTemplatedDecl()); - } + + CheckPreviousDecl(ToDef, ToProto); } void TypedTest_ImportPrototypeThenDefinition() { @@ -4213,14 +4209,8 @@ struct RedeclChain : ASTImporterOptionSpecificTestBase { EXPECT_TRUE(ImportedProto == ToProto); EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); - EXPECT_EQ(ToDef->getPreviousDecl(), ToProto); - if (auto *ToDefT = dyn_cast(ToDef)) { - auto *ToProtoT = cast(ToProto); - ASSERT_TRUE(ToDefT->getTemplatedDecl()); - ASSERT_TRUE(ToProtoT->getTemplatedDecl()); - EXPECT_EQ(ToDefT->getTemplatedDecl()->getPreviousDecl(), - ToProtoT->getTemplatedDecl()); - } + + CheckPreviousDecl(ToProto, ToDef); } void TypedTest_WholeRedeclChainIsImportedAtOnce() { @@ -4262,7 +4252,8 @@ struct RedeclChain : ASTImporterOptionSpecificTestBase { EXPECT_TRUE(DefinitionD->getPreviousDecl()); EXPECT_FALSE( DefinitionD->getPreviousDecl()->isThisDeclarationADefinition()); - EXPECT_EQ(DefinitionD->getPreviousDecl()->getPreviousDecl(), ProtoD); + + CheckPreviousDecl(ProtoD, DefinitionD->getPreviousDecl()); } }; @@ -4366,11 +4357,10 @@ ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, , ImportPrototypes) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, , ImportPrototypes) -// FIXME This does not pass, possible error with Spec import. -ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, - DISABLED_, ImportPrototypes) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, , ImportPrototypes) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , + ImportPrototypes) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, , ImportDefinitions) @@ -4382,11 +4372,10 @@ ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, , ImportDefinitions) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, , ImportDefinitions) -// FIXME This does not pass, possible error with Spec import. -ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, - DISABLED_, ImportDefinitions) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, , ImportDefinitions) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , + ImportDefinitions) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, , ImportDefinitionThenPrototype) @@ -4398,9 +4387,7 @@ ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, , ImportDefinitionThenPrototype) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, , ImportDefinitionThenPrototype) -// FIXME This does not pass, possible error with Spec import. -ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, - DISABLED_, +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , ImportDefinitionThenPrototype) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, , ImportDefinitionThenPrototype) @@ -4415,9 +4402,7 @@ ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, , ImportPrototypeThenDefinition) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, , ImportPrototypeThenDefinition) -// FIXME This does not pass, possible error with Spec import. -ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, - DISABLED_, +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , ImportPrototypeThenDefinition) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, , ImportPrototypeThenDefinition) @@ -4437,9 +4422,7 @@ ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, , ImportPrototypeThenProtoAndDefinition) ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, , ImportPrototypeThenProtoAndDefinition) -// FIXME This does not pass, possible error with Spec import. -ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, - DISABLED_, +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , ImportPrototypeThenProtoAndDefinition) INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainFunction, -- 2.7.4