[clang] 25234fd - [ASTImporter] Support functions with placeholder return types ...
Gabor Marton via cfe-commits
cfe-commits at lists.llvm.org
Thu Dec 12 08:49:19 PST 2019
Author: Gabor Marton
Date: 2019-12-12T17:49:03+01:00
New Revision: 25234fd69e32e51bc800686075265f535d69d990
URL: https://github.com/llvm/llvm-project/commit/25234fd69e32e51bc800686075265f535d69d990
DIFF: https://github.com/llvm/llvm-project/commit/25234fd69e32e51bc800686075265f535d69d990.diff
LOG: [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
Added:
Modified:
clang/lib/AST/ASTImporter.cpp
clang/unittests/AST/ASTImporterTest.cpp
clang/unittests/AST/Language.cpp
clang/unittests/AST/Language.h
clang/unittests/AST/MatchVerifier.h
Removed:
################################################################################
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 7d71a4a143cc..414092f33c47 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -648,6 +648,11 @@ namespace clang {
Expected<FunctionDecl *> FindFunctionTemplateSpecialization(
FunctionDecl *FromFD);
+
+ // Returns true if the given function has a placeholder return type and
+ // that type is declared inside the body of the function.
+ // E.g. auto f() { struct X{}; return X(); }
+ bool hasAutoReturnTypeDeclaredInside(FunctionDecl *D);
};
template <typename InContainerTy>
@@ -1547,6 +1552,10 @@ Error ASTNodeImporter::ImportDeclParts(
DeclarationName &Name, NamedDecl *&ToD, SourceLocation &Loc) {
// Check if RecordDecl is in FunctionDecl parameters to avoid infinite loop.
// example: int struct_in_proto(struct data_t{int a;int b;} *d);
+ // FIXME: We could support these constructs by importing a
diff erent type of
+ // this parameter and by importing the original type of the parameter only
+ // after the FunctionDecl is created. See
+ // VisitFunctionDecl::UsedDifferentProtoType.
DeclContext *OrigDC = D->getDeclContext();
FunctionDecl *FunDecl;
if (isa<RecordDecl>(D) && (FunDecl = dyn_cast<FunctionDecl>(OrigDC)) &&
@@ -3005,6 +3014,46 @@ Error ASTNodeImporter::ImportFunctionDeclBody(FunctionDecl *FromFD,
return Error::success();
}
+// Returns true if the given D has a DeclContext up to the TranslationUnitDecl
+// which is equal to the given DC.
+bool isAncestorDeclContextOf(const DeclContext *DC, const Decl *D) {
+ const DeclContext *DCi = D->getDeclContext();
+ while (DCi != D->getTranslationUnitDecl()) {
+ if (DCi == DC)
+ return true;
+ DCi = DCi->getParent();
+ }
+ return false;
+}
+
+bool ASTNodeImporter::hasAutoReturnTypeDeclaredInside(FunctionDecl *D) {
+ QualType FromTy = D->getType();
+ const FunctionProtoType *FromFPT = FromTy->getAs<FunctionProtoType>();
+ assert(FromFPT && "Must be called on FunctionProtoType");
+ if (AutoType *AutoT = FromFPT->getReturnType()->getContainedAutoType()) {
+ QualType DeducedT = AutoT->getDeducedType();
+ if (const RecordType *RecordT =
+ DeducedT.isNull() ? nullptr : dyn_cast<RecordType>(DeducedT)) {
+ RecordDecl *RD = RecordT->getDecl();
+ assert(RD);
+ if (isAncestorDeclContextOf(D, RD)) {
+ assert(RD->getLexicalDeclContext() == RD->getDeclContext());
+ return true;
+ }
+ }
+ }
+ if (const TypedefType *TypedefT =
+ dyn_cast<TypedefType>(FromFPT->getReturnType())) {
+ TypedefNameDecl *TD = TypedefT->getDecl();
+ assert(TD);
+ if (isAncestorDeclContextOf(D, TD)) {
+ assert(TD->getLexicalDeclContext() == TD->getDeclContext());
+ return true;
+ }
+ }
+ return false;
+}
+
ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
SmallVector<Decl *, 2> Redecls = getCanonicalForwardRedeclChain(D);
@@ -3128,22 +3177,37 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
return std::move(Err);
QualType FromTy = D->getType();
- bool usedDifferentExceptionSpec = false;
-
- if (const auto *FromFPT = D->getType()->getAs<FunctionProtoType>()) {
+ // Set to true if we do not import the type of the function as is. There are
+ // cases when the original type would result in an infinite recursion during
+ // the import. To avoid an infinite recursion when importing, we create the
+ // FunctionDecl with a simplified function type and update it only after the
+ // relevant AST nodes are already imported.
+ bool UsedDifferentProtoType = false;
+ if (const auto *FromFPT = FromTy->getAs<FunctionProtoType>()) {
+ QualType FromReturnTy = FromFPT->getReturnType();
+ // Functions with auto return type may define a struct inside their body
+ // and the return type could refer to that struct.
+ // E.g.: auto foo() { struct X{}; return X(); }
+ // To avoid an infinite recursion when importing, create the FunctionDecl
+ // with a simplified return type.
+ if (hasAutoReturnTypeDeclaredInside(D)) {
+ FromReturnTy = Importer.getFromContext().VoidTy;
+ UsedDifferentProtoType = true;
+ }
FunctionProtoType::ExtProtoInfo FromEPI = FromFPT->getExtProtoInfo();
// FunctionProtoType::ExtProtoInfo's ExceptionSpecDecl can point to the
// FunctionDecl that we are importing the FunctionProtoType for.
// To avoid an infinite recursion when importing, create the FunctionDecl
- // with a simplified function type and update it afterwards.
+ // with a simplified function type.
if (FromEPI.ExceptionSpec.SourceDecl ||
FromEPI.ExceptionSpec.SourceTemplate ||
FromEPI.ExceptionSpec.NoexceptExpr) {
FunctionProtoType::ExtProtoInfo DefaultEPI;
- FromTy = Importer.getFromContext().getFunctionType(
- FromFPT->getReturnType(), FromFPT->getParamTypes(), DefaultEPI);
- usedDifferentExceptionSpec = true;
+ FromEPI = DefaultEPI;
+ UsedDifferentProtoType = true;
}
+ FromTy = Importer.getFromContext().getFunctionType(
+ FromReturnTy, FromFPT->getParamTypes(), FromEPI);
}
QualType T;
@@ -3277,14 +3341,6 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
}
}
- if (usedDifferentExceptionSpec) {
- // Update FunctionProtoType::ExtProtoInfo.
- if (ExpectedType TyOrErr = import(D->getType()))
- ToFunction->setType(*TyOrErr);
- else
- return TyOrErr.takeError();
- }
-
// Import the describing template function, if any.
if (FromFT) {
auto ToFTOrErr = import(FromFT);
@@ -3316,6 +3372,14 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
return std::move(Err);
}
+ // Import and set the original type in case we used another type.
+ if (UsedDifferentProtoType) {
+ if (ExpectedType TyOrErr = import(D->getType()))
+ ToFunction->setType(*TyOrErr);
+ else
+ return TyOrErr.takeError();
+ }
+
// FIXME: Other bits to merge?
// If it is a template, import all related things.
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index abf29966e92f..6652111fd48e 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -10,9 +10,11 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/ASTMatchers/ASTMatchers.h"
#include "llvm/ADT/StringMap.h"
#include "clang/AST/DeclContextInternals.h"
+#include "gtest/gtest.h"
#include "ASTImporterFixtures.h"
#include "MatchVerifier.h"
@@ -5623,6 +5625,188 @@ TEST_P(ASTImporterOptionSpecificTestBase, ImplicitlyDeclareSelf) {
EXPECT_TRUE(ToMethod->getSelfDecl() != nullptr);
}
+struct ImportAutoFunctions : ASTImporterOptionSpecificTestBase {};
+
+TEST_P(ImportAutoFunctions, ReturnWithTypedefDeclaredInside) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto X = [](long l) {
+ using int_type = long;
+ auto dur = 13;
+ return static_cast<int_type>(dur);
+ };
+ )",
+ Lang_CXX14, "input0.cc");
+ CXXMethodDecl *From =
+ FirstDeclMatcher<CXXMethodDecl>().match(FromTU, cxxMethodDecl());
+
+ // Explicitly set the return type of the lambda's operator() to the TypeAlias.
+ // Normally the return type would be the built-in 'long' type. However, there
+ // are cases when Clang does not use the canonical type and the TypeAlias is
+ // used. I could not create such an AST from regular source code, it requires
+ // some special state in the preprocessor. I've found such an AST when Clang
+ // parsed libcxx/src/filesystem/directory_iterator.cpp, but could not reduce
+ // that with creduce, because after preprocessing, the AST no longer
+ // contained the TypeAlias as a return type of the lambda.
+ ASTContext &Ctx = From->getASTContext();
+ TypeAliasDecl *FromTA =
+ FirstDeclMatcher<TypeAliasDecl>().match(FromTU, typeAliasDecl());
+ QualType TT = Ctx.getTypedefType(FromTA);
+ const FunctionProtoType *FPT = cast<FunctionProtoType>(From->getType());
+ QualType NewFunType =
+ Ctx.getFunctionType(TT, FPT->getParamTypes(), FPT->getExtProtoInfo());
+ From->setType(NewFunType);
+
+ CXXMethodDecl *To = Import(From, Lang_CXX14);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<TypedefType>(To->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto foo() {
+ struct X {};
+ return X();
+ }
+ )",
+ Lang_CXX14, "input0.cc");
+ FunctionDecl *From =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
+
+ FunctionDecl *To = Import(From, Lang_CXX14);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside2) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto foo() {
+ struct X {};
+ return X();
+ }
+ )",
+ Lang_CXX14, "input0.cc");
+ FunctionDecl *From =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
+
+ // This time import the type directly.
+ QualType ToT = ImportType(From->getType(), From, Lang_CXX14);
+ const FunctionProtoType *FPT = cast<FunctionProtoType>(ToT);
+ EXPECT_TRUE(isa<AutoType>(FPT->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithTypedefToStructDeclaredInside) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto foo() {
+ struct X {};
+ using Y = X;
+ return Y();
+ }
+ )",
+ Lang_CXX14, "input0.cc");
+ FunctionDecl *From =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
+
+ FunctionDecl *To = Import(From, Lang_CXX14);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredNestedInside) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto foo() {
+ struct X { struct Y{}; };
+ return X::Y();
+ }
+ )",
+ Lang_CXX14, "input0.cc");
+ FunctionDecl *From =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
+
+ FunctionDecl *To = Import(From, Lang_CXX14);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithInternalLambdaType) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto f() {
+ auto l = []() {
+ struct X {};
+ return X();
+ };
+ return l();
+ }
+ )",
+ Lang_CXX17, "input0.cc");
+ FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+
+ FunctionDecl *To = Import(From, Lang_CXX17);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithTypeInIf) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto f() {
+ if (struct X {} x; true)
+ return X();
+ else
+ return X();
+ }
+ )",
+ Lang_CXX17, "input0.cc");
+ FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+
+ FunctionDecl *To = Import(From, Lang_CXX17);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithTypeInFor) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto f() {
+ for (struct X {} x;;)
+ return X();
+ }
+ )",
+ Lang_CXX17, "input0.cc");
+ FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+
+ FunctionDecl *To = Import(From, Lang_CXX17);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
+}
+
+TEST_P(ImportAutoFunctions, ReturnWithTypeInSwitch) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ auto f() {
+ switch (struct X {} x; 10) {
+ case 10:
+ return X();
+ }
+ }
+ )",
+ Lang_CXX17, "input0.cc");
+ FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+
+ FunctionDecl *To = Import(From, Lang_CXX17);
+ EXPECT_TRUE(To);
+ EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
+}
+
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest,
DefaultTestValuesForRunOptions, );
@@ -5650,6 +5834,9 @@ INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedirectingImporterTest,
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions,
DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportAutoFunctions,
+ DefaultTestValuesForRunOptions, );
+
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctionTemplates,
DefaultTestValuesForRunOptions, );
diff --git a/clang/unittests/AST/Language.cpp b/clang/unittests/AST/Language.cpp
index 47993cbb99ec..eeb3303a1278 100644
--- a/clang/unittests/AST/Language.cpp
+++ b/clang/unittests/AST/Language.cpp
@@ -34,6 +34,9 @@ ArgVector getBasicRunOptionsForLanguage(Language Lang) {
case Lang_CXX14:
BasicArgs = {"-std=c++14", "-frtti"};
break;
+ case Lang_CXX17:
+ BasicArgs = {"-std=c++17", "-frtti"};
+ break;
case Lang_CXX2a:
BasicArgs = {"-std=c++2a", "-frtti"};
break;
diff --git a/clang/unittests/AST/Language.h b/clang/unittests/AST/Language.h
index dd61ef8223e1..6ba40be743b7 100644
--- a/clang/unittests/AST/Language.h
+++ b/clang/unittests/AST/Language.h
@@ -28,6 +28,7 @@ enum Language {
Lang_CXX,
Lang_CXX11,
Lang_CXX14,
+ Lang_CXX17,
Lang_CXX2a,
Lang_OpenCL,
Lang_OBJCXX
diff --git a/clang/unittests/AST/MatchVerifier.h b/clang/unittests/AST/MatchVerifier.h
index 7aca7f498e9b..dd0569a58243 100644
--- a/clang/unittests/AST/MatchVerifier.h
+++ b/clang/unittests/AST/MatchVerifier.h
@@ -108,6 +108,10 @@ testing::AssertionResult MatchVerifier<NodeType>::match(
Args.push_back("-std=c++14");
FileName = "input.cc";
break;
+ case Lang_CXX17:
+ Args.push_back("-std=c++17");
+ FileName = "input.cc";
+ break;
case Lang_CXX2a:
Args.push_back("-std=c++2a");
FileName = "input.cc";
More information about the cfe-commits
mailing list