diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3248,56 +3248,162 @@ return false; } -static bool hasTypeDeclaredInsideFunction(QualType T, const FunctionDecl *FD) { - if (T.isNull()) +namespace { +/// Check if a type has any reference to a declaration that is inside the body +/// of a function. +/// The \c CheckType(QualType) function should be used to determine +/// this property. +/// +/// The type visitor visits one type object only (not recursive). +/// To find all referenced declarations we must discover all type objects until +/// the canonical type is reached (walk over typedef and similar objects). This +/// is done by loop over all "sugar" type objects. For every such type we must +/// check all declarations that are referenced from it. For this check the +/// visitor is used. In the visit functions all referenced declarations except +/// the one that follows in the sugar chain (if any) must be checked. For this +/// check the same visitor is re-used (it has no state-dependent data). +/// +/// The visit functions have 3 possible return values: +/// - True, found a declaration inside \c ParentDC. +/// - False, found declarations only outside \c ParentDC and it is not possible +/// to find more declarations (the "sugar" chain does not continue). +/// - Empty optional value, found no declarations or only outside \c ParentDC, +/// but it is possible to find more declarations in the type "sugar" chain. +/// The loop over the "sugar" types can be implemented by using type visit +/// functions only (call \c CheckType with the desugared type). With the current +/// solution no visit function is needed if the type has only a desugared type +/// as data. +class IsTypeDeclaredInsideVisitor + : public TypeVisitor> { +public: + IsTypeDeclaredInsideVisitor(const FunctionDecl *ParentDC) + : ParentDC(ParentDC) {} + + bool CheckType(QualType T) { + // Check the chain of "sugar" types. + // The "sugar" types are typedef or similar types that have the same + // canonical type. + if (Optional Res = Visit(T.getTypePtr())) + return *Res; + QualType DsT = + T.getSingleStepDesugaredType(ParentDC->getParentASTContext()); + while (DsT != T) { + if (Optional Res = Visit(DsT.getTypePtr())) + return *Res; + T = DsT; + DsT = T.getSingleStepDesugaredType(ParentDC->getParentASTContext()); + } return false; + } + + Optional VisitTypedefType(const TypedefType *T) { + const TypedefNameDecl *TD = T->getDecl(); + assert(TD); + return isAncestorDeclContextOf(ParentDC, TD); + } + + Optional + VisitTemplateSpecializationType(const TemplateSpecializationType *T) { + for (const auto &Arg : T->template_arguments()) + if (checkTemplateArgument(Arg)) + return true; + // This type is a "sugar" to a record type, it can have a desugared type. + return {}; + } + + Optional VisitTagType(const TagType *T) { + return isAncestorDeclContextOf(ParentDC, T->getDecl()); + } + + Optional VisitPointerType(const PointerType *T) { + return CheckType(T->getPointeeType()); + } + + Optional VisitReferenceType(const ReferenceType *T) { + return CheckType(T->getPointeeTypeAsWritten()); + } + + Optional VisitConstantArrayType(const ConstantArrayType *T) { + if (T->getSizeExpr() && isAncestorDeclContextOf(ParentDC, T->getSizeExpr())) + return true; + + return CheckType(T->getElementType()); + } + + Optional VisitUsingType(const UsingType *T) { + if (T->getFoundDecl() && + isAncestorDeclContextOf(ParentDC, T->getFoundDecl())) + return true; - auto CheckTemplateArgument = [FD](const TemplateArgument &Arg) { + return {}; + } + + Optional VisitVariableArrayType(const VariableArrayType *T) { + llvm_unreachable( + "Variable array should not occur in deduced return type of a function"); + } + + Optional VisitIncompleteArrayType(const IncompleteArrayType *T) { + llvm_unreachable("Incomplete array should not occur in deduced return type " + "of a function"); + } + + Optional VisitDependentArrayType(const IncompleteArrayType *T) { + llvm_unreachable("Dependent array should not occur in deduced return type " + "of a function"); + } + +private: + const DeclContext *const ParentDC; + + bool checkTemplateArgument(const TemplateArgument &Arg) { switch (Arg.getKind()) { + case TemplateArgument::Null: + return false; + case TemplateArgument::Integral: + return CheckType(Arg.getIntegralType()); case TemplateArgument::Type: - return hasTypeDeclaredInsideFunction(Arg.getAsType(), FD); + return CheckType(Arg.getAsType()); case TemplateArgument::Expression: - return isAncestorDeclContextOf(FD, Arg.getAsExpr()); - default: - // FIXME: Handle other argument kinds. + return isAncestorDeclContextOf(ParentDC, Arg.getAsExpr()); + case TemplateArgument::Declaration: + // FIXME: The declaration in this case is not allowed to be in a function? + return isAncestorDeclContextOf(ParentDC, Arg.getAsDecl()); + case TemplateArgument::NullPtr: + // FIXME: The type is not allowed to be in the function? + return CheckType(Arg.getNullPtrType()); + case TemplateArgument::Pack: + llvm_unreachable( + "Add the code below and test for it if this place is reached"); + // for (const auto &PackArg: Arg.getPackAsArray()) + // if (checkTemplateArgument(PackArg)) + // return true; + // return false; + case TemplateArgument::Template: + // Templates can not be defined locally in functions. + // A template passed as argument can be not in ParentDC. + return false; + case TemplateArgument::TemplateExpansion: + // Templates can not be defined locally in functions. + // A template passed as argument can be not in ParentDC. return false; } }; - - if (const auto *RecordT = T->getAs()) { - const RecordDecl *RD = RecordT->getDecl(); - assert(RD); - if (isAncestorDeclContextOf(FD, RD)) { - assert(RD->getLexicalDeclContext() == RD->getDeclContext()); - return true; - } - if (const auto *RDTempl = dyn_cast(RD)) - if (llvm::count_if(RDTempl->getTemplateArgs().asArray(), - CheckTemplateArgument)) - return true; - // Note: It is possible that T can be get as both a RecordType and a - // TemplateSpecializationType. - } - if (const auto *TST = T->getAs()) - return llvm::count_if(TST->template_arguments(), CheckTemplateArgument); - - return false; -} +}; +} // namespace bool ASTNodeImporter::hasAutoReturnTypeDeclaredInside(FunctionDecl *D) { QualType FromTy = D->getType(); const auto *FromFPT = FromTy->getAs(); assert(FromFPT && "Must be called on FunctionProtoType"); - if (const AutoType *AutoT = FromFPT->getReturnType()->getContainedAutoType()) - return hasTypeDeclaredInsideFunction(AutoT->getDeducedType(), D); - if (const auto *TypedefT = FromFPT->getReturnType()->getAs()) { - const TypedefNameDecl *TD = TypedefT->getDecl(); - assert(TD); - if (isAncestorDeclContextOf(D, TD)) { - assert(TD->getLexicalDeclContext() == TD->getDeclContext()); - return true; - } + + QualType RetT = FromFPT->getReturnType(); + if (isa(RetT.getTypePtr())) { + FunctionDecl *Def = D->getDefinition(); + IsTypeDeclaredInsideVisitor Visitor(Def ? Def : D); + return Visitor.CheckType(RetT); } + return false; } diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -6318,64 +6318,195 @@ EXPECT_EQ(ToEPI.ExceptionSpec.SourceDecl, ToCtor); } -struct ImportAutoFunctions : ASTImporterOptionSpecificTestBase {}; +struct ImportAutoFunctions : ASTImporterOptionSpecificTestBase { + void testImport(llvm::StringRef Code, clang::TestLanguage Lang = Lang_CXX14) { + Decl *FromTU = getTuDecl(Code, Lang, "input0.cc"); + FunctionDecl *From = FirstDeclMatcher().match( + FromTU, functionDecl(hasName("foo"))); + + FunctionDecl *To = Import(From, Lang); + EXPECT_TRUE(To); + // We check here only that the type is auto type. + // These tests are to verify that no crash happens. + // The crash possibility is the presence of a reference to a declaration + // in the function's body from the return type, if the function has auto + // return type. + EXPECT_TRUE(isa(To->getReturnType())); + } +}; + +TEST_P(ImportAutoFunctions, ReturnWithAutoUnresolvedArg) { + testImport( + R"( + template + auto foo() { + return 22; + } + )"); +} + +TEST_P(ImportAutoFunctions, ReturnWithTemplateWithTemplateTemplateArg) { + // FIXME: Is it possible to have the template arg inside the function? + testImport( + R"( + template struct Tmpl {}; + template class> struct TmplTmpl {}; + auto foo() { + return TmplTmpl(); + } + )"); +} + +TEST_P(ImportAutoFunctions, ReturnWithTemplateWithDeclarationTemplateArg) { + // FIXME: Is it possible to have the template arg inside the function? + testImport( + R"( + template struct Tmpl {}; + int A[10]; + auto foo() { + return Tmpl(); + } + )"); +} + +TEST_P(ImportAutoFunctions, ReturnWithTemplateWithNullPtrTemplateArg) { + testImport( + R"( + template struct Tmpl {}; + auto foo() { + constexpr int* A = nullptr; + return Tmpl(); + } + )"); +} + +TEST_P(ImportAutoFunctions, ReturnWithTemplateWithIntegralTemplateArg) { + testImport( + R"( + template struct Tmpl {}; + auto foo() { + using Int = int; + constexpr Int A = 7; + return Tmpl(); + } + )"); +} + +TEST_P(ImportAutoFunctions, ReturnWithTemplateWithDecltypeTypeDeclaredInside) { + testImport( + R"( + template struct Tmpl {}; + auto foo() { + struct X {}; + X x; + return Tmpl(); + } + )"); +} + +TEST_P(ImportAutoFunctions, ReturnWithTemplateWithUsingTypeDeclaredInside) { + testImport( + R"( + template struct Tmpl {}; + namespace A { struct X {}; } + auto foo() { + using A::X; + return Tmpl(); + } + )"); +} + +TEST_P(ImportAutoFunctions, ReturnWithTemplateWithArrayTypeDeclaredInside) { + testImport( + R"( + template struct Tmpl {}; + auto foo() { + struct X {}; + return Tmpl(); + } + )"); +} + +TEST_P(ImportAutoFunctions, ReturnWithTemplateWithArraySizeExprDeclaredInside) { + testImport( + R"( + template struct Tmpl {}; + auto foo() { + constexpr int S = 10; + return Tmpl(); + } + )"); +} + +TEST_P(ImportAutoFunctions, ReturnWithTemplateWithPackArgDeclaredInside) { + testImport( + R"( + template struct Tmpl {}; + auto foo() { + using X = bool; + return Tmpl(); + } + )"); +} TEST_P(ImportAutoFunctions, ReturnWithTemplateWithIntegerArgDeclaredInside) { - Decl *FromTU = getTuDecl( + testImport( R"( template struct Tmpl {}; auto foo() { constexpr int X = 1; return Tmpl(); } - )", - Lang_CXX14, "input0.cc"); - FunctionDecl *From = FirstDeclMatcher().match( - FromTU, functionDecl(hasName("foo"))); + )"); +} - FunctionDecl *To = Import(From, Lang_CXX14); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); +TEST_P(ImportAutoFunctions, ReturnWithTemplateWithPtrToStructDeclaredInside) { + testImport( + R"( + template struct Tmpl {}; + auto foo() { + struct X {}; + return Tmpl(); + } + )"); +} + +TEST_P(ImportAutoFunctions, ReturnWithTemplateWithRefToStructDeclaredInside) { + testImport( + R"( + template struct Tmpl {}; + struct X {}; + auto foo() { + using Y = X; + return Tmpl(); + } + )"); } TEST_P(ImportAutoFunctions, ReturnWithTemplateWithStructDeclaredInside1) { - Decl *FromTU = getTuDecl( + testImport( R"( template struct Tmpl {}; auto foo() { struct X {}; return Tmpl(); } - )", - Lang_CXX14, "input0.cc"); - FunctionDecl *From = FirstDeclMatcher().match( - FromTU, functionDecl(hasName("foo"))); - - FunctionDecl *To = Import(From, Lang_CXX14); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); + )"); } TEST_P(ImportAutoFunctions, ReturnWithTemplateWithStructDeclaredInside2) { - Decl *FromTU = getTuDecl( + testImport( R"( template struct Tmpl {}; auto foo() { struct X {}; return Tmpl>(); } - )", - Lang_CXX14, "input0.cc"); - FunctionDecl *From = FirstDeclMatcher().match( - FromTU, functionDecl(hasName("foo"))); - - FunctionDecl *To = Import(From, Lang_CXX14); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); + )"); } TEST_P(ImportAutoFunctions, ReturnWithTemplateWithTypedefDeclaredInside) { - Decl *FromTU = getTuDecl( + testImport( R"( template struct Tmpl {}; auto foo() { @@ -6383,14 +6514,7 @@ using x_type = X; return Tmpl(); } - )", - Lang_CXX14, "input0.cc"); - FunctionDecl *From = FirstDeclMatcher().match( - FromTU, functionDecl(hasName("foo"))); - - FunctionDecl *To = Import(From, Lang_CXX14); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); + )"); } TEST_P(ImportAutoFunctions, ReturnWithTypedefDeclaredInside) { @@ -6429,20 +6553,13 @@ } TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside) { - Decl *FromTU = getTuDecl( + testImport( R"( auto foo() { struct X {}; return X(); } - )", - Lang_CXX14, "input0.cc"); - FunctionDecl *From = - FirstDeclMatcher().match(FromTU, functionDecl()); - - FunctionDecl *To = Import(From, Lang_CXX14); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); + )"); } TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside2) { @@ -6463,45 +6580,53 @@ EXPECT_TRUE(isa(FPT->getReturnType())); } -TEST_P(ImportAutoFunctions, ReturnWithTypedefToStructDeclaredInside) { +TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside3) { Decl *FromTU = getTuDecl( R"( - auto foo() { + struct S { + constexpr auto foo(); + }; + constexpr auto S::foo() { struct X {}; - using Y = X; - return Y(); + return X(); } )", Lang_CXX14, "input0.cc"); - FunctionDecl *From = - FirstDeclMatcher().match(FromTU, functionDecl()); + FunctionDecl *From = FirstDeclMatcher().match( + FromTU, functionDecl(hasName("foo"), unless(hasBody(stmt())))); + ASSERT_FALSE(From->isThisDeclarationADefinition()); - FunctionDecl *To = Import(From, Lang_CXX14); + FunctionDecl *To = Import(From, Lang_CXX17); EXPECT_TRUE(To); EXPECT_TRUE(isa(To->getReturnType())); + EXPECT_FALSE(To->isThisDeclarationADefinition()); +} + +TEST_P(ImportAutoFunctions, ReturnWithTypedefToStructDeclaredInside) { + testImport( + R"( + auto foo() { + struct X {}; + using Y = X; + return Y(); + } + )"); } TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredNestedInside) { - Decl *FromTU = getTuDecl( + testImport( R"( auto foo() { struct X { struct Y{}; }; return X::Y(); } - )", - Lang_CXX14, "input0.cc"); - FunctionDecl *From = - FirstDeclMatcher().match(FromTU, functionDecl()); - - FunctionDecl *To = Import(From, Lang_CXX14); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); + )"); } TEST_P(ImportAutoFunctions, ReturnWithInternalLambdaType) { - Decl *FromTU = getTuDecl( + testImport( R"( - auto f() { + auto foo() { auto l = []() { struct X {}; return X(); @@ -6509,68 +6634,44 @@ return l(); } )", - Lang_CXX17, "input0.cc"); - FunctionDecl *From = FirstDeclMatcher().match( - FromTU, functionDecl(hasName("f"))); - - FunctionDecl *To = Import(From, Lang_CXX17); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); + Lang_CXX17); } TEST_P(ImportAutoFunctions, ReturnWithTypeInIf) { - Decl *FromTU = getTuDecl( + testImport( R"( - auto f() { + auto foo() { if (struct X {} x; true) return X(); else return X(); } )", - Lang_CXX17, "input0.cc"); - FunctionDecl *From = FirstDeclMatcher().match( - FromTU, functionDecl(hasName("f"))); - - FunctionDecl *To = Import(From, Lang_CXX17); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); + Lang_CXX17); } TEST_P(ImportAutoFunctions, ReturnWithTypeInFor) { - Decl *FromTU = getTuDecl( + testImport( R"( - auto f() { + auto foo() { for (struct X {} x;;) return X(); } )", - Lang_CXX17, "input0.cc"); - FunctionDecl *From = FirstDeclMatcher().match( - FromTU, functionDecl(hasName("f"))); - - FunctionDecl *To = Import(From, Lang_CXX17); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); + Lang_CXX17); } TEST_P(ImportAutoFunctions, ReturnWithTypeInSwitch) { - Decl *FromTU = getTuDecl( + testImport( R"( - auto f() { + auto foo() { switch (struct X {} x; 10) { case 10: return X(); } } )", - Lang_CXX17, "input0.cc"); - FunctionDecl *From = FirstDeclMatcher().match( - FromTU, functionDecl(hasName("f"))); - - FunctionDecl *To = Import(From, Lang_CXX17); - EXPECT_TRUE(To); - EXPECT_TRUE(isa(To->getReturnType())); + Lang_CXX17); } struct ImportSourceLocations : ASTImporterOptionSpecificTestBase {};