Skip to content
This repository was archived by the owner on Mar 30, 2021. It is now read-only.

Commit 446c84d

Browse files
authored
Improved import of 'C implicit' functions. (#689)
1 parent 8cbee8f commit 446c84d

File tree

4 files changed

+142
-3
lines changed

4 files changed

+142
-3
lines changed

lib/AST/ASTImporter.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3001,7 +3001,6 @@ Error ASTNodeImporter::ImportFunctionDeclBody(FunctionDecl *FromFD,
30013001
}
30023002

30033003
ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
3004-
30053004
SmallVector<Decl *, 2> Redecls = getCanonicalForwardRedeclChain(D);
30063005
auto RedeclIt = Redecls.begin();
30073006
// Import the first part of the decl chain. I.e. import all previous
@@ -3048,9 +3047,22 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
30483047
}
30493048
// Try to find a function in our own ("to") context with the same name, same
30503049
// type, and in the same context as the function we're importing.
3051-
else if (!LexicalDC->isFunctionOrMethod()) {
3050+
else {
3051+
// Detect functions that have no prototype before (valid in C only).
3052+
// These are implicitly contained in a CallExpr without previous
3053+
// FunctionDecl, in namespace IDNS_LocalExtern. The AST contains the same
3054+
// FunctionDecl at every CallExpr where that function is called.
3055+
auto HasNoPrototype = [](FunctionDecl *D) {
3056+
return D->getLexicalDeclContext()->isFunctionOrMethod() &&
3057+
D->isImplicit();
3058+
};
3059+
bool NoPrototype = HasNoPrototype(D);
3060+
assert((!NoPrototype || !D->getPreviousDecl()) &&
3061+
"Function without prototype but with previous decl?");
3062+
30523063
SmallVector<NamedDecl *, 4> ConflictingDecls;
3053-
unsigned IDNS = Decl::IDNS_Ordinary | Decl::IDNS_OrdinaryFriend;
3064+
unsigned IDNS = Decl::IDNS_Ordinary | Decl::IDNS_OrdinaryFriend |
3065+
Decl::IDNS_LocalExtern;
30543066
auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
30553067
for (auto *FoundDecl : FoundDecls) {
30563068
if (!FoundDecl->isInIdentifierNamespace(IDNS))
@@ -3063,6 +3075,14 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
30633075
if (isStructuralMatch(D, FoundFunction, false)) {
30643076
if (Decl *Def = FindAndMapDefinition(D, FoundFunction))
30653077
return Def;
3078+
3079+
if (NoPrototype && HasNoPrototype(FoundFunction)) {
3080+
// We want to use the same FoundFunction for every occurrence.
3081+
// FIXME: In C the same function can be called with different
3082+
// parameters.
3083+
return Importer.MapImported(D, FoundFunction);
3084+
}
3085+
30663086
FoundByLookup = FoundFunction;
30673087
break;
30683088
}

lib/AST/ASTImporterLookupTable.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,20 @@ struct Builder : RecursiveASTVisitor<Builder> {
5656
return true;
5757
}
5858

59+
// Functions without previous prototype are not visited by the AST visitor
60+
// so they must be added explicitly (like the FriendDecl cases). These are
61+
// referenced by the expression that calls them so find these.
62+
// It is necessary to visit these to find and link them together at AST
63+
// import.
64+
bool VisitCallExpr(CallExpr *E) {
65+
FunctionDecl *Callee = E->getDirectCallee();
66+
// Our case: Declaration that is inside a function and implicit.
67+
if (Callee && Callee->getLexicalDeclContext()->isFunctionOrMethod() &&
68+
Callee->isImplicit())
69+
LT.add(Callee);
70+
return true;
71+
}
72+
5973
// Override default settings of base.
6074
bool shouldVisitTemplateInstantiations() const { return true; }
6175
bool shouldVisitImplicitCode() const { return true; }

unittests/AST/ASTImporterFixtures.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@ void checkImportedSourceLocations(const Decl *FromD, const Decl *ToD) {
4949
// Print debug information.
5050
const bool Print = false;
5151

52+
// Skip non-declared C functions: The same FunctionDecl can be linked into
53+
// multiple contexts of different functions that have different source
54+
// locations but the implicit function has always the same.
55+
if (FromD->getLexicalDeclContext()->isFunctionOrMethod() &&
56+
FromD->isImplicit()) {
57+
// FIXME: Should check FromD->getLocation() "==" ToD->getLocation().
58+
return;
59+
}
60+
5261
SmallString<1024> ToPrinted;
5362
SmallString<1024> FromPrinted;
5463
llvm::raw_svector_ostream ToStream(ToPrinted);

unittests/AST/ASTImporterTest.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5171,6 +5171,102 @@ TEST_P(ASTImporterOptionSpecificTestBase, ImportOfFriendTemplateWithArgExpr) {
51715171
EXPECT_NE(ToD1->getCanonicalDecl(), ToD2->getCanonicalDecl());
51725172
}
51735173

5174+
struct ASTImporterCImplicitFunctionTest : ASTImporterOptionSpecificTestBase {
5175+
static FunctionDecl *GetImplicitF(FunctionDecl *F) {
5176+
return cast<CallExpr>(cast<CompoundStmt>(F->getBody())->body_front())
5177+
->getDirectCallee();
5178+
};
5179+
};
5180+
5181+
TEST_P(ASTImporterCImplicitFunctionTest, ImportCImplicitFunction) {
5182+
auto Code = "void f() { implicit_f(); }";
5183+
5184+
TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_C, "input.c");
5185+
auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
5186+
FromTU, functionDecl(hasName("f")));
5187+
FunctionDecl *FromImplicitF = GetImplicitF(FromF);
5188+
5189+
ASSERT_TRUE(FromImplicitF->getDeclContext() == cast<DeclContext>(FromTU));
5190+
ASSERT_TRUE(FromImplicitF->getLexicalDeclContext() ==
5191+
cast<DeclContext>(FromF));
5192+
ASSERT_FALSE(FromTU->containsDecl(FromImplicitF));
5193+
ASSERT_TRUE(FromF->containsDecl(FromImplicitF));
5194+
5195+
auto *ToImplicitF = Import(FromImplicitF, Lang_C);
5196+
auto *ToF = Import(FromF, Lang_C);
5197+
ASSERT_TRUE(ToImplicitF);
5198+
TranslationUnitDecl *ToTU = ToImplicitF->getTranslationUnitDecl();
5199+
EXPECT_FALSE(ToTU->containsDecl(ToImplicitF));
5200+
EXPECT_TRUE(ToF->containsDecl(ToImplicitF));
5201+
}
5202+
5203+
TEST_P(ASTImporterCImplicitFunctionTest, ImportedCImplicitFunctionsLinked) {
5204+
auto Code =
5205+
R"(
5206+
void f1() { implicit_f(); }
5207+
void f2() { implicit_f(); }
5208+
)";
5209+
5210+
TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_C, "input.c");
5211+
auto *FromF1 = FirstDeclMatcher<FunctionDecl>().match(
5212+
FromTU, functionDecl(hasName("f1")));
5213+
FunctionDecl *FromImplicitF1 = GetImplicitF(FromF1);
5214+
auto *FromF2 = FirstDeclMatcher<FunctionDecl>().match(
5215+
FromTU, functionDecl(hasName("f2")));
5216+
FunctionDecl *FromImplicitF2 = GetImplicitF(FromF2);
5217+
5218+
ASSERT_EQ(FromImplicitF1, FromImplicitF2);
5219+
5220+
auto *ToImplicitF1 = Import(FromImplicitF1, Lang_C);
5221+
auto *ToF1 = Import(FromF1, Lang_C);
5222+
auto *ToF2 = Import(FromF2, Lang_C);
5223+
ASSERT_TRUE(ToImplicitF1);
5224+
EXPECT_EQ(GetImplicitF(ToF1), ToImplicitF1);
5225+
EXPECT_EQ(GetImplicitF(ToF2), ToImplicitF1);
5226+
}
5227+
5228+
TEST_P(ASTImporterCImplicitFunctionTest,
5229+
ImportAndLinkCImplicitFunctionAfterExisting) {
5230+
auto ToCode = "void f1() { implicit_f(); }";
5231+
auto FromCode = "void f2() { implicit_f(); }";
5232+
5233+
TranslationUnitDecl *ToTU = getToTuDecl(ToCode, Lang_C);
5234+
TranslationUnitDecl *FromTU = getTuDecl(FromCode, Lang_C);
5235+
auto *ToF1 =
5236+
FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl(hasName("f1")));
5237+
FunctionDecl *ToImplicitF1 = GetImplicitF(ToF1);
5238+
auto *FromF2 = FirstDeclMatcher<FunctionDecl>().match(
5239+
FromTU, functionDecl(hasName("f2")));
5240+
FunctionDecl *FromImplicitF2 = GetImplicitF(FromF2);
5241+
5242+
auto *ToImplicitF2 = Import(FromImplicitF2, Lang_C);
5243+
ASSERT_TRUE(ToImplicitF2);
5244+
EXPECT_EQ(ToImplicitF2, ToImplicitF1);
5245+
}
5246+
5247+
TEST_P(ASTImporterCImplicitFunctionTest,
5248+
ImportAndLinkCImplicitFunctionDefinition) {
5249+
auto ToCode = "void f() { implicit_f(); }";
5250+
auto FromCode = "int implicit_f() {}";
5251+
5252+
TranslationUnitDecl *ToTU = getToTuDecl(ToCode, Lang_C);
5253+
TranslationUnitDecl *FromTU = getTuDecl(FromCode, Lang_C);
5254+
auto *ToF =
5255+
FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl(hasName("f")));
5256+
FunctionDecl *ToImplicitF = GetImplicitF(ToF);
5257+
auto *FromImplicitFDef = FirstDeclMatcher<FunctionDecl>().match(
5258+
FromTU, functionDecl(hasName("implicit_f")));
5259+
5260+
auto *ToImplicitFDef = Import(FromImplicitFDef, Lang_C);
5261+
ASSERT_TRUE(ToImplicitFDef);
5262+
EXPECT_NE(ToImplicitFDef, ToImplicitF);
5263+
EXPECT_EQ(ToImplicitFDef->getPreviousDecl(), ToImplicitF);
5264+
EXPECT_EQ(ToImplicitF->getDefinition(), ToImplicitFDef);
5265+
}
5266+
5267+
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterCImplicitFunctionTest,
5268+
DefaultTestValuesForRunOptions, );
5269+
51745270
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest,
51755271
DefaultTestValuesForRunOptions, );
51765272

0 commit comments

Comments
 (0)