Skip to content

Commit 25234fd

Browse files
author
Gabor Marton
committed
[ASTImporter] Support functions with placeholder return types ...
Summary: Support functions with placeholder return types even in cases when the type is declared in the body of the function. Example: auto f() { struct X{}; return X(); } Reviewers: balazske, a_sidorin, a.sidorin, shafik Subscribers: rnkovacs, dkrupp, Szelethus, gamesh411, teemperor, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D70819
1 parent 11b2b2f commit 25234fd

File tree

5 files changed

+274
-15
lines changed

5 files changed

+274
-15
lines changed

clang/lib/AST/ASTImporter.cpp

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,11 @@ namespace clang {
648648

649649
Expected<FunctionDecl *> FindFunctionTemplateSpecialization(
650650
FunctionDecl *FromFD);
651+
652+
// Returns true if the given function has a placeholder return type and
653+
// that type is declared inside the body of the function.
654+
// E.g. auto f() { struct X{}; return X(); }
655+
bool hasAutoReturnTypeDeclaredInside(FunctionDecl *D);
651656
};
652657

653658
template <typename InContainerTy>
@@ -1547,6 +1552,10 @@ Error ASTNodeImporter::ImportDeclParts(
15471552
DeclarationName &Name, NamedDecl *&ToD, SourceLocation &Loc) {
15481553
// Check if RecordDecl is in FunctionDecl parameters to avoid infinite loop.
15491554
// example: int struct_in_proto(struct data_t{int a;int b;} *d);
1555+
// FIXME: We could support these constructs by importing a different type of
1556+
// this parameter and by importing the original type of the parameter only
1557+
// after the FunctionDecl is created. See
1558+
// VisitFunctionDecl::UsedDifferentProtoType.
15501559
DeclContext *OrigDC = D->getDeclContext();
15511560
FunctionDecl *FunDecl;
15521561
if (isa<RecordDecl>(D) && (FunDecl = dyn_cast<FunctionDecl>(OrigDC)) &&
@@ -3005,6 +3014,46 @@ Error ASTNodeImporter::ImportFunctionDeclBody(FunctionDecl *FromFD,
30053014
return Error::success();
30063015
}
30073016

3017+
// Returns true if the given D has a DeclContext up to the TranslationUnitDecl
3018+
// which is equal to the given DC.
3019+
bool isAncestorDeclContextOf(const DeclContext *DC, const Decl *D) {
3020+
const DeclContext *DCi = D->getDeclContext();
3021+
while (DCi != D->getTranslationUnitDecl()) {
3022+
if (DCi == DC)
3023+
return true;
3024+
DCi = DCi->getParent();
3025+
}
3026+
return false;
3027+
}
3028+
3029+
bool ASTNodeImporter::hasAutoReturnTypeDeclaredInside(FunctionDecl *D) {
3030+
QualType FromTy = D->getType();
3031+
const FunctionProtoType *FromFPT = FromTy->getAs<FunctionProtoType>();
3032+
assert(FromFPT && "Must be called on FunctionProtoType");
3033+
if (AutoType *AutoT = FromFPT->getReturnType()->getContainedAutoType()) {
3034+
QualType DeducedT = AutoT->getDeducedType();
3035+
if (const RecordType *RecordT =
3036+
DeducedT.isNull() ? nullptr : dyn_cast<RecordType>(DeducedT)) {
3037+
RecordDecl *RD = RecordT->getDecl();
3038+
assert(RD);
3039+
if (isAncestorDeclContextOf(D, RD)) {
3040+
assert(RD->getLexicalDeclContext() == RD->getDeclContext());
3041+
return true;
3042+
}
3043+
}
3044+
}
3045+
if (const TypedefType *TypedefT =
3046+
dyn_cast<TypedefType>(FromFPT->getReturnType())) {
3047+
TypedefNameDecl *TD = TypedefT->getDecl();
3048+
assert(TD);
3049+
if (isAncestorDeclContextOf(D, TD)) {
3050+
assert(TD->getLexicalDeclContext() == TD->getDeclContext());
3051+
return true;
3052+
}
3053+
}
3054+
return false;
3055+
}
3056+
30083057
ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
30093058

30103059
SmallVector<Decl *, 2> Redecls = getCanonicalForwardRedeclChain(D);
@@ -3128,22 +3177,37 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
31283177
return std::move(Err);
31293178

31303179
QualType FromTy = D->getType();
3131-
bool usedDifferentExceptionSpec = false;
3132-
3133-
if (const auto *FromFPT = D->getType()->getAs<FunctionProtoType>()) {
3180+
// Set to true if we do not import the type of the function as is. There are
3181+
// cases when the original type would result in an infinite recursion during
3182+
// the import. To avoid an infinite recursion when importing, we create the
3183+
// FunctionDecl with a simplified function type and update it only after the
3184+
// relevant AST nodes are already imported.
3185+
bool UsedDifferentProtoType = false;
3186+
if (const auto *FromFPT = FromTy->getAs<FunctionProtoType>()) {
3187+
QualType FromReturnTy = FromFPT->getReturnType();
3188+
// Functions with auto return type may define a struct inside their body
3189+
// and the return type could refer to that struct.
3190+
// E.g.: auto foo() { struct X{}; return X(); }
3191+
// To avoid an infinite recursion when importing, create the FunctionDecl
3192+
// with a simplified return type.
3193+
if (hasAutoReturnTypeDeclaredInside(D)) {
3194+
FromReturnTy = Importer.getFromContext().VoidTy;
3195+
UsedDifferentProtoType = true;
3196+
}
31343197
FunctionProtoType::ExtProtoInfo FromEPI = FromFPT->getExtProtoInfo();
31353198
// FunctionProtoType::ExtProtoInfo's ExceptionSpecDecl can point to the
31363199
// FunctionDecl that we are importing the FunctionProtoType for.
31373200
// To avoid an infinite recursion when importing, create the FunctionDecl
3138-
// with a simplified function type and update it afterwards.
3201+
// with a simplified function type.
31393202
if (FromEPI.ExceptionSpec.SourceDecl ||
31403203
FromEPI.ExceptionSpec.SourceTemplate ||
31413204
FromEPI.ExceptionSpec.NoexceptExpr) {
31423205
FunctionProtoType::ExtProtoInfo DefaultEPI;
3143-
FromTy = Importer.getFromContext().getFunctionType(
3144-
FromFPT->getReturnType(), FromFPT->getParamTypes(), DefaultEPI);
3145-
usedDifferentExceptionSpec = true;
3206+
FromEPI = DefaultEPI;
3207+
UsedDifferentProtoType = true;
31463208
}
3209+
FromTy = Importer.getFromContext().getFunctionType(
3210+
FromReturnTy, FromFPT->getParamTypes(), FromEPI);
31473211
}
31483212

31493213
QualType T;
@@ -3277,14 +3341,6 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
32773341
}
32783342
}
32793343

3280-
if (usedDifferentExceptionSpec) {
3281-
// Update FunctionProtoType::ExtProtoInfo.
3282-
if (ExpectedType TyOrErr = import(D->getType()))
3283-
ToFunction->setType(*TyOrErr);
3284-
else
3285-
return TyOrErr.takeError();
3286-
}
3287-
32883344
// Import the describing template function, if any.
32893345
if (FromFT) {
32903346
auto ToFTOrErr = import(FromFT);
@@ -3316,6 +3372,14 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
33163372
return std::move(Err);
33173373
}
33183374

3375+
// Import and set the original type in case we used another type.
3376+
if (UsedDifferentProtoType) {
3377+
if (ExpectedType TyOrErr = import(D->getType()))
3378+
ToFunction->setType(*TyOrErr);
3379+
else
3380+
return TyOrErr.takeError();
3381+
}
3382+
33193383
// FIXME: Other bits to merge?
33203384

33213385
// If it is a template, import all related things.

clang/unittests/AST/ASTImporterTest.cpp

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "clang/ASTMatchers/ASTMatchers.h"
1314
#include "llvm/ADT/StringMap.h"
1415

1516
#include "clang/AST/DeclContextInternals.h"
17+
#include "gtest/gtest.h"
1618

1719
#include "ASTImporterFixtures.h"
1820
#include "MatchVerifier.h"
@@ -5623,6 +5625,188 @@ TEST_P(ASTImporterOptionSpecificTestBase, ImplicitlyDeclareSelf) {
56235625
EXPECT_TRUE(ToMethod->getSelfDecl() != nullptr);
56245626
}
56255627

5628+
struct ImportAutoFunctions : ASTImporterOptionSpecificTestBase {};
5629+
5630+
TEST_P(ImportAutoFunctions, ReturnWithTypedefDeclaredInside) {
5631+
Decl *FromTU = getTuDecl(
5632+
R"(
5633+
auto X = [](long l) {
5634+
using int_type = long;
5635+
auto dur = 13;
5636+
return static_cast<int_type>(dur);
5637+
};
5638+
)",
5639+
Lang_CXX14, "input0.cc");
5640+
CXXMethodDecl *From =
5641+
FirstDeclMatcher<CXXMethodDecl>().match(FromTU, cxxMethodDecl());
5642+
5643+
// Explicitly set the return type of the lambda's operator() to the TypeAlias.
5644+
// Normally the return type would be the built-in 'long' type. However, there
5645+
// are cases when Clang does not use the canonical type and the TypeAlias is
5646+
// used. I could not create such an AST from regular source code, it requires
5647+
// some special state in the preprocessor. I've found such an AST when Clang
5648+
// parsed libcxx/src/filesystem/directory_iterator.cpp, but could not reduce
5649+
// that with creduce, because after preprocessing, the AST no longer
5650+
// contained the TypeAlias as a return type of the lambda.
5651+
ASTContext &Ctx = From->getASTContext();
5652+
TypeAliasDecl *FromTA =
5653+
FirstDeclMatcher<TypeAliasDecl>().match(FromTU, typeAliasDecl());
5654+
QualType TT = Ctx.getTypedefType(FromTA);
5655+
const FunctionProtoType *FPT = cast<FunctionProtoType>(From->getType());
5656+
QualType NewFunType =
5657+
Ctx.getFunctionType(TT, FPT->getParamTypes(), FPT->getExtProtoInfo());
5658+
From->setType(NewFunType);
5659+
5660+
CXXMethodDecl *To = Import(From, Lang_CXX14);
5661+
EXPECT_TRUE(To);
5662+
EXPECT_TRUE(isa<TypedefType>(To->getReturnType()));
5663+
}
5664+
5665+
TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside) {
5666+
Decl *FromTU = getTuDecl(
5667+
R"(
5668+
auto foo() {
5669+
struct X {};
5670+
return X();
5671+
}
5672+
)",
5673+
Lang_CXX14, "input0.cc");
5674+
FunctionDecl *From =
5675+
FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
5676+
5677+
FunctionDecl *To = Import(From, Lang_CXX14);
5678+
EXPECT_TRUE(To);
5679+
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
5680+
}
5681+
5682+
TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside2) {
5683+
Decl *FromTU = getTuDecl(
5684+
R"(
5685+
auto foo() {
5686+
struct X {};
5687+
return X();
5688+
}
5689+
)",
5690+
Lang_CXX14, "input0.cc");
5691+
FunctionDecl *From =
5692+
FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
5693+
5694+
// This time import the type directly.
5695+
QualType ToT = ImportType(From->getType(), From, Lang_CXX14);
5696+
const FunctionProtoType *FPT = cast<FunctionProtoType>(ToT);
5697+
EXPECT_TRUE(isa<AutoType>(FPT->getReturnType()));
5698+
}
5699+
5700+
TEST_P(ImportAutoFunctions, ReturnWithTypedefToStructDeclaredInside) {
5701+
Decl *FromTU = getTuDecl(
5702+
R"(
5703+
auto foo() {
5704+
struct X {};
5705+
using Y = X;
5706+
return Y();
5707+
}
5708+
)",
5709+
Lang_CXX14, "input0.cc");
5710+
FunctionDecl *From =
5711+
FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
5712+
5713+
FunctionDecl *To = Import(From, Lang_CXX14);
5714+
EXPECT_TRUE(To);
5715+
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
5716+
}
5717+
5718+
TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredNestedInside) {
5719+
Decl *FromTU = getTuDecl(
5720+
R"(
5721+
auto foo() {
5722+
struct X { struct Y{}; };
5723+
return X::Y();
5724+
}
5725+
)",
5726+
Lang_CXX14, "input0.cc");
5727+
FunctionDecl *From =
5728+
FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
5729+
5730+
FunctionDecl *To = Import(From, Lang_CXX14);
5731+
EXPECT_TRUE(To);
5732+
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
5733+
}
5734+
5735+
TEST_P(ImportAutoFunctions, ReturnWithInternalLambdaType) {
5736+
Decl *FromTU = getTuDecl(
5737+
R"(
5738+
auto f() {
5739+
auto l = []() {
5740+
struct X {};
5741+
return X();
5742+
};
5743+
return l();
5744+
}
5745+
)",
5746+
Lang_CXX17, "input0.cc");
5747+
FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
5748+
FromTU, functionDecl(hasName("f")));
5749+
5750+
FunctionDecl *To = Import(From, Lang_CXX17);
5751+
EXPECT_TRUE(To);
5752+
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
5753+
}
5754+
5755+
TEST_P(ImportAutoFunctions, ReturnWithTypeInIf) {
5756+
Decl *FromTU = getTuDecl(
5757+
R"(
5758+
auto f() {
5759+
if (struct X {} x; true)
5760+
return X();
5761+
else
5762+
return X();
5763+
}
5764+
)",
5765+
Lang_CXX17, "input0.cc");
5766+
FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
5767+
FromTU, functionDecl(hasName("f")));
5768+
5769+
FunctionDecl *To = Import(From, Lang_CXX17);
5770+
EXPECT_TRUE(To);
5771+
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
5772+
}
5773+
5774+
TEST_P(ImportAutoFunctions, ReturnWithTypeInFor) {
5775+
Decl *FromTU = getTuDecl(
5776+
R"(
5777+
auto f() {
5778+
for (struct X {} x;;)
5779+
return X();
5780+
}
5781+
)",
5782+
Lang_CXX17, "input0.cc");
5783+
FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
5784+
FromTU, functionDecl(hasName("f")));
5785+
5786+
FunctionDecl *To = Import(From, Lang_CXX17);
5787+
EXPECT_TRUE(To);
5788+
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
5789+
}
5790+
5791+
TEST_P(ImportAutoFunctions, ReturnWithTypeInSwitch) {
5792+
Decl *FromTU = getTuDecl(
5793+
R"(
5794+
auto f() {
5795+
switch (struct X {} x; 10) {
5796+
case 10:
5797+
return X();
5798+
}
5799+
}
5800+
)",
5801+
Lang_CXX17, "input0.cc");
5802+
FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
5803+
FromTU, functionDecl(hasName("f")));
5804+
5805+
FunctionDecl *To = Import(From, Lang_CXX17);
5806+
EXPECT_TRUE(To);
5807+
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
5808+
}
5809+
56265810
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest,
56275811
DefaultTestValuesForRunOptions, );
56285812

