OSDN Git Service

PR46648: Do not eagerly instantiate default arguments for a generic
authorRichard Smith <richard@metafoo.co.uk>
Thu, 9 Jul 2020 21:11:21 +0000 (14:11 -0700)
committerRichard Smith <richard@metafoo.co.uk>
Fri, 10 Jul 2020 00:24:20 +0000 (17:24 -0700)
lambda when instantiating a call operator specialization.

We previously incorrectly thought that such substitution was happening
in the context of substitution into a local scope, which is a context
where we should perform eager default argument instantiation.

clang/include/clang/AST/DeclBase.h
clang/lib/AST/DeclBase.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/test/SemaTemplate/default-arguments-cxx0x.cpp
clang/test/SemaTemplate/dependent-expr.cpp

index 5e00be3..4f33ff1 100644 (file)
@@ -875,15 +875,19 @@ public:
     return getParentFunctionOrMethod() == nullptr;
   }
 
-  /// Returns true if this declaration is lexically inside a function or inside
-  /// a variable initializer. It recognizes non-defining declarations as well
-  /// as members of local classes and lambdas:
+  /// Determine whether a substitution into this declaration would occur as
+  /// part of a substitution into a dependent local scope. Such a substitution
+  /// transitively substitutes into all constructs nested within this
+  /// declaration.
+  ///
+  /// This recognizes non-defining declarations as well as members of local
+  /// classes and lambdas:
   /// \code
-  ///     void foo() { void bar(); }
-  ///     void foo2() { class ABC { void bar(); }; }
-  ///     inline int x = [](){ return 0; }();
+  ///     template<typename T> void foo() { void bar(); }
+  ///     template<typename T> void foo2() { class ABC { void bar(); }; }
+  ///     template<typename T> inline int x = [](){ return 0; }();
   /// \endcode
-  bool isInLocalScope() const;
+  bool isInLocalScopeForInstantiation() const;
 
   /// If this decl is defined inside a function/method/block it returns
   /// the corresponding DeclContext, otherwise it returns null.
index 3fd2775..da1eadd 100644 (file)
@@ -364,8 +364,10 @@ void Decl::setDeclContextsImpl(DeclContext *SemaDC, DeclContext *LexicalDC,
   }
 }
 
-bool Decl::isInLocalScope() const {
+bool Decl::isInLocalScopeForInstantiation() const {
   const DeclContext *LDC = getLexicalDeclContext();
+  if (!LDC->isDependentContext())
+    return false;
   while (true) {
     if (LDC->isFunctionOrMethod())
       return true;
index 8197e7d..11e03c5 100644 (file)
@@ -2426,7 +2426,7 @@ ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
     UnparsedDefaultArgInstantiations[OldParm].push_back(NewParm);
   } else if (Expr *Arg = OldParm->getDefaultArg()) {
     FunctionDecl *OwningFunc = cast<FunctionDecl>(OldParm->getDeclContext());
-    if (OwningFunc->isInLocalScope()) {
+    if (OwningFunc->isInLocalScopeForInstantiation()) {
       // Instantiate default arguments for methods of local classes (DR1484)
       // and non-defining declarations.
       Sema::ContextRAII SavedContext(*this, OwningFunc);
index 6179d90..85adc4e 100644 (file)
@@ -4458,7 +4458,7 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New,
         EPI.ExceptionSpec.Type != EST_None &&
         EPI.ExceptionSpec.Type != EST_DynamicNone &&
         EPI.ExceptionSpec.Type != EST_BasicNoexcept &&
-        !Tmpl->isInLocalScope()) {
+        !Tmpl->isInLocalScopeForInstantiation()) {
       FunctionDecl *ExceptionSpecTemplate = Tmpl;
       if (EPI.ExceptionSpec.Type == EST_Uninstantiated)
         ExceptionSpecTemplate = EPI.ExceptionSpec.SourceTemplate;
index 02696a8..1aa4565 100644 (file)
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
 // RUN: %clang_cc1 -fsyntax-only -std=c++14 -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
 // expected-no-diagnostics
 
 // Test default template arguments for function templates.
@@ -132,5 +133,32 @@ namespace lambda {
   void foo() {
     bar<int>();
   }
+
+#if __cplusplus >= 202002L
+  // PR46648: ensure we don't reject this by triggering default argument
+  // instantiation spuriously.
+  auto x = []<typename T>(T x = 123) {};
+  void y() { x(nullptr); }
+
+  template<int A> struct X {
+    template<int B> constexpr int f() {
+      auto l = []<int C>(int n = A + B + C) { return n; };
+      return l.template operator()<3>();
+    }
+  };
+  static_assert(X<100>().f<20>() == 123);
+
+  template<> template<int B> constexpr int X<200>::f() {
+    auto l = []<int C>(int n = 300 + B + C) { return n; };
+    return l.template operator()<1>();
+  }
+  static_assert(X<200>().f<20>() == 321);
+
+  template<> template<> constexpr int X<300>::f<20>() {
+    auto l = []<int C>(int n = 450 + C) { return n; };
+    return l.template operator()<6>();
+  }
+  static_assert(X<300>().f<20>() == 456);
+#endif
 } // namespace lambda
 #endif
index 0ce4cb3..abdb8e9 100644 (file)
@@ -144,7 +144,7 @@ namespace PR45083 {
   void h(auto a, void*) {} // expected-error {{redefinition}}
 
   void i(auto a) {
-    [](auto a, int = ({decltype(a) i; i * 2;})){}(a); // expected-error {{no matching function}} expected-note {{substitution failure}}
+    [](auto a, int = ({decltype(a) i; i * 2;})){}(a); // expected-error {{invalid operands to binary expression ('decltype(a)' (aka 'void *') and 'int')}} expected-note {{in instantiation of}}
   }
   void use_i() {
     i(0);