Reinstantiate unsorted index group check
authorTeemu Murtola <teemu.murtola@gmail.com>
Sun, 29 Jun 2014 05:00:45 +0000 (08:00 +0300)
committerGerrit Code Review <gerrit@gerrit.gromacs.org>
Sat, 5 Jul 2014 12:18:36 +0000 (14:18 +0200)
In most contexts, providing an index group with duplicate atoms or atom
indices not in ascending order results in the selection getting silently
evaluated incorrectly.  The selection engine now detects this case and
gives a fatal error, hopefully reducing confusion if such a case arises.

The case where an unsorted index group is provided as a sole value of a
selection is still supported.

Change-Id: I0b647a26d9a1b47da10c84bdd3d073412d166a75

src/gromacs/selection/parsetree.cpp
src/gromacs/selection/selectioncollection.cpp
src/gromacs/selection/selelem.cpp
src/gromacs/selection/selelem.h
src/gromacs/selection/selmethod.h
src/gromacs/selection/sm_position.cpp
src/gromacs/selection/tests/refdata/SelectionCollectionDataTest_HandlesUnsortedIndexGroupsInSelections.xml
src/gromacs/selection/tests/refdata/SelectionCollectionDataTest_HandlesUnsortedIndexGroupsInSelectionsDelayed.xml [new file with mode: 0644]
src/gromacs/selection/tests/selectioncollection.cpp
src/gromacs/utility/exceptions.h

index 3035805bd4e11ce0f19f0631faff4351c6952f91..753da3b07d1040986e8fbb2539c9f1617f6e1aa6 100644 (file)
@@ -1002,6 +1002,12 @@ _gmx_sel_init_selection(const char                             *name,
     }
     /* Update the flags */
     _gmx_selelem_update_flags(root);
+    gmx::ExceptionInitializer errors("Invalid index group reference(s)");
+    root->checkUnsortedAtoms(true, &errors);
+    if (errors.hasNestedExceptions())
+    {
+        GMX_THROW(gmx::InconsistentInputError(errors));
+    }
 
     root->fillNameIfMissing(_gmx_sel_lexer_pselstr(scanner));
 