@@ -5650,6 +5834,9 @@ INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedirectingImporterTest,
56505834
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions,
56515835
DefaultTestValuesForRunOptions, );
56525836

5837+
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportAutoFunctions,
5838+
DefaultTestValuesForRunOptions, );
5839+
56535840
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctionTemplates,
56545841
DefaultTestValuesForRunOptions, );
56555842

clang/unittests/AST/Language.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ ArgVector getBasicRunOptionsForLanguage(Language Lang) {
3434
case Lang_CXX14:
3535
BasicArgs = {"-std=c++14", "-frtti"};
3636
break;
37+
case Lang_CXX17:
38+
BasicArgs = {"-std=c++17", "-frtti"};
39+
break;
3740
case Lang_CXX2a:
3841
BasicArgs = {"-std=c++2a", "-frtti"};
3942
break;

clang/unittests/AST/Language.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ enum Language {
2828
Lang_CXX,
2929
Lang_CXX11,
3030
Lang_CXX14,
31+
Lang_CXX17,
3132
Lang_CXX2a,
3233
Lang_OpenCL,
3334
Lang_OBJCXX

clang/unittests/AST/MatchVerifier.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ testing::AssertionResult MatchVerifier<NodeType>::match(
108108
Args.push_back("-std=c++14");
109109
FileName = "input.cc";
110110
break;
111+
case Lang_CXX17:
112+
Args.push_back("-std=c++17");
113+
FileName = "input.cc";
114+
break;
111115
case Lang_CXX2a:
112116
Args.push_back("-std=c++2a");
113117
FileName = "input.cc";

0 commit comments

Comments
 (0)