]> granicus.if.org Git - clang/commitdiff
If a non-template constructor instantiated to X(X),
authorJohn McCall <rjmccall@apple.com>
Sun, 14 Dec 2014 01:46:53 +0000 (01:46 +0000)
committerJohn McCall <rjmccall@apple.com>
Sun, 14 Dec 2014 01:46:53 +0000 (01:46 +0000)
ignore it during overload resolution when initializing
X from a value of type cv X.

Previously, our rule here only ignored specializations
of constructor templates.  That's probably because the
standard says that constructors are outright ill-formed
if their first parameter is literally X and they're
callable with one argument.  However, Clang only
enforces that prohibition against non-implicit
instantiations; I'm not sure why, but it seems to be
deliberate.  Given that, the most sensible thing to
do is to just ignore the "illegal" constructor
regardless of where it came from.

Also, stop ignoring such constructors silently:
print a note explaining why they're being ignored.

Fixes <rdar://19199836>.

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

include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Overload.h
lib/AST/DeclCXX.cpp
lib/Sema/SemaOverload.cpp
test/SemaTemplate/constructor-template.cpp

index 48fdf6f826e8aaadbba90c9915bdffa97cf6628f..32bf1b067b7b60caec889db792f791fd2405d7f3 100644 (file)
@@ -2847,6 +2847,9 @@ def note_ovl_candidate : Note<"candidate "
     "|volatile and restrict|const, volatile, and restrict}4)}2">;
 
 def note_ovl_candidate_inherited_constructor : Note<"inherited from here">;
+def note_ovl_candidate_illegal_constructor : Note<
+    "candidate %select{constructor|template}0 ignored: "
+    "instantiation %select{takes|would take}0 its own class type by value">;
 def note_ovl_candidate_bad_deduction : Note<
     "candidate template ignored: failed template argument deduction">;
 def note_ovl_candidate_incomplete_deduction : Note<"candidate template ignored: "
