Fix template instantiation of a non-dependent call to an inherited
authorRichard Smith <richard@metafoo.co.uk>
Tue, 7 Apr 2020 02:17:55 +0000 (19:17 -0700)
committerRichard Smith <richard@metafoo.co.uk>
Tue, 7 Apr 2020 02:20:30 +0000 (19:20 -0700)
constructor with default arguments.

We used to try to rebuild the call as a call to the faked-up inherited
constructor, which is only a placeholder and lacks (for example) default
arguments. Instead, build the call by reference to the original
constructor.

In passing, add a note to say where a call that recursively uses a
default argument from within itself occurs. This is usually pretty
obvious, but still at least somewhat useful, and would have saved
significant debugging time for this particular bug.

clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/TreeTransform.h
clang/test/SemaCXX/default2.cpp
clang/test/SemaTemplate/instantiate-init.cpp

index 6ccb1c4..148f23f 100644 (file)
@@ -3952,6 +3952,8 @@ def err_use_of_default_argument_to_function_declared_later : Error<
 def note_default_argument_declared_here : Note<
   "default argument declared here">;
 def err_recursive_default_argument : Error<"recursive evaluation of default argument">;
+def note_recursive_default_argument_used_here : Note<
+  "default argument used here">;
 
 def ext_param_promoted_not_compatible_with_prototype : ExtWarn<
   "%diff{promoted type $ of K&R function parameter is not compatible with the "
index b311aad..a4f9c22 100644 (file)
@@ -5284,6 +5284,7 @@ bool Sema::CheckCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD,
   // If the default argument expression is not set yet, we are building it now.
   if (!Param->hasInit()) {
     Diag(Param->getBeginLoc(), diag::err_recursive_default_argument) << FD;
+    Diag(CallLoc, diag::note_recursive_default_argument_used_here);
     Param->setInvalidDecl();
     return true;
   }
index 1f88f9c..e9f4b11 100644 (file)
@@ -3028,9 +3028,14 @@ public:
                                      bool RequiresZeroInit,
                              CXXConstructExpr::ConstructionKind ConstructKind,
                                      SourceRange ParenRange) {
+    // Reconstruct the constructor we originally found, which might be
+    // different if this is a call to an inherited constructor.
+    CXXConstructorDecl *FoundCtor = Constructor;
+    if (Constructor->isInheritingConstructor())
+      FoundCtor = Constructor->getInheritedConstructor().getConstructor();
+
     SmallVector<Expr*, 8> ConvertedArgs;
-    if (getSema().CompleteConstructorCall(Constructor, Args, Loc,
-                                          ConvertedArgs))
+    if (getSema().CompleteConstructorCall(FoundCtor, Args, Loc, ConvertedArgs))
       return ExprError();
 
     return getSema().BuildCXXConstructExpr(Loc, T, Constructor,
index 8f77f30..4c8e8ce 100644 (file)
@@ -130,5 +130,8 @@ template <int I1 = I2, int I2 = 1> struct T {};  // expected-error-re {{use of u
 T<0, 1> t;
 
 struct PR28105 {
-  PR28105 (int = 0, int = 0, PR28105 = 0);  // expected-error{{recursive evaluation of default argument}}
+  PR28105 (int = 0, int = 0,
+      PR28105  // expected-error{{recursive evaluation of default argument}}
+      =
+      0); // expected-note {{default argument used here}}
 };
index 99b29c7..6a4f650 100644 (file)
@@ -1,5 +1,13 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
 
+namespace std {
+  template<typename T> struct initializer_list {
+    T *p;
+    __SIZE_TYPE__ n;
+    initializer_list(T*, __SIZE_TYPE__);
+  };
+}
+
 struct X0 { // expected-note 8{{candidate}}
   X0(int*, float*); // expected-note 4{{candidate}}
 };
@@ -158,3 +166,15 @@ namespace InitListUpdate {
   void g(AA, AA);
   void h() { f<1, 2>(); } // expected-note {{instantiation of}}
 }
+
+namespace RebuildStdInitList {
+  struct A { A(std::initializer_list<int>, int = 0) {} };
+  struct B : A { using A::A; };
+  struct PES { PES(B); };
+
+  // Check we can rebuild the use of the default argument here. This requires
+  // going to the original (base class) constructor, because we don't copy
+  // default arguments onto our fake derived class inherited constructors.
+  template<typename U> void f() { PES({1, 2, 3}); }
+  void g() { f<int>(); }
+}