Revert r335019 "Update NRVO logic to support early return (Attempt 2)"

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@335022 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h
index 0a0b2c1..c10ca9e 100644
--- a/include/clang/AST/Decl.h
+++ b/include/clang/AST/Decl.h
@@ -879,12 +879,6 @@
     DAK_Normal
   };
 
-  enum NRVOMode {
-    NRVO_Candidate,
-    NRVO_Disabled,
-    NRVO_Enabled,
-  };
-
   class ParmVarDeclBitfields {
     friend class ASTDeclReader;
     friend class ParmVarDecl;
@@ -937,7 +931,7 @@
     /// Whether this local variable could be allocated in the return
     /// slot of its function, enabling the named return value optimization
     /// (NRVO).
-    unsigned NRVOMode : 2;
+    unsigned NRVOVariable : 1;
 
     /// Whether this variable is the for-range-declaration in a C++0x
     /// for-range statement.
@@ -1325,20 +1319,12 @@
   /// return slot when returning from the function. Within the function body,
   /// each return that returns the NRVO object will have this variable as its
   /// NRVO candidate.
-  NRVOMode getNRVOMode() const {
-    if (isa<ParmVarDecl>(this))
-      return NRVO_Disabled;
-    return static_cast<NRVOMode>(NonParmVarDeclBits.NRVOMode);
-  }
-  bool isNRVOCandidate() const {
-    return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.NRVOMode == NRVO_Candidate;
-  }
   bool isNRVOVariable() const {
-    return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.NRVOMode == NRVO_Enabled;
+    return isa<ParmVarDecl>(this) ? false : NonParmVarDeclBits.NRVOVariable;
   }
   void setNRVOVariable(bool NRVO) {
     assert(!isa<ParmVarDecl>(this));
-    NonParmVarDeclBits.NRVOMode = NRVO ? NRVO_Enabled : NRVO_Disabled;
+    NonParmVarDeclBits.NRVOVariable = NRVO;
   }
 
   /// Determine whether this variable is the for-range-declaration in
diff --git a/include/clang/Sema/Scope.h b/include/clang/Sema/Scope.h
index 298c546..a3379ff 100644
--- a/include/clang/Sema/Scope.h
+++ b/include/clang/Sema/Scope.h
@@ -201,6 +201,10 @@
   /// Used to determine if errors occurred in this scope.
   DiagnosticErrorTrap ErrorTrap;
 
+  /// A lattice consisting of undefined, a single NRVO candidate variable in
+  /// this scope, or over-defined. The bit is true when over-defined.
+  llvm::PointerIntPair<VarDecl *, 1, bool> NRVO;
+
   void setFlags(Scope *Parent, unsigned F);
 
 public:
@@ -462,7 +466,23 @@
                                   UsingDirectives.end());
   }
 
-  void setNRVOCandidate(VarDecl *Candidate);
+  void addNRVOCandidate(VarDecl *VD) {
+    if (NRVO.getInt())
+      return;
+    if (NRVO.getPointer() == nullptr) {
+      NRVO.setPointer(VD);
+      return;
+    }
+    if (NRVO.getPointer() != VD)
+      setNoNRVO();
+  }
+
+  void setNoNRVO() {
+    NRVO.setInt(true);
+    NRVO.setPointer(nullptr);
+  }
+
+  void mergeNRVOIntoParent();
 
   /// Init - This is used by the parser to implement scope caching.
   void Init(Scope *parent, unsigned flags);
diff --git a/lib/Sema/Scope.cpp b/lib/Sema/Scope.cpp
index 5a46ba2..eae5a32 100644
--- a/lib/Sema/Scope.cpp
+++ b/lib/Sema/Scope.cpp
@@ -92,6 +92,7 @@
   UsingDirectives.clear();
   Entity = nullptr;
   ErrorTrap.reset();
+  NRVO.setPointerAndInt(nullptr, 0);
 }
 
 bool Scope::containedInPrototypeScope() const {
@@ -118,15 +119,19 @@
   Flags |= FlagsToSet;
 }
 
