From de69ff9b7c90db760c2d145e006f4d368efc0afe Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Tue, 16 May 2017 10:23:58 +0000 Subject: [PATCH] Fix PR 10758: Infinite recursion when dealing with copy-initialization This commit fixes a bug that's tracked by PR 10758 and duplicates like PR 30343. The bug causes clang to crash with a stack overflow while recursing infinitely trying to perform copy-initialization on a type without a copy constructor but with a constructor that accepts another type that can be constructed using the original type. The commit fixes this bug by detecting the recursive behavior and failing correctly with an appropriate error message. It also tries to provide a meaningful diagnostic note about the constructor which leads to this behavior. rdar://28483944 Differential Revision: https://reviews.llvm.org/D25051 llvm-svn: 303156 --- clang/include/clang/Sema/Sema.h | 4 ++ clang/lib/Sema/SemaInit.cpp | 38 +++++++++++++++++++ .../test/SemaCXX/constructor-initializer.cpp | 19 ++++++++++ 3 files changed, 61 insertions(+) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 86c995c54fda..e910be14f969 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1074,6 +1074,10 @@ public: /// correctly named definition after the renamed definition. llvm::SmallPtrSet TypoCorrectedFunctionDefinitions; + /// Stack of types that correspond to the parameter entities that are + /// currently being copy-initialized. Can be empty. + llvm::SmallVector CurrentParameterCopyTypes; + void ReadMethodPool(Selector Sel); void updateOutOfDateSelector(Selector Sel); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 8d19b82c3f9d..32024cb335dc 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -8296,8 +8296,46 @@ Sema::PerformCopyInitialization(const InitializedEntity &Entity, AllowExplicit); InitializationSequence Seq(*this, Entity, Kind, InitE, TopLevelOfInitList); + // Prevent infinite recursion when performing parameter copy-initialization. + const bool ShouldTrackCopy = + Entity.isParameterKind() && Seq.isConstructorInitialization(); + if (ShouldTrackCopy) { + if (llvm::find(CurrentParameterCopyTypes, Entity.getType()) != + CurrentParameterCopyTypes.end()) { + Seq.SetOverloadFailure( + InitializationSequence::FK_ConstructorOverloadFailed, + OR_No_Viable_Function); + + // Try to give a meaningful diagnostic note for the problematic + // constructor. + const auto LastStep = Seq.step_end() - 1; + assert(LastStep->Kind == + InitializationSequence::SK_ConstructorInitialization); + const FunctionDecl *Function = LastStep->Function.Function; + auto Candidate = + llvm::find_if(Seq.getFailedCandidateSet(), + [Function](const OverloadCandidate &Candidate) -> bool { + return Candidate.Viable && + Candidate.Function == Function && + Candidate.Conversions.size() > 0; + }); + if (Candidate != Seq.getFailedCandidateSet().end() && + Function->getNumParams() > 0) { + Candidate->Viable = false; + Candidate->FailureKind = ovl_fail_bad_conversion; + Candidate->Conversions[0].setBad(BadConversionSequence::no_conversion, + InitE, + Function->getParamDecl(0)->getType()); + } + } + CurrentParameterCopyTypes.push_back(Entity.getType()); + } + ExprResult Result = Seq.Perform(*this, Entity, Kind, InitE); + if (ShouldTrackCopy) + CurrentParameterCopyTypes.pop_back(); + return Result; } diff --git a/clang/test/SemaCXX/constructor-initializer.cpp b/clang/test/SemaCXX/constructor-initializer.cpp index c5de33cedb90..102ff1e80d03 100644 --- a/clang/test/SemaCXX/constructor-initializer.cpp +++ b/clang/test/SemaCXX/constructor-initializer.cpp @@ -302,3 +302,22 @@ namespace PR14073 { struct S2 { union { union { int n; }; char c; }; S2() : n(n) {} }; // expected-warning {{field 'n' is uninitialized when used here}} struct S3 { struct { int n; }; S3() : n(n) {} }; // expected-warning {{field 'n' is uninitialized when used here}} } + +namespace PR10758 { +struct A; +struct B { + B (A const &); // expected-note 2 {{candidate constructor not viable: no known conversion from 'const PR10758::B' to 'const PR10758::A &' for 1st argument}} + B (B &); // expected-note 2 {{candidate constructor not viable: 1st argument ('const PR10758::B') would lose const qualifier}} +}; +struct A { + A (B); // expected-note 2 {{passing argument to parameter here}} +}; + +B f(B const &b) { + return b; // expected-error {{no matching constructor for initialization of 'PR10758::B'}} +} + +A f2(const B &b) { + return b; // expected-error {{no matching constructor for initialization of 'PR10758::B'}} +} +}