tests: check $$'s destruction with variant, YYERROR, and no error recovery

When variant are enabled, the yylhs variable (the left-hand side of
the rule being reduced, i.e. $$ and @$) is explicitly destroyed when
YYERROR is called.  This is because before running the user code, $$
is initialized, so that the user can properly use it.

However, when quitting yyparse, yylhs is also reclaimed by the C++
compiler: the variable goes out of scope.

This was not detected by the test suite because (i) the Object tracker
was too weak, and (ii) the problem does not show when there is error
recovery.

Reported by Paolo Simone Gasparello.
<http://lists.gnu.org/archive/html/bug-bison/2013-10/msg00003.html>

* tests/c++.at (Exception safety): Improve the objects logger to make
sure that we never destroy twice an object.
Also track copy-constructors.
Use a set instead of a list.
Display the logs before running the function body, this is more
useful in case of failure.
Generalize to track with and without error recovery.
This commit is contained in:
Akim Demaille
2013-11-15 09:43:01 +01:00
parent 16bb9f1647
commit 5cf6e669af
2 changed files with 42 additions and 25 deletions

1
THANKS
View File

@@ -96,6 +96,7 @@ Odd Arild Olsen oao@fibula.no
Oleg Smolsky oleg.smolsky@pacific-simulators.co.nz Oleg Smolsky oleg.smolsky@pacific-simulators.co.nz
Oleksii Taran oleksii.taran@gmail.com Oleksii Taran oleksii.taran@gmail.com
Paolo Bonzini bonzini@gnu.org Paolo Bonzini bonzini@gnu.org
Paolo Simone Gasparello djgaspa@gmail.com
Pascal Bart pascal.bart@epita.fr Pascal Bart pascal.bart@epita.fr
Paul Eggert eggert@cs.ucla.edu Paul Eggert eggert@cs.ucla.edu
Paul Hilfinger Hilfinger@CS.Berkeley.EDU Paul Hilfinger Hilfinger@CS.Berkeley.EDU

View File

@@ -649,14 +649,18 @@ AT_CLEANUP
## Exception safety. ## ## Exception safety. ##
## ------------------ ## ## ------------------ ##
# AT_TEST([BISON-DIRECTIVES]) # AT_TEST([BISON-DIRECTIVES = ''], [WITH-RECOVERY = "with"])
# --------------------------- # ----------------------------------------------------------
# Check that no object is leaked when exceptions are thrown. # Check that no object is leaked when exceptions are thrown.
# WITH-RECOVERY = "with" or "without".
m4_pushdef([AT_TEST], m4_pushdef([AT_TEST],
[AT_SETUP([[Exception safety $1]]) [AT_SETUP([[Exception safety $2 error recovery $1]])
AT_SKIP_IF_EXCEPTION_SUPPORT_IS_POOR AT_SKIP_IF_EXCEPTION_SUPPORT_IS_POOR
m4_if([$1], [], [],
[m4_if([$2], [without], [AT_XFAIL_IF([true])])])
AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc" $1]) AT_BISON_OPTION_PUSHDEFS([%skeleton "lalr1.cc" $1])
AT_DATA_GRAMMAR([[input.yy]], AT_DATA_GRAMMAR([[input.yy]],
@@ -669,27 +673,43 @@ $1
#include <cassert> #include <cassert>
#include <cstdlib> // size_t and getenv. #include <cstdlib> // size_t and getenv.
#include <iostream> #include <iostream>
#include <list> #include <set>
bool debug = false; bool debug = false;
/// A class that counts its number of instances. /// A class that tracks its instances.
struct Object struct Object
{ {
char val; char val;
Object (char v)
: val (v)
{
Object::instances.push_back(this);
log (this, "Object::Object");
}
Object () Object ()
: val ('?') : val ('?')
{ {
Object::instances.push_back(this);
log (this, "Object::Object"); log (this, "Object::Object");
Object::instances.insert (this);
}
Object (const Object& that)
: val (that.val)
{
log (this, "Object::Object");
Object::instances.insert (this);
}
Object (char v)
: val (v)
{
log (this, "Object::Object");
Object::instances.insert (this);
}
~Object ()
{
log (this, "Object::~Object");
objects::const_iterator i = instances.find (this);
// Make sure this object is alive.
assert (i != instances.end ());
Object::instances.erase (i);
} }
Object& operator= (char v) Object& operator= (char v)
@@ -698,14 +718,8 @@ $1
return *this; return *this;
} }
~Object ()
{
Object::instances.remove (this);
log (this, "Object::~Object");
}
// Static part. // Static part.
typedef std::list<const Object*> objects; typedef std::set<const Object*> objects;
static objects instances; static objects instances;
static bool static bool
@@ -800,7 +814,8 @@ item:
| 'p' { $$ = $][1; } | 'p' { $$ = $][1; }
| 's' { $$ = $][1; throw std::runtime_error ("reduction"); } | 's' { $$ = $][1; throw std::runtime_error ("reduction"); }
| 'T' { ]AT_VARIANT_IF([], [$$ = YY_NULLPTR; delete $][1]; )[YYABORT; } | 'T' { ]AT_VARIANT_IF([], [$$ = YY_NULLPTR; delete $][1]; )[YYABORT; }
| error { ]AT_VARIANT_IF([], [$][$ = YY_NULLPTR; ])[yyerrok; } ]m4_if([$2], [with],
[[| error { $$ = ]AT_VARIANT_IF([], [new ])[Object ('R'); yyerrok; }]])[
; ;
%% %%
@@ -903,16 +918,17 @@ AT_PARSER_CHECK([[./input aaaaE]], [[2]], [[]],
AT_PARSER_CHECK([[./input aaaaT]], [[1]]) AT_PARSER_CHECK([[./input aaaaT]], [[1]])
# There is error-recovery, so exit success. AT_PARSER_CHECK([[./input aaaaR]], [m4_if([$2], [with], [0], [1])])
AT_PARSER_CHECK([[./input aaaaR]], [[0]])
AT_BISON_OPTION_POPDEFS AT_BISON_OPTION_POPDEFS
AT_CLEANUP AT_CLEANUP
]) ])
AT_TEST AT_TEST([], [with])
AT_TEST([%define api.value.type variant]) AT_TEST([], [without])
AT_TEST([%define api.value.type variant], [with])
AT_TEST([%define api.value.type variant], [without])
m4_popdef([AT_TEST]) m4_popdef([AT_TEST])