@@ -1041,28 +1047,34 @@ _gmx_sel_assign_variable(const char                             *name,
     {
         /* If so, just assign the constant value to the variable */
         sc->symtab->addVariable(name, expr);
-        goto finish;
     }
     /* Check if we are assigning a variable to another variable */
-    if (expr->type == SEL_SUBEXPRREF)
+    else if (expr->type == SEL_SUBEXPRREF)
     {
         /* If so, make a simple alias */
         sc->symtab->addVariable(name, expr->child);
-        goto finish;
     }
-    /* Create the root element */
-    root.reset(new SelectionTreeElement(SEL_ROOT));
-    root->setName(name);
-    /* Create the subexpression element */
-    root->child.reset(new SelectionTreeElement(SEL_SUBEXPR));
-    root->child->setName(name);
-    _gmx_selelem_set_vtype(root->child, expr->v.type);
-    root->child->child  = expr;
-    /* Update flags */
-    _gmx_selelem_update_flags(root);
-    /* Add the variable to the symbol table */
-    sc->symtab->addVariable(name, root->child);
-finish:
+    else
+    {
+        /* Create the root element */
+        root.reset(new SelectionTreeElement(SEL_ROOT));
+        root->setName(name);
+        /* Create the subexpression element */
+        root->child.reset(new SelectionTreeElement(SEL_SUBEXPR));
+        root->child->setName(name);
+        _gmx_selelem_set_vtype(root->child, expr->v.type);
+        root->child->child  = expr;
+        /* Update flags */
+        _gmx_selelem_update_flags(root);
+        gmx::ExceptionInitializer errors("Invalid index group reference(s)");
+        root->checkUnsortedAtoms(true, &errors);
+        if (errors.hasNestedExceptions())
+        {
+            GMX_THROW(gmx::InconsistentInputError(errors));
+        }
+        /* Add the variable to the symbol table */
+        sc->symtab->addVariable(name, root->child);
+    }
     srenew(sc->varstrs, sc->nvars + 1);
     sc->varstrs[sc->nvars] = strdup(pselstr);
     ++sc->nvars;
index 400e7d8d3b8f9444f5387e85347da19168202fe9..6237fe0a05a5abb01c1d6c3b5783dfc7b69cd65c 100644 (file)
@@ -568,11 +568,12 @@ SelectionCollection::setIndexGroups(gmx_ana_indexgrps_t *grps)
     impl_->grps_               = grps;
     impl_->bExternalGroupsSet_ = true;
 
-    ExceptionInitializer        errors("Unknown index group references encountered");
+    ExceptionInitializer        errors("Invalid index group reference(s)");
     SelectionTreeElementPointer root = impl_->sc_.root;
     while (root)
     {
         impl_->resolveExternalGroups(root, &errors);
+        root->checkUnsortedAtoms(true, &errors);
         root = root->next;
     }
     if (errors.hasNestedExceptions())
index e80e4f75c8d241d946505ec85d6e5901a2666206..a10d5a5802af892480c4664f5984b2e459165660 100644 (file)
@@ -53,6 +53,7 @@
 #include "keywords.h"
 #include "mempool.h"
 #include "selelem.h"
+#include "selmethod.h"
 
 /*!
  * \param[in] sel Selection for which the string is requested
@@ -328,6 +329,43 @@ void SelectionTreeElement::fillNameIfMissing(const char *selectionText)
     }
 }
 
+void SelectionTreeElement::checkUnsortedAtoms(
+        bool bUnsortedAllowed, ExceptionInitializer *errors) const
+{
+    const bool bUnsortedSupported
+        = (type == SEL_CONST && v.type == GROUP_VALUE)
+            || type == SEL_ROOT || type == SEL_SUBEXPR || type == SEL_SUBEXPRREF
+            // TODO: Consolidate.
+            || type == SEL_MODIFIER
+            || (type == SEL_EXPRESSION && (u.expr.method->flags & SMETH_ALLOW_UNSORTED));
+
+    // TODO: For some complicated selections, this may result in the same
+    // index group reference being flagged as an error multiple times for the
+    // same selection.
+    SelectionTreeElementPointer child = this->child;
+    while (child)
+    {
+        child->checkUnsortedAtoms(bUnsortedAllowed && bUnsortedSupported,
+                                  errors);
+        child = child->next;
+    }
+
+    // The logic here is simplified by the fact that only constant groups can
+    // currently be the root cause of SEL_UNSORTED being set, so only those
+    // need to be considered in triggering the error.
+    if (!bUnsortedAllowed && (flags & SEL_UNSORTED)
+        && type == SEL_CONST && v.type == GROUP_VALUE)
+    {
+        std::string message = formatString(
+                    "Group '%s' cannot be used in selections except "
+                    "as a full value of the selection, "
+                    "because atom indices in it are not sorted and/or "
+                    "it contains duplicate atoms.",
+                    name().c_str());
+        errors->addNested(InconsistentInputError(message));
+    }
+}
+
 void SelectionTreeElement::resolveIndexGroupReference(gmx_ana_indexgrps_t *grps)
 {
     GMX_RELEASE_ASSERT(type == SEL_GROUPREF,
@@ -366,16 +404,6 @@ void SelectionTreeElement::resolveIndexGroupReference(gmx_ana_indexgrps_t *grps)
     if (!gmx_ana_index_check_sorted(&foundGroup))
     {
         flags |= SEL_UNSORTED;
-        // TODO: Add this test elsewhere, where it does not break valid use cases.
-#if 0
-        gmx_ana_index_deinit(&foundGroup);
-        std::string message = formatString(
-                    "Group '%s' ('%s') cannot be used in selections, "
-                    "because atom indices in it are not sorted and/or "
-                    "it contains duplicate atoms.",
-                    foundName.c_str(), name().c_str());
-        GMX_THROW(InconsistentInputError(message));
-#endif
     }
 
     sfree(u.gref.name);
index e47d162c0cd10f885408e3ac34cf37a889246158..bcb67c768ac030533c09e6aa1c6dac53e92d52ee 100644 (file)
@@ -237,6 +237,8 @@ _gmx_sel_value_type_str(const gmx_ana_selvalue_t *val);
 namespace gmx
 {
 
+class ExceptionInitializer;
+
 /*! \brief
  * Function pointer for evaluating a gmx::SelectionTreeElement.
  */
@@ -323,6 +325,19 @@ class SelectionTreeElement
          */
         void fillNameIfMissing(const char *selectionText);
 
+        /*! \brief
+         * Checks that this element and its children do not contain unsupported
+         * elements with unsorted atoms.
+         *
+         * \param[in] bUnsortedAllowed Whether this element's parents allow it
+         *     to have unsorted atoms.
+         * \param     errors           Object for reporting any error messages.
+         * \throws    std::bad_alloc if out of memory.
+         *
+         * Errors are reported as nested exceptions in \p errors.
+         */
+        void checkUnsortedAtoms(bool                  bUnsortedAllowed,
+                                ExceptionInitializer *errors) const;
         /*! \brief
          * Resolved an unresolved reference to an index group.
          *
index 5d80abe934ed2a22832ccc6f060208bc7b6d3db3..c803945ab881b31bcbb42bf6d212e42d5b1aabc0 100644 (file)
@@ -91,6 +91,9 @@
  *  - \ref SMETH_MODIFIER : If set, the method is a selection modifier and
  *    not an actual selection method.
  *    For more details, see \ref selmethods_modifiers.
+ *  - \ref SMETH_ALLOW_UNSORTED : If set, the method supports unsorted atoms
+ *    in its input parameters. \ref SMETH_MODIFIER methods are assumed to always
+ *    support unsorted atoms, as their purpose is to affect the ordering.
  *
  * There are two additional flags that specify the number of values the
  * method returns. Only one of them can be set at a time.
@@ -344,6 +347,15 @@ struct gmx_ana_selcollection_t;
  * the string pointers.
  */
 #define SMETH_CHARVAL    64
+/*! \brief
+ * If set, the method accepts unsorted atoms in its input parameters.
+ *
+ * Currently, the support for this functionality is fairly limited, and only
+ * static index group references can actually contain unsorted atoms.
+ * But to make this single case work, the position evaluation must support
+ * unsorted atoms as well.
+ */
+#define SMETH_ALLOW_UNSORTED 128
 /*! \brief
  * If set, the method is a selection modifier.
  *
index aeeb9573c4c2616ad17cb856b22a732e7388901b..31f3c0a8b2dd9c6f84182d5255be263680eecf43 100644 (file)
@@ -142,7 +142,7 @@ static gmx_ana_selparam_t smparams_com[] = {
 
 /** Selection method data for position keyword evaluation. */
 gmx_ana_selmethod_t sm_keyword_pos = {
-    "kw_pos", POS_VALUE, SMETH_DYNAMIC | SMETH_VARNUMVAL,
+    "kw_pos", POS_VALUE, SMETH_DYNAMIC | SMETH_VARNUMVAL | SMETH_ALLOW_UNSORTED,
     asize(smparams_keyword_pos), smparams_keyword_pos,
     &init_data_pos,
     &set_poscoll_pos,
index ddcda26bc4aaa0b116f666abafee6be0e98b270f..cc54442ee6b41ebd6748db90dd2807739ab77c16 100644 (file)
@@ -2,6 +2,9 @@
 <?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
 <ReferenceData>
   <ParsedSelections Name="Parsed">
+    <ParsedVariable Name="Variable1">
+      <String Name="Input">foo = group "GrpUnsorted"</String>
+    </ParsedVariable>
     <ParsedSelection Name="Selection1">
       <String Name="Input">group "GrpUnsorted"</String>
       <String Name="Name">GrpUnsorted</String>
       <String Name="Text">group "GrpUnsorted" permute 2 1</String>
       <Bool Name="Dynamic">false</Bool>
     </ParsedSelection>
+    <ParsedSelection Name="Selection6">
+      <String Name="Input">foo</String>
+      <String Name="Name">foo</String>
+      <String Name="Text">foo</String>
+      <Bool Name="Dynamic">false</Bool>
+    </ParsedSelection>
   </ParsedSelections>
   <CompiledSelections Name="Compiled">
     <Selection Name="Selection1">
         </Position>
       </Sequence>
     </Selection>
+    <Selection Name="Selection6">
+      <String Name="Name">foo</String>
+      <Sequence Name="Atoms">
+        <Int Name="Length">8</Int>
+        <Int>0</Int>
+        <Int>2</Int>
+        <Int>1</Int>
+        <Int>8</Int>
+        <Int>6</Int>
+        <Int>4</Int>
+        <Int>2</Int>
+        <Int>11</Int>
+      </Sequence>
+      <Sequence Name="Positions">
+        <Int Name="Length">8</Int>
+        <Position>
+          <Sequence Name="Atoms">
+            <Int Name="Length">1</Int>
+            <Int>0</Int>
+          </Sequence>
+          <Int Name="RefId">0</Int>
+          <Int Name="MappedId">0</Int>
+        </Position>
+        <Position>
+          <Sequence Name="Atoms">
+            <Int Name="Length">1</Int>
+            <Int>2</Int>
+          </Sequence>
+          <Int Name="RefId">1</Int>
+          <Int Name="MappedId">2</Int>
+        </Position>
+        <Position>
+          <Sequence Name="Atoms">
+            <Int Name="Length">1</Int>
+            <Int>1</Int>
+          </Sequence>
+          <Int Name="RefId">2</Int>
+          <Int Name="MappedId">1</Int>
+        </Position>
+        <Position>
+          <Sequence Name="Atoms">
+            <Int Name="Length">1</Int>
+            <Int>8</Int>
+          </Sequence>
+          <Int Name="RefId">3</Int>
+          <Int Name="MappedId">8</Int>
+        </Position>
+        <Position>
+          <Sequence Name="Atoms">
+            <Int Name="Length">1</Int>
+            <Int>6</Int>
+          </Sequence>
+          <Int Name="RefId">4</Int>
+          <Int Name="MappedId">6</Int>
+        </Position>
+        <Position>
+          <Sequence Name="Atoms">
+            <Int Name="Length">1</Int>
+            <Int>4</Int>
+          </Sequence>
+          <Int Name="RefId">5</Int>
+          <Int Name="MappedId">4</Int>
+        </Position>
+        <Position>
+          <Sequence Name="Atoms">
+            <Int Name="Length">1</Int>
+            <Int>2</Int>
+          </Sequence>
+          <Int Name="RefId">6</Int>
+          <Int Name="MappedId">2</Int>
+        </Position>
+        <Position>
+          <Sequence Name="Atoms">
+            <Int Name="Length">1</Int>
+            <Int>11</Int>
+          </Sequence>
+          <Int Name="RefId">7</Int>
+          <Int Name="MappedId">11</Int>
+        </Position>
+      </Sequence>
+    </Selection>
   </CompiledSelections>
 </ReferenceData>
diff --git a/src/gromacs/selection/tests/refdata/SelectionCollectionDataTest_HandlesUnsortedIndexGroupsInSelectionsDelayed.xml b/src/gromacs/selection/tests/refdata/SelectionCollectionDataTest_HandlesUnsortedIndexGroupsInSelectionsDelayed.xml
new file mode 100644 (file)
index 0000000..c7fd97c
--- /dev/null
@@ -0,0 +1,119 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <ParsedSelections Name="Parsed">
+    <ParsedVariable Name="Variable1">
+      <String Name="Input">foo = group "GrpUnsorted"</String>
+    </ParsedVariable>
+    <ParsedSelection Name="Selection1">
+      <String Name="Input">group "GrpUnsorted"</String>
+      <String Name="Text">group "GrpUnsorted"</String>
+      <Bool Name="Dynamic">false</Bool>
+    </ParsedSelection>
+    <ParsedSelection Name="Selection2">
+      <String Name="Input">GrpUnsorted</String>
+      <String Name="Text">GrpUnsorted</String>
+      <Bool Name="Dynamic">false</Bool>
+    </ParsedSelection>
+    <ParsedSelection Name="Selection3">
+      <String Name="Input">2</String>
+      <String Name="Text">2</String>
+      <Bool Name="Dynamic">false</Bool>
+    </ParsedSelection>
+    <ParsedSelection Name="Selection4">
+      <String Name="Input">res_cog of group "GrpUnsorted"</String>
+      <String Name="Text">res_cog of group "GrpUnsorted"</String>
+      <Bool Name="Dynamic">false</Bool>
+    </ParsedSelection>
+    <ParsedSelection Name="Selection5">
+      <String Name="Input">group "GrpUnsorted" permute 2 1</String>
+      <String Name="Text">group "GrpUnsorted" permute 2 1</String>
+      <Bool Name="Dynamic">false</Bool>
+    </ParsedSelection>
+    <ParsedSelection Name="Selection6">
+      <String Name="Input">foo</String>
+      <String Name="Text">foo</String>
+      <Bool Name="Dynamic">false</Bool>
+    </ParsedSelection>
+  </ParsedSelections>
+  <CompiledSelections Name="Compiled">
+    <Selection Name="Selection1">
+      <Sequence Name="Atoms">
+        <Int Name="Length">8</Int>
+        <Int>0</Int>
+        <Int>2</Int>
+        <Int>1</Int>
+        <Int>8</Int>
+        <Int>6</Int>
+        <Int>4</Int>
+        <Int>2</Int>
+        <Int>11</Int>
+      </Sequence>
+    </Selection>
+    <Selection Name="Selection2">
+      <Sequence Name="Atoms">
+        <Int Name="Length">8</Int>
+        <Int>0</Int>
+        <Int>2</Int>
+        <Int>1</Int>
+        <Int>8</Int>
+        <Int>6</Int>
+        <Int>4</Int>
+        <Int>2</Int>
+        <Int>11</Int>
+      </Sequence>
+    </Selection>
+    <Selection Name="Selection3">
+      <Sequence Name="Atoms">
+        <Int Name="Length">8</Int>
+        <Int>0</Int>
+        <Int>2</Int>
+        <Int>1</Int>
+        <Int>8</Int>
+        <Int>6</Int>
+        <Int>4</Int>
+        <Int>2</Int>
+        <Int>11</Int>
+      </Sequence>
+    </Selection>
+    <Selection Name="Selection4">
+      <Sequence Name="Atoms">
+        <Int Name="Length">8</Int>
+        <Int>0</Int>
+        <Int>2</Int>
+        <Int>1</Int>
+        <Int>8</Int>
+        <Int>6</Int>
+        <Int>4</Int>
+        <Int>2</Int>
+        <Int>11</Int>
+      </Sequence>
+    </Selection>
+    <Selection Name="Selection5">
+      <Sequence Name="Atoms">
+        <Int Name="Length">8</Int>
+        <Int>2</Int>
+        <Int>0</Int>
+        <Int>8</Int>
+        <Int>1</Int>
+        <Int>4</Int>
+        <Int>6</Int>
+        <Int>11</Int>
+        <Int>2</Int>
+      </Sequence>
+    </Selection>
+    <Selection Name="Selection6">
+      <Sequence Name="Atoms">
+        <Int Name="Length">8</Int>
+        <Int>0</Int>
+        <Int>2</Int>
+        <Int>1</Int>
+        <Int>8</Int>
+        <Int>6</Int>
+        <Int>4</Int>
+        <Int>2</Int>
+        <Int>11</Int>
+      </Sequence>
+    </Selection>
+  </CompiledSelections>
+</ReferenceData>
index 0727c881f00eba89fee14d79c396cf72c592cadf..e4c10b132a402120f45c34dcf20e87cba5608007 100644 (file)
@@ -487,21 +487,26 @@ TEST_F(SelectionCollectionTest, HandlesUnknownGroupReferenceDelayed2)
     EXPECT_THROW_GMX(sc_.compile(), gmx::APIError);
 }
 
-// TODO: Make the check less eager so that it doesn't break other tests, and
-// adapt these tests accordingly.
-TEST_F(SelectionCollectionTest, DISABLED_HandlesUnsortedGroupReference)
+TEST_F(SelectionCollectionTest, HandlesUnsortedGroupReference)
 {
     ASSERT_NO_THROW_GMX(loadIndexGroups("simple.ndx"));
-    EXPECT_THROW_GMX(sc_.parseFromString("group \"GrpUnsorted\""),
+    EXPECT_THROW_GMX(sc_.parseFromString("atomnr 1 to 3 and group \"GrpUnsorted\""),
+                     gmx::InconsistentInputError);
+    EXPECT_THROW_GMX(sc_.parseFromString("group 2 or atomnr 2 to 5"),
+                     gmx::InconsistentInputError);
+    EXPECT_THROW_GMX(sc_.parseFromString("within 1 of group 2"),
                      gmx::InconsistentInputError);
-    EXPECT_THROW_GMX(sc_.parseFromString("2"), gmx::InconsistentInputError);
 }
 
-TEST_F(SelectionCollectionTest, DISABLED_HandlesUnsortedGroupReferenceDelayed)
+TEST_F(SelectionCollectionTest, HandlesUnsortedGroupReferenceDelayed)
 {
-    ASSERT_NO_THROW_GMX(sc_.parseFromString("group 2; group \"GrpUnsorted\""));
+    ASSERT_NO_THROW_GMX(sc_.parseFromString("atomnr 1 to 3 and group \"GrpUnsorted\""));
+    ASSERT_NO_THROW_GMX(sc_.parseFromString("atomnr 1 to 3 and group 2"));
     EXPECT_THROW_GMX(loadIndexGroups("simple.ndx"), gmx::InconsistentInputError);
-    EXPECT_THROW_GMX(sc_.compile(), gmx::APIError);
+    // TODO: Add a separate check in the selection compiler for a safer API
+    // (makes sense in the future if the compiler needs the information for
+    // other purposes as well).
+    // EXPECT_THROW_GMX(sc_.compile(), gmx::APIError);
 }
 
 TEST_F(SelectionCollectionTest, RecoversFromMissingMoleculeInfo)
@@ -929,11 +934,13 @@ TEST_F(SelectionCollectionDataTest, HandlesIndexGroupsInSelectionsDelayed)
 TEST_F(SelectionCollectionDataTest, HandlesUnsortedIndexGroupsInSelections)
 {
     static const char * const selections[] = {
+        "foo = group \"GrpUnsorted\"",
         "group \"GrpUnsorted\"",
         "GrpUnsorted",
         "2",
         "res_cog of group \"GrpUnsorted\"",
-        "group \"GrpUnsorted\" permute 2 1"
+        "group \"GrpUnsorted\" permute 2 1",
+        "foo"
     };
     setFlags(TestFlags() | efTestPositionAtoms | efTestPositionMapping
              | efTestSelectionNames);
@@ -941,6 +948,23 @@ TEST_F(SelectionCollectionDataTest, HandlesUnsortedIndexGroupsInSelections)
     runTest("simple.gro", selections);
 }
 
+TEST_F(SelectionCollectionDataTest, HandlesUnsortedIndexGroupsInSelectionsDelayed)
+{
+    static const char * const selections[] = {
+        "foo = group \"GrpUnsorted\"",
+        "group \"GrpUnsorted\"",
+        "GrpUnsorted",
+        "2",
+        "res_cog of group \"GrpUnsorted\"",
+        "group \"GrpUnsorted\" permute 2 1",
+        "foo"
+    };
+    ASSERT_NO_FATAL_FAILURE(runParser(selections));
+    ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
+    ASSERT_NO_THROW_GMX(loadIndexGroups("simple.ndx"));
+    ASSERT_NO_FATAL_FAILURE(runCompiler());
+}
+
 TEST_F(SelectionCollectionDataTest, HandlesConstantPositions)
 {
     static const char * const selections[] = {
index 0425dc6d5bffe5200e7b944d15642e30cbd77cdb..ace7b66224c830146d2d9bc503b8f3e840234987 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2011,2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013,2014, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -135,6 +135,21 @@ class ExceptionInitializer
         {
             nested_.push_back(boost::current_exception());
         }
+        /*! \brief
+         * Adds the specified exception as a nested exception.
+         *
+         * May be called multiple times; all provided exceptions will be added
+         * in a list of nested exceptions.
+         *
+         * This is equivalent to throwing \p ex and calling
+         * addCurrentExceptionAsNested() in the catch block, but potentially
+         * more efficient.
+         */
+        template <class Exception>
+        void addNested(const Exception &ex)
+        {
+            nested_.push_back(boost::copy_exception(ex));
+        }
 
     private:
         std::string                     reason_;