]> granicus.if.org Git - clang/commitdiff
Fix PR 10758: Infinite recursion when dealing with copy-initialization
authorAlex Lorenz <arphaman@gmail.com>
Tue, 16 May 2017 10:23:58 +0000 (10:23 +0000)
committerAlex Lorenz <arphaman@gmail.com>
Tue, 16 May 2017 10:23:58 +0000 (10:23 +0000)
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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@303156 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Sema/Sema.h
lib/Sema/SemaInit.cpp
test/SemaCXX/constructor-initializer.cpp

index 86c995c54fdae5b85b1a3ba23371e953dbf1aa52..e910be14f9696ae280de926137a4e4cacc68dd48 100644 (file)
@@ -1074,6 +1074,10 @@ public:
   /// correctly named definition after the renamed definition.
   llvm::SmallPtrSet<const NamedDecl *, 4> TypoCorrectedFunctionDefinitions;
 
+  /// Stack of types that correspond to the parameter entities that are
+  /// currently being copy-initialized. Can be empty.
+  llvm::SmallVector<QualType, 4> CurrentParameterCopyTypes;
+
   void ReadMethodPool(Selector Sel);
   void updateOutOfDateSelector(Selector Sel);
 
index 8d19b82c3f9d5ea868d23f0ee0fc869fa416ddd6..32024cb335dc30c97d37521c46bb018f7b603579 100644 (file)
@@ -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;
 }
 
index c5de33cedb90487f45bf60d4781127697667163e..102ff1e80d032746e70f84d10f91fa5b7ca864d3 100644 (file)
@@ -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'}}
+}
+}