-void Scope::setNRVOCandidate(VarDecl *Candidate) {
-  for (Decl *D : DeclsInScope) {
-    VarDecl *VD = dyn_cast<VarDecl>(D);
-    if (VD && VD != Candidate && VD->isNRVOCandidate())
-      VD->setNRVOVariable(false);
+void Scope::mergeNRVOIntoParent() {
+  if (VarDecl *Candidate = NRVO.getPointer()) {
+    if (isDeclScope(Candidate))
+      Candidate->setNRVOVariable(true);
   }
 
-  if (Scope *parent = getParent())
-    parent->setNRVOCandidate(Candidate);
+  if (getEntity())
+    return;
+
+  if (NRVO.getInt())
+    getParent()->setNoNRVO();
+  else if (NRVO.getPointer())
+    getParent()->addNRVOCandidate(NRVO.getPointer());
 }
 
 LLVM_DUMP_METHOD void Scope::dump() const { dumpImpl(llvm::errs()); }
@@ -186,4 +191,9 @@
   OS << "MSCurManglingNumber: " << getMSCurManglingNumber() << '\n';
   if (const DeclContext *DC = getEntity())
     OS << "Entity : (clang::DeclContext*)" << DC << '\n';
+
+  if (NRVO.getInt())
+    OS << "NRVO not allowed\n";
+  else if (NRVO.getPointer())
+    OS << "NRVO candidate : (clang::VarDecl*)" << NRVO.getPointer() << '\n';
 }
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 4c98ffe..f6faf38 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -1798,6 +1798,8 @@
 }
 
 void Sema::ActOnPopScope(SourceLocation Loc, Scope *S) {
+  S->mergeNRVOIntoParent();
+
   if (S->decl_empty()) return;
   assert((S->getFlags() & (Scope::DeclScope | Scope::TemplateParamScope)) &&
          "Scope shouldn't contain decls!");
@@ -12561,24 +12563,21 @@
 /// optimization.
 ///
 /// Each of the variables that is subject to the named return value
-/// optimization will be marked as NRVO variable candidates in the AST, and any
+/// optimization will be marked as NRVO variables in the AST, and any
 /// return statement that has a marked NRVO variable as its NRVO candidate can
 /// use the named return value optimization.
 ///
-/// This function applies a very simplistic algorithm for NRVO: if every
-/// reachable return statement in the scope of a variable has the same NRVO
-/// candidate, that candidate is an NRVO variable.
+/// This function applies a very simplistic algorithm for NRVO: if every return
+/// statement in the scope of a variable has the same NRVO candidate, that
+/// candidate is an NRVO variable.
 void Sema::computeNRVO(Stmt *Body, FunctionScopeInfo *Scope) {
-  for (ReturnStmt *Return : Scope->Returns) {
-    const VarDecl *Candidate = Return->getNRVOCandidate();
-    if (!Candidate)
-      continue;
+  ReturnStmt **Returns = Scope->Returns.data();
 
-    if (Candidate->isNRVOCandidate())
-      const_cast<VarDecl*>(Candidate)->setNRVOVariable(true);
-
-    if (!Candidate->isNRVOVariable())
-      Return->setNRVOCandidate(nullptr);
+  for (unsigned I = 0, E = Scope->Returns.size(); I != E; ++I) {
+    if (const VarDecl *NRVOCandidate = Returns[I]->getNRVOCandidate()) {
+      if (!NRVOCandidate->isNRVOVariable())
+        Returns[I]->setNRVOCandidate(nullptr);
+    }
   }
 }
 
@@ -12713,8 +12712,12 @@
       else if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(FD))
         MarkVTableUsed(FD->getLocation(), Destructor->getParent());
 
-      // Try to apply the named return value optimization.
-      computeNRVO(Body, getCurFunction());
+      // Try to apply the named return value optimization. We have to check
+      // if we can do this here because lambdas keep return statements around
+      // to deduce an implicit return type.
+      if (FD->getReturnType()->isRecordType() &&
+          (!getLangOpts().CPlusPlus || !FD->isDependentContext()))
+        computeNRVO(Body, getCurFunction());
     }
 
     // GNU warning -Wmissing-prototypes:
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 220d690..aeedd6b 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -13385,9 +13385,13 @@
   if (Body && getCurFunction()->HasPotentialAvailabilityViolations)
     DiagnoseUnguardedAvailabilityViolations(BSI->TheDecl);
 
-  // Try to apply the named return value optimization.
-  computeNRVO(Body, BSI);
-
+  // Try to apply the named return value optimization. We have to check again
+  // if we can do this, though, because blocks keep return statements around
+  // to deduce an implicit return type.
+  if (getLangOpts().CPlusPlus && RetTy->isRecordType() &&
+      !BSI->TheDecl->isDependentContext())
+    computeNRVO(Body, BSI);
+  
   BlockExpr *Result = new (Context) BlockExpr(BSI->TheDecl, BlockTy);
   AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy();
   PopFunctionScopeInfo(&WP, Result->getBlockDecl(), Result);
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index 04f5114..ccf25ee 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -3455,9 +3455,12 @@
                            ExpressionEvaluationContext::DiscardedStatement)
     return R;
 