index 41dcf974e515838107a7abc12d50c56b6c157999..4447db2b3ef015de04f90c3cc4afa1b2db43a542 100644 (file)
@@ -553,6 +553,17 @@ namespace clang {
     /// conversion.
     ovl_fail_trivial_conversion,
 
+    /// This conversion candidate was not considered because it is
+    /// an illegal instantiation of a constructor temploid: it is
+    /// callable with one argument, we only have one argument, and
+    /// its first parameter type is exactly the type of the class.
+    ///
+    /// Defining such a constructor directly is illegal, and
+    /// template-argument deduction is supposed to ignore such
+    /// instantiations, but we can still get one with the right
+    /// kind of implicit instantiation.
+    ovl_fail_illegal_constructor,
+
     /// This conversion candidate is not viable because its result
     /// type is not implicitly convertible to the desired type.
     ovl_fail_bad_final_conversion,
index 0ee3e3e90407226fa4abcef895b8b632f3129b46..a6d9d411eef31002e8c170dbc11db7305819bd5a 100644 (file)
@@ -1831,7 +1831,6 @@ bool CXXConstructorDecl::isConvertingConstructor(bool AllowExplicit) const {
 bool CXXConstructorDecl::isSpecializationCopyingObject() const {
   if ((getNumParams() < 1) ||
       (getNumParams() > 1 && !getParamDecl(1)->hasDefaultArg()) ||
-      (getPrimaryTemplate() == nullptr) ||
       (getDescribedFunctionTemplate() != nullptr))
     return false;
 
index d33f126db1c37c679962029f55e59ff4e1c882e6..9058fc098d8b65a99718718a8ca9f7d7fb3777fb 100644 (file)
@@ -5586,6 +5586,15 @@ Sema::AddOverloadCandidate(FunctionDecl *Function,
   // Overload resolution is always an unevaluated context.
   EnterExpressionEvaluationContext Unevaluated(*this, Sema::Unevaluated);
 
+  // Add this candidate
+  OverloadCandidate &Candidate = CandidateSet.addCandidate(Args.size());
+  Candidate.FoundDecl = FoundDecl;
+  Candidate.Function = Function;
+  Candidate.Viable = true;
+  Candidate.IsSurrogate = false;
+  Candidate.IgnoreObjectArgument = false;
+  Candidate.ExplicitCallArguments = Args.size();
+
   if (Constructor) {
     // C++ [class.copy]p3:
     //   A member function template is never instantiated to perform the copy
@@ -5594,19 +5603,13 @@ Sema::AddOverloadCandidate(FunctionDecl *Function,
     if (Args.size() == 1 &&
         Constructor->isSpecializationCopyingObject() &&
         (Context.hasSameUnqualifiedType(ClassType, Args[0]->getType()) ||
-         IsDerivedFrom(Args[0]->getType(), ClassType)))
+         IsDerivedFrom(Args[0]->getType(), ClassType))) {
+      Candidate.Viable = false;
+      Candidate.FailureKind = ovl_fail_illegal_constructor;
       return;
+    }
   }
 
-  // Add this candidate
-  OverloadCandidate &Candidate = CandidateSet.addCandidate(Args.size());
-  Candidate.FoundDecl = FoundDecl;
-  Candidate.Function = Function;
-  Candidate.Viable = true;
-  Candidate.IsSurrogate = false;
-  Candidate.IgnoreObjectArgument = false;
-  Candidate.ExplicitCallArguments = Args.size();
-
   unsigned NumParams = Proto->getNumParams();
 
   // (C++ 13.3.2p2): A candidate function having fewer than m
@@ -9235,6 +9238,13 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
   case ovl_fail_bad_deduction:
     return DiagnoseBadDeduction(S, Cand, NumArgs);
 
+  case ovl_fail_illegal_constructor: {
+    S.Diag(Fn->getLocation(), diag::note_ovl_candidate_illegal_constructor)
+      << (Fn->getPrimaryTemplate() ? 1 : 0);
+    MaybeEmitInheritedConstructorNote(S, Fn);
+    return;
+  }
+
   case ovl_fail_trivial_conversion:
   case ovl_fail_bad_final_conversion:
   case ovl_fail_final_conversion_not_exact:
index cf3fdfb7c1ea557e00c9d0ae094596fbcedde125..c5306a6e32f4a584278d4eaf39870ff6b7c78a1e 100644 (file)
@@ -54,7 +54,7 @@ struct B { A<int> x; B(B& a) : x(a.x) {} };
 struct X2 {
   X2(); // expected-note{{candidate constructor}}
   X2(X2&);     // expected-note {{candidate constructor}}
-  template<typename T> X2(T);
+  template<typename T> X2(T); // expected-note {{candidate template ignored: instantiation would take its own class type by value}}
 };
 
 X2 test(bool Cond, X2 x2) {
@@ -126,3 +126,51 @@ namespace PR8182 {
   }
 
 }
+
+// Don't blow out the stack trying to call an illegal constructor
+// instantiation.  We intentionally allow implicit instantiations to
+// exist, so make sure they're unusable.
+//
+// rdar://19199836
+namespace self_by_value {
+  template <class T, class U> struct A {
+    A() {}
+    A(const A<T,U> &o) {}
+    A(A<T,T> o) {}
+  };
+
+  void helper(A<int,float>);
+
+  void test1(A<int,int> a) {
+    helper(a);
+  }
+  void test2() {
+    helper(A<int,int>());
+  }
+}
+
+namespace self_by_value_2 {
+  template <class T, class U> struct A {
+    A() {} // expected-note {{not viable: requires 0 arguments}}
+    A(A<T,U> &o) {} // expected-note {{not viable: expects an l-value}}
+    A(A<T,T> o) {} // expected-note {{ignored: instantiation takes its own class type by value}}
+  };
+
+  void helper_A(A<int,int>); // expected-note {{passing argument to parameter here}}
+  void test_A() {
+    helper_A(A<int,int>()); // expected-error {{no matching constructor}}
+  }
+}
+
+namespace self_by_value_3 {
+  template <class T, class U> struct A {
+    A() {}
+    A(A<T,U> &o) {}
+    A(A<T,T> o) {}
+  };
+
+  void helper_A(A<int,int>);
+  void test_A(A<int,int> b) {
+    helper_A(b);
+  }
+}