From c56975e299e17a503066c98a3afaf02c5b231f9e Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 6 Apr 2020 19:17:55 -0700 Subject: [PATCH] Fix template instantiation of a non-dependent call to an inherited 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 | 2 ++ clang/lib/Sema/SemaExpr.cpp | 1 + clang/lib/Sema/TreeTransform.h | 9 +++++++-- clang/test/SemaCXX/default2.cpp | 5 ++++- clang/test/SemaTemplate/instantiate-init.cpp | 20 ++++++++++++++++++++ 5 files changed, 34 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 6ccb1c4..148f23f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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 " diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index b311aad..a4f9c22 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -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; } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 1f88f9c..e9f4b11 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -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 ConvertedArgs; - if (getSema().CompleteConstructorCall(Constructor, Args, Loc, - ConvertedArgs)) + if (getSema().CompleteConstructorCall(FoundCtor, Args, Loc, ConvertedArgs)) return ExprError(); return getSema().BuildCXXConstructExpr(Loc, T, Constructor, diff --git a/clang/test/SemaCXX/default2.cpp b/clang/test/SemaCXX/default2.cpp index 8f77f30..4c8e8ce 100644 --- a/clang/test/SemaCXX/default2.cpp +++ b/clang/test/SemaCXX/default2.cpp @@ -130,5 +130,8 @@ template 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}} }; diff --git a/clang/test/SemaTemplate/instantiate-init.cpp b/clang/test/SemaTemplate/instantiate-init.cpp index 99b29c7..6a4f650 100644 --- a/clang/test/SemaTemplate/instantiate-init.cpp +++ b/clang/test/SemaTemplate/instantiate-init.cpp @@ -1,5 +1,13 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +namespace std { + template 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 = 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 void f() { PES({1, 2, 3}); } + void g() { f(); } +} -- 2.7.4