-  VarDecl *VD =
-      const_cast<VarDecl*>(cast<ReturnStmt>(R.get())->getNRVOCandidate());
-  CurScope->setNRVOCandidate(VD);
+  if (VarDecl *VD =
+      const_cast<VarDecl*>(cast<ReturnStmt>(R.get())->getNRVOCandidate())) {
+    CurScope->addNRVOCandidate(VD);
+  } else {
+    CurScope->setNoNRVO();
+  }
 
   CheckJumpOutOfSEHFinally(*this, ReturnLoc, *CurScope->getFnParent());
 
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 7e38dcc..68857d9 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -740,13 +740,12 @@
   SemaRef.BuildVariableInstantiation(Var, D, TemplateArgs, LateAttrs, Owner,
                                      StartingScope, InstantiatingVarTemplate);
 
-  bool NRVO = false;
   if (D->isNRVOVariable()) {
     QualType ReturnType = cast<FunctionDecl>(DC)->getReturnType();
     if (SemaRef.isCopyElisionCandidate(ReturnType, Var, Sema::CES_Strict))
-      NRVO = true;
+      Var->setNRVOVariable(true);
   }
-  Var->setNRVOVariable(NRVO);
+
   Var->setImplicit(D->isImplicit());
 
   return Var;
diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp
index 699a98c..a1ce26d 100644
--- a/lib/Serialization/ASTReaderDecl.cpp
+++ b/lib/Serialization/ASTReaderDecl.cpp
@@ -1326,7 +1326,7 @@
     VD->NonParmVarDeclBits.IsThisDeclarationADemotedDefinition =
         Record.readInt();
     VD->NonParmVarDeclBits.ExceptionVar = Record.readInt();
-    VD->NonParmVarDeclBits.NRVOMode = Record.readInt();
+    VD->NonParmVarDeclBits.NRVOVariable = Record.readInt();
     VD->NonParmVarDeclBits.CXXForRangeDecl = Record.readInt();
     VD->NonParmVarDeclBits.ObjCForDecl = Record.readInt();
     VD->NonParmVarDeclBits.ARCPseudoStrong = Record.readInt();
diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp
index c1f983e..cf35a2b 100644
--- a/lib/Serialization/ASTWriterDecl.cpp
+++ b/lib/Serialization/ASTWriterDecl.cpp
@@ -918,7 +918,7 @@
   if (!isa<ParmVarDecl>(D)) {
     Record.push_back(D->isThisDeclarationADemotedDefinition());
     Record.push_back(D->isExceptionVariable());
-    Record.push_back(D->getNRVOMode());
+    Record.push_back(D->isNRVOVariable());
     Record.push_back(D->isCXXForRangeDecl());
     Record.push_back(D->isObjCForDecl());
     Record.push_back(D->isARCPseudoStrong());
@@ -2031,7 +2031,7 @@
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // InitStyle
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsThisDeclarationADemotedDefinition
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isExceptionVariable
-  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // NRVOMode
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isNRVOVariable
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isCXXForRangeDecl
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isObjCForDecl
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isARCPseudoStrong
diff --git a/test/CodeGenCXX/nrvo-noopt.cpp b/test/CodeGenCXX/nrvo-noopt.cpp
deleted file mode 100644
index 46da7cd..0000000
--- a/test/CodeGenCXX/nrvo-noopt.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
-
-struct X {
-  X();
-  X(X&&);
-};
-
-// CHECK-LABEL: define void @_Z7test_00b
-X test_00(bool b) {
-  if (b) {
-    // CHECK-NOT: call void @_ZN1XC1EOS_
-    // CHECK: call void @_ZN1XC1Ev
-    // CHECK-NEXT: br label %return
-    X x;
-    return x;
-  } else {
-    // CHECK-NOT: call void @_ZN1XC1EOS_
-    // CHECK: call void @_ZN1XC1Ev
-    // CHECK-NEXT: br label %return
-    X x;
-    return x;
-  }
-}
-
-// CHECK-LABEL: define void @_Z7test_01b
-X test_01(bool b) {
-  if (b) {
-    // CHECK-NOT: call void @_ZN1XC1EOS_
-    // CHECK: call void @_ZN1XC1Ev
-    // CHECK-NEXT: br label %return
-    X x;
-    return x;
-  }
-  // CHECK-NOT: call void @_ZN1XC1EOS_
-  // CHECK: call void @_ZN1XC1Ev
-  // CHECK-NEXT: br label %return
-  X x;
-  return x;
-}
-
-// CHECK-LABEL: define void @_Z7test_02b
-X test_02(bool b) {
-  // CHECK: call void @_ZN1XC1Ev
-  X x;
-
-  if (b) {
-    // CHECK-NOT: call void @_ZN1XC1EOS_
-    // CHECK: call void @_ZN1XC1Ev
-    // CHECK-NEXT: br label %return
-    X y;
-    return y;
-  }
-
-  // CHECK-NOT: call void @_ZN1XC1Ev
-  // CHECK: call void @_ZN1XC1EOS_
-  // CHECK-NEXT: br label %return
-  return x;
-}
diff --git a/test/CodeGenCXX/nrvo.cpp b/test/CodeGenCXX/nrvo.cpp
index 1da4308..0f359b9 100644
--- a/test/CodeGenCXX/nrvo.cpp
+++ b/test/CodeGenCXX/nrvo.cpp
@@ -130,13 +130,17 @@
 }
 
 // CHECK-LABEL: define void @_Z5test3b
