[PATCH] [RFC] PR debug/49951 - jumpy stepping at end of scope in C++
Dodji Seketeli
dodji@redhat.com
Sat Dec 3 18:18:00 GMT 2011
Hello,
Consider this short C++ example annotated with line numbers for better
legibility and compiled without optimization:
1 class MyClass
2 {
3 public:
4 MyClass() {};
5 ~MyClass() {};
6 };
7
8 int
9 main ()
10 {
11 MyClass m;
12 return 0;
13 }
When one steps (using e.g, 'next' in GDB) into the "main" function,
reaches line 12, and issues a 'next' again, the current line pointer
jumps back to line 11 (where 'm' is) before the function exits.
Jumping backward like this seems unexpected and annoying.
In the audit trail of the PR, Richard Guenther noted that the gimple
dump of the example code above is:
int main() ()
{
int D.2222;
[test.cc : 12:14] {
struct MyClass m;
try
{
[test.cc : 11:14] MyClass::MyClass ([test.cc : 11] &m);
[test.cc : 12:14] try
{
[test.cc : 12:13] D.2222 = 0;
[test.cc : 12:13] return D.2222;
}
finally
{
[test.cc : 11:14] MyClass::~MyClass ([test.cc : 11] &m);
}
}
finally
{
m = {CLOBBER};
}
}
[test.cc : 13:1] D.2222 = 0;
[test.cc : 13:1] return D.2222;
}
We see that the line location of the call to the destructor of MyClass
is set to 11. I think it should be set to the location of '}' of the
enclosing scope of the 'm' variable, thus 13.
My understanding is that when G++ sees the variable 'm' at line 11,
cp_finish_decl, via initialize_local_var builds a CLEANUP_STMT (by
indirectly calling push_cleanup) whose CLEANUP_EXPR is a call
expression to the destructor of m. This will later be expanded to the
GIMPLE_TRY_FINALLY we see in the gimple dump above.
The problem is that the location of the call expression representing
the call to MyClass::~MyClass is set to the line that is current when
we are at 'm'. Thus 11.
What I am proposing in the patch below is to wait until the closing
'}' of a given scope, walk the statements of that scope, and fixup the
locations of CLEANUP_STMTs by setting them the current location, i.e,
the location of the closing '}'.
I wanted to do that directly in the code of pop_stmt_list, but it's in
gcc/c-family/c-semantics.c so it won't know about the G++-specific
CLEANUP_STMTs tree. So I went for a language hook instead. It's
named lang_hooks.decls.statements and is called at the end of a scope
with the statements of that scope in argument. The line location
fixup is thus done in the G++ specific implementation of that language
hook.
Bootstrapped and tested on x86_64-unknown-linux-gnu against trunk.
Comments?
gcc/
PR debug/49951
* langhooks.h (lang_hooks_for_decls::statement_at_end_of_scope):
New language hook.
* gcc/langhooks-def.h (LANG_HOOKS_STATEMENT_AT_END_OF_SCOPE):
Initialize the new lang hook.
gcc/c-family/
PR debug/49951
* c-semantics.c (pop_stmt_list): Call
lang_hooks.decls.statement_at_end_of_scope for the statements of
the scope.
gcc/cp/
PR debug/49951
* cp-objcp-common.h (LANG_HOOKS_STATEMENT_AT_END_OF_SCOPE): Set
this hook to cxx_statement_at_end_of_scope for G++.
* cp/cp-tree.h (cxx_statement_at_end_of_scope): Declare new
function.
* cp-objcp-common.c (cxx_statement_at_end_of_scope): Define it.
* parser.c (cp_parser_compound_statement): Make sure that
input_location is set to the closing '}' when finish_compound_stmt
is called.
* Make-lang.in: add tree-iterator.h as a dependency of
cp/cp-objcp-common.o.
gcc/testsuite/
PR debug/49951
* g++.dg/gcov/gcov-2.C: Adjust test.
---
gcc/c-family/c-semantics.c | 8 +++++++-
gcc/cp/Make-lang.in | 3 ++-
gcc/cp/cp-objcp-common.c | 32 ++++++++++++++++++++++++++++++++
gcc/cp/cp-objcp-common.h | 2 ++
gcc/cp/cp-tree.h | 1 +
gcc/cp/parser.c | 9 ++++++---
gcc/langhooks-def.h | 2 ++
gcc/langhooks.h | 4 ++++
gcc/testsuite/g++.dg/gcov/gcov-2.C | 4 ++--
9 files changed, 58 insertions(+), 7 deletions(-)
diff --git a/gcc/c-family/c-semantics.c b/gcc/c-family/c-semantics.c
index cb0f2be..222071b 100644
--- a/gcc/c-family/c-semantics.c
+++ b/gcc/c-family/c-semantics.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see
#include "flags.h"
#include "output.h"
#include "tree-iterator.h"
+#include "langhooks.h"
/* Create an empty statement tree rooted at T. */
@@ -42,7 +43,11 @@ push_stmt_list (void)
return t;
}
-/* Finish the statement tree rooted at T. */
+/* Finish the statement tree rooted at T. Note that this will call
+ lang_hooks.decls.statement_at_end_of_scope with the list of
+ statements of the current scope in argument. That language hook
+ expects input_location to be set to the closing '}' of the current
+ scope. */
tree
pop_stmt_list (tree t)
@@ -59,6 +64,7 @@ pop_stmt_list (tree t)
tree x = VEC_last (tree, stmt_list_stack);
STATEMENT_LIST_HAS_LABEL (x) |= STATEMENT_LIST_HAS_LABEL (u);
}
+ lang_hooks.decls.statement_at_end_of_scope (u);
if (t == u)
break;
}
diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in
index 0ce01ac..20a276d 100644
--- a/gcc/cp/Make-lang.in
+++ b/gcc/cp/Make-lang.in
@@ -284,7 +284,8 @@ cp/decl2.o: cp/decl2.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) cp/decl.h \
cp/cp-objcp-common.o : cp/cp-objcp-common.c $(CONFIG_H) $(SYSTEM_H) \
coretypes.h $(TM_H) $(TREE_H) $(CXX_TREE_H) $(C_COMMON_H) \
langhooks.h $(LANGHOOKS_DEF_H) $(DIAGNOSTIC_H) debug.h \
- $(CXX_PRETTY_PRINT_H) cp/cp-objcp-common.h gt-cp-cp-objcp-common.h
+ $(CXX_PRETTY_PRINT_H) cp/cp-objcp-common.h gt-cp-cp-objcp-common.h \
+ tree-iterator.h
cp/typeck2.o: cp/typeck2.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) output.h \
$(TM_P_H) $(DIAGNOSTIC_CORE_H) gt-cp-typeck2.h $(REAL_H) intl.h
cp/typeck.o: cp/typeck.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) \
diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c
index a957a03..6e7df1a 100644
--- a/gcc/cp/cp-objcp-common.c
+++ b/gcc/cp/cp-objcp-common.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
#include "debug.h"
#include "cxx-pretty-print.h"
#include "cp-objcp-common.h"
+#include "tree-iterator.h"
/* Special routine to get the alias set for C++. */
@@ -69,6 +70,37 @@ cxx_warn_unused_global_decl (const_tree decl)
return true;
}
+/* Langhook for struct
+ lang_hooks_for_decls::statement_at_end_of_scope. Is called at the
+ end of each scope with the statement list of that scope as an
+ argument. */
+
+void
+cxx_statement_at_end_of_scope (tree stmt_list)
+{
+ tree_stmt_iterator i;
+
+ if (stmt_list == NULL_TREE
+ || TREE_CODE (stmt_list) != STATEMENT_LIST)
+ return;
+
+ /* Let's find the CLEANUP_STMTs and set their location to input_location
+ that we expect to be the location of of the ending '}' of their
+ containing block. */
+ i = tsi_start (stmt_list);
+ for (i = tsi_start (stmt_list); !tsi_end_p (i); tsi_next (&i))
+ {
+ tree cleanup, stmt = tsi_stmt (i);
+ if (TREE_CODE (stmt) != CLEANUP_STMT)
+ continue;
+
+ cleanup = CLEANUP_EXPR (stmt);
+ if (cleanup != NULL_TREE
+ && IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (cleanup))))
+ SET_EXPR_LOCATION (cleanup, input_location);
+ }
+}
+
/* Langhook for tree_size: determine size of our 'x' and 'c' nodes. */
size_t
cp_tree_size (enum tree_code code)
diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h
index c668ad8..61f88e4 100644
--- a/gcc/cp/cp-objcp-common.h
+++ b/gcc/cp/cp-objcp-common.h
@@ -84,6 +84,8 @@ extern void cp_common_init_ts (void);
#define LANG_HOOKS_PRINT_ERROR_FUNCTION cxx_print_error_function
#undef LANG_HOOKS_WARN_UNUSED_GLOBAL_DECL
#define LANG_HOOKS_WARN_UNUSED_GLOBAL_DECL cxx_warn_unused_global_decl
+#undef LANG_HOOKS_STATEMENT_AT_END_OF_SCOPE
+#define LANG_HOOKS_STATEMENT_AT_END_OF_SCOPE cxx_statement_at_end_of_scope
#undef LANG_HOOKS_WRITE_GLOBALS
#define LANG_HOOKS_WRITE_GLOBALS cp_write_global_declarations
#undef LANG_HOOKS_BUILTIN_FUNCTION
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 3f4f408..7d05906 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5873,6 +5873,7 @@ extern bool cp_dump_tree (void *, tree);
extern alias_set_type cxx_get_alias_set (tree);
extern bool cxx_warn_unused_global_decl (const_tree);
+extern void cxx_statement_at_end_of_scope (tree);
extern size_t cp_tree_size (enum tree_code);
extern bool cp_var_mod_type_p (tree, tree);
extern void cxx_initialize_diagnostics (diagnostic_context *);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 2fdd675..888d1dc 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -8918,11 +8918,14 @@ cp_parser_compound_statement (cp_parser *parser, tree in_statement_expr,
cp_parser_label_declaration (parser);
/* Parse an (optional) statement-seq. */
cp_parser_statement_seq_opt (parser, in_statement_expr);
- /* Finish the compound-statement. */
- finish_compound_stmt (compound_stmt);
/* Consume the `}'. */
cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
-
+ /* Finish the compound-statement. Note that among other things,
+ this will call lang_hooks.decls.statement_at_end_of_scope (via
+ pop_stmt_list) with the list of statements in this scope and it
+ expects input_location to be set to the location of the
+ enclosing '}' that we just parsed above. */
+ finish_compound_stmt (compound_stmt);
return compound_stmt;
}
diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
index ed3230c..4792eaf 100644
--- a/gcc/langhooks-def.h
+++ b/gcc/langhooks-def.h
@@ -204,6 +204,7 @@ extern tree lhd_make_node (enum tree_code);
#define LANG_HOOKS_GETDECLS getdecls
#define LANG_HOOKS_FUNCTION_DECL_EXPLICIT_P hook_bool_tree_false
#define LANG_HOOKS_WARN_UNUSED_GLOBAL_DECL lhd_warn_unused_global_decl
+#define LANG_HOOKS_STATEMENT_AT_END_OF_SCOPE hook_void_tree
#define LANG_HOOKS_WRITE_GLOBALS write_global_declarations
#define LANG_HOOKS_DECL_OK_FOR_SIBCALL lhd_decl_ok_for_sibcall
#define LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE hook_bool_const_tree_false
@@ -227,6 +228,7 @@ extern tree lhd_make_node (enum tree_code);
LANG_HOOKS_FUNCTION_PARM_EXPANDED_FROM_PACK_P, \
LANG_HOOKS_GET_GENERIC_FUNCTION_DECL, \
LANG_HOOKS_WARN_UNUSED_GLOBAL_DECL, \
+ LANG_HOOKS_STATEMENT_AT_END_OF_SCOPE, \
LANG_HOOKS_WRITE_GLOBALS, \
LANG_HOOKS_DECL_OK_FOR_SIBCALL, \
LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE, \
diff --git a/gcc/langhooks.h b/gcc/langhooks.h
index 89e74f9..f20b1d0 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -180,6 +180,10 @@ struct lang_hooks_for_decls
We will already have checked that it has static binding. */
bool (*warn_unused_global) (const_tree);
+ /* Called on each statement, at the point of the of that
+ statement's scope. */
+ void (*statement_at_end_of_scope) (tree);
+
/* Obtain a list of globals and do final output on them at end
of compilation */
void (*final_write_globals) (void);
diff --git a/gcc/testsuite/g++.dg/gcov/gcov-2.C b/gcc/testsuite/g++.dg/gcov/gcov-2.C
index 6d002f5..016094f 100644
--- a/gcc/testsuite/g++.dg/gcov/gcov-2.C
+++ b/gcc/testsuite/g++.dg/gcov/gcov-2.C
@@ -20,9 +20,9 @@ private:
void foo()
{
- C c; /* count(2) */
+ C c; /* count(1) */
c.seti (1); /* count(1) */
-}
+} /* count(1) <- destructor of c runs here. */
int main()
{
--
1.7.6.4
--
Dodji
More information about the Gcc-patches
mailing list