-X test3(bool B, X x) {
+X test3(bool B) {
   // CHECK: tail call {{.*}} @_ZN1XC1Ev
+  // CHECK-NOT: call {{.*}} @_ZN1XC1ERKS_
+  // CHECK: call {{.*}} @_ZN1XC1Ev
+  // CHECK: call {{.*}} @_ZN1XC1ERKS_
   if (B) {
     X y;
     return y;
   }
-  // CHECK: tail call {{.*}} @_ZN1XC1ERKS_
+  // FIXME: we should NRVO this variable too.
+  X x;
   return x;
 }
 
@@ -187,13 +191,9 @@
 }
 
 // CHECK-LABEL: define void @_Z5test7b
-// CHECK-EH-LABEL: define void @_Z5test7b
 X test7(bool b) {
   // CHECK: tail call {{.*}} @_ZN1XC1Ev
   // CHECK-NEXT: ret
-
-  // CHECK-EH: tail call {{.*}} @_ZN1XC1Ev
-  // CHECK-EH-NEXT: ret
   if (b) {
     X x;
     return x;
@@ -202,14 +202,10 @@
 }
 
 // CHECK-LABEL: define void @_Z5test8b
-// CHECK-EH-LABEL: define void @_Z5test8b
 X test8(bool b) {
   // CHECK: tail call {{.*}} @_ZN1XC1Ev
   // CHECK-NEXT: ret
-
-  // CHECK-EH: tail call {{.*}} @_ZN1XC1Ev
-  // CHECK-EH-NEXT: ret
-if (b) {
+  if (b) {
     X x;
     return x;
   } else {
@@ -225,37 +221,4 @@
 // CHECK-LABEL: define linkonce_odr void @_ZN1YIiE1fEv
 // CHECK: tail call {{.*}} @_ZN1YIiEC1Ev
 
-// CHECK-LABEL: define void @_Z6test10b
-X test10(bool B, X x) {
-  if (B) {
-    // CHECK: tail call {{.*}} @_ZN1XC1ERKS_
-    // CHECK-EH: tail call {{.*}} @_ZN1XC1ERKS_
-    return x;
-  }
-  // CHECK: tail call {{.*}} @_ZN1XC1Ev
-  // CHECK-NOT: call {{.*}} @_ZN1XC1ERKS_
-
-  // CHECK-EH: tail call {{.*}} @_ZN1XC1Ev
-  // CHECK-EH-NOT: call {{.*}} @_ZN1XC1ERKS_
-  X y;
-  return y;
-}
-
-// CHECK-LABEL: define {{.*}} void @_Z6test11I1XET_v
-// CHECK-EH-LABEL: define {{.*}} void @_Z6test11I1XET_v
-template <typename T>
-T test11() {
-  // CHECK:      tail call {{.*}} @_ZN1XC1Ev
-  // CHECK-NEXT: ret void
-
-  // CHECK-EH:      tail call {{.*}} @_ZN1XC1Ev
-  // CHECK-EH-NEXT: ret void
-  T t;
-  return t;
-}
-
-void test12() {
-  test11<X>();
-}
-
 // CHECK-EH-03: attributes [[NR_NUW]] = { noreturn nounwind }
diff --git a/test/SemaCXX/nrvo-ast.cpp b/test/SemaCXX/nrvo-ast.cpp
deleted file mode 100644
index 63fac00..0000000
--- a/test/SemaCXX/nrvo-ast.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -ast-dump -o - %s | FileCheck %s
-
-struct X {
-  X();
-  X(const X&);
-  X(X&&);
-};
-
-// CHECK-LABEL: FunctionDecl {{.*}} test_00
-X test_00() {
-  // CHECK: VarDecl {{.*}} x {{.*}} nrvo
-  X x;
-  return x;
-}
-
-// CHECK-LABEL: FunctionDecl {{.*}} test_01
-X test_01(bool b) {
-  // CHECK: VarDecl {{.*}} x {{.*}} nrvo
-  X x;
-  if (b)
-    return x;
-  return x;
-}
-
-// CHECK-LABEL: FunctionDecl {{.*}} test_02
-X test_02(bool b) {
-  // CHECK-NOT: VarDecl {{.*}} x {{.*}} nrvo
-  X x;
-  // CHECK-NOT: VarDecl {{.*}} y {{.*}} nrvo
-  X y;
-  if (b)
-    return y;
-  return x;
-}
-
-// CHECK-LABEL: FunctionDecl {{.*}} test_03
-X test_03(bool b) {
-  if (b) {
-    // CHECK: VarDecl {{.*}} y {{.*}} nrvo
-    X y;
-    return y;
-  }
-  // CHECK: VarDecl {{.*}} x {{.*}} nrvo
-  X x;
-  return x;
-}
-
-extern "C" _Noreturn void exit(int) throw();
-
-// CHECK-LABEL: FunctionDecl {{.*}} test_04
-X test_04(bool b) {
-  {
-    // CHECK: VarDecl {{.*}} x {{.*}} nrvo
-    X x;
-    if (b)
-      return x;
-  }
-  exit(1);
-}
-
-void may_throw();
-// CHECK-LABEL: FunctionDecl {{.*}} test_05
-X test_05() {
-  try {
-    may_throw();
-    return X();
-  } catch (X x) {
-    // CHECK-NOT: VarDecl {{.*}} x {{.*}} nrvo
-    return x;
-  }
-}
-
-// CHECK-LABEL: FunctionDecl {{.*}} test_06
-X test_06() {
-  // CHECK-NOT: VarDecl {{.*}} x {{.*}} nrvo
-  X x __attribute__((aligned(8)));
-  return x;
-}
-
-// CHECK-LABEL: FunctionDecl {{.*}} test_07
-X test_07(bool b) {
-  if (b) {
-    // CHECK: VarDecl {{.*}} x {{.*}} nrvo
-    X x;
-    return x;
-  }
-  return X();
-}
-
-// CHECK-LABEL: FunctionDecl {{.*}} test_08
-X test_08(bool b) {
-  if (b) {
-    // CHECK: VarDecl {{.*}} x {{.*}} nrvo
-    X x;
-    return x;
-  } else {
-    // CHECK: VarDecl {{.*}} y {{.*}} nrvo
-    X y;
-    return y;
-  }
-}
-
-template <typename T>
-struct Y {
-  Y();
-  // CHECK-LABEL: CXXMethodDecl {{.*}} test_09 'Y<T> ()'
-  // CHECK: VarDecl {{.*}} y 'Y<T>' nrvo
-
-  // CHECK-LABEL: CXXMethodDecl {{.*}} test_09 'Y<int> ()'
-  // CHECK: VarDecl {{.*}} y 'Y<int>' nrvo
-  static Y test_09() {
-    Y y;
-    return y;
-  }
-};
-
-struct Z {
-  Z(const X&);
-};
-
-// CHECK-LABEL: FunctionDecl {{.*}} test_10 'A ()'
-// CHECK: VarDecl {{.*}} b 'B' nrvo
-
-// CHECK-LABEL: FunctionDecl {{.*}} test_10 'X ()'
-// CHECK: VarDecl {{.*}} b {{.*}} nrvo
-
-// CHECK-LABEL: FunctionDecl {{.*}} test_10 'Z ()'
-// CHECK-NOT: VarDecl {{.*}} b {{.*}} nrvo
-template <typename A, typename B>
-A test_10() {
-  B b;
-  return b;
-}
-
-// CHECK-LABEL: FunctionDecl {{.*}} test_11 'A (bool)'
-// CHECK-NOT: VarDecl {{.*}} a {{.*}} nrvo
-
-// CHECK-LABEL: FunctionDecl {{.*}} test_11 'X (bool)'
-// CHECK-NOT: VarDecl {{.*}} a {{.*}} nrvo
-template <typename A>
-A test_11(bool b) {
-  A a;
-  if (b)
-    return A();
-  return a;
-}
-
-void instantiate() {
-  Y<int>::test_09();
-  test_10<X, X>();
-  test_10<Z, X>();
-  test_11<X>(true);
-}