Discussion:
[basic-improvements] try/finally support for c/c++
(too old to reply)
Aldy Hernandez
2002-11-05 23:19:02 UTC
Permalink
As promised. Here are the try/finally patches for C and C++.

As can be seen by the ChangeLog, the C++ bits were kindly supplied by
Jason Merrill (thank-you beers still pending).

Jakub has some tests for try/finally mixing C and C++ exceptions.
He'll be posting those later, right? :)

In a nutshell, try/finally is the coolest thing since sliced bread.
Now you can do stuff like:

__try {
blah;
blah;
return;
}
__finally {
/* guaranteed to run before the return */
}

Even cooler would be code in the kernel:

__try {
clear_interrupts();
stuff;
stuff;
}
__finally {
set_interrupts();
}

That way you'll never forget to turn on interrupts again :). Well...

You can even have C call C++, C++ throw an exception, and the finally
clause in C execute as the stack unwinds up to the caller.

C_function() {
__try {
C++_function_that_throws_exception();
}
__finally {
/* guaranteed to be executed as the stack unwinds up to
C_function's caller */
}
}

Bootstrapped on x86 (c, c++, java, fortran). Regtest almost done.

Ok?

2002-11-05 Aldy Hernandez <***@redhat.com>

* Makefile.in (LIB2ADDEH): Add unwind-c.c.
(c-semantics.o): Depend on libfuncs.h and except.h.

* config/t-linux (LIB2ADDEH): Same.

* config/ia64/t-ia64 (LIB2ADDEH): Add unwind-c.c.

* testsuite/gcc.c-torture/execute/try-finally-1.c: New.

* testsuite/gcc.c-torture/execute/try-finally-2.c: New.

* c-decl.c (c_init_decl_processing): Set eh_personality_libfunc.
Use EH for cleanups.
Include libfuncs.h, except.h.

* unwind-c.c: New file.

* c-common.def (TRY_FINALLY_STMT): New.

* c-semantics.c (expand_stmt): Add TRY_FINALLY_STMT case.
(genrtl_try_finally_stmt): New.

* c-tree.h: Add prototype to build_try_finally_stmt.

* c-typeck.c (build_try_finally_stmt): New.

* c-common.c (statement_code_p): Add case for TRY_FINALLY_STMT.

* c-common.h (enum rid): Add RID_FINALLY.
Add prototype for genrtl_try_finally_stmt.
(TRY_FINALLY_TRY): New.
(TRY_FINALLY_FINALLY): New.

* c-parse.in: Add FINALLY tokens.
(reswords): Add __try and __finally.
(rid_to_yy): Add RID_FINALLY. Enable RID_TRY.
(stmt): Add try/finally grammar case.
(try_finally_begin): New production.

* cp/lex.c (reswords): Add __finally, __try tokens.
(rid_to_yy): Add FINALLY.

* doc/extend.texi: Add documentation for try/finally.

2002-11-05 Jason Merrill <***@redhat.com>

* cp/cp-tree.h: Add prototypes for finish_try_finally_try and
finish_try_finally_finally.

* cp/parse.y: Add FINALLY token.
Add handler_seq and finally_stmt productions.
Modify try_block to handle try/finally.

* cp/pt.c (tsubst_expr): Add case for TRY_FINALLY_STMT.

* cp/semantics.c (genrtl_try_block): Handle try/finally.
(begin_try_finally): New.
(finish_try_finally_try): New.
(finish_try_finally_finally): New.

Index: Makefile.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Makefile.in,v
retrieving revision 1.939.2.10
diff -c -p -r1.939.2.10 Makefile.in
*** Makefile.in 21 Oct 2002 17:51:50 -0000 1.939.2.10
--- Makefile.in 5 Nov 2002 23:01:34 -0000
*************** CRTSTUFF_CFLAGS = -O2 $(GCC_CFLAGS) $(IN
*** 399,405 ****

# Additional sources to handle exceptions; overridden by targets as needed.
LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde.c \
! $(srcdir)/unwind-sjlj.c
LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h

# nm flags to list global symbols in libgcc object files.
--- 399,405 ----

# Additional sources to handle exceptions; overridden by targets as needed.
LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde.c \
! $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h

# nm flags to list global symbols in libgcc object files.
*************** c-format.o : c-format.c $(CONFIG_H) $(SY
*** 1252,1257 ****
--- 1252,1258 ----

c-semantics.o : c-semantics.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(C_TREE_H) \
flags.h toplev.h output.h c-pragma.h $(RTL_H) $(GGC_H) \
+ libfuncs.h except.h \
$(EXPR_H) $(PREDICT_H)

c-dump.o : c-dump.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(C_TREE_H) tree-dump.h
Index: unwind-c.c
===================================================================
RCS file: unwind-c.c
diff -N unwind-c.c
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- unwind-c.c 5 Nov 2002 23:01:38 -0000
***************
*** 0 ****
--- 1,185 ----
+ /* Supporting functions for C exception handling.
+
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Contributed by Aldy Hernandez <***@quesejoda.com>.
+ Shamelessly stolen from the Java front end.
+
+ This file is part of GCC.
+
+ GCC is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2, or (at your option) any later
+ version.
+
+ GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+ #include "tconfig.h"
+ #include "tsystem.h"
+ #include "unwind.h"
+ #include "unwind-pe.h"
+
+ typedef struct
+ {
+ _Unwind_Ptr Start;
+ _Unwind_Ptr LPStart;
+ _Unwind_Ptr ttype_base;
+ const unsigned char *TType;
+ const unsigned char *action_table;
+ unsigned char ttype_encoding;
+ unsigned char call_site_encoding;
+ } lsda_header_info;
+
+ static const unsigned char *
+ parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
+ lsda_header_info *info)
+ {
+ _Unwind_Word tmp;
+ unsigned char lpstart_encoding;
+
+ info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
+
+ /* Find @LPStart, the base to which landing pad offsets are relative. */
+ lpstart_encoding = *p++;
+ if (lpstart_encoding != DW_EH_PE_omit)
+ p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart);
+ else
+ info->LPStart = info->Start;
+
+ /* Find @TType, the base of the handler and exception spec type data. */
+ info->ttype_encoding = *p++;
+ if (info->ttype_encoding != DW_EH_PE_omit)
+ {
+ p = read_uleb128 (p, &tmp);
+ info->TType = p + tmp;
+ }
+ else
+ info->TType = 0;
+
+ /* The encoding and length of the call-site table; the action table
+ immediately follows. */
+ info->call_site_encoding = *p++;
+ p = read_uleb128 (p, &tmp);
+ info->action_table = p + tmp;
+
+ return p;
+ }
+
+ #ifdef __USING_SJLJ_EXCEPTIONS__
+ #define PERSONALITY_FUNCTION __gcc_personality_sj0
+ #define __builtin_eh_return_data_regno(x) x
+ #else
+ #define PERSONALITY_FUNCTION __gcc_personality_v0
+ #endif
+ #define PERSONALITY_FUNCTION __gcc_personality_v0
+
+ _Unwind_Reason_Code
+ PERSONALITY_FUNCTION (int, _Unwind_Action, _Unwind_Exception_Class,
+ struct _Unwind_Exception *, struct _Unwind_Context *);
+
+ _Unwind_Reason_Code
+ PERSONALITY_FUNCTION (int version,
+ _Unwind_Action actions,
+ _Unwind_Exception_Class exception_class ATTRIBUTE_UNUSED,
+ struct _Unwind_Exception *ue_header,
+ struct _Unwind_Context *context)
+ {
+ lsda_header_info info;
+ const unsigned char *language_specific_data, *p, *action_record;
+ _Unwind_Ptr landing_pad, ip;
+
+ if (version != 1)
+ return _URC_FATAL_PHASE1_ERROR;
+
+ /* Currently we only support cleanups for C. */
+ if (actions != _UA_CLEANUP_PHASE)
+ return _URC_CONTINUE_UNWIND;
+
+ language_specific_data = (const unsigned char *)
+ _Unwind_GetLanguageSpecificData (context);
+
+ /* If no LSDA, then there are no handlers or cleanups. */
+ if (! language_specific_data)
+ return _URC_CONTINUE_UNWIND;
+
+ /* Parse the LSDA header. */
+ p = parse_lsda_header (context, language_specific_data, &info);
+ ip = _Unwind_GetIP (context) - 1;
+ landing_pad = 0;
+
+ #ifdef __USING_SJLJ_EXCEPTIONS__
+ /* The given "IP" is an index into the call-site table, with two
+ exceptions -- -1 means no-action, and 0 means terminate. But
+ since we're using uleb128 values, we've not got random access
+ to the array. */
+ if ((int) ip <= 0)
+ return _URC_CONTINUE_UNWIND;
+ else
+ {
+ _Unwind_Word cs_lp, cs_action;
+ do
+ {
+ p = read_uleb128 (p, &cs_lp);
+ p = read_uleb128 (p, &cs_action);
+ }
+ while (--ip);
+
+ /* Can never have null landing pad for sjlj -- that would have
+ been indicated by a -1 call site index. */
+ landing_pad = cs_lp + 1;
+ if (cs_action)
+ action_record = info.action_table + cs_action - 1;
+ goto found_something;
+ }
+ #else
+ /* Search the call-site table for the action associated with this IP. */
+ while (p < info.action_table)
+ {
+ _Unwind_Ptr cs_start, cs_len, cs_lp;
+ _Unwind_Word cs_action;
+
+ /* Note that all call-site encodings are "absolute" displacements. */
+ p = read_encoded_value (0, info.call_site_encoding, p, &cs_start);
+ p = read_encoded_value (0, info.call_site_encoding, p, &cs_len);
+ p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp);
+ p = read_uleb128 (p, &cs_action);
+
+ /* The table is sorted, so if we've passed the ip, stop. */
+ if (ip < info.Start + cs_start)
+ p = info.action_table;
+ else if (ip < info.Start + cs_start + cs_len)
+ {
+ if (cs_lp)
+ landing_pad = info.LPStart + cs_lp;
+ if (cs_action)
+ action_record = info.action_table + cs_action - 1;
+ goto found_something;
+ }
+ }
+
+ #endif
+
+ /* Blahhh. IP is not in table. Run away. */
+ return _URC_CONTINUE_UNWIND;
+
+ found_something:
+ if (landing_pad == 0)
+ {
+ /* IP is present, but has a null landing pad. No handler to be
+ run. */
+ return _URC_CONTINUE_UNWIND;
+ }
+
+ _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
+ (_Unwind_Ptr) ue_header);
+ _Unwind_SetGR (context, __builtin_eh_return_data_regno (1), 0);
+ _Unwind_SetIP (context, landing_pad);
+ return _URC_INSTALL_CONTEXT;
+ }
Index: c-common.def
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-common.def,v
retrieving revision 1.11.8.2
diff -c -p -r1.11.8.2 c-common.def
*** c-common.def 1 Oct 2002 17:31:37 -0000 1.11.8.2
--- c-common.def 5 Nov 2002 23:01:39 -0000
*************** DEFTREECODE (SCOPE_STMT, "scope_stmt", '
*** 96,101 ****
--- 96,105 ----
other semantics. FILE_STMT_FILENAME gives the name. */
DEFTREECODE (FILE_STMT, "file_stmt", 'e', 1)

+ /* Describe a try/finally construct. TRY_FINALLY_TRY gives the try
+ block. TRY_FINALLY_FINALLY gives the finally block. */
+ DEFTREECODE (TRY_FINALLY_STMT, "try_finally_stmt", 'e', 2)
+
/* Used to represent a CASE_LABEL. The operands are CASE_LOW and
CASE_HIGH, respectively. If CASE_LOW is NULL_TREE, the label is a
'default' label. If CASE_HIGH is NULL_TREE, the label is a normal case
Index: c-semantics.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-semantics.c,v
retrieving revision 1.44.4.4
diff -c -p -r1.44.4.4 c-semantics.c
*** c-semantics.c 1 Oct 2002 17:31:38 -0000 1.44.4.4
--- c-semantics.c 5 Nov 2002 23:01:39 -0000
*************** Software Foundation, 59 Temple Place - S
*** 37,42 ****
--- 37,44 ----
#include "output.h"
#include "timevar.h"
#include "predict.h"
+ #include "except.h"
+ #include "libfuncs.h"

/* If non-NULL, the address of a language-specific function for
expanding statements. */
*************** genrtl_decl_cleanup (t)
*** 762,767 ****
--- 764,813 ----
expand_decl_cleanup_eh (decl, CLEANUP_EXPR (t), CLEANUP_EH_ONLY (t));
}

+ /* Generate the RTL for a TRY_FINALLY_STMT. */
+
+ void
+ genrtl_try_finally_stmt (t)
+ tree t;
+ {
+ tree try_block, finally_block;
+ static bool eh_initialized_p;
+
+ if (!TRY_FINALLY_FINALLY (t))
+ {
+ /* For a C++ try-block, we create both a TRY_FINALLY_STMT and a
+ TRY_BLOCK, even if one of them is unnecessary. We have to do this
+ because they need to be added before the contents of the
+ try-block. Hopefully this will be fixed with the new parser.
+
+ If there's no finally block, ignore the TRY_FINALLY_STMT. */
+ expand_stmt (TRY_FINALLY_TRY (t));
+ return;
+ }
+
+ if (flag_exceptions && !eh_initialized_p)
+ {
+ eh_initialized_p = true;
+ eh_personality_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS
+ ? "__gcc_personality_sj0"
+ : "__gcc_personality_v0");
+ using_eh_for_cleanups ();
+ }
+
+ try_block = TRY_FINALLY_TRY (t);
+ finally_block = TRY_FINALLY_FINALLY (t);
+
+ try_block = build1 (STMT_EXPR, void_type_node, try_block);
+ finally_block = build1 (STMT_EXPR, void_type_node, finally_block);
+ TREE_SIDE_EFFECTS (try_block) = 1;
+ TREE_SIDE_EFFECTS (finally_block) = 1;
+
+ /* Wrap in TRY_FINALLY_EXPR and punt it off. */
+ t = build (TRY_FINALLY_EXPR, void_type_node, try_block, finally_block);
+ expand_expr (t, NULL, VOIDmode, EXPAND_NORMAL);
+ return;
+ }
+
/* We're about to expand T, a statement. Set up appropriate context
for the substitution. */

*************** expand_stmt (t)
*** 874,879 ****
--- 920,929 ----

case CLEANUP_STMT:
genrtl_decl_cleanup (t);
+ break;
+
+ case TRY_FINALLY_STMT:
+ genrtl_try_finally_stmt (t);
break;

default:
Index: c-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-tree.h,v
retrieving revision 1.105.4.2
diff -c -p -r1.105.4.2 c-tree.h
*** c-tree.h 17 Sep 2002 22:58:37 -0000 1.105.4.2
--- c-tree.h 5 Nov 2002 23:01:39 -0000
*************** extern void c_finish_case
*** 296,301 ****
--- 296,302 ----
extern tree simple_asm_stmt PARAMS ((tree));
extern tree build_asm_stmt PARAMS ((tree, tree, tree,
tree, tree));
+ extern tree build_try_finally_stmt PARAMS ((tree, tree));
extern tree c_convert_parm_for_inlining PARAMS ((tree, tree, tree));

/* Set to 0 at beginning of a function definition, set to 1 if
Index: c-typeck.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-typeck.c,v
retrieving revision 1.203.4.5
diff -c -p -r1.203.4.5 c-typeck.c
*** c-typeck.c 15 Oct 2002 01:32:45 -0000 1.203.4.5
--- c-typeck.c 5 Nov 2002 23:01:44 -0000
*************** build_asm_stmt (cv_qualifier, string, ou
*** 6904,6909 ****
--- 6904,6919 ----
outputs, inputs, clobbers));
}

+ tree
+ build_try_finally_stmt (try_block, finally_block)
+ tree try_block, finally_block;
+ {
+ tree t;
+
+ t = build (TRY_FINALLY_STMT, void_type_node, try_block, finally_block);
+ return t;
+ }
+
/* Expand an ASM statement with operands, handling output operands
that are not variables or INDIRECT_REFS by transforming such
cases into cases that expand_asm_operands can handle.
Index: c-common.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-common.c,v
retrieving revision 1.366.4.6
diff -c -p -r1.366.4.6 c-common.c
*** c-common.c 21 Oct 2002 17:51:51 -0000 1.366.4.6
--- c-common.c 5 Nov 2002 23:01:48 -0000
*************** statement_code_p (code)
*** 3914,3919 ****
--- 3914,3920 ----
case ASM_STMT:
case FILE_STMT:
case CASE_LABEL:
+ case TRY_FINALLY_STMT:
return 1;

default:
Index: c-common.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-common.h,v
retrieving revision 1.154.4.3
diff -c -p -r1.154.4.3 c-common.h
*** c-common.h 1 Oct 2002 17:31:37 -0000 1.154.4.3
--- c-common.h 5 Nov 2002 23:01:49 -0000
*************** enum rid
*** 77,82 ****
--- 77,83 ----
RID_ASM, RID_TYPEOF, RID_ALIGNOF, RID_ATTRIBUTE, RID_VA_ARG,
RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL, RID_PTRBASE,
RID_PTREXTENT, RID_PTRVALUE, RID_CHOOSE_EXPR, RID_TYPES_COMPATIBLE_P,
+ RID_FINALLY,

/* Too many ways of getting the name of a function as a string */
RID_FUNCTION_NAME, RID_PRETTY_FUNCTION_NAME, RID_C99_FUNCTION_NAME,
*************** extern tree strip_array_types
*** 1097,1102 ****
--- 1098,1110 ----
#define FILE_STMT_FILENAME(NODE) \
(IDENTIFIER_POINTER (FILE_STMT_FILENAME_NODE (NODE)))

+ /* The try block of a try/finally construct. */
+ #define TRY_FINALLY_TRY(NODE) \
+ (TREE_OPERAND (TRY_FINALLY_STMT_CHECK (NODE), 0))
+ /* The finally block of a try/finally construct. */
+ #define TRY_FINALLY_FINALLY(NODE) \
+ (TREE_OPERAND (TRY_FINALLY_STMT_CHECK (NODE), 1))
+
/* The line-number at which a statement began. But if
STMT_LINENO_FOR_FN_P does holds, then this macro gives the
line number for the end of the current function instead. */
*************** extern void genrtl_asm_stmt
*** 1146,1151 ****
--- 1154,1160 ----
tree, tree,
tree, int));
extern void genrtl_decl_cleanup PARAMS ((tree));
+ extern void genrtl_try_finally_stmt PARAMS ((tree));
extern int stmts_are_full_exprs_p PARAMS ((void));
extern int anon_aggr_type_p PARAMS ((tree));

Index: c-parse.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-parse.in,v
retrieving revision 1.152
diff -c -p -r1.152 c-parse.in
*** c-parse.in 15 Aug 2002 21:16:23 -0000 1.152
--- c-parse.in 5 Nov 2002 23:01:51 -0000
*************** do { \
*** 150,155 ****
--- 150,156 ----
%token ATTRIBUTE EXTENSION LABEL
%token REALPART IMAGPART VA_ARG CHOOSE_EXPR TYPES_COMPATIBLE_P
%token PTR_VALUE PTR_BASE PTR_EXTENT
+ %token TRY FINALLY

/* function name can be a string const or a var decl. */
%token STRING_FUNC_NAME VAR_FUNC_NAME
*************** do { \
*** 212,218 ****
%type <ttype> any_word extension

%type <ttype> compstmt compstmt_start compstmt_nostart compstmt_primary_start
! %type <ttype> do_stmt_start poplevel stmt label

%type <ttype> c99_block_start c99_block_end
%type <ttype> declarator
--- 213,219 ----
%type <ttype> any_word extension

%type <ttype> compstmt compstmt_start compstmt_nostart compstmt_primary_start
! %type <ttype> do_stmt_start poplevel stmt label try_finally_begin

%type <ttype> c99_block_start c99_block_end
%type <ttype> declarator
*************** for_init_stmt:
*** 2363,2372 ****
--- 2364,2386 ----
{ check_for_loop_decls (); }
;

+ try_finally_begin: TRY
+ {
+ $$ = add_stmt (build_stmt (TRY_FINALLY_STMT, NULL_TREE));
+ }
+ ;
+
/* Parse a single real statement, not including any labels. */
stmt:
compstmt
{ stmt_count++; $$ = $1; }
+ | try_finally_begin compstmt
+ { RECHAIN_STMTS ($1, TRY_FINALLY_TRY ($1)); }
+ FINALLY compstmt
+ { stmt_count++;
+ RECHAIN_STMTS ($1, TRY_FINALLY_FINALLY ($1));
+ $$ = $1;
+ }
| expr ';'
{ stmt_count++;
$$ = c_expand_expr_stmt ($1); }
*************** static const struct resword reswords[] =
*** 3322,3327 ****
--- 3336,3342 ----
{ "__const", RID_CONST, 0 },
{ "__const__", RID_CONST, 0 },
{ "__extension__", RID_EXTENSION, 0 },
+ { "__finally", RID_FINALLY, 0 },
{ "__func__", RID_C99_FUNCTION_NAME, 0 },
{ "__imag", RID_IMAGPART, 0 },
{ "__imag__", RID_IMAGPART, 0 },
*************** static const struct resword reswords[] =
*** 3341,3346 ****
--- 3356,3362 ----
{ "__signed", RID_SIGNED, 0 },
{ "__signed__", RID_SIGNED, 0 },
{ "__thread", RID_THREAD, 0 },
+ { "__try", RID_TRY, 0 },
{ "__typeof", RID_TYPEOF, 0 },
{ "__typeof__", RID_TYPEOF, 0 },
{ "__unbounded", RID_UNBOUNDED, 0 },
*************** static const short rid_to_yy[RID_MAX] =
*** 3493,3498 ****
--- 3509,3516 ----
/* RID_CHOOSE_EXPR */ CHOOSE_EXPR,
/* RID_TYPES_COMPATIBLE_P */ TYPES_COMPATIBLE_P,

+ /* RID_FINALLY */ FINALLY,
+
/* RID_FUNCTION_NAME */ STRING_FUNC_NAME,
/* RID_PRETTY_FUNCTION_NAME */ STRING_FUNC_NAME,
/* RID_C99_FUNCTION_NAME */ VAR_FUNC_NAME,
*************** static const short rid_to_yy[RID_MAX] =
*** 3515,3521 ****
/* RID_THIS */ 0,
/* RID_THROW */ 0,
/* RID_TRUE */ 0,
! /* RID_TRY */ 0,
/* RID_TYPENAME */ 0,
/* RID_TYPEID */ 0,
/* RID_USING */ 0,
--- 3533,3539 ----
/* RID_THIS */ 0,
/* RID_THROW */ 0,
/* RID_TRUE */ 0,
! /* RID_TRY */ TRY,
/* RID_TYPENAME */ 0,
/* RID_TYPEID */ 0,
/* RID_USING */ 0,
Index: config/t-linux
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/t-linux,v
retrieving revision 1.12
diff -c -p -r1.12 t-linux
*** config/t-linux 15 Dec 2001 11:46:55 -0000 1.12
--- config/t-linux 5 Nov 2002 23:01:51 -0000
*************** SHLIB_MAPFILES += $(srcdir)/config/libgc
*** 12,16 ****

# Use unwind-dw2-fde-glibc
LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde-glibc.c \
! $(srcdir)/unwind-sjlj.c
LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h unwind-dw2-fde.c
--- 12,16 ----

# Use unwind-dw2-fde-glibc
LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde-glibc.c \
! $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h unwind-dw2-fde.c
Index: config/ia64/t-ia64
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/ia64/t-ia64,v
retrieving revision 1.13.20.2
diff -c -p -r1.13.20.2 t-ia64
*** config/ia64/t-ia64 17 Sep 2002 22:59:00 -0000 1.13.20.2
--- config/ia64/t-ia64 5 Nov 2002 23:01:51 -0000
*************** crtendS.o: $(srcdir)/config/ia64/crtend.
*** 34,40 ****
crtfastmath.o: $(srcdir)/config/ia64/crtfastmath.c $(GCC_PASSES)
$(GCC_FOR_TARGET) $(LIBGCC2_CFLAGS) -c -o crtfastmath.o $(srcdir)/config/ia64/crtfastmath.c

! LIB2ADDEH = $(srcdir)/config/ia64/unwind-ia64.c $(srcdir)/unwind-sjlj.c

ia64-c.o: $(srcdir)/config/ia64/ia64-c.c $(CONFIG_H) $(SYSTEM_H) \
$(TREE_H) $(CPPLIB_H) $(C_COMMON_H) c-pragma.h toplev.h
--- 34,41 ----
crtfastmath.o: $(srcdir)/config/ia64/crtfastmath.c $(GCC_PASSES)
$(GCC_FOR_TARGET) $(LIBGCC2_CFLAGS) -c -o crtfastmath.o $(srcdir)/config/ia64/crtfastmath.c

! LIB2ADDEH = $(srcdir)/config/ia64/unwind-ia64.c $(srcdir)/unwind-sjlj.c $(srcdir
! )/unwind-c.c

ia64-c.o: $(srcdir)/config/ia64/ia64-c.c $(CONFIG_H) $(SYSTEM_H) \
$(TREE_H) $(CPPLIB_H) $(C_COMMON_H) c-pragma.h toplev.h
Index: testsuite/gcc.c-torture/execute/try-finally-1.c
===================================================================
RCS file: testsuite/gcc.c-torture/execute/try-finally-1.c
diff -N testsuite/gcc.c-torture/execute/try-finally-1.c
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- testsuite/gcc.c-torture/execute/try-finally-1.c 5 Nov 2002 23:01:51 -0000
***************
*** 0 ****
--- 1,43 ----
+ int caught;
+
+ void doit (void)
+ {
+ __try {
+ return;
+ } __finally {
+ caught = 1;
+ }
+ }
+
+ void doit_again (void)
+ {
+ caught = 0;
+
+ while (1) {
+ __try {
+ __try {
+ return;
+ } __finally {
+ caught = 1;
+ }
+ } __finally {
+ if (!caught)
+ abort ();
+ caught = 2;
+ }
+ }
+ abort ();
+ }
+
+ int main ()
+ {
+ doit ();
+ if (caught != 1)
+ abort ();
+
+ doit_again ();
+ if (caught != 2)
+ abort ();
+
+ return 0;
+ }
Index: testsuite/gcc.c-torture/execute/try-finally-2.c
===================================================================
RCS file: testsuite/gcc.c-torture/execute/try-finally-2.c
diff -N testsuite/gcc.c-torture/execute/try-finally-2.c
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- testsuite/gcc.c-torture/execute/try-finally-2.c 5 Nov 2002 23:01:51 -0000
***************
*** 0 ****
--- 1,26 ----
+ int caught;
+
+ void doit (int i)
+ {
+ caught = 0;
+ __try {
+ if (!i)
+ return;
+ } __finally {
+ caught = 1;
+ }
+ caught = 2;
+ }
+
+ int main ()
+ {
+ doit (0);
+ if (caught != 1)
+ abort ();
+
+ doit (1);
+ if (caught != 2)
+ abort ();
+
+ return 0;
+ }
Index: cp/lex.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/lex.c,v
retrieving revision 1.289.4.3
diff -c -p -r1.289.4.3 lex.c
*** cp/lex.c 21 Oct 2002 17:52:56 -0000 1.289.4.3
--- cp/lex.c 5 Nov 2002 23:01:52 -0000
*************** static const struct resword reswords[] =
*** 345,350 ****
--- 345,351 ----
{ "__const__", RID_CONST, 0 },
{ "__extension__", RID_EXTENSION, 0 },
{ "__func__", RID_C99_FUNCTION_NAME, 0 },
+ { "__finally", RID_FINALLY, 0 },
{ "__imag", RID_IMAGPART, 0 },
{ "__imag__", RID_IMAGPART, 0 },
{ "__inline", RID_INLINE, 0 },
*************** static const struct resword reswords[] =
*** 358,363 ****
--- 359,365 ----
{ "__signed", RID_SIGNED, 0 },
{ "__signed__", RID_SIGNED, 0 },
{ "__thread", RID_THREAD, 0 },
+ { "__try", RID_TRY, 0 },
{ "__typeof", RID_TYPEOF, 0 },
{ "__typeof__", RID_TYPEOF, 0 },
{ "__volatile", RID_VOLATILE, 0 },
*************** const short rid_to_yy[RID_MAX] =
*** 507,512 ****
--- 509,515 ----
/* RID_PTRVALUE */ 0,
/* RID_CHOOSE_EXPR */ 0,
/* RID_TYPES_COMPATIBLE_P */ 0,
+ /* RID_FINALLY */ FINALLY,

/* RID_FUNCTION_NAME */ VAR_FUNC_NAME,
/* RID_PRETTY_FUNCTION_NAME */ VAR_FUNC_NAME,
Index: cp/cp-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-tree.h,v
retrieving revision 1.744.4.7
diff -c -p -r1.744.4.7 cp-tree.h
*** cp/cp-tree.h 21 Oct 2002 17:52:51 -0000 1.744.4.7
--- cp/cp-tree.h 5 Nov 2002 23:01:55 -0000
*************** extern void finish_handler_parms
*** 4106,4111 ****
--- 4106,4113 ----
extern void begin_catch_block PARAMS ((tree));
extern void finish_handler PARAMS ((tree));
extern void finish_cleanup PARAMS ((tree, tree));
+ extern void finish_try_finally_try PARAMS ((tree));
+ extern void finish_try_finally_finally PARAMS ((tree));
extern tree begin_compound_stmt PARAMS ((int));
extern tree finish_compound_stmt PARAMS ((int, tree));
extern tree finish_asm_stmt PARAMS ((tree, tree, tree, tree, tree));
Index: cp/parse.y
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/parse.y,v
retrieving revision 1.279.4.3
diff -c -p -r1.279.4.3 parse.y
*** cp/parse.y 21 Oct 2002 17:52:57 -0000 1.279.4.3
--- cp/parse.y 5 Nov 2002 23:01:57 -0000
*************** check_class_key (key, aggr)
*** 323,328 ****
--- 323,329 ----
%token SIGOF
%token ATTRIBUTE EXTENSION LABEL
%token REALPART IMAGPART VA_ARG
+ %token FINALLY

/* the reserved words... C++ extensions */
%token <ttype> AGGR
*************** check_class_key (key, aggr)
*** 463,468 ****
--- 464,470 ----
%type <ttype> identifier_defn IDENTIFIER_DEFN TYPENAME_DEFN PTYPENAME_DEFN
%type <ttype> handler_args
%type <ttype> self_template_type finish_template_type_
+ %type <itype> handler_seq finally_stmt

%token NSNAME
%type <ttype> NSNAME
*************** function_try_block:
*** 3552,3575 ****

try_block:
TRY
! { $<ttype>$ = begin_try_block (); }
compstmt
{ finish_try_block ($<ttype>2); }
handler_seq
! { finish_handler_sequence ($<ttype>2); }
;

handler_seq:
handler
| handler_seq handler
| /* empty */
{ /* Generate a fake handler block to avoid later aborts. */
tree fake_handler = begin_handler ();
finish_handler_parms (NULL_TREE, fake_handler);
finish_handler (fake_handler);
! $<ttype>$ = fake_handler;
!
! error ("must have at least one catch per try block");
}
;

--- 3554,3597 ----

try_block:
TRY
! {
! $<ttype>1 = begin_try_finally ();
! $<ttype>$ = begin_try_block ();
! }
compstmt
{ finish_try_block ($<ttype>2); }
handler_seq
! {
! finish_handler_sequence ($<ttype>2);
! finish_try_finally_try ($<ttype>1);
! }
! finally_stmt
! {
! finish_try_finally_finally ($<ttype>1);
!
! if (!$5 && !$7)
! error ("must have at least one catch per try block");
! }
! ;
!
! finally_stmt:
! FINALLY compstmt
! { $$ = 1; }
! | /* empty */
! { $$ = 0; }
;

handler_seq:
handler
+ { $$ = 1; }
| handler_seq handler
+ { $$ = 1; }
| /* empty */
{ /* Generate a fake handler block to avoid later aborts. */
tree fake_handler = begin_handler ();
finish_handler_parms (NULL_TREE, fake_handler);
finish_handler (fake_handler);
! $$ = 0;
}
;

Index: cp/pt.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/pt.c,v
retrieving revision 1.610.4.4
diff -c -p -r1.610.4.4 pt.c
*** cp/pt.c 15 Oct 2002 01:33:31 -0000 1.610.4.4
--- cp/pt.c 5 Nov 2002 23:02:04 -0000
*************** tsubst_expr (t, args, complain, in_decl)
*** 7657,7662 ****
--- 7657,7671 ----
finish_handler_sequence (stmt);
}
break;
+
+ case TRY_FINALLY_STMT:
+ prep_stmt (t);
+ stmt = begin_try_finally ();
+ tsubst_expr (TRY_FINALLY_TRY (t), args, complain, in_decl);
+ finish_try_finally_try (stmt);
+ tsubst_expr (TRY_FINALLY_FINALLY (t), args, complain, in_decl);
+ finish_try_finally_finally (stmt);
+ break;

case HANDLER:
{
Index: cp/semantics.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/semantics.c,v
retrieving revision 1.274.4.5
diff -c -p -r1.274.4.5 semantics.c
*** cp/semantics.c 21 Oct 2002 17:52:57 -0000 1.274.4.5
--- cp/semantics.c 5 Nov 2002 23:02:05 -0000
*************** static void
*** 598,603 ****
--- 598,615 ----
genrtl_try_block (t)
tree t;
{
+ if (!TRY_HANDLERS (t))
+ {
+ /* For a C++ try-block, we create both a TRY_FINALLY_STMT and a
+ TRY_BLOCK, even if one of them is unnecessary. We have to do this
+ because they need to be added before the contents of the
+ try-block. Hopefully this will be fixed with the new parser.
+
+ If there are no handlers, ignore the TRY_BLOCK. */
+ expand_stmt (TRY_STMTS (t));
+ return;
+ }
+
if (CLEANUP_P (t))
{
expand_eh_region_start ();
*************** finish_handler (handler)
*** 805,810 ****
--- 817,854 ----
expand_end_catch_block ();
do_poplevel ();
RECHAIN_STMTS (handler, HANDLER_BODY (handler));
+ }
+
+ /* Begin a try/finally construct. The parser calls this immediately before
+ begin_try_block, as one 'try' could start either a normal C++ try-block,
+ a try/finally construct, or both. */
+
+ tree
+ begin_try_finally ()
+ {
+ tree r = build_stmt (TRY_FINALLY_STMT, NULL_TREE, NULL_TREE);
+ add_stmt (r);
+ return r;
+ }
+
+ /* Finish the body of a try/finally construct. This usually consists of a
+ TRY_BLOCK. */
+
+ void
+ finish_try_finally_try (try_finally)
+ tree try_finally;
+ {
+ RECHAIN_STMTS (try_finally, TRY_FINALLY_TRY (try_finally));
+ }
+
+ /* Finish the finally block of a try/finally construct. This will be
+ NULL_TREE in the case of a normal C++ try-block. */
+
+ void
+ finish_try_finally_finally (try_finally)
+ tree try_finally;
+ {
+ RECHAIN_STMTS (try_finally, TRY_FINALLY_FINALLY (try_finally));
}

/* Begin a compound-statement. If HAS_NO_SCOPE is nonzero, the
Index: doc/extend.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/extend.texi,v
retrieving revision 1.95.4.5
diff -c -p -r1.95.4.5 extend.texi
*** doc/extend.texi 21 Oct 2002 17:52:59 -0000 1.95.4.5
--- doc/extend.texi 5 Nov 2002 23:02:12 -0000
*************** extensions, accepted by GCC in C89 mode
*** 473,478 ****
--- 473,479 ----
* Target Builtins:: Built-in functions specific to particular targets.
* Pragmas:: Pragmas accepted by GCC.
* Unnamed Fields:: Unnamed struct/union fields within structs/unions.
+ * Try-finally:: Try/finally construct.
* Thread-Local:: Per-thread variables.
@end menu

*************** struct @{
*** 6506,6511 ****
--- 6507,6715 ----
It is ambiguous which @code{a} is being referred to with @samp{foo.a}.
Such constructs are not supported and must be avoided. In the future,
such constructs may be detected and treated as compilation errors.
+
+ @node Try-finally
+ @section Try/finally exceptions
+ @cindex Try/finally exceptions
+ @cindex try-finally
+ @cindex __try
+ @cindex __finally
+
+ The @code{__try/__finally} statement consists of a @code{__try} block
+ containing one or statements, and a @code{__finally} block contatining
+ one or more statements. The @code{__finally} block will execute after
+ the @code{__try} block but before the code following the
+ @code{__try/__finally} construct, even if the @code{__try} block exits
+ the current function.
+
+ For example:
+
+ @example
+ __try @{
+ do_something();
+ return;
+ @} __finally @{
+ fin = 1;
+ @}
+ @end example
+
+ In the above example, @code{fin} will get set to 1 before the function
+ returns to its caller.
+
+ You can nest more than one @code{__try/__finally} construct.
+
+ Since C++ already has a @code{try} keyword, both @code{try} and
+ @code{__try} variations are allowed. Also, the C++ implementation
+ allows using @code{__finally} after a series of @code{try/catch}
+ blocks. The @code{__finally} code will be executed after the
+ applicable @code{catch}. For example:
+
+ @example
+ try @{
+ do_something();
+ @} catch (blah) @{
+ x = 0;
+ @} catch (...) @{
+ x = 1;
+ @} __finally @{
+ y = 2;
+ @}
+ @end example
+
+ In the example above, @code{y} will be set after the set to @code{x}
+ in the catch clause.
+
+ It is possible to have a C @code{__try/__finally} construct call C++
+ code that throws an exception. As part of the unwind process, the C
+ @code{__finally} block will be called before control is returned to
+ the C's calling routine.
+
+ Exiting out of a finally block through a @code{return} or @code{goto}
+ has undefined behaviour.
+
+ GCC's @code{__try/__finally} implementation is analogous to the
+ corresponding construct in Java.
+
+ @menu
+ * C99 Try-Finally Edits::
+ * C++98 Try-Finally Edits::
+ @end menu
+
+ @node C99 Try-Finally Edits
+ @subsection ISO/IEC 9899:1999 Edits for Try-Finally
+
+ The following are a set of changes to ISO/IEC 9899:1999 (aka C99)
+ that document the exact semantics of the language extension.
+
+ @itemize @bullet
+ @item
+ @cite{6.4.1 Keywords}
+
+ Add @code{__try}.
+ Add @code{__finally}.
+
+ @item
+ @cite{6.8.2b The try/finally statement}
+
+ New section.
+
+ @quotation
+ Syntax:
+ @example
+ try-finally-statement:
+ @b{__try}
+ @b{@{} block-item-list @b{@}}
+ @b{__finally}
+ @b{@{} block-item-list @b{@}}
+
+ @end example
+ @end quotation
+
+ The @code{__try} block is executed. After the __try block exits,
+ control is transferred to the @code{__finally} block regardless of how
+ the __try block exits.
+
+ The __finally block is guaranteed to execute after the __try block,
+ provided the program does not terminate while inside such block.
+ After the __finally block executes, the flow of the program will
+ continue where the __try block was meant to transfer control to, had
+ there been no __finally block.
+
+ This construct is meant to provide a mechanism through which a series
+ of cleanups can be executed upon exit from a block (the __try block).
+ If somewhere inside the __try block an exception occurs and the
+ corresponding exception handler lies in the frame above the __finally
+ block, the __finally block will execute before control reaches the
+ exception handler above.
+
+ This mechanism is by no ways a complete exception handling system for
+ C.
+
+ @end itemize
+
+ @node C++98 Try-Finally Edits
+ @subsection ISO/IEC 14882:1998 Edits for Try-Finally
+
+ @itemize @bullet
+ @item
+ @cite{2.11 Keywords: Table 3}
+
+ Add @code{__finally}.
+ Add @code{finally}.
+
+ @item
+ @cite{15 Exception handling}
+
+ Rename the @code{handler-seq} production by the following:
+
+ @example
+ handler-seq:
+ handler-seq-catch finally-block
+
+ handler-seq-catch:
+ handler handler-seq-catch
+
+ finally-block:
+ @b{__finally} compound-statement
+ @end example
+
+ Add new text at the end of section 2.
+
+ @quotation
+ Before control is transferred to the desired destination, the
+ __finally block is executed. After such block is executed, control is
+ transferred to the desired destination.
+ @end quotation
+
+ Add to code below to the example in section 3.
+
+ @example
+ __finally
+ @{
+ // code to be executed after the try block and
+ // applicable catch block is executed.
+ @}
+ @end example
+
+ @item
+ @cite{15.1 Throwing an exception}
+
+ Add new section before section 2 and 3.
+
+ @quotation
+ After an exception is caught by an applicable handler (if present),
+ but before control is transferred elsewhere (provided the program does
+ not exit), the code in the __finally block is executed.
+ @end quotation
+
+ @item
+ @cite{15.2 Constructors and destructors}
+
+ Add new text to section 3.
+
+ @quotation
+ If code within a @i{try} block (or within code called from a try
+ block) causes an exception that is not caught by the subsequent
+ @i{catch} blocks, the code in @i{__finally} executes as the stack
+ unwinds upwards.
+ @end quotation
+
+ Add new text to section 7.
+
+ @quotation
+ If the try block has a corresponding __finally block, the code in the
+ __finally block will execute before control reaches the dynamically
+ surrounding try block.
+ @end quotation
+
+ Add new section 2.
+
+ @quotation
+ If the __finally block contains a jump outside stated block, the
+ program is ill-formed and the behavior is undefined.
+ @end quotation
+
+ @end itemize

@node Thread-Local
@section Thread-Local Storage
Richard Henderson
2002-11-05 23:57:18 UTC
Permalink
Post by Aldy Hernandez
+ /* Currently we only support cleanups for C. */
+ if (actions != _UA_CLEANUP_PHASE)
+ return _URC_CONTINUE_UNWIND;
IIRC, Jakub already pointed out that actions is a bit mask.
Should be

if (!(actions & _UA_CLEANUP_PHASE))
The braces should not be required. This should be

@b{__try}
@i{statement}
@b{__finally}
@i{statement}

and, indeed, we should have a test for

__try
q = 1;
__finally
x = 2;

or something.

Hmm. I see that C++ *does* require the braces.
In which case you should use compound-statment instead of
calling block-item-list directly.
Post by Aldy Hernandez
+ the __try block exits.
This implies that longjmp is try/finally aware, which is false.

Probably should have text similar to C++ 15/2, such that you
can't goto into a try handler. (I think that breaks with sjlj
exceptions.)
Post by Aldy Hernandez
+
+
"finally" is not a keyword.
Post by Aldy Hernandez
+
+
+ handler-seq-catch finally-block
+
+ handler handler-seq-catch
+
This says __finally is required. Which is false. Also doesn't
indicate that __finally can be used without catch.

Should probabably mention that

try {
A;
} catch (...) {
B;
} __finally {
C;
}

is equivalent to

try {
try {
A;
} catch (...) {
B;
}
} __finally {
C;
}
Post by Aldy Hernandez
+ Add new text at the end of section 2.
+
+ Before control is transferred to the desired destination, the
+ __finally block is executed. After such block is executed, control is
+ transferred to the desired destination.
There's got to be a better way to say this... Dunno what though.
Post by Aldy Hernandez
+
+ Add new section before section 2 and 3.
+
+ After an exception is caught by an applicable handler (if present),
+ but before control is transferred elsewhere (provided the program does
+ not exit), the code in the __finally block is executed.
This isn't needed if you define try/finally correctly elsewhere.
Post by Aldy Hernandez
+
+ Add new text to section 3.
+
+ block) causes an exception that is not caught by the subsequent
+ unwinds upwards.
"Upwards" is bad language. See the existing language that
uses "path".

You probably need to define __finally in terms of destructors
in order for this to work out properly. E.g. a finally block
is the destructor for an anonymous object of an anonymous type.
Or something. Perhaps Jason or Mark could help here?


r~
Aldy Hernandez
2002-11-06 07:06:22 UTC
Permalink
All your suggestions (and Geoff's) addressed.
Post by Richard Henderson
Post by Aldy Hernandez
+ Add new text at the end of section 2.
+
+ Before control is transferred to the desired destination, the
+ __finally block is executed. After such block is executed, control is
+ transferred to the desired destination.
There's got to be a better way to say this... Dunno what though.
See next attempt.
Post by Richard Henderson
Post by Aldy Hernandez
+ block) causes an exception that is not caught by the subsequent
+ unwinds upwards.
"Upwards" is bad language. See the existing language that
uses "path".
See next attempt.
Post by Richard Henderson
You probably need to define __finally in terms of destructors
in order for this to work out properly. E.g. a finally block
is the destructor for an anonymous object of an anonymous type.
Or something. Perhaps Jason or Mark could help here?
Help, help. I'm being repressed. Jason, Mark?

BTW: If you're comfortable with the code I can start just posting the
doc/extend.texi patches so we can iterate over that and not spam the
list every time with the enire patchset.

How does this new version look?

Aldy

2002-11-05 Aldy Hernandez <***@redhat.com>

* Makefile.in (LIB2FUNCS_EXTRA): Add eh-personality-c.c.
(c-semantics.o): Depend on libfuncs.h and except.h.

* testsuite/gcc.c-torture/execute/try-finally-1.c: New.

* testsuite/gcc.c-torture/execute/try-finally-2.c: New.

* c-decl.c (c_init_decl_processing): Set eh_personality_libfunc.
Use EH for cleanups.
Include libfuncs.h, except.h.

* eh-personality-c.c: New file.

* c-common.def (TRY_FINALLY_STMT): New.

* c-semantics.c (expand_stmt): Add TRY_FINALLY_STMT case.
(genrtl_try_finally_stmt): New.

* c-tree.h: Add prototype to build_try_finally_stmt.

* c-typeck.c (build_try_finally_stmt): New.

* c-common.c (statement_code_p): Add case for TRY_FINALLY_STMT.

* c-common.h (enum rid): Add RID_FINALLY.
Add prototype for genrtl_try_finally_stmt.
(TRY_FINALLY_TRY): New.
(TRY_FINALLY_FINALLY): New.

* c-parse.in: Add FINALLY tokens.
(reswords): Add __try and __finally.
(rid_to_yy): Add RID_FINALLY. Enable RID_TRY.
(stmt): Add try/finally grammar case.
(try_finally_begin): New production.

* cp/lex.c (reswords): Add __finally, __try tokens.
(rid_to_yy): Add FINALLY.

* doc/extend.texi: Add documentation for try/finally.

2002-11-05 Jason Merrill <***@redhat.com>

* cp/cp-tree.h: Add prototypes for finish_try_finally_try and
finish_try_finally_finally.

* cp/parse.y: Add FINALLY token.
Add handler_seq and finally_stmt productions.
Modify try_block to handle try/finally.

* cp/pt.c (tsubst_expr): Add case for TRY_FINALLY_STMT.

* cp/semantics.c (genrtl_try_block): Handle try/finally.
(begin_try_finally): New.
(finish_try_finally_try): New.
(finish_try_finally_finally): New.

Index: Makefile.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Makefile.in,v
retrieving revision 1.939.2.10
diff -c -p -r1.939.2.10 Makefile.in
*** Makefile.in 21 Oct 2002 17:51:50 -0000 1.939.2.10
--- Makefile.in 6 Nov 2002 06:58:05 -0000
*************** USE_COLLECT2 = collect2$(exeext)
*** 435,441 ****

# List of extra C and assembler files to add to static and shared libgcc2.
# Assembler files should have names ending in `.asm'.
! LIB2FUNCS_EXTRA =

# List of extra C and assembler files to add to static libgcc2.
# Assembler files should have names ending in `.asm'.
--- 435,441 ----

# List of extra C and assembler files to add to static and shared libgcc2.
# Assembler files should have names ending in `.asm'.
! LIB2FUNCS_EXTRA = $(srcdir)/eh-personality-c.c

# List of extra C and assembler files to add to static libgcc2.
# Assembler files should have names ending in `.asm'.
*************** c-format.o : c-format.c $(CONFIG_H) $(SY
*** 1252,1257 ****
--- 1252,1258 ----

c-semantics.o : c-semantics.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(C_TREE_H) \
flags.h toplev.h output.h c-pragma.h $(RTL_H) $(GGC_H) \
+ libfuncs.h except.h \
$(EXPR_H) $(PREDICT_H)

c-dump.o : c-dump.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(C_TREE_H) tree-dump.h
Index: eh-personality-c.c
===================================================================
RCS file: eh-personality-c.c
diff -N eh-personality-c.c
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- eh-personality-c.c 6 Nov 2002 06:58:10 -0000
***************
*** 0 ****
--- 1,185 ----
+ /* Supporting functions for C exception handling.
+
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Contributed by Aldy Hernandez <***@quesejoda.com>.
+ Shamelessly stolen from the Java front end.
+
+ This file is part of GCC.
+
+ GCC is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2, or (at your option) any later
+ version.
+
+ GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+ #include "tconfig.h"
+ #include "tsystem.h"
+ #include "unwind.h"
+ #include "unwind-pe.h"
+
+ typedef struct
+ {
+ _Unwind_Ptr Start;
+ _Unwind_Ptr LPStart;
+ _Unwind_Ptr ttype_base;
+ const unsigned char *TType;
+ const unsigned char *action_table;
+ unsigned char ttype_encoding;
+ unsigned char call_site_encoding;
+ } lsda_header_info;
+
+ static const unsigned char *
+ parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
+ lsda_header_info *info)
+ {
+ _Unwind_Word tmp;
+ unsigned char lpstart_encoding;
+
+ info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
+
+ /* Find @LPStart, the base to which landing pad offsets are relative. */
+ lpstart_encoding = *p++;
+ if (lpstart_encoding != DW_EH_PE_omit)
+ p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart);
+ else
+ info->LPStart = info->Start;
+
+ /* Find @TType, the base of the handler and exception spec type data. */
+ info->ttype_encoding = *p++;
+ if (info->ttype_encoding != DW_EH_PE_omit)
+ {
+ p = read_uleb128 (p, &tmp);
+ info->TType = p + tmp;
+ }
+ else
+ info->TType = 0;
+
+ /* The encoding and length of the call-site table; the action table
+ immediately follows. */
+ info->call_site_encoding = *p++;
+ p = read_uleb128 (p, &tmp);
+ info->action_table = p + tmp;
+
+ return p;
+ }
+
+ #ifdef __USING_SJLJ_EXCEPTIONS__
+ #define PERSONALITY_FUNCTION __gcc_personality_sj0
+ #define __builtin_eh_return_data_regno(x) x
+ #else
+ #define PERSONALITY_FUNCTION __gcc_personality_v0
+ #endif
+ #define PERSONALITY_FUNCTION __gcc_personality_v0
+
+ _Unwind_Reason_Code
+ PERSONALITY_FUNCTION (int, _Unwind_Action, _Unwind_Exception_Class,
+ struct _Unwind_Exception *, struct _Unwind_Context *);
+
+ _Unwind_Reason_Code
+ PERSONALITY_FUNCTION (int version,
+ _Unwind_Action actions,
+ _Unwind_Exception_Class exception_class ATTRIBUTE_UNUSED,
+ struct _Unwind_Exception *ue_header,
+ struct _Unwind_Context *context)
+ {
+ lsda_header_info info;
+ const unsigned char *language_specific_data, *p, *action_record;
+ _Unwind_Ptr landing_pad, ip;
+
+ if (version != 1)
+ return _URC_FATAL_PHASE1_ERROR;
+
+ /* Currently we only support cleanups for C. */
+ if (!(actions &_UA_CLEANUP_PHASE))
+ return _URC_CONTINUE_UNWIND;
+
+ language_specific_data = (const unsigned char *)
+ _Unwind_GetLanguageSpecificData (context);
+
+ /* If no LSDA, then there are no handlers or cleanups. */
+ if (! language_specific_data)
+ return _URC_CONTINUE_UNWIND;
+
+ /* Parse the LSDA header. */
+ p = parse_lsda_header (context, language_specific_data, &info);
+ ip = _Unwind_GetIP (context) - 1;
+ landing_pad = 0;
+
+ #ifdef __USING_SJLJ_EXCEPTIONS__
+ /* The given "IP" is an index into the call-site table, with two
+ exceptions -- -1 means no-action, and 0 means terminate. But
+ since we're using uleb128 values, we've not got random access
+ to the array. */
+ if ((int) ip <= 0)
+ return _URC_CONTINUE_UNWIND;
+ else
+ {
+ _Unwind_Word cs_lp, cs_action;
+ do
+ {
+ p = read_uleb128 (p, &cs_lp);
+ p = read_uleb128 (p, &cs_action);
+ }
+ while (--ip);
+
+ /* Can never have null landing pad for sjlj -- that would have
+ been indicated by a -1 call site index. */
+ landing_pad = cs_lp + 1;
+ if (cs_action)
+ action_record = info.action_table + cs_action - 1;
+ goto found_something;
+ }
+ #else
+ /* Search the call-site table for the action associated with this IP. */
+ while (p < info.action_table)
+ {
+ _Unwind_Ptr cs_start, cs_len, cs_lp;
+ _Unwind_Word cs_action;
+
+ /* Note that all call-site encodings are "absolute" displacements. */
+ p = read_encoded_value (0, info.call_site_encoding, p, &cs_start);
+ p = read_encoded_value (0, info.call_site_encoding, p, &cs_len);
+ p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp);
+ p = read_uleb128 (p, &cs_action);
+
+ /* The table is sorted, so if we've passed the ip, stop. */
+ if (ip < info.Start + cs_start)
+ p = info.action_table;
+ else if (ip < info.Start + cs_start + cs_len)
+ {
+ if (cs_lp)
+ landing_pad = info.LPStart + cs_lp;
+ if (cs_action)
+ action_record = info.action_table + cs_action - 1;
+ goto found_something;
+ }
+ }
+
+ #endif
+
+ /* Blahhh. IP is not in table. Run away. */
+ return _URC_CONTINUE_UNWIND;
+
+ found_something:
+ if (landing_pad == 0)
+ {
+ /* IP is present, but has a null landing pad. No handler to be
+ run. */
+ return _URC_CONTINUE_UNWIND;
+ }
+
+ _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
+ (_Unwind_Ptr) ue_header);
+ _Unwind_SetGR (context, __builtin_eh_return_data_regno (1), 0);
+ _Unwind_SetIP (context, landing_pad);
+ return _URC_INSTALL_CONTEXT;
+ }
Index: c-common.def
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-common.def,v
retrieving revision 1.11.8.2
diff -c -p -r1.11.8.2 c-common.def
*** c-common.def 1 Oct 2002 17:31:37 -0000 1.11.8.2
--- c-common.def 6 Nov 2002 06:58:10 -0000
*************** DEFTREECODE (SCOPE_STMT, "scope_stmt", '
*** 96,101 ****
--- 96,105 ----
other semantics. FILE_STMT_FILENAME gives the name. */
DEFTREECODE (FILE_STMT, "file_stmt", 'e', 1)

+ /* Describe a try/finally construct. TRY_FINALLY_TRY gives the try
+ block. TRY_FINALLY_FINALLY gives the finally block. */
+ DEFTREECODE (TRY_FINALLY_STMT, "try_finally_stmt", 'e', 2)
+
/* Used to represent a CASE_LABEL. The operands are CASE_LOW and
CASE_HIGH, respectively. If CASE_LOW is NULL_TREE, the label is a
'default' label. If CASE_HIGH is NULL_TREE, the label is a normal case
Index: c-semantics.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-semantics.c,v
retrieving revision 1.44.4.4
diff -c -p -r1.44.4.4 c-semantics.c
*** c-semantics.c 1 Oct 2002 17:31:38 -0000 1.44.4.4
--- c-semantics.c 6 Nov 2002 06:58:10 -0000
*************** Software Foundation, 59 Temple Place - S
*** 37,42 ****
--- 37,44 ----
#include "output.h"
#include "timevar.h"
#include "predict.h"
+ #include "except.h"
+ #include "libfuncs.h"

/* If non-NULL, the address of a language-specific function for
expanding statements. */
*************** genrtl_decl_cleanup (t)
*** 762,767 ****
--- 764,813 ----
expand_decl_cleanup_eh (decl, CLEANUP_EXPR (t), CLEANUP_EH_ONLY (t));
}

+ /* Generate the RTL for a TRY_FINALLY_STMT. */
+
+ void
+ genrtl_try_finally_stmt (t)
+ tree t;
+ {
+ tree try_block, finally_block;
+ static bool eh_initialized_p;
+
+ if (!TRY_FINALLY_FINALLY (t))
+ {
+ /* For a C++ try-block, we create both a TRY_FINALLY_STMT and a
+ TRY_BLOCK, even if one of them is unnecessary. We have to do this
+ because they need to be added before the contents of the
+ try-block. Hopefully this will be fixed with the new parser.
+
+ If there's no finally block, ignore the TRY_FINALLY_STMT. */
+ expand_stmt (TRY_FINALLY_TRY (t));
+ return;
+ }
+
+ if (flag_exceptions && !eh_initialized_p)
+ {
+ eh_initialized_p = true;
+ eh_personality_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS
+ ? "__gcc_personality_sj0"
+ : "__gcc_personality_v0");
+ using_eh_for_cleanups ();
+ }
+
+ try_block = TRY_FINALLY_TRY (t);
+ finally_block = TRY_FINALLY_FINALLY (t);
+
+ try_block = build1 (STMT_EXPR, void_type_node, try_block);
+ finally_block = build1 (STMT_EXPR, void_type_node, finally_block);
+ TREE_SIDE_EFFECTS (try_block) = 1;
+ TREE_SIDE_EFFECTS (finally_block) = 1;
+
+ /* Wrap in TRY_FINALLY_EXPR and punt it off. */
+ t = build (TRY_FINALLY_EXPR, void_type_node, try_block, finally_block);
+ expand_expr (t, NULL, VOIDmode, EXPAND_NORMAL);
+ return;
+ }
+
/* We're about to expand T, a statement. Set up appropriate context
for the substitution. */

*************** expand_stmt (t)
*** 874,879 ****
--- 920,929 ----

case CLEANUP_STMT:
genrtl_decl_cleanup (t);
+ break;
+
+ case TRY_FINALLY_STMT:
+ genrtl_try_finally_stmt (t);
break;

default:
Index: c-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-tree.h,v
retrieving revision 1.105.4.2
diff -c -p -r1.105.4.2 c-tree.h
*** c-tree.h 17 Sep 2002 22:58:37 -0000 1.105.4.2
--- c-tree.h 6 Nov 2002 06:58:10 -0000
*************** extern void c_finish_case
*** 296,301 ****
--- 296,302 ----
extern tree simple_asm_stmt PARAMS ((tree));
extern tree build_asm_stmt PARAMS ((tree, tree, tree,
tree, tree));
+ extern tree build_try_finally_stmt PARAMS ((tree, tree));
extern tree c_convert_parm_for_inlining PARAMS ((tree, tree, tree));

/* Set to 0 at beginning of a function definition, set to 1 if
Index: c-typeck.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-typeck.c,v
retrieving revision 1.203.4.5
diff -c -p -r1.203.4.5 c-typeck.c
*** c-typeck.c 15 Oct 2002 01:32:45 -0000 1.203.4.5
--- c-typeck.c 6 Nov 2002 06:58:15 -0000
*************** build_asm_stmt (cv_qualifier, string, ou
*** 6904,6909 ****
--- 6904,6919 ----
outputs, inputs, clobbers));
}

+ tree
+ build_try_finally_stmt (try_block, finally_block)
+ tree try_block, finally_block;
+ {
+ tree t;
+
+ t = build (TRY_FINALLY_STMT, void_type_node, try_block, finally_block);
+ return t;
+ }
+
/* Expand an ASM statement with operands, handling output operands
that are not variables or INDIRECT_REFS by transforming such
cases into cases that expand_asm_operands can handle.
Index: c-common.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-common.c,v
retrieving revision 1.366.4.6
diff -c -p -r1.366.4.6 c-common.c
*** c-common.c 21 Oct 2002 17:51:51 -0000 1.366.4.6
--- c-common.c 6 Nov 2002 06:58:19 -0000
*************** statement_code_p (code)
*** 3914,3919 ****
--- 3914,3920 ----
case ASM_STMT:
case FILE_STMT:
case CASE_LABEL:
+ case TRY_FINALLY_STMT:
return 1;

default:
Index: c-common.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-common.h,v
retrieving revision 1.154.4.3
diff -c -p -r1.154.4.3 c-common.h
*** c-common.h 1 Oct 2002 17:31:37 -0000 1.154.4.3
--- c-common.h 6 Nov 2002 06:58:20 -0000
*************** enum rid
*** 77,82 ****
--- 77,83 ----
RID_ASM, RID_TYPEOF, RID_ALIGNOF, RID_ATTRIBUTE, RID_VA_ARG,
RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL, RID_PTRBASE,
RID_PTREXTENT, RID_PTRVALUE, RID_CHOOSE_EXPR, RID_TYPES_COMPATIBLE_P,
+ RID_FINALLY,

/* Too many ways of getting the name of a function as a string */
RID_FUNCTION_NAME, RID_PRETTY_FUNCTION_NAME, RID_C99_FUNCTION_NAME,
*************** extern tree strip_array_types
*** 1097,1102 ****
--- 1098,1110 ----
#define FILE_STMT_FILENAME(NODE) \
(IDENTIFIER_POINTER (FILE_STMT_FILENAME_NODE (NODE)))

+ /* The try block of a try/finally construct. */
+ #define TRY_FINALLY_TRY(NODE) \
+ (TREE_OPERAND (TRY_FINALLY_STMT_CHECK (NODE), 0))
+ /* The finally block of a try/finally construct. */
+ #define TRY_FINALLY_FINALLY(NODE) \
+ (TREE_OPERAND (TRY_FINALLY_STMT_CHECK (NODE), 1))
+
/* The line-number at which a statement began. But if
STMT_LINENO_FOR_FN_P does holds, then this macro gives the
line number for the end of the current function instead. */
*************** extern void genrtl_asm_stmt
*** 1146,1151 ****
--- 1154,1160 ----
tree, tree,
tree, int));
extern void genrtl_decl_cleanup PARAMS ((tree));
+ extern void genrtl_try_finally_stmt PARAMS ((tree));
extern int stmts_are_full_exprs_p PARAMS ((void));
extern int anon_aggr_type_p PARAMS ((tree));

Index: c-parse.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-parse.in,v
retrieving revision 1.152
diff -c -p -r1.152 c-parse.in
*** c-parse.in 15 Aug 2002 21:16:23 -0000 1.152
--- c-parse.in 6 Nov 2002 06:58:22 -0000
*************** do { \
*** 150,155 ****
--- 150,156 ----
%token ATTRIBUTE EXTENSION LABEL
%token REALPART IMAGPART VA_ARG CHOOSE_EXPR TYPES_COMPATIBLE_P
%token PTR_VALUE PTR_BASE PTR_EXTENT
+ %token TRY FINALLY

/* function name can be a string const or a var decl. */
%token STRING_FUNC_NAME VAR_FUNC_NAME
*************** do { \
*** 212,218 ****
%type <ttype> any_word extension

%type <ttype> compstmt compstmt_start compstmt_nostart compstmt_primary_start
! %type <ttype> do_stmt_start poplevel stmt label

%type <ttype> c99_block_start c99_block_end
%type <ttype> declarator
--- 213,219 ----
%type <ttype> any_word extension

%type <ttype> compstmt compstmt_start compstmt_nostart compstmt_primary_start
! %type <ttype> do_stmt_start poplevel stmt label try_finally_begin

%type <ttype> c99_block_start c99_block_end
%type <ttype> declarator
*************** for_init_stmt:
*** 2363,2372 ****
--- 2364,2386 ----
{ check_for_loop_decls (); }
;

+ try_finally_begin: TRY
+ {
+ $$ = add_stmt (build_stmt (TRY_FINALLY_STMT, NULL_TREE));
+ }
+ ;
+
/* Parse a single real statement, not including any labels. */
stmt:
compstmt
{ stmt_count++; $$ = $1; }
+ | try_finally_begin compstmt
+ { RECHAIN_STMTS ($1, TRY_FINALLY_TRY ($1)); }
+ FINALLY compstmt
+ { stmt_count++;
+ RECHAIN_STMTS ($1, TRY_FINALLY_FINALLY ($1));
+ $$ = $1;
+ }
| expr ';'
{ stmt_count++;
$$ = c_expand_expr_stmt ($1); }
*************** static const struct resword reswords[] =
*** 3322,3327 ****
--- 3336,3342 ----
{ "__const", RID_CONST, 0 },
{ "__const__", RID_CONST, 0 },
{ "__extension__", RID_EXTENSION, 0 },
+ { "__finally", RID_FINALLY, 0 },
{ "__func__", RID_C99_FUNCTION_NAME, 0 },
{ "__imag", RID_IMAGPART, 0 },
{ "__imag__", RID_IMAGPART, 0 },
*************** static const struct resword reswords[] =
*** 3341,3346 ****
--- 3356,3362 ----
{ "__signed", RID_SIGNED, 0 },
{ "__signed__", RID_SIGNED, 0 },
{ "__thread", RID_THREAD, 0 },
+ { "__try", RID_TRY, 0 },
{ "__typeof", RID_TYPEOF, 0 },
{ "__typeof__", RID_TYPEOF, 0 },
{ "__unbounded", RID_UNBOUNDED, 0 },
*************** static const short rid_to_yy[RID_MAX] =
*** 3493,3498 ****
--- 3509,3516 ----
/* RID_CHOOSE_EXPR */ CHOOSE_EXPR,
/* RID_TYPES_COMPATIBLE_P */ TYPES_COMPATIBLE_P,

+ /* RID_FINALLY */ FINALLY,
+
/* RID_FUNCTION_NAME */ STRING_FUNC_NAME,
/* RID_PRETTY_FUNCTION_NAME */ STRING_FUNC_NAME,
/* RID_C99_FUNCTION_NAME */ VAR_FUNC_NAME,
*************** static const short rid_to_yy[RID_MAX] =
*** 3515,3521 ****
/* RID_THIS */ 0,
/* RID_THROW */ 0,
/* RID_TRUE */ 0,
! /* RID_TRY */ 0,
/* RID_TYPENAME */ 0,
/* RID_TYPEID */ 0,
/* RID_USING */ 0,
--- 3533,3539 ----
/* RID_THIS */ 0,
/* RID_THROW */ 0,
/* RID_TRUE */ 0,
! /* RID_TRY */ TRY,
/* RID_TYPENAME */ 0,
/* RID_TYPEID */ 0,
/* RID_USING */ 0,
Index: testsuite/gcc.c-torture/execute/try-finally-1.c
===================================================================
RCS file: testsuite/gcc.c-torture/execute/try-finally-1.c
diff -N testsuite/gcc.c-torture/execute/try-finally-1.c
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- testsuite/gcc.c-torture/execute/try-finally-1.c 6 Nov 2002 06:58:22 -0000
***************
*** 0 ****
--- 1,43 ----
+ int caught;
+
+ void doit (void)
+ {
+ __try {
+ return;
+ } __finally {
+ caught = 1;
+ }
+ }
+
+ void doit_again (void)
+ {
+ caught = 0;
+
+ while (1) {
+ __try {
+ __try {
+ return;
+ } __finally {
+ caught = 1;
+ }
+ } __finally {
+ if (!caught)
+ abort ();
+ caught = 2;
+ }
+ }
+ abort ();
+ }
+
+ int main ()
+ {
+ doit ();
+ if (caught != 1)
+ abort ();
+
+ doit_again ();
+ if (caught != 2)
+ abort ();
+
+ return 0;
+ }
Index: testsuite/gcc.c-torture/execute/try-finally-2.c
===================================================================
RCS file: testsuite/gcc.c-torture/execute/try-finally-2.c
diff -N testsuite/gcc.c-torture/execute/try-finally-2.c
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- testsuite/gcc.c-torture/execute/try-finally-2.c 6 Nov 2002 06:58:22 -0000
***************
*** 0 ****
--- 1,26 ----
+ int caught;
+
+ void doit (int i)
+ {
+ caught = 0;
+ __try {
+ if (!i)
+ return;
+ } __finally {
+ caught = 1;
+ }
+ caught = 2;
+ }
+
+ int main ()
+ {
+ doit (0);
+ if (caught != 1)
+ abort ();
+
+ doit (1);
+ if (caught != 2)
+ abort ();
+
+ return 0;
+ }
Index: cp/lex.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/lex.c,v
retrieving revision 1.289.4.3
diff -c -p -r1.289.4.3 lex.c
*** cp/lex.c 21 Oct 2002 17:52:56 -0000 1.289.4.3
--- cp/lex.c 6 Nov 2002 06:58:23 -0000
*************** static const struct resword reswords[] =
*** 345,350 ****
--- 345,351 ----
{ "__const__", RID_CONST, 0 },
{ "__extension__", RID_EXTENSION, 0 },
{ "__func__", RID_C99_FUNCTION_NAME, 0 },
+ { "__finally", RID_FINALLY, 0 },
{ "__imag", RID_IMAGPART, 0 },
{ "__imag__", RID_IMAGPART, 0 },
{ "__inline", RID_INLINE, 0 },
*************** static const struct resword reswords[] =
*** 358,363 ****
--- 359,365 ----
{ "__signed", RID_SIGNED, 0 },
{ "__signed__", RID_SIGNED, 0 },
{ "__thread", RID_THREAD, 0 },
+ { "__try", RID_TRY, 0 },
{ "__typeof", RID_TYPEOF, 0 },
{ "__typeof__", RID_TYPEOF, 0 },
{ "__volatile", RID_VOLATILE, 0 },
*************** const short rid_to_yy[RID_MAX] =
*** 507,512 ****
--- 509,515 ----
/* RID_PTRVALUE */ 0,
/* RID_CHOOSE_EXPR */ 0,
/* RID_TYPES_COMPATIBLE_P */ 0,
+ /* RID_FINALLY */ FINALLY,

/* RID_FUNCTION_NAME */ VAR_FUNC_NAME,
/* RID_PRETTY_FUNCTION_NAME */ VAR_FUNC_NAME,
Index: cp/cp-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-tree.h,v
retrieving revision 1.744.4.7
diff -c -p -r1.744.4.7 cp-tree.h
*** cp/cp-tree.h 21 Oct 2002 17:52:51 -0000 1.744.4.7
--- cp/cp-tree.h 6 Nov 2002 06:58:27 -0000
*************** extern void finish_handler_parms
*** 4106,4111 ****
--- 4106,4113 ----
extern void begin_catch_block PARAMS ((tree));
extern void finish_handler PARAMS ((tree));
extern void finish_cleanup PARAMS ((tree, tree));
+ extern void finish_try_finally_try PARAMS ((tree));
+ extern void finish_try_finally_finally PARAMS ((tree));
extern tree begin_compound_stmt PARAMS ((int));
extern tree finish_compound_stmt PARAMS ((int, tree));
extern tree finish_asm_stmt PARAMS ((tree, tree, tree, tree, tree));
Index: cp/parse.y
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/parse.y,v
retrieving revision 1.279.4.3
diff -c -p -r1.279.4.3 parse.y
*** cp/parse.y 21 Oct 2002 17:52:57 -0000 1.279.4.3
--- cp/parse.y 6 Nov 2002 06:58:29 -0000
*************** check_class_key (key, aggr)
*** 323,328 ****
--- 323,329 ----
%token SIGOF
%token ATTRIBUTE EXTENSION LABEL
%token REALPART IMAGPART VA_ARG
+ %token FINALLY

/* the reserved words... C++ extensions */
%token <ttype> AGGR
*************** check_class_key (key, aggr)
*** 463,468 ****
--- 464,470 ----
%type <ttype> identifier_defn IDENTIFIER_DEFN TYPENAME_DEFN PTYPENAME_DEFN
%type <ttype> handler_args
%type <ttype> self_template_type finish_template_type_
+ %type <itype> handler_seq finally_stmt

%token NSNAME
%type <ttype> NSNAME
*************** function_try_block:
*** 3552,3575 ****

try_block:
TRY
! { $<ttype>$ = begin_try_block (); }
compstmt
{ finish_try_block ($<ttype>2); }
handler_seq
! { finish_handler_sequence ($<ttype>2); }
;

handler_seq:
handler
| handler_seq handler
| /* empty */
{ /* Generate a fake handler block to avoid later aborts. */
tree fake_handler = begin_handler ();
finish_handler_parms (NULL_TREE, fake_handler);
finish_handler (fake_handler);
! $<ttype>$ = fake_handler;
!
! error ("must have at least one catch per try block");
}
;

--- 3554,3597 ----

try_block:
TRY
! {
! $<ttype>1 = begin_try_finally ();
! $<ttype>$ = begin_try_block ();
! }
compstmt
{ finish_try_block ($<ttype>2); }
handler_seq
! {
! finish_handler_sequence ($<ttype>2);
! finish_try_finally_try ($<ttype>1);
! }
! finally_stmt
! {
! finish_try_finally_finally ($<ttype>1);
!
! if (!$5 && !$7)
! error ("must have at least one catch per try block");
! }
! ;
!
! finally_stmt:
! FINALLY compstmt
! { $$ = 1; }
! | /* empty */
! { $$ = 0; }
;

handler_seq:
handler
+ { $$ = 1; }
| handler_seq handler
+ { $$ = 1; }
| /* empty */
{ /* Generate a fake handler block to avoid later aborts. */
tree fake_handler = begin_handler ();
finish_handler_parms (NULL_TREE, fake_handler);
finish_handler (fake_handler);
! $$ = 0;
}
;

Index: cp/pt.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/pt.c,v
retrieving revision 1.610.4.4
diff -c -p -r1.610.4.4 pt.c
*** cp/pt.c 15 Oct 2002 01:33:31 -0000 1.610.4.4
--- cp/pt.c 6 Nov 2002 06:58:35 -0000
*************** tsubst_expr (t, args, complain, in_decl)
*** 7657,7662 ****
--- 7657,7671 ----
finish_handler_sequence (stmt);
}
break;
+
+ case TRY_FINALLY_STMT:
+ prep_stmt (t);
+ stmt = begin_try_finally ();
+ tsubst_expr (TRY_FINALLY_TRY (t), args, complain, in_decl);
+ finish_try_finally_try (stmt);
+ tsubst_expr (TRY_FINALLY_FINALLY (t), args, complain, in_decl);
+ finish_try_finally_finally (stmt);
+ break;

case HANDLER:
{
Index: cp/semantics.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/semantics.c,v
retrieving revision 1.274.4.5
diff -c -p -r1.274.4.5 semantics.c
*** cp/semantics.c 21 Oct 2002 17:52:57 -0000 1.274.4.5
--- cp/semantics.c 6 Nov 2002 06:58:37 -0000
*************** static void
*** 598,603 ****
--- 598,615 ----
genrtl_try_block (t)
tree t;
{
+ if (!TRY_HANDLERS (t))
+ {
+ /* For a C++ try-block, we create both a TRY_FINALLY_STMT and a
+ TRY_BLOCK, even if one of them is unnecessary. We have to do this
+ because they need to be added before the contents of the
+ try-block. Hopefully this will be fixed with the new parser.
+
+ If there are no handlers, ignore the TRY_BLOCK. */
+ expand_stmt (TRY_STMTS (t));
+ return;
+ }
+
if (CLEANUP_P (t))
{
expand_eh_region_start ();
*************** finish_handler (handler)
*** 805,810 ****
--- 817,854 ----
expand_end_catch_block ();
do_poplevel ();
RECHAIN_STMTS (handler, HANDLER_BODY (handler));
+ }
+
+ /* Begin a try/finally construct. The parser calls this immediately before
+ begin_try_block, as one 'try' could start either a normal C++ try-block,
+ a try/finally construct, or both. */
+
+ tree
+ begin_try_finally ()
+ {
+ tree r = build_stmt (TRY_FINALLY_STMT, NULL_TREE, NULL_TREE);
+ add_stmt (r);
+ return r;
+ }
+
+ /* Finish the body of a try/finally construct. This usually consists of a
+ TRY_BLOCK. */
+
+ void
+ finish_try_finally_try (try_finally)
+ tree try_finally;
+ {
+ RECHAIN_STMTS (try_finally, TRY_FINALLY_TRY (try_finally));
+ }
+
+ /* Finish the finally block of a try/finally construct. This will be
+ NULL_TREE in the case of a normal C++ try-block. */
+
+ void
+ finish_try_finally_finally (try_finally)
+ tree try_finally;
+ {
+ RECHAIN_STMTS (try_finally, TRY_FINALLY_FINALLY (try_finally));
}

/* Begin a compound-statement. If HAS_NO_SCOPE is nonzero, the
Index: doc/extend.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/extend.texi,v
retrieving revision 1.95.4.5
diff -c -p -r1.95.4.5 extend.texi
*** doc/extend.texi 21 Oct 2002 17:52:59 -0000 1.95.4.5
--- doc/extend.texi 6 Nov 2002 06:58:43 -0000
*************** extensions, accepted by GCC in C89 mode
*** 473,478 ****
--- 473,479 ----
* Target Builtins:: Built-in functions specific to particular targets.
* Pragmas:: Pragmas accepted by GCC.
* Unnamed Fields:: Unnamed struct/union fields within structs/unions.
+ * Try-finally:: Try/finally construct.
* Thread-Local:: Per-thread variables.
@end menu

*************** struct @{
*** 6506,6511 ****
--- 6507,6746 ----
It is ambiguous which @code{a} is being referred to with @samp{foo.a}.
Such constructs are not supported and must be avoided. In the future,
such constructs may be detected and treated as compilation errors.
+
+ @node Try-finally
+ @section Try/finally exceptions
+ @cindex Try/finally exceptions
+ @cindex try-finally
+ @cindex __try
+ @cindex __finally
+
+ The @code{__try/__finally} statement consists of a @code{__try} block
+ containing one or statements, and a @code{__finally} block contatining
+ one or more statements. The @code{__finally} block will execute after
+ the @code{__try} block but before the code following the
+ @code{__try/__finally} construct, even if the @code{__try} block exits
+ the current function.
+
+ For example:
+
+ @example
+ __try @{
+ do_something();
+ return;
+ @} __finally @{
+ fin = 1;
+ @}
+ @end example
+
+ In the above example, @code{fin} will get set to 1 before the function
+ returns to its caller.
+
+ You can nest more than one @code{__try/__finally} construct.
+
+ Since C++ already has a @code{try} keyword, both @code{try} and
+ @code{__try} variations are allowed. Also, the C++ implementation
+ allows using @code{__finally} after a series of @code{try/catch}
+ blocks. The @code{__finally} code will be executed after the
+ applicable @code{catch}. For example:
+
+ @example
+ try @{
+ do_something();
+ @} catch (blah) @{
+ x = 0;
+ @} catch (...) @{
+ x = 1;
+ @} __finally @{
+ y = 2;
+ @}
+ @end example
+
+ In the example above, @code{y} will be set after the set to @code{x}
+ in the catch clause.
+
+ It is possible to have a C @code{__try/__finally} construct call C++
+ code that throws an exception. As part of the unwind process, the C
+ @code{__finally} block will be called before control is returned to
+ the C's calling routine.
+
+ Exiting out of a finally block through a @code{return} or @code{goto}
+ has undefined behaviour.
+
+ GCC's @code{__try/__finally} implementation is analogous to the
+ corresponding construct in Java.
+
+ @menu
+ * C99 Try-Finally Edits::
+ * C++98 Try-Finally Edits::
+ @end menu
+
+ @node C99 Try-Finally Edits
+ @subsection ISO/IEC 9899:1999 Edits for Try-Finally
+
+ The following are a set of changes to ISO/IEC 9899:1999 (aka C99)
+ that document the exact semantics of the language extension.
+
+ @itemize @bullet
+ @item
+ @cite{6.4.1 Keywords}
+
+ Add @code{__try}.
+ Add @code{__finally}.
+
+ @item
+ @cite{6.8.2b The try/finally statement}
+
+ New section.
+
+ @quotation
+ Syntax:
+ @example
+ try-finally-statement:
+ @b{__try}
+ @i{compound-statement}
+ @b{__finally}
+ @i{compound-statement}
+
+ @end example
+ @end quotation
+
+ The @code{__try} block is executed. After the __try block exits,
+ control is transferred to the @code{__finally} block regardless of how
+ the __try block exits (with the exception of program termination or
+ longjump exits out of the try block).
+
+ The __finally block is guaranteed to execute after the __try block,
+ provided the program does not terminate while inside such block.
+ After the __finally block executes, the flow of the program will
+ continue where the __try block was meant to transfer control to, had
+ there been no __finally block.
+
+ A @code{goto}, @code{break}, @code{return}, or @code{continue}
+ statement can be used to transfer control out of a try block but not
+ into one. Also, a longjump may not be used to transfer control into a
+ try or finally block. If done, the behavior is undefined.
+
+ statement can be used to transfer control out of a try block but not
+ into one. If such a statement is used to transfer control into
+
+ This construct is meant to provide a mechanism through which a series
+ of cleanups can be executed upon exit from a block (the __try block).
+ If somewhere inside the __try block an exception occurs and the
+ corresponding exception handler lies in the frame above the __finally
+ block, the __finally block will execute before control reaches the
+ exception handler above.
+
+ This mechanism is by no ways a complete exception handling system for
+ C.
+
+ @end itemize
+
+ @node C++98 Try-Finally Edits
+ @subsection ISO/IEC 14882:1998 Edits for Try-Finally
+
+ @itemize @bullet
+ @item
+ @cite{2.11 Keywords: Table 3}
+
+ Add @code{__finally}.
+
+ @item
+ @cite{15 Exception handling}
+
+ Rename the @code{handler-seq} production by the following:
+
+ @example
+ handler-seq:
+ handler-seq-catch finally-block-opt
+ finally-block
+
+ handler-seq-catch:
+ handler handler-seq-catch
+
+ finally-block-opt:
+ finally-block
+
+ finally-block:
+ @b{__finally} compound-statement
+ @end example
+
+ Add new text at the end of section 2.
+
+ @quotation
+ When control is transferred from a try block to a destination (say
+ destination @b{A}), be it through a @code{continue}, @code{break},
+ @code{return}, or @code{goto}, control is first redirected to the
+ finally block. When the finally block finishes, control is then
+ transferred to destination @b{A}.
+ @end quotation
+
+ Add to code below to the example in section 3.
+
+ @example
+ __finally
+ @{
+ // code to be executed after the try block and
+ // applicable catch block is executed.
+ @}
+ @end example
+
+ Add new example at the end of section 3.
+
+ @quotation
+ The following code samples are equivalent.
+ @end quotation
+
+ @example
+ A)
+ __try @{
+ A;
+ @} catch (...) @{
+ B;
+ @} __finally @{
+ C;
+ @}
+
+ B)
+ __try @{
+ __try @{
+ A;
+ @} catch (...) @{
+ B;
+ @}
+ @} __finally @{
+ C;
+ @}
+ @end example
+
+ @item
+ @cite{15.2 Constructors and destructors}
+
+ Add new text to section 3.
+
+ @quotation
+ If code within a @i{try} block (or within code called from a try
+ block) causes an exception that is not caught by the subsequent
+ @i{catch} blocks, the code in @i{__finally} executes on the path up to
+ the handler that will catch the exception (if one is available).
+ @end quotation
+
+ Add new text to section 7.
+
+ @quotation
+ If the try block has a corresponding __finally block, the code in the
+ __finally block will execute before control reaches the dynamically
+ surrounding try block.
+ @end quotation
+
+ Add new section 2.
+
+ @quotation
+ If the __finally block contains a jump outside stated block, the
+ program is ill-formed and the behavior is undefined.
+ @end quotation
+
+ @end itemize

@node Thread-Local
@section Thread-Local Storage
Joseph S. Myers
2002-11-06 08:44:01 UTC
Permalink
Post by Aldy Hernandez
+ the current function.
There should be a corresponding mention of longjmp to that in the formal
edits.
Post by Aldy Hernandez
+ has undefined behaviour.
^^^^ ior
Post by Aldy Hernandez
+ the __try block exits (with the exception of program termination or
+ longjump exits out of the try block).
+ The __finally block is guaranteed to execute after the __try block,
+ provided the program does not terminate while inside such block.
+ After the __finally block executes, the flow of the program will
+ continue where the __try block was meant to transfer control to, had
+ there been no __finally block.
I believe the intention is that variables defined outside the try block
are live, but variables definied within it are not, while the finally
block is executing, regardless of how entered? Also, if the try block
terminates by falling off the end, the intended point of transfer of
control is after the end of the finally block?
Post by Aldy Hernandez
+ statement can be used to transfer control out of a try block but not
+ into one. Also, a longjump may not be used to transfer control into a
+ try or finally block. If done, the behavior is undefined.
@code{longjmp}. May it be used to jump out of such a block? All the
rules that jumps into try blocks are not permitted (and you'll need to add
switch to the statements that might jump into a block) should be listed as
constraints (with corresponding testcases for the errors from the
constraint violations); although violating them at runtime with GNU C
computed gotos would be undefined. May a finally block be jumped into
(and if so, where does execution pass after the finally block is
finished)? May a finally block be jumped out of?
Post by Aldy Hernandez
+
+ statement can be used to transfer control out of a try block but not
+ into one. If such a statement is used to transfer control into
+
Stray partial paragraph.
--
Joseph S. Myers
***@cam.ac.uk
Jakub Jelinek
2002-11-06 08:51:59 UTC
Permalink
Post by Joseph S. Myers
Post by Aldy Hernandez
+ The __finally block is guaranteed to execute after the __try block,
+ provided the program does not terminate while inside such block.
+ After the __finally block executes, the flow of the program will
+ continue where the __try block was meant to transfer control to, had
+ there been no __finally block.
I believe the intention is that variables defined outside the try block
are live, but variables definied within it are not, while the finally
block is executing, regardless of how entered? Also, if the try block
terminates by falling off the end, the intended point of transfer of
control is after the end of the finally block?
If __try block was exited by return, then the function returns after
finishing the __finally block (unless inside other __try block), if
__try is exited normally, then execution continues after the end
of the finally block, if __try block is exited through exception being
thrown (or forced unwinding), then after __finally block exits the
exception is resumed or forced unwinding continues.

Jakub
Michael Matz
2002-11-06 11:02:42 UTC
Permalink
Hi,
Post by Jakub Jelinek
If __try block was exited by return, then the function returns after
finishing the __finally block (unless inside other __try block), if
__try is exited normally, then execution continues after the end
of the finally block,
This would be fairly weird semantic. If the finally is formulated as a
destructor of an anonymous object (like Richard suggested) then it should
be run after the __try block exits, in _whichever_ way it does so. This
also makes much more sense to me. I wouldn't like to have to write:

__try {
if (bla())
return;
}
__finally {
cleanup();
}
cleanup();

just because the __finally block isn't executed sometimes. It also makes
the rule quite easy. The finally is executed after the try block, no
matter what.


Ciao,
Michael.
Jakub Jelinek
2002-11-06 11:11:02 UTC
Permalink
Post by Michael Matz
Hi,
Post by Jakub Jelinek
If __try block was exited by return, then the function returns after
finishing the __finally block (unless inside other __try block), if
__try is exited normally, then execution continues after the end
of the finally block,
This would be fairly weird semantic. If the finally is formulated as a
destructor of an anonymous object (like Richard suggested) then it should
be run after the __try block exits, in _whichever_ way it does so. This
__try {
if (bla())
return;
}
__finally {
cleanup();
}
cleanup();
just because the __finally block isn't executed sometimes. It also makes
the rule quite easy. The finally is executed after the try block, no
matter what.
The __finally block is executed in all cases, the question was what is
executed AFTER the __finally block.

__try {
if (bla())
return;
}
__finally {
code1();
}
code2();

will do:
1) if bla() returns non-zero, code1() is called and then the function returns.
2) if bla() returns zero, code1() is called, then code2().
3) if exception is thrown from within bla or forced unwinding, then code1()
is executed and the stack unwinding continues

Jakub
Michael Matz
2002-11-06 12:16:10 UTC
Permalink
Hi,
Post by Jakub Jelinek
The __finally block is executed in all cases, the question was what is
executed AFTER the __finally block.
Ahh, that was unclear (to me) in your message. Ok, then we all agree
again ;-)


Ciao,
Michael.
Aldy Hernandez
2002-11-06 18:32:15 UTC
Permalink
Fixed.
Post by Joseph S. Myers
Post by Aldy Hernandez
+ the current function.
There should be a corresponding mention of longjmp to that in the formal
edits.
I don't understand what you want here. Could you elaborate please?
Post by Joseph S. Myers
Post by Aldy Hernandez
+ has undefined behaviour.
^^^^ ior
fixed.
Post by Joseph S. Myers
Post by Aldy Hernandez
+ the __try block exits (with the exception of program termination or
+ longjump exits out of the try block).
Fixed. Using "try block" and "finally block" all throughout now.
Post by Joseph S. Myers
Post by Aldy Hernandez
+ The __finally block is guaranteed to execute after the __try block,
+ provided the program does not terminate while inside such block.
+ After the __finally block executes, the flow of the program will
+ continue where the __try block was meant to transfer control to, had
+ there been no __finally block.
I believe the intention is that variables defined outside the try block
are live, but variables definied within it are not, while the finally
block is executing, regardless of how entered? Also, if the try block
terminates by falling off the end, the intended point of transfer of
control is after the end of the finally block?
Added Jakub's description.
Post by Joseph S. Myers
Post by Aldy Hernandez
+ statement can be used to transfer control out of a try block but not
+ into one. Also, a longjump may not be used to transfer control into a
+ try or finally block. If done, the behavior is undefined.
@code{longjmp}. May it be used to jump out of such a block? All the
rules that jumps into try blocks are not permitted (and you'll need to add
switch to the statements that might jump into a block) should be listed as
constraints (with corresponding testcases for the errors from the
constraint violations); although violating them at runtime with GNU C
computed gotos would be undefined. May a finally block be jumped into
(and if so, where does execution pass after the finally block is
finished)? May a finally block be jumped out of?
Richard could you elaborate here? It was my understanding that
longjmp may not be used to jump into a try/finally block or out of
one? What are the actual restrictions?
Post by Joseph S. Myers
Post by Aldy Hernandez
+ statement can be used to transfer control out of a try block but not
+ into one. If such a statement is used to transfer control into
+
Stray partial paragraph.
Fixed.

Joseph, how does this one look?

Aldy

Index: doc/extend.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/extend.texi,v
retrieving revision 1.95.4.5
diff -c -p -r1.95.4.5 extend.texi
*** doc/extend.texi 21 Oct 2002 17:52:59 -0000 1.95.4.5
--- doc/extend.texi 6 Nov 2002 18:26:48 -0000
*************** extensions, accepted by GCC in C89 mode
*** 473,478 ****
--- 473,479 ----
* Target Builtins:: Built-in functions specific to particular targets.
* Pragmas:: Pragmas accepted by GCC.
* Unnamed Fields:: Unnamed struct/union fields within structs/unions.
+ * Try-finally:: Try/finally construct.
* Thread-Local:: Per-thread variables.
@end menu

*************** struct @{
*** 6506,6511 ****
--- 6507,6747 ----
It is ambiguous which @code{a} is being referred to with @samp{foo.a}.
Such constructs are not supported and must be avoided. In the future,
such constructs may be detected and treated as compilation errors.
+
+ @node Try-finally
+ @section Try/finally exceptions
+ @cindex Try/finally exceptions
+ @cindex try-finally
+ @cindex __try
+ @cindex __finally
+
+ The @code{__try}/@code{__finally} statement consists of a try block
+ containing one or more statements, and a finally block contatining one
+ or more statements. The finally block will execute after the try
+ block but before the code following the @code{__try}/@code{__finally}
+ construct, even if the try block exits the current function.
+
+ For example:
+
+ @example
+ __try @{
+ do_something();
+ return;
+ @} __finally @{
+ fin = 1;
+ @}
+ @end example
+
+ In the above example, @code{fin} will get set to 1 before the function
+ returns to its caller.
+
+ You can nest more than one @code{__try}/@code{__finally} construct.
+
+ Since C++ already has a @code{try} keyword, both @code{try} and
+ @code{__try} variations are allowed. Also, the C++ implementation
+ allows using @code{__finally} after a series of
+ @code{try}/@code{catch} blocks. The finally block will be executed
+ after the applicable @code{catch}. For example:
+
+ @example
+ try @{
+ do_something();
+ @} catch (blah) @{
+ x = 0;
+ @} catch (...) @{
+ x = 1;
+ @} __finally @{
+ y = 2;
+ @}
+ @end example
+
+ In the example above, @code{y} will be set after the set to @code{x}
+ in the catch clause.
+
+ It is possible to have a C @code{__try}/@code{__finally} construct
+ call C++ code that throws an exception. As part of the unwind
+ process, the C finally block will be called before control is returned
+ to the C's calling routine.
+
+ Exiting out of a finally block through a @code{return} or @code{goto}
+ has undefined behavior.
+
+ GCC's @code{__try}/@code{__finally} implementation is analogous to the
+ corresponding construct in Java.
+
+ @menu
+ * C99 Try-Finally Edits::
+ * C++98 Try-Finally Edits::
+ @end menu
+
+ @node C99 Try-Finally Edits
+ @subsection ISO/IEC 9899:1999 Edits for Try-Finally
+
+ The following are a set of changes to ISO/IEC 9899:1999 (aka C99)
+ that document the exact semantics of the language extension.
+
+ @itemize @bullet
+ @item
+ @cite{6.4.1 Keywords}
+
+ Add @code{__try}.
+ Add @code{__finally}.
+
+ @item
+ @cite{6.8.2b The try/finally statement}
+
+ New section.
+
+ @quotation
+ Syntax:
+ @example
+ try-finally-statement:
+ @b{__try}
+ @i{compound-statement}
+ @b{__finally}
+ @i{compound-statement}
+
+ @end example
+ @end quotation
+
+ The try block is executed. After the __try block exits, control is
+ transferred to the finally block regardless of how the try block exits
+ (with the exception of program termination or @code{longjmp} exits out
+ of the try block).
+
+ If a try block was exited by @code{return}, then the function returns
+ after finishing the finally block (unless inside other try block). If
+ a try block is exited normally, then execution continues after the end
+ of the finally block. If a try block is exited through an exception
+ being thrown (or forced unwinding), then after finally block exits,
+ the exception is resumed or forced unwinding continues.
+
+ A @code{goto}, @code{break}, @code{return}, or @code{continue}
+ statement can be used to transfer control out of a try block but not
+ into one. Also, a @code{longjmp} may not be used to transfer control
+ into a try or finally block. If done, the behavior is undefined.
+
+ This construct is meant to provide a mechanism through which a series
+ of cleanups can be executed upon exit from a block (the try block).
+ If somewhere inside the try block an exception occurs and the
+ corresponding exception handler lies in the frame above the finally
+ block, the finally block will execute before control reaches the
+ exception handler above.
+
+ This mechanism is by no ways a complete exception handling system for
+ C.
+
+ @end itemize
+
+ @node C++98 Try-Finally Edits
+ @subsection ISO/IEC 14882:1998 Edits for Try-Finally
+
+ @itemize @bullet
+ @item
+ @cite{2.11 Keywords: Table 3}
+
+ Add @code{__finally}.
+
+ @item
+ @cite{15 Exception handling}
+
+ Rename the @code{handler-seq} production by the following:
+
+ @example
+ handler-seq:
+ handler-seq-catch finally-block-opt
+ finally-block
+
+ handler-seq-catch:
+ handler handler-seq-catch
+
+ finally-block-opt:
+ finally-block
+
+ finally-block:
+ @b{__finally} compound-statement
+ @end example
+
+ Add new text at the end of section 2.
+
+ @quotation
+ When control is transferred from a try block to a destination (say
+ destination @b{A}), be it through a @code{continue}, @code{break},
+ @code{return}, or @code{goto}, control is first redirected to the
+ finally block. When the finally block finishes, control is then
+ transferred to destination @b{A}.
+ @end quotation
+
+ Add to code below to the example in section 3.
+
+ @example
+ __finally
+ @{
+ // code to be executed after the try block and
+ // applicable catch block is executed.
+ @}
+ @end example
+
+ Add new example at the end of section 3.
+
+ @quotation
+ The following code samples are equivalent.
+ @end quotation
+
+ @example
+ A)
+ __try @{
+ A;
+ @} catch (...) @{
+ B;
+ @} __finally @{
+ C;
+ @}
+
+ B)
+ __try @{
+ __try @{
+ A;
+ @} catch (...) @{
+ B;
+ @}
+ @} __finally @{
+ C;
+ @}
+ @end example
+
+ @item
+ @cite{15.2 Constructors and destructors}
+
+ Add new text to section 3.
+
+ @quotation
+ If code within a try block (or within code called from a try
+ block) causes an exception that is not caught by the subsequent
+ catch blocks, the code in __finally executes on the path up to
+ the handler that will catch the exception (if one is available).
+
+ Even though the code in the finally block is run on exit from the try
+ block, exiting a try block with a corresponding finally block via
+ longjmp or computed goto has undefined behavior.
+ @end quotation
+
+ Add new text to section 7.
+
+ @quotation
+ If the try block has a corresponding finally block, the code in the
+ finally block will execute before control reaches the dynamically
+ surrounding try block.
+ @end quotation
+
+ Add new section 2.
+
+ @quotation
+ If the finally block contains a jump outside stated block, the
+ program is ill-formed and the behavior is undefined.
+ @end quotation
+
+ @end itemize

@node Thread-Local
@section Thread-Local Storage
Richard Henderson
2002-11-06 18:53:33 UTC
Permalink
Post by Aldy Hernandez
Post by Joseph S. Myers
Post by Aldy Hernandez
+ statement can be used to transfer control out of a try block but not
+ into one. Also, a longjump may not be used to transfer control into a
+ try or finally block. If done, the behavior is undefined.
@code{longjmp}. May it be used to jump out of such a block? All the
rules that jumps into try blocks are not permitted (and you'll need to add
switch to the statements that might jump into a block) should be listed as
constraints (with corresponding testcases for the errors from the
constraint violations); although violating them at runtime with GNU C
computed gotos would be undefined. May a finally block be jumped into
(and if so, where does execution pass after the finally block is
finished)? May a finally block be jumped out of?
Richard could you elaborate here? It was my understanding that
longjmp may not be used to jump into a try/finally block or out of
one? What are the actual restrictions?
Well, longjmp pretty much bypasses all the logic one would care to
ask about. I would say that such would have to be undefined. There
is mention in the IA-64 psABI docs of a separate longjmp_unwind
function that _is_ EH aware, but that's the exception not the rule.
So leaving a try block via longjmp is going to have to be undefined.

I don't see how one could enter a try block via longjmp.

Something I just thought of here is GCC's computed goto extension.
I don't know if the middle-end cleanup code handles this properly.
We should have a test in the testsuite for this.


r~
Aldy Hernandez
2002-11-06 18:59:38 UTC
Permalink
Post by Richard Henderson
I don't see how one could enter a try block via longjmp.
So, you can't enter or leave a try/finally block with longjmp.
Post by Richard Henderson
Something I just thought of here is GCC's computed goto extension.
I don't know if the middle-end cleanup code handles this properly.
We should have a test in the testsuite for this.
Jason, in his blurb, mentioned that leaving try/f via computed gotos
would be undefined.

Aldy
Joseph S. Myers
2002-11-06 19:24:27 UTC
Permalink
Post by Richard Henderson
I don't see how one could enter a try block via longjmp.
setjmp within the block. longjmp after it's finished executing but before
the containing function has returned (whether within the finally block or
after that too has finished).
--
Joseph S. Myers
***@cam.ac.uk
Richard Henderson
2002-11-06 21:49:12 UTC
Permalink
Post by Joseph S. Myers
setjmp within the block. longjmp after it's finished executing but before
the containing function has returned (whether within the finally block or
after that too has finished).
Ah, yes.

While I suspect that in the current implementation this would work
as expected, I certainly don't want to promise anything of the sort
in the specification.


r~
Fergus Henderson
2002-11-07 01:45:08 UTC
Permalink
Post by Richard Henderson
Post by Aldy Hernandez
Richard could you elaborate here? It was my understanding that
longjmp may not be used to jump into a try/finally block or out of
one? What are the actual restrictions?
Well, longjmp pretty much bypasses all the logic one would care to
ask about. I would say that such would have to be undefined.
If longjumping out of a try/finally block results in undefined
behaviour, then either

(1) programmers should avoid using longjmp()

(2) programmers should avoid using try/finally

(3) programmers must take great care when using longjmp()
or try/finally to ensure that the two are not used together in
a way that will result in undefined behaviour. This requires
global (or at least non-local) analysis of the program.
The analysis must be redone whenever any code is added which
uses try/finally or longjmp, or even just when code which
might use those constructs is rearranged. Furthermore,
there are no tools which will help in this analysis, no
support for run-time checking of this property, and bugs
caused in this way may only show up on some platforms,
so on some platforms testing will never detect the bug.
OUCH!

Which is it?

In C++ the answer is (1). This makes sense, because C++ has exceptions,
which provide a replacement for longjmp(). But it doesn't work for GNU C
with try/finally.

(3) is bad since it makes programs using either longjmp() or try/finally
much harder to write and maintain.

(2) works, but there's no point adding try/finally and then immediately
deprecating it ;-)

longjmp() is a very useful feature. If you're not going to provide a
replacement, then please don't make longjmp() harder to use!
Post by Richard Henderson
Something I just thought of here is GCC's computed goto extension.
Another issue is using goto to jump out of nested functions.
This has similar properties to longjmp or throwing exceptions,
i.e. it unwinds the stack. The relationship between that and
__try/__finally needs to be documented.
--
Fergus Henderson <***@cs.mu.oz.au> | "I have always known that the pursuit
The University of Melbourne | of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.
Richard Henderson
2002-11-07 17:54:29 UTC
Permalink
Post by Fergus Henderson
(3) programmers must take great care when using longjmp()
or try/finally to ensure that the two are not used together in
a way that will result in undefined behaviour. This requires
global (or at least non-local) analysis of the program.
The analysis must be redone whenever any code is added which
uses try/finally or longjmp, or even just when code which
might use those constructs is rearranged. Furthermore,
there are no tools which will help in this analysis, no
support for run-time checking of this property, and bugs
caused in this way may only show up on some platforms,
so on some platforms testing will never detect the bug.
OUCH!
3, really. Though ideally we'd have the function longjmp_unwind,
which is mentioned in the IA-64 psABI as a function that runs cleanups
along the path to the setjmp target.
Post by Fergus Henderson
Another issue is using goto to jump out of nested functions.
This has similar properties to longjmp or throwing exceptions,
i.e. it unwinds the stack. The relationship between that and
__try/__finally needs to be documented.
Same problem, though I'm happy to deprecate it.


r~
Fergus Henderson
2002-11-07 01:48:32 UTC
Permalink
Post by Richard Henderson
longjmp pretty much bypasses all the logic one would care to
ask about. I would say that such would have to be undefined. There
is mention in the IA-64 psABI docs of a separate longjmp_unwind
function that _is_ EH aware, but that's the exception not the rule.
So leaving a try block via longjmp is going to have to be undefined.
Undefined behaviour is pretty drastic. Would it be sufficient to just
make it implementation-defined whether or not longjmp() invokes cleanups
(i.e. destructors and finally blocks)?
--
Fergus Henderson <***@cs.mu.oz.au> | "I have always known that the pursuit
The University of Melbourne | of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.
Richard Henderson
2002-11-07 17:58:38 UTC
Permalink
Post by Fergus Henderson
Undefined behaviour is pretty drastic. Would it be sufficient to just
make it implementation-defined whether or not longjmp() invokes cleanups
(i.e. destructors and finally blocks)?
If you're using sjlj exceptions, you'll actually confuse the
unwinder by using longjmp. Something that might be fixable,
but requires more processor-specific code.


r~
Joseph S. Myers
2002-11-06 19:22:33 UTC
Permalink
Post by Aldy Hernandez
Post by Joseph S. Myers
Post by Aldy Hernandez
+ the current function.
There should be a corresponding mention of longjmp to that in the formal
edits.
I don't understand what you want here. Could you elaborate please?
It must be made clear in the user docs that exiting via longjmp yields
undefined behavior, rather than the finally block executing before longjmp
goes to its destination.
Post by Aldy Hernandez
Joseph, how does this one look?
You still need to make the jumps that aren't permitted into constraint
violations (with corresponding testcases for the diagnostics) except for
longjmp and computed goto cases (undefined behavior at runtime).

I don't think it's yet been stated whether you may jump into the finally
block from outside, or what happens after the execution of the finally
block if you do.
Post by Aldy Hernandez
+ after finishing the finally block (unless inside other try block). If
+ a try block is exited normally, then execution continues after the end
+ of the finally block. If a try block is exited through an exception
+ being thrown (or forced unwinding), then after finally block exits,
+ the exception is resumed or forced unwinding continues.
This paragraph fails to mention what happens if the try block is exited by
a jump (goto, break or continue).

Whichever cases of computed goto are defined / undefined need to be
documented.
Post by Aldy Hernandez
+ statement can be used to transfer control out of a try block but not
+ into a try or finally block. If done, the behavior is undefined.
You need to include @code{switch} in the statements that aren't permitted
to transfer control into a try block. Wasn't it intended that longjmp
can't jump out either?

(Note: I'm not examining the C++98 edits, there may be similar issues
there.)
--
Joseph S. Myers
***@cam.ac.uk
Richard Henderson
2002-11-06 22:00:16 UTC
Permalink
Post by Joseph S. Myers
I don't think it's yet been stated whether you may jump into the finally
block from outside, or what happens after the execution of the finally
block if you do.
No, you cannot jump into the finally block. There is no well-defined
"next" block for us to proceed to after executing the finally block.
I wouldn't even want to consider the consequences of a goto from the
try block into its finally block.
Post by Joseph S. Myers
Post by Aldy Hernandez
+ after finishing the finally block (unless inside other try block). If
+ a try block is exited normally, then execution continues after the end
+ of the finally block. If a try block is exited through an exception
+ being thrown (or forced unwinding), then after finally block exits,
+ the exception is resumed or forced unwinding continues.
This paragraph fails to mention what happens if the try block is exited by
a jump (goto, break or continue).
Indeed, I think this paragraph should follow C++ in enumerating the
ways in which you *may* leave a block, rather than enumerating the
ways in which you may not leave a block.



r~
Fergus Henderson
2002-11-07 01:20:50 UTC
Permalink
Post by Aldy Hernandez
Index: doc/extend.texi
...
"try" is not a keyword in C if -ansi (or equivalent) is specified, I assume.
This should be mentioned here and/or in the "Alternate Keywords" section.
--
Fergus Henderson <***@cs.mu.oz.au> | "I have always known that the pursuit
The University of Melbourne | of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.
Gabriel Dos Reis
2002-11-06 12:21:20 UTC
Permalink
Richard Henderson <***@redhat.com> writes:

| in order for this to work out properly. E.g. a finally block
| is the destructor for an anonymous object of an anonymous type.
| Or something. Perhaps Jason or Mark could help here?

I would like to express my concerns also (already stated by Stan and
Matt). Before this patch goes in, it would be highly helpful to see
discussions about the problem, and the proposed solution. I think
this is such a dramatic addition that it wouldn't be wise to let it go
it just as if it were a basic improvement to the compiler.

-- Gaby
Aldy Hernandez
2002-11-06 17:08:09 UTC
Permalink
Post by Gabriel Dos Reis
| in order for this to work out properly. E.g. a finally block
| is the destructor for an anonymous object of an anonymous type.
| Or something. Perhaps Jason or Mark could help here?
I would like to express my concerns also (already stated by Stan and
Matt). Before this patch goes in, it would be highly helpful to see
discussions about the problem, and the proposed solution. I think
this is such a dramatic addition that it wouldn't be wise to let it go
it just as if it were a basic improvement to the compiler.
I'mna let y'all discuss this. I was just the hacker, nothing else.
Ulrich Drepper and rth asked me to work on this for Uli's libpthread
rewrite for glibc. (Not that this is a valid argument, but a few
other compilers have try/finally for C :-)).

Meanwhile I'll keep iteracting with Joseph Myers, rth, etc over
documentation changes ;-).

Aldy
Matt Austern
2002-11-06 17:12:15 UTC
Permalink
Post by Aldy Hernandez
I'mna let y'all discuss this. I was just the hacker, nothing else.
Ulrich Drepper and rth asked me to work on this for Uli's libpthread
rewrite for glibc. (Not that this is a valid argument, but a few
other compilers have try/finally for C :-)).
Meanwhile I'll keep iteracting with Joseph Myers, rth, etc over
documentation changes ;-).
I hope the documentation will include a rationale, not just for
exceptions in general, but for the specific design decisions you
made. There are an awful lot of ways in which a language can
support exceptions, after all: why this way instead of one of all
the others?

I'm not necessarily opposed to this change, but mostly I want us
to recognize that what we're doing is language design and that it
ought to be treated as such. It's a much more drastic thing than
just adding a new optimization pass.

--Matt
Gabriel Dos Reis
2002-11-06 17:25:41 UTC
Permalink
Matt Austern <***@apple.com> writes:

| I'm not necessarily opposed to this change, but mostly I want us
| to recognize that what we're doing is language design and that it
| ought to be treated as such.

Same here.

-- Gaby
Richard Henderson
2002-11-06 19:15:09 UTC
Permalink
Post by Matt Austern
There are an awful lot of ways in which a language can
support exceptions, after all: why this way instead of one of all
the others?
In my opinion, this is the most minimal way in which exceptions
can be supported in a language.

The worst problem we have with generic exception handling is the
problem of type correspondence across language boundaries. This
is of course related to the type of the exception object. If we
added the ability to throw or catch exceptions in C, the average
user would expect the "right" thing to happen with mixed C and C++
code. This without any clear description of what the "right"
thing is. How, for example can C even talk about a template type?

Of course, we have the same problem with mixing Ada <-> C++, and
C++ -> Java (note that Java -> C++ is handled via gcj's CNI), but
as yet no one has either complained or attempted to address the
issue.

With this extension, we have no facilities either for throwing or
catching exceptions; only the running of a bit of code while an
exception propagates through the call chain. Thus we are able to
avoid all issues related to the type of the exception object.


r~
Kai Henningsen
2002-11-07 06:54:00 UTC
Permalink
Post by Richard Henderson
In my opinion, this is the most minimal way in which exceptions
can be supported in a language.
As a user, I'd say this is too minimal.
Post by Richard Henderson
The worst problem we have with generic exception handling is the
problem of type correspondence across language boundaries. This
is of course related to the type of the exception object. If we
added the ability to throw or catch exceptions in C, the average
user would expect the "right" thing to happen with mixed C and C++
code. This without any clear description of what the "right"
thing is. How, for example can C even talk about a template type?
Does it *need* to? Could C not simply define an interface which deals in
opaque values? True, that is not worth much when throwing or catching
across language boundaries, but it should do enough to make this usable
from inside C, no?
Post by Richard Henderson
Of course, we have the same problem with mixing Ada <-> C++, and
C++ -> Java (note that Java -> C++ is handled via gcj's CNI), but
as yet no one has either complained or attempted to address the
issue.
Indicating that the inter-language case does not need to do this part.
Post by Richard Henderson
With this extension, we have no facilities either for throwing or
catching exceptions; only the running of a bit of code while an
exception propagates through the call chain. Thus we are able to
avoid all issues related to the type of the exception object.
And that is why this is not useful enough to normal C use.

I've wanted exceptions for C for a long time; this isn't it - neither for
a C programmer, nor for compiling into C.

MfG Kai
Richard Henderson
2002-11-07 20:56:32 UTC
Permalink
Post by Kai Henningsen
Post by Richard Henderson
Of course, we have the same problem with mixing Ada <-> C++, and
C++ -> Java (note that Java -> C++ is handled via gcj's CNI), but
as yet no one has either complained or attempted to address the
issue.
Indicating that the inter-language case does not need to do this part.
Indicating that no one mixes C++ and Ada, actaully, since at the
moment Ada has not been updated to use the generic mechanisms.



r~
Jason Merrill
2002-11-06 14:03:45 UTC
Permalink
Post by Richard Henderson
You probably need to define __finally in terms of destructors
in order for this to work out properly. E.g. a finally block
is the destructor for an anonymous object of an anonymous type.
Or something. Perhaps Jason or Mark could help here?
The code in the finally block is run on exit from the try block, except
that exiting a try block with a corresponding finally block via longjmp or
computed goto has undefined behavior.

Jason
Richard Henderson
2002-11-06 18:56:30 UTC
Permalink
Post by Jason Merrill
Post by Richard Henderson
You probably need to define __finally in terms of destructors
in order for this to work out properly. E.g. a finally block
is the destructor for an anonymous object of an anonymous type.
Or something. Perhaps Jason or Mark could help here?
The code in the finally block is run on exit from the try block, except
that exiting a try block with a corresponding finally block via longjmp or
computed goto has undefined behavior.
Yes, clearly, but how to integrate this statement cleanly with
the ISO document? That is why I think we need to piggyback on
the existing destructor language.


r~
Jason Merrill
2002-11-06 20:11:16 UTC
Permalink
Post by Richard Henderson
Post by Jason Merrill
Post by Richard Henderson
You probably need to define __finally in terms of destructors
in order for this to work out properly. E.g. a finally block
is the destructor for an anonymous object of an anonymous type.
Or something. Perhaps Jason or Mark could help here?
The code in the finally block is run on exit from the try block, except
that exiting a try block with a corresponding finally block via longjmp or
computed goto has undefined behavior.
Yes, clearly, but how to integrate this statement cleanly with
the ISO document? That is why I think we need to piggyback on
the existing destructor language.
The above is proposed wording. :)

For destructors, the C++ standard just says (in stmt.dcl)

Variables
with automatic storage duration declared in the block are destroyed on
exit from the block (_stmt.jump_).

I think trying to word it in terms of a destructor would be too
complicated; it's easier to express cleanups in terms of try/finally.

Jason
Stan Shebs
2002-11-06 00:38:55 UTC
Permalink
Post by Aldy Hernandez
As promised. Here are the try/finally patches for C and C++.
So, uh, did I miss the part where we discussed whether this
was a good extension to add?

Stan
Matt Austern
2002-11-06 00:48:33 UTC
Permalink
Post by Stan Shebs
Post by Aldy Hernandez
As promised. Here are the try/finally patches for C and C++.
So, uh, did I miss the part where we discussed whether this
was a good extension to add?
I'm a bit concerned about that too. I hate to see major
language design decisions get made without a lot of thought
given to the overall direction of the language.

One big question is whether the intention is for this to be
part of the GNU C dialect only, or whether there's to be an
attempt to make it part of standard C.

--Matt
Jason Merrill
2002-11-06 01:19:29 UTC
Permalink
Post by Matt Austern
Post by Stan Shebs
Post by Aldy Hernandez
As promised. Here are the try/finally patches for C and C++.
So, uh, did I miss the part where we discussed whether this
was a good extension to add?
I'm a bit concerned about that too. I hate to see major
language design decisions get made without a lot of thought
given to the overall direction of the language.
I believe that the purpose of this stuff is to handle pthread cleanups
using EH so that we can implement thread cancellation using EH. Matt, you
may remember discussing this strategy at the ABI meetings (it gave rise to
the forced_unwind stuff).

Jason
Matt Austern
2002-11-06 03:25:18 UTC
Permalink
Post by Jason Merrill
I believe that the purpose of this stuff is to handle pthread cleanups
using EH so that we can implement thread cancellation using EH. Matt, you
may remember discussing this strategy at the ABI meetings (it gave rise to
the forced_unwind stuff).
Thanks for the explanation, Jason. Yes, I can see that
motivation.

I'm still not 100% sure that I find it completely compelling,
though. Yes, I do remember the forced unwind stuff, but that
started from the question of how thread cancellation would
interact with automatic variables' destructors, which is, um,
less of a concern for C.

I also have to wonder if there's a less drastic solution for
implementing thread cancellation than making a major language
extension. After all, pthreads was designed and implemented
with standard C90 in mind; do we really need this sort of
language change either to implement pthread_cancel or for
programmers to be able to use it effectively? If this is the
motivation, I think there may be a mismatch between the scope
of the problem and the scope of the solution.

(One thing I've learned since the ABI discussions, by the way,
is that asynchronous cancelation with pthread_cancel was only
designed for some extremely specialized cases; pthreads experts
advise against using it in general. Take a look at what Dave
Butenhof writes, for example.)

--Matt
Richard Henderson
2002-11-06 18:25:25 UTC
Permalink
Post by Matt Austern
I'm still not 100% sure that I find it completely compelling,
though. Yes, I do remember the forced unwind stuff, but that
started from the question of how thread cancellation would
interact with automatic variables' destructors, which is, um,
less of a concern for C.
Actually, it is a concern for libc itself. Thread cancelation
is required to release various locks acquired within stdio.
Post by Matt Austern
After all, pthreads was designed and implemented
with standard C90 in mind; do we really need this sort of
language change either to implement pthread_cancel or for
programmers to be able to use it effectively?
We have two choices:

One, libpthread and libc can continue to use a setjmp/longjmp
based solution, which _does_ work within the framework of C90.
This, however, ignores the many many (somewhat misinformed)
bug reports that complain about C++ destructors not being run
at thread cancelation. Despite the fact that POSIX does not
mention C++ at all, one must agree that having destructors run
at thread cancelation is a highly desireable feature.

Two, we can add just enough EH support in C such that we can
drop the setjmp/longjmp solution and use EH alone for thread
cancelation. (Apparently the glibc folk consider building
libc with a C++ compiler to be an abomination. Given the
sometimes subtle differences between the two languages, I can't
really fault this point of view.)
Post by Matt Austern
(One thing I've learned since the ABI discussions, by the way,
is that asynchronous cancelation with pthread_cancel was only
designed for some extremely specialized cases...
I'm talking about synchronous cancelation only.


r~
Mike Stump
2002-11-06 19:07:08 UTC
Permalink
Post by Matt Austern
Post by Jason Merrill
I believe that the purpose of this stuff is to handle pthread cleanups
using EH so that we can implement thread cancellation using EH.
Matt, you
may remember discussing this strategy at the ABI meetings (it gave rise to
the forced_unwind stuff).
Thanks for the explanation, Jason. Yes, I can see that
motivation.
I'm still not 100% sure that I find it completely compelling,
though.
As I recall... the problem is that in a C only world, existing
pthreads implementations are fine, and in a C++ only world, EH is fine.
If one wants to combine those two features pthreads and C++, there is
a slight semantic rubbing that happens. One wants the stack cleanup
actions of threads and the stack cleanup actions of C++ to interleave
normally. From the C++ perspective, the way that we insist that this
work, is if the EH mechanism, throw, is used. If you accept that
design point, then you come to the conclusion that pthreads wants to
throw an object to cleanup, so that the C++ actions run. And from
there, we then need to use a more EH style mechanism to register the C
cleanups, so that they interleave nicely with C++.

We can avoid this by either mandating that pthreads and C++ should not
be used at the same time, or by mandating non-exceptional overhead to
track the entering and leaving of regions for C++, or by removing
cleanups from pthreads or removing EH from C++.

So, the question isn't, do you oppose it, it is rather, which solution
to the problem do you prefer, and why?

A design goal of the gcc family of languages has always been
interoperability between the languages, or at least as much as
possible. This is one of the motivations behind wanting pthreads from
the C world and cleanups actions from the C++ world to interoperate
nicely. Then both features can be used to their fullest, even in a
complex system when C/pthreads and C++ are mixed.

The C++ world is used to the zero-overhead EH chanting, and are loath
to give it up. We could, but, I suspect we'd not be C++ abi compliant
with the existing C++ abi standard. These two points, not wanting to
give up C++ abi, and not wanting overhead I think constrain the C world
to need to access the EH data the C++ world creates. From there, the
wanting of C++ to run the cleanup actions from pthreads, means the C++
EH mechanism has to be able to read the data the C/pthreads
implementation produces. In the end, you can think about it anyway you
want, but in reality you've just unified C/pthreads and C++ EH. And
once you've done that, __try/__finally or whatever you want to call it
is just syntactic sugar on __builtin_pthread_register_cleanup or some
other such nonsense.

Also, this isn't new or out of thin air. It was designed and planned
around many years years ago (5, 8?). I think that discussion mostly
happened on the eh list, though, some of it did happen in smaller email
groups. I could be mistaken, and maybe the discussions were just
seeded off the eh list. In that timeframe and after thinking about it,
I don't think we found anything we like better, and apparently the
`problem' hasn't just gone away.

I tried to recount the history of this, with a little luck it is close.
If I got it any of it wrong, I'm sure other will chime in and correct
it.
Aldy Hernandez
2002-11-06 05:22:29 UTC
Permalink
Post by Matt Austern
Post by Stan Shebs
So, uh, did I miss the part where we discussed whether this
was a good extension to add?
I'm a bit concerned about that too. I hate to see major
language design decisions get made without a lot of thought
given to the overall direction of the language.
Matt, Stan: see Jason's followup email.
Post by Matt Austern
One big question is whether the intention is for this to be
part of the GNU C dialect only, or whether there's to be an
attempt to make it part of standard C.
GNU C dialect for now.

This was needed for glibc's rewrite of threads. I'm hoping Uli will
chime in here.

Aldy
Jakub Jelinek
2002-11-06 17:43:04 UTC
Permalink
Hi!
Post by Aldy Hernandez
As promised. Here are the try/finally patches for C and C++.
As can be seen by the ChangeLog, the C++ bits were kindly supplied by
Jason Merrill (thank-you beers still pending).
Jakub has some tests for try/finally mixing C and C++ exceptions.
He'll be posting those later, right? :)
Attached.
One test for throwing from C++ through C code with __try/__finally,
one test showing what approximately could libpthread do (well, there
is still a problem if some function in the backtrace from the point of
cancellation to __libc_start_main resp. pthread_start_thread is compiled
with -fno-exceptions, _Unwind_Resume will abort. I think a callback is
needed for that (e.g. calling the stop callback with
some special arguments).

2002-11-06 Jakub Jelinek <***@redhat.com>

* Makefile.in (USER_H): Install unwind.h.
* libgcc-std.ver (__gcc_personality_v0, __gcc_personality_sj0):
Export @GCC_3.4.

* lib/g++-dg.exp (g++-dg-test): Add g++-dg-aux-sources to additional
flags.
(dg-aux-sources): New subroutine.
* g++.dg/ext/try-finally-1.C: New test.
* g++.dg/ext/try-finally-1-aux.c: New auxiliary file for the test.
* gcc.dg/try-finally-1.c: New test.

--- gcc/Makefile.in.jj 2002-10-25 14:12:50.000000000 +0200
+++ gcc/Makefile.in 2002-11-06 18:44:16.000000000 +0100
@@ -157,7 +157,7 @@ INSTALL_HEADERS_DIR = @build_install_hea
USER_H = $(srcdir)/ginclude/stdarg.h $(srcdir)/ginclude/stddef.h \
$(srcdir)/ginclude/varargs.h \
$(srcdir)/ginclude/stdbool.h $(srcdir)/ginclude/iso646.h \
- $(EXTRA_HEADERS)
+ $(srcdir)/unwind.h $(EXTRA_HEADERS)

# The GCC to use for compiling libgcc.a, enquire, and crt*.o.
# Usually the one we just built.
--- gcc/libgcc-std.ver.jj 2001-07-22 21:34:05.000000000 +0200
+++ gcc/libgcc-std.ver 2002-11-05 14:11:03.000000000 +0100
@@ -175,3 +175,7 @@ GCC_3.0 {
_Unwind_SjLj_ForcedUnwind
_Unwind_SjLj_Resume
}
+GCC_3.4 {
+ __gcc_personality_v0
+ __gcc_personality_sj0
+}
--- gcc/testsuite/lib/g++-dg.exp.jj 2002-01-23 16:29:52.000000000 +0100
+++ gcc/testsuite/lib/g++-dg.exp 2002-11-05 13:57:13.000000000 +0100
@@ -23,6 +23,7 @@ load_lib scanasm.exp

proc g++-dg-test { prog do_what extra_tool_flags } {
# Set up the compiler flags, based on what we're going to do.
+ global g++-dg-aux-sources

switch $do_what {
"preprocess" {
@@ -59,6 +60,10 @@ proc g++-dg-test { prog do_what extra_to
}
}
set options ""
+ if { ${g++-dg-aux-sources} != "" } {
+ append extra_tool_flags "${g++-dg-aux-sources}"
+ set g++-dg-aux-sources ""
+ }
if { $extra_tool_flags != "" } {
lappend options "additional_flags=$extra_tool_flags"
}
@@ -82,3 +87,22 @@ proc g++-dg-prune { system text } {

return $text
}
+
+
+#
+# Add additional sources to compile together with the main source file
+#
+
+proc dg-aux-sources { args } {
+ global g++-dg-aux-sources
+ upvar prog myprog
+
+ foreach arg [lrange $args 1 [llength $args]] {
+ set filename "[file dirname $myprog]/$arg"
+ if { [file extension $arg] == ".c" } {
+ append g++-dg-aux-sources " -xc $filename -xnone"
+ } else {
+ append g++-dg-aux-sources " $filename"
+ }
+ }
+}
--- gcc/testsuite/g++.dg/ext/try-finally-1.C.jj 2002-11-05 14:13:46.000000000 +0100
+++ gcc/testsuite/g++.dg/ext/try-finally-1.C 2002-11-05 12:47:17.000000000 +0100
@@ -0,0 +1,31 @@
+// { dg-do run }
+// { dg-options "-O2 -fexceptions" }
+// { dg-aux-sources try-finally-1-aux.c }
+
+extern int caught;
+extern "C" void test (void);
+extern "C" void do_throw (void);
+extern "C" void abort (void);
+
+void
+do_throw (void)
+{
+ caught |= 4;
+ throw 1;
+}
+
+int
+main ()
+{
+ try
+ {
+ test ();
+ }
+ catch(...)
+ {
+ caught |= 8;
+ }
+ if (caught != 15)
+ abort ();
+ return 0;
+}
--- gcc/testsuite/g++.dg/ext/try-finally-1-aux.c.jj 2002-11-05 14:13:48.000000000 +0100
+++ gcc/testsuite/g++.dg/ext/try-finally-1-aux.c 2002-11-05 12:43:30.000000000 +0100
@@ -0,0 +1,23 @@
+int caught;
+extern void do_throw (void);
+extern void abort (void);
+
+void
+test (void)
+{
+ caught |= 1;
+ __try
+ {
+ do_throw ();
+ }
+ __finally
+ {
+ finally ();
+ }
+ abort ();
+}
+
+finally()
+{
+ caught |= 2;
+}
--- gcc/testsuite/gcc.dg/try-finally-1.c.jj 2002-07-23 20:50:16.000000000 +0200
+++ gcc/testsuite/gcc.dg/try-finally-1.c 2002-11-06 18:46:39.000000000 +0100
@@ -0,0 +1,94 @@
+/* { dg-do run { target i?86-*-linux* x86_64-*-linux* ia64-*-linux* sparc*-*-linux* } } */
+/* { dg-options "-O2 -fexceptions" } */
+
+#include <unwind.h>
+
+int caught;
+
+void finally (void);
+void do_throw (void);
+extern void abort (void);
+extern void exit (int);
+
+void
+test1 (void)
+{
+ caught |= 1;
+ __try
+ {
+ do_throw ();
+ abort ();
+ }
+ __finally
+ {
+ finally ();
+ }
+ abort ();
+}
+
+void
+test2 (void)
+{
+ caught |= 2;
+ __try
+ {
+ test1 ();
+ abort ();
+ }
+ __finally
+ {
+ caught |= 4;
+ }
+ abort ();
+}
+
+void
+test3 (void)
+{
+ test2 ();
+ caught = 0;
+}
+
+void
+finally (void)
+{
+ caught |= 8;
+}
+
+void
+exception_cleanup (_Unwind_Reason_Code code,
+ struct _Unwind_Exception *exc)
+{
+}
+
+_Unwind_Reason_Code
+stop_fn (int x, _Unwind_Action action, _Unwind_Exception_Class class,
+ struct _Unwind_Exception *exc, struct _Unwind_Context *ctx, void *arg)
+{
+ /* This would actually check for __libc_start_main or
+ pthread_start_thread. */
+ if ((void *) _Unwind_GetRegionStart (ctx) == (void *) test3)
+ {
+ if (caught != 15)
+ abort ();
+ exit (0);
+ }
+ return _URC_NO_REASON;
+}
+
+struct _Unwind_Exception ue;
+
+void
+do_throw (void)
+{
+ ue.exception_class = 1;
+ ue.exception_cleanup = exception_cleanup;
+ _Unwind_ForcedUnwind (&ue, stop_fn, 0);
+}
+
+int
+main (void)
+{
+ test3 ();
+ abort ();
+}

Jakub
Joseph S. Myers
2002-11-06 19:03:57 UTC
Permalink
Post by Jakub Jelinek
* Makefile.in (USER_H): Install unwind.h.
Installed header files are documented in sourcebuild.texi.
--
Joseph S. Myers
***@cam.ac.uk
Mark Mitchell
2002-11-06 23:32:20 UTC
Permalink
--On Wednesday, November 06, 2002 12:43:04 PM -0500 Jakub Jelinek
Hi!
Post by Aldy Hernandez
As promised. Here are the try/finally patches for C and C++.
I've been offline for a few days, and am trying to catch up on email,
not to mention the release. So, I apologize for jumping in late.

But, I do not approve of adding try/finally to C and/or C++ until
and unless ISO adds it.

There is nothing you can do with try/finally in C++ you can't already
do:

try { X } finally { Y }

can always be replaced with either of:

(1) try { X } catch (...) { Y; throw; } Y;

or:

(2) { struct S { ~S() { Y } } s; X }

depending on your stylistic preference.

These examples show that Richard's point about needing to deal with
unknown exception types is moot; the use of ... already allows you
to avoid knowing the type.

I do not think we should add exception support of any kind to GNU C; use
C++ if you want exceptions.

Please do not check in these patches until we've had a chance to hash
this out more fully.

If we do want to check these patches in, the semantics should be given
in terms of translation to (1) above.
--
Mark Mitchell ***@codesourcery.com
CodeSourcery, LLC http://www.codesourcery.com
Richard Henderson
2002-11-07 00:03:07 UTC
Permalink
Post by Mark Mitchell
There is nothing you can do with try/finally in C++ you can't already
try { X } finally { Y }
(1) try { X } catch (...) { Y; throw; } Y;
(2) { struct S { ~S() { Y } } s; X }
depending on your stylistic preference.
True. I was simply trying to make things easier for the
thread library in having one macro work for both languages.
Post by Mark Mitchell
I do not think we should add exception support of any kind to GNU C; use
C++ if you want exceptions.
See elsewhere about resistance compiling libc with a c++ compiler.

The main problem is that you simply cannot implement cleanups with
a few calls to a runtime library. There are data structures that
the compiler must set up related to how it generated code.
Post by Mark Mitchell
If we do want to check these patches in, the semantics should be given
in terms of translation to (1) above.
I don't agree with this.

The EH ABI clearly distingishes between cleanups and catches.
Thus the semantics are exactly those of (2), and not those of
(1) at all. Indeed, even ISO C++ makes this distinction with
std::uncaught_exception.

Further, try/catch/rethrow does not express what happens when
you use goto/break/continue/return to leave the try block.


r~
Gabriel Dos Reis
2002-11-07 00:10:57 UTC
Permalink
Richard Henderson <***@redhat.com> writes:

| Further, try/catch/rethrow does not express what happens when
| you use goto/break/continue/return to leave the try block.

Leaving a scope always implies destructors being run for local
objects.

-- Gaby
Richard Henderson
2002-11-07 00:12:24 UTC
Permalink
Post by Gabriel Dos Reis
| Further, try/catch/rethrow does not express what happens when
| you use goto/break/continue/return to leave the try block.
Leaving a scope always implies destructors being run for local
objects.
Correct. But it doesn't imply running Y in Mark's example.
Which is exactly my point.


r~
Gabriel Dos Reis
2002-11-07 00:21:19 UTC
Permalink
Richard Henderson <***@redhat.com> writes:

| On Thu, Nov 07, 2002 at 01:10:57AM +0100, Gabriel Dos Reis wrote:
| > | Further, try/catch/rethrow does not express what happens when
| > | you use goto/break/continue/return to leave the try block.
| >
| > Leaving a scope always implies destructors being run for local
| > objects.
|
| Correct. But it doesn't imply running Y in Mark's example.
| Which is exactly my point.

Let me see if I'm not lost.

I assume you're talking of

try { X } finally { Y }

being translated into

(1) try { X } catch (...) { Y; throw; } Y;

Right?

Maybe I got the same (mis)understanding as Mark that Y should always
be run. Am I wrong?

-- Gaby
Per Bothner
2002-11-07 01:01:06 UTC
Permalink
Post by Gabriel Dos Reis
I assume you're talking of
try { X } finally { Y }
being translated into
(1) try { X } catch (...) { Y; throw; } Y;
Right?
Maybe I got the same (mis)understanding as Mark that Y should always
be run. Am I wrong?
What about:

for {;;} { try { break; } finally { Y } }

which runs Y, while:

for (;;) { try { break; } catch (...) { Y; throw; } Y; }

doesn't, if I understand things correctly.
--
--Per Bothner
***@bothner.com http://www.bothner.com/per/
Gabriel Dos Reis
2002-11-07 01:15:16 UTC
Permalink
Per Bothner <***@bothner.com> writes:

| Gabriel Dos Reis wrote:
|
| > I assume you're talking of
| >
| > try { X } finally { Y }
| >
| > being translated into
| >
| > (1) try { X } catch (...) { Y; throw; } Y;
| >
| > Right?
| >
| > Maybe I got the same (mis)understanding as Mark that Y should always
| > be run. Am I wrong?
|
| What about:
|
| for {;;} { try { break; } finally { Y } }
|
| which runs Y, while:
|
| for (;;) { try { break; } catch (...) { Y; throw; } Y; }
|
| doesn't, if I understand things correctly.

Thanks for the clarification!

Then I think I would lean toward the "Resource Acquisition Is
Initialization" approach i.e. option (2) offered by Mark in his
message.

-- Gaby
Mark Mitchell
2002-11-07 00:44:49 UTC
Permalink
Post by Richard Henderson
See elsewhere about resistance compiling libc with a c++ compiler.
I know there's resistance, but frankly I think that's just predjudice.

Compile the bits you need C++ features for in C++. Avoid stuff that
seems fragile, like virtual bases/virtual functions etc; just use
try/catch and/or destructors if that's all you want. The functionality
you need has been stable for ages and ages.

I feel strongly that we should not try to bring C++ features into C,
just to avoid having to compile some stuff in libc with G++. Especially
as similar features may eventually go into ISO C with subtly different
semantics.
Post by Richard Henderson
The main problem is that you simply cannot implement cleanups with
a few calls to a runtime library. There are data structures that
the compiler must set up related to how it generated code.
Post by Mark Mitchell
If we do want to check these patches in, the semantics should be given
in terms of translation to (1) above.
I should have said (1) or (2). I'm comfortable with either formulation,
and you're right that they are subtly different. If we do (2), we have
to word it cleverly; a literal substitution wouldn't work because local
variables wouldn't be available in S::~S.
Post by Richard Henderson
Further, try/catch/rethrow does not express what happens when
you use goto/break/continue/return to leave the try block.
True.

I don't want to distract us by getting into that; I'm happy to go with
the translation (2) which seems to be what you prefer, if we decide
we absolutely must introduce this feature.

I, however, am far from convinced.
--
Mark Mitchell ***@codesourcery.com
CodeSourcery, LLC http://www.codesourcery.com
Matt Austern
2002-11-07 00:55:32 UTC
Permalink
Post by Mark Mitchell
I feel strongly that we should not try to bring C++ features into C,
just to avoid having to compile some stuff in libc with G++.
Especially
as similar features may eventually go into ISO C with subtly different
semantics.
I have the same concerns as Mark. I'm nervous about the
possibility that adding a major new language feature to
our C compiler now, just to meet the needs of a single
project, may conflict with future work on more general
exception handling.

I'd feel a lot better about this either if
(1) it could be hidden behind builtins that looked like
function calls, so that it didn't do anything like
introducing new keywords or new kinds of scope into
the language; or
(2) it was done in the context of explicit consultation
with WG14 and we could be pretty sure that we were
headed in the same direction as standard C.

--Matt
Zack Weinberg
2002-11-07 01:59:59 UTC
Permalink
Post by Mark Mitchell
Post by Richard Henderson
See elsewhere about resistance compiling libc with a c++ compiler.
I know there's resistance, but frankly I think that's just predjudice.
Compile the bits you need C++ features for in C++. Avoid stuff that
seems fragile, like virtual bases/virtual functions etc; just use
try/catch and/or destructors if that's all you want. The functionality
you need has been stable for ages and ages.
I feel strongly that we should not try to bring C++ features into C,
just to avoid having to compile some stuff in libc with G++. Especially
as similar features may eventually go into ISO C with subtly different
semantics.
To clarify, this is about pthread_cleanup_push/pop, right? Which are
used in code that _uses_ libc, not just inside libc itself. I got the
impression that the idea here was to be able to do

#define pthread_cleanup_push(routine_, arg_) { \
__pthread_cleanup_t __cleanup_info; \
__cleanup_info.routine = routine_; \
__cleanup_info.arg = arg_; \
__cleanup_info.doit = 1; \
__try {

#define pthread_cleanup_pop(doit_) \
__cleanup_info.doit = doit_; \
} __finally { \
if (__cleanup_info.doit) \
__cleanup_info.routine(__cleanup_info.arg); \
} }

And the point of doing this is to get pthread_cancel handlers to be
run when a C++ exception is thrown, and vice versa to get destructors
to be run when pthread_cancel() happens. (pthread_cancel itself will
presumably call _Unwind_RaiseException directly, in this scheme.)

This is actually an important correctness issue for threading in C++.
Currently, you can be reliable in the face of pthread_cancel, or you can
be reliable in the face of exceptions being thrown, but not both at the
same time. It is also, obviously, desirable not to have to maintain
separate versions of these macros for C and C++. And I sympathize
somewhat with the desire to create a generic extension rather than one
specific to this precise situation.

However. The code I sketched above has a number of correctness issues --
mostly due to the unhygenic nature of C preprocessor macros rather than
to the try/finally construct itself. It is difficult to make them go
away while still using the present proposal, but it would be very easy
to get rid of them using a narrowly tailored one:

#define pthread_cleanup_push(routine_, arg_) \
__builtin_cleanup_block(routine_, arg_) {

#define pthread_cleanup_pop(doit_) \
__builtin_toggle_do_cleanup(doit_); }

These are equivalent to this C++:

struct cleanup {
void (*routine)(void *);
void *arg;
int doit;
cleanup(void (*R)(void *), void *A) : routine(R), arg(A), doit(1) { };
~cleanup() { if(doit) routine(arg); }
};

#define pthread_cleanup_push(R, A) { cleanup C(R, A);
#define pthread_cleanup_pop(DOIT) C.doit = DOIT; }

except that (a) the local variable is anonymous, (b) they work in C
as well as C++, and (d) no declaration of struct cleanup is visible.

Are there other uses of __try/__finally which y'all have in mind that
wouldn't be covered by this?

zw

p.s. It would be nifty if setjmp/longjmp could be defined in terms of
exception handling. Anyone put thought into that?
Gabriel Dos Reis
2002-11-07 02:14:25 UTC
Permalink
Zack Weinberg <***@codesourcery.com> writes:

| p.s. It would be nifty if setjmp/longjmp could be defined in terms of
| exception handling. Anyone put thought into that?

EH in C++ sense essentially makes control flow flows one direction
whereas setjmp/longjmp transfers control virtually anywhere.

-- Gaby
Zack Weinberg
2002-11-07 02:58:07 UTC
Permalink
Post by Gabriel Dos Reis
| p.s. It would be nifty if setjmp/longjmp could be defined in terms of
| exception handling. Anyone put thought into that?
EH in C++ sense essentially makes control flow flows one direction
whereas setjmp/longjmp transfers control virtually anywhere.
Imagine a setup like this:

class jmp_buf {
int setjmp_return_value;
} __attribute__ ((always_passed_by_reference));

void calls_longjmp(jmp_buf env)
{
env.setjmp_return_value = 1;
throw env;
}

void calls_setjmp(void)
{
try {
jmp_buf env;
env.setjmp_return_value = 0;

control_transfers_here_twice:
if (env.setjmp_return_value == 0) {
puts("first call to setjmp()");
calls_longjmp(env);
} else {
puts("second call to setjmp()");
}

} catch (jmp_buf E) {
if (&E != &env)
throw;
goto control_transfers_here_twice;
}
}

The "always_passed_by_reference" bit ensures that when control
transfers to the catch clause, &E == &env if and only if it was
'env' that got longjmped to.

This is not possible to achieve with macros, of course -- imagine this
as a compact representation of the tree structure generated when
__builtin_setjmp and __builtin_longjmp are used. (Which builtins
should not be confused with the existing ones of the same names.)

I claim that any use of setjmp/longjmp that won't work with this
implementation is already invoking undefined behavior.

Code that calls longjmp from a signal handler would have to be
compiled with -fasynchronous-exceptions, and we wouldn't want to do
this on a platform that couldn't unwind through a signal handler
frame.

(I'm assuming it is okay to perform that goto. I think such is the
case, but I could be wrong.)

zw
Gabriel Dos Reis
2002-11-07 03:34:06 UTC
Permalink
Zack Weinberg <***@codesourcery.com> writes:

| On Thu, Nov 07, 2002 at 03:14:25AM +0100, Gabriel Dos Reis wrote:
| > Zack Weinberg <***@codesourcery.com> writes:
| >
| > | p.s. It would be nifty if setjmp/longjmp could be defined in terms of
| > | exception handling. Anyone put thought into that?
| >
| > EH in C++ sense essentially makes control flow flows one direction
| > whereas setjmp/longjmp transfers control virtually anywhere.
|
| Imagine a setup like this:
|
| class jmp_buf {
| int setjmp_return_value;
| } __attribute__ ((always_passed_by_reference));
|
| void calls_longjmp(jmp_buf env)
| {
| env.setjmp_return_value = 1;
| throw env;
| }
|
| void calls_setjmp(void)
| {
| try {
| jmp_buf env;
| env.setjmp_return_value = 0;
|
| control_transfers_here_twice:
| if (env.setjmp_return_value == 0) {
| puts("first call to setjmp()");
| calls_longjmp(env);
| } else {
| puts("second call to setjmp()");
| }
|
| } catch (jmp_buf E) {
| if (&E != &env)
| throw;
| goto control_transfers_here_twice;
| }
| }
|
| The "always_passed_by_reference" bit ensures that when control
| transfers to the catch clause, &E == &env if and only if it was
| 'env' that got longjmped to.

I'm not sure that extra linguistic facility is really in accordance
with C++ rules (I'm not sure 12.8/15 applies here). Furthermore after
call to calls_longjmp(), the object env ceases existing -- it isn't
even visible in the catch block so I assume you wanted

jmp_buf env;
try {
env.setjmp_return_value = 0;
// ...

[...]

| I claim that any use of setjmp/longjmp that won't work with this
| implementation is already invoking undefined behavior.

Well, let's first address the case of situations that "work" with the
above scheme before addressing the validity of the claim. What is
supposed to happen for the rethrow case?

-- Gaby
Zack Weinberg
2002-11-07 06:15:22 UTC
Permalink
Post by Gabriel Dos Reis
| The "always_passed_by_reference" bit ensures that when control
| transfers to the catch clause, &E == &env if and only if it was
| 'env' that got longjmped to.
I'm not sure that extra linguistic facility is really in accordance
with C++ rules (I'm not sure 12.8/15 applies here).
I couldn't think of a clearer way to express the necessary effect.
Each longjmp (throw) needs to transfer control to one specific setjmp
(catch block); leveraging the uniqueness of objects seemed like the
obvious way to do it. Unfortunately, jmp_buf objects are passed
around by value, so I couldn't see any way to avoid some sort of
internal hack.
Post by Gabriel Dos Reis
Furthermore after call to calls_longjmp(), the object env ceases
existing -- it isn't even visible in the catch block so I assume you
wanted
jmp_buf env;
try {
env.setjmp_return_value = 0;
// ...
Yes. Oops. You can tell I've done more Python than C++, eh?

That's actually good; we could then restructure it as

jmp_buf env;
env.setjmp_return_value = 0;
for (;;) {
try {
...
break;
} catch (jmp_buf E) {
if (E is env)
continue;
throw;
}
}

and avoid the dubious goto. Read "E is env" as whatever has to be
done to check the identity constraint.
Post by Gabriel Dos Reis
| I claim that any use of setjmp/longjmp that won't work with this
| implementation is already invoking undefined behavior.
Well, let's first address the case of situations that "work" with the
above scheme before addressing the validity of the claim. What is
supposed to happen for the rethrow case?
If control reaches "throw;" you mean? That happens when the jmp_buf
that was jumped to, isn't the jmp_buf that was initialized in this
stack frame, so we should keep unwinding. Presumably, higher up the
stack, there is a jmp_buf that we do want to jump to. If not, do
whatever is normally done for an unhandled exception (call
std::terminate, isn't it? Come to think, if we're doing exceptions in
C, we need a C equivalent of that...)

zw
Mark Mitchell
2002-11-07 06:34:34 UTC
Permalink
Post by Zack Weinberg
#define pthread_cleanup_push(routine_, arg_) \
__builtin_cleanup_block(routine_, arg_) {
#define pthread_cleanup_pop(doit_) \
__builtin_toggle_do_cleanup(doit_); }
Something like this is a much, much closer to what I think we should
do.

Thanks for working this through.
--
Mark Mitchell ***@codesourcery.com
CodeSourcery, LLC http://www.codesourcery.com
Aldy Hernandez
2002-11-07 07:33:23 UTC
Permalink
Post by Mark Mitchell
Post by Zack Weinberg
#define pthread_cleanup_push(routine_, arg_) \
__builtin_cleanup_block(routine_, arg_) {
#define pthread_cleanup_pop(doit_) \
__builtin_toggle_do_cleanup(doit_); }
Something like this is a much, much closer to what I think we should
do.
You know, I ain't gonna comment much on this because I was just an
innocent bystander who just got told what to do, but...

It seems we sometimes, in an effort to be purists, bend over backwards
to avoid adding front end extensions and end up with rather ugly
interfaces. It's not like try/finally isn't available in other
compilers, and it's not like the __builtin_cleanup_block() stuff is
aesthetically pleasing either.

Just think, if any of the nifty features presently available as
extensions in GCC (inline, nested functions, labels as values, inline
assembly, etc) were to be brought up now, I'm 99% sure they'd be shot
down, purely because they were front end extensions. I think we're
loosing the GNU's Not Unix spirit.

Be that as it may, my point is purely a philosophical one. I will not
argue for either standpoint. Exception handling is not my thing.
Post by Mark Mitchell
Thanks for working this through.
--
CodeSourcery, LLC http://www.codesourcery.com
Gabriel Dos Reis
2002-11-07 09:04:44 UTC
Permalink
Aldy Hernandez <***@redhat.com> writes:

[...]

| Just think, if any of the nifty features presently available as
| extensions in GCC (inline, nested functions, labels as values, inline
| assembly, etc) were to be brought up now, I'm 99% sure they'd be shot
| down, purely because they were front end extensions. I think we're
| loosing the GNU's Not Unix spirit.

I don't understand how you came to that conclusion.

Anyway, if you were to track, understand and fix the zillions of bugs
caused by extensions that were put there with no in-depth
understanding of its implications and interactions with the language
and explorations of alternatives (and sometimes with conflicting
semantics) I'm sure you would have a different stance. [ I did a tiny
part of bug tracking; that wasn't picnic and I certainly don't like
*some* extensions there in the language. I do find other useful. ]

The issue isn't to shot down every extension. The issue is, given
current experience with extensions (in the broad sense), given current
C++ (resp. C) semantics and facilities, given current front-end
complexities, bugs and plans, given a *clear* statement of
the problem what points of the solutions space should we consider.

The proposed patch is such a dramatic change that I can not imagine
that it would be checked in with no discussion.

-- Gaby
Michael Matz
2002-11-07 13:34:38 UTC
Permalink
Hi,
Post by Mark Mitchell
Post by Zack Weinberg
#define pthread_cleanup_push(routine_, arg_) \
__builtin_cleanup_block(routine_, arg_) {
#define pthread_cleanup_pop(doit_) \
__builtin_toggle_do_cleanup(doit_); }
Something like this is a much, much closer to what I think we should
do.
Thanks for working this through.
Uhh, that's exceedingly ugly. Try to think of the proposed semantic and
then try to come up with syntax implementing that semantic. The special
property of this extension is, that it changes control flow in a very
non-trivial (but well defined) way. Every control flow related construct
in C (except calls) are done by "top-level" keywords and syntactic
structure (if, while, for, goto ...). Therefore I'm totally and strictly
against implementing cleanups with the help of builtins, which have
function-call like syntax.

Now let's look at the wanted semantic of cleanups: "Do something (A), and
if A is done, no matter what, do B". This means, we at least have the
notion of two different statement sets, i.e. two blocks. Then we need at
least one syntactic element to bind them together and give them that
semantic. A keyword is the natural choice. The best choice of the
placement of that keyword seems to be in front of the whole construct.
That leads to:

keyword
{ A }
{ B }

This is esthetically unpleasant, because now it isn't very easy to see,
that both blocks belong to the same construct, and are on the same level,
i.e. it could be confused (structure-wise) with 'if (...) { A } { B }'.
Therefore we do a similar thing like in if-else (it's not
"if-else (...) { A } { B }" for a reason), which leads to

keyword1
{ A }
keyword2
{ B }

Now you only need to choose how to call both keywords, and __finally seems
to be good enough for the second. __try for the first is not optimal,
because we not only "try" A, but really do it completely, but looking at
other compilers having such constructs and C++ exception handling, it also
isn't the worst choice.

That some people here consider the lack of ability to pass around
exception objects to be a deficiency stems from the fact, that sometimes
in this thread exception handling and cleanups were confused. The
proposed language extension has next to nothing to do with exceptions, but
just with cleanups. That cleanups can happen while unwinding the stack
for throwing an exceptions is the only connection.

Regarding semantic again: block A shall only be entered by fall-through
from the syntactically preceding statements, and shall be left only by
return, break, fallthrough, goto to label (_not_ computed), by throwing
an exception (maybe from a called function) or by exiting the program (in
which case the cleanup needs not to be run). The same for B, except the
language about the cleanup.

I'm of course not sure, but I can't think of many variations how to
design cleanups, ergo I think if ISO C once includes it, it will be very
similar to what we would have now.

I really think, that this is one of the more well-designed and
unproblematic extensions. And contrary to some people here I don't even
think it's such a invasive language change at all. It's quite orthogonal
to the rest of the language by introducing a new toplevel construct.
Contrary to statement expressions for instance.

If people have problems with this extension they should name them so they
can be fixed, instead of just being "sceptic" or "nervous", or generally
just against it until some standard standardizes it (which by the way is
anyway the wrong direction. Standards fixate existing practice, they
don't establish it.)


Ciao,
Michael.
Mark Mitchell
2002-11-07 16:12:05 UTC
Permalink
Standards fixate existing practice, they don't establish it.
We've already got a standard that's relevant here. In particular,
we have the C++ programming language, which provides the functionality
needed. If you need that functionality, use that language, or another
language (like Java, Ada, etc.) that provides that functionality.

Now, I'm not fully understanding how this is going to be used, which
is perhaps coloring my thinking. I'm getting the impression that this
functionality is going to get buried in C headers that are going to
get seen in *user* C code. Is that true?

If it is true, it makes my above remark irrelevant; we need something
that works in C. The question, of course, is *what* do we need.

Let me try to see if I've got right what it is we're trying to
accomplish. If I don't have it right, I'll not be intelligent in the
discussion, so please correct me.

We're trying to implement one particular piece of functionality required
by the POSIX threads interface; namely "pthread_cleanup_push" and friends.
(I know that any feature might get used more widely, but this is the
motivation, as I understand it.) Furthermore, we're trying to make the
POSIX threads cleanups play nice with exception-handling. Or are we
trying to use EH mechanisms to make things go faster?

Anyhow, if I write:

try { pthread_cleanup_push (...);
throw 3; }
catch (...) { }

and the system arranges to run the cleanup when the exception is thrown,
the system is badly broken.

So, I think I'm not getting the point.

What interface is it that we think we need this functionality to
implement?
--
Mark Mitchell ***@codesourcery.com
CodeSourcery, LLC http://www.codesourcery.com
Daniel Jacobowitz
2002-11-07 16:38:18 UTC
Permalink
Post by Mark Mitchell
Standards fixate existing practice, they don't establish it.
We've already got a standard that's relevant here. In particular,
we have the C++ programming language, which provides the functionality
needed. If you need that functionality, use that language, or another
language (like Java, Ada, etc.) that provides that functionality.
Now, I'm not fully understanding how this is going to be used, which
is perhaps coloring my thinking. I'm getting the impression that this
functionality is going to get buried in C headers that are going to
get seen in *user* C code. Is that true?
If it is true, it makes my above remark irrelevant; we need something
that works in C. The question, of course, is *what* do we need.
That's right. pthread_cleanup_push/pop are user-accessible
macros/functions.
Post by Mark Mitchell
Let me try to see if I've got right what it is we're trying to
accomplish. If I don't have it right, I'll not be intelligent in the
discussion, so please correct me.
We're trying to implement one particular piece of functionality required
by the POSIX threads interface; namely "pthread_cleanup_push" and friends.
(I know that any feature might get used more widely, but this is the
motivation, as I understand it.) Furthermore, we're trying to make the
Before people fixate on this any further, it's worth remembering that
we've had requests for this feature in C before. I see one in
September:
From: "Michael Lovett" <***@morpace.com>
Subject: GCC Feature question

I'm pretty sure there have been others. That's not any kind of
overwhelming consideration, but it is something to keep this in
perspective.
Post by Mark Mitchell
POSIX threads cleanups play nice with exception-handling. Or are we
trying to use EH mechanisms to make things go faster?
The former. The discussion about try/finally being faster is in
comparision to some of the other suggestions which involve nested
functions or calling into C++.
Post by Mark Mitchell
try { pthread_cleanup_push (...);
throw 3; }
catch (...) { }
and the system arranges to run the cleanup when the exception is thrown,
the system is badly broken.
So, I think I'm not getting the point.
What interface is it that we think we need this functionality to
implement?
On the contrary, these are for cleanups that _would_ have to be run
when that "throw 3" was triggered. It would have to be:
try {
pthread_cleanup_push (...);
throw 3;
pthread_cleanup_pop (...);
} catch (...) { }

though.

For instance, if a thread is cancelled while holding stdio locks, the
cleanups must be run in order to restore the state of the stdio
structures and release the lock. Otherwise stdio becomes unusable
after cancellation.

(malloc has the same problem but doesn't use the cleanup handlers at
present, I think...)
--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer
Mark Mitchell
2002-11-07 17:07:02 UTC
Permalink
Post by Daniel Jacobowitz
On the contrary, these are for cleanups that _would_ have to be run
try {
pthread_cleanup_push (...);
throw 3;
pthread_cleanup_pop (...);
} catch (...) { }
though.
I did not understand the requirement that pthread_cleanup_push and
pthread_cleanup_pop be within the same lexical scope.

It's still arguable whether or not you should be calling the cleanup
at this point. I don't see anything in the pthreads spec that says
what happens if you do a "goto" out of the block, for example. I agree
that, without my language lawyer hat on, doing the cleanup on any block
exit seems like a good thing.

You will, however, break code that presently does:

try {
pthread_cleanup_push (...);
throw 3;
pthread_cleanup_pop (...);
} catch (...) {
// Do the cleanup by hand; I know it will not have been run.
}

Well, let's not argue that.

Our goals, then, seem to be:

(1) Make life easier for C++ programmers by calling cleanups as
exceptions propagate. (This can already be managed explicitly,
with try/catch, but if we can fold it into the library, that's
a good QOI thing. We do have to be very careful to document it;)

(2) Shield GNU libc developers from having to write the few bits of
the library that they want to be exception-safe in C++.

The second bit carries no weight with me at all. It's no big deal to
write a few source files in the C++ almost-superset of C.

The first bit is, however, a good, user-focused idea.

But why not just do that at the source level? In pthread.h:

#ifdef __cplusplus
#define pthread_cleanup_push(routine, arg) \
{ struct pthread_cleanup c(routine, arg);

#define pthread_cleanup_pop(execute) \
c.run(execute); }
#endif

with something like:

struct pthread_cleanup {
pthread_cleanup (void (*r)(void*), void *a)
: r_(r), a_(a) {}
~pthread_cleanup () { if (r_) r_(a); }
void run(int execute) { if (execute) { _r(_a); } _r = 0; }
};

I'm sure there are subtleties I got wrong, but that's the idea.
--
Mark Mitchell ***@codesourcery.com
CodeSourcery, LLC http://www.codesourcery.com
Daniel Jacobowitz
2002-11-07 17:20:03 UTC
Permalink
Post by Mark Mitchell
(1) Make life easier for C++ programmers by calling cleanups as
exceptions propagate. (This can already be managed explicitly,
with try/catch, but if we can fold it into the library, that's
a good QOI thing. We do have to be very careful to document it;)
(2) Shield GNU libc developers from having to write the few bits of
the library that they want to be exception-safe in C++.
The second bit carries no weight with me at all. It's no big deal to
write a few source files in the C++ almost-superset of C.
It is when they're written in gnu99. It's also a royal pain to require
a C++ compiler when building glibc; bootstrapping gets harder every
day...
Post by Mark Mitchell
The first bit is, however, a good, user-focused idea.
Then you require that all pthread_cleanup_push's which need to
be unwound properly be written in C++ source files. In other words,
using these in a library now requires that part of the library to be
converted to C++ in order to be used from C++ safely. Not just talking
the implementation of GNU libc here; all libraries which use pthreads
cleanups.

I'm sure there are other reasons but I've started to lose track of them
in this discussion, so I'll step back now. I also approve of the
extension on its own...
--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer
Mark Mitchell
2002-11-07 17:51:03 UTC
Permalink
Post by Daniel Jacobowitz
It is when they're written in gnu99. It's also a royal pain to require
a C++ compiler when building glibc; bootstrapping gets harder every
day...
Well, you want C++ features, you need a C++ compiler. :-)

I'd rather see ISO C99 extensions getting added to G++ than features
that don't even exist in C++ getting added to GCC.
Post by Daniel Jacobowitz
Then you require that all pthread_cleanup_push's which need to
be unwound properly be written in C++ source files.
Precisely.

I think that if you want exception-handling, you should use a language
that has that feature. (Otherwise, I want to add Prolog unification and
ML type inference to GNU C. :-))

And, if we really, really want exceptions in C, we should pick an
existing, standardized model from the C family of languages -- namely
the model in C++. Add try/catch, not try/finally.

Or, perhaps better, and taking into account RTH's comments, add
destructors.
--
Mark Mitchell ***@codesourcery.com
CodeSourcery, LLC http://www.codesourcery.com
Matt Austern
2002-11-07 17:57:30 UTC
Permalink
Post by Mark Mitchell
Post by Daniel Jacobowitz
It is when they're written in gnu99. It's also a royal pain to require
a C++ compiler when building glibc; bootstrapping gets harder every
day...
Well, you want C++ features, you need a C++ compiler. :-)
I'd rather see ISO C99 extensions getting added to G++ than features
that don't even exist in C++ getting added to GCC.
I suspect that most C99 features will be added to a future version
of the C++ Standard in any case. There are a few that I think the
C++ Standards Committee will be skeptical of, but only a few. I
certainly wouldn't mind making some guesses about where C++
is going and anticipating the standard by a couple years.

(Obviously C++ will get long long, for example.)

--Matt
Jason Merrill
2002-11-07 18:40:32 UTC
Permalink
Post by Mark Mitchell
I think that if you want exception-handling, you should use a language
that has that feature. (Otherwise, I want to add Prolog unification and
ML type inference to GNU C. :-))
This isn't about exception handling, it's about exception-safe cleanups,
a somewhat smaller target.
Post by Mark Mitchell
And, if we really, really want exceptions in C, we should pick an
existing, standardized model from the C family of languages -- namely
the model in C++. Add try/catch, not try/finally.
try/catch is handling. Using a catch(...) for cleanups is horrible style,
as you have to duplicate the code. And the EH runtime treats them
differently.
Post by Mark Mitchell
Or, perhaps better, and taking into account RTH's comments, add
destructors.
How would you do that?

try/finally is a much simpler notion than destructors--it makes more sense
to express destructors using try/finally than the other way around--and the
explicit syntax seems more appropriate to the spirit of C. And there's
lots of prior art for it, in Java, other C compilers, and many macro-based
C exception handling implementations.

It might be feasible to use something more specifically tailored to pthread
cleanups, such as Zack's builtins, but if we want a more generally useful
mechanism, this is it.

Jason
Jakub Jelinek
2002-11-07 16:43:58 UTC
Permalink
Post by Mark Mitchell
Standards fixate existing practice, they don't establish it.
We've already got a standard that's relevant here. In particular,
we have the C++ programming language, which provides the functionality
needed. If you need that functionality, use that language, or another
language (like Java, Ada, etc.) that provides that functionality.
As has been mentioned previously, compiling GNU libc as C++ is hard, since
it is a heavy user of -std=gnu99 features and extensions which are not in C++.
Post by Mark Mitchell
Now, I'm not fully understanding how this is going to be used, which
is perhaps coloring my thinking. I'm getting the impression that this
functionality is going to get buried in C headers that are going to
get seen in *user* C code. Is that true?
Yes, among other places.
Post by Mark Mitchell
We're trying to implement one particular piece of functionality required
by the POSIX threads interface; namely "pthread_cleanup_push" and friends.
(I know that any feature might get used more widely, but this is the
motivation, as I understand it.) Furthermore, we're trying to make the
POSIX threads cleanups play nice with exception-handling. Or are we
trying to use EH mechanisms to make things go faster?
Both. One goal is to make sure C++ destructors are run when cancelling a
thread, properly interleaved with C cleanups.
Of course the smaller overhead the mechanism has in the normal (ie. thread
is not being cancelled) case, the better.
Post by Mark Mitchell
try { pthread_cleanup_push (...);
throw 3;
pthread_cleanup_pop (doit);
Post by Mark Mitchell
}
catch (...) { }
and the system arranges to run the cleanup when the exception is thrown,
the system is badly broken.
Implementation of pthread_cleanup_pop using __try/__finally would have to
do if ((doit) || __pthread_cancelling()) at the beginning of the __finally
block, so if you do pthread_cleanup_pop (0), the cleanup will not be run
when the exception is thrown, just when cancellation happens.
__pthread_cancelling may be just a simple TLS variable access.

Though if C has something like __try/__finally, then the programmer can as
well use it directly and not through the horrible pthread_cleanup_*
interface, while if pthread_cleanup_{push,pop} is implemented using
builtins which match exactly what pthread_cleanup_{push,pop}
is doing (as has been already suggested), then all the code is stuck
in using it.

Jakub
Geoff Keating
2002-11-07 20:18:33 UTC
Permalink
Post by Mark Mitchell
We're trying to implement one particular piece of functionality required
by the POSIX threads interface; namely "pthread_cleanup_push" and friends.
(I know that any feature might get used more widely, but this is the
motivation, as I understand it.) Furthermore, we're trying to make the
POSIX threads cleanups play nice with exception-handling.
Yes, this is right, but your example is not the case we really care
about. What we particularly want to make work is this C++ code:

struct A { ~A() { ... } };

{
A foo;
sigpause (); // cancellation point, I hope
}

Here, if 'sigpause' gets cancelled, we want A's destructor to not be
run. At present, cancellation is implemented using setjmp/longjmp,
and so knows nothing about C++ destructors, and so this doesn't work.
If we made C++ use setjmp/longjmp for destructors, that would be very
expensive for all C++ code. Instead, we want to make cancellation use
the C++ exception mechanism, because that's already there and so
wouldn't impose any extra overhead on C++ code. The problem is that
then we can't implement pthread_cleanup_push in C, because C can't
interact with the exception mechanism.
--
- Geoffrey Keating <***@geoffk.org>
Daniel Jacobowitz
2002-11-07 20:25:01 UTC
Permalink
Post by Geoff Keating
Post by Mark Mitchell
We're trying to implement one particular piece of functionality required
by the POSIX threads interface; namely "pthread_cleanup_push" and friends.
(I know that any feature might get used more widely, but this is the
motivation, as I understand it.) Furthermore, we're trying to make the
POSIX threads cleanups play nice with exception-handling.
Yes, this is right, but your example is not the case we really care
struct A { ~A() { ... } };
{
A foo;
sigpause (); // cancellation point, I hope
}
Here, if 'sigpause' gets cancelled, we want A's destructor to not be
run. At present, cancellation is implemented using setjmp/longjmp,
Don't you mean, "to be run"?
Post by Geoff Keating
and so knows nothing about C++ destructors, and so this doesn't work.
If we made C++ use setjmp/longjmp for destructors, that would be very
expensive for all C++ code. Instead, we want to make cancellation use
the C++ exception mechanism, because that's already there and so
wouldn't impose any extra overhead on C++ code. The problem is that
then we can't implement pthread_cleanup_push in C, because C can't
interact with the exception mechanism.
--
--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer
Mark Mitchell
2002-11-07 20:29:46 UTC
Permalink
--On Thursday, November 07, 2002 12:18:33 PM -0800 Geoff Keating
Post by Geoff Keating
Post by Mark Mitchell
We're trying to implement one particular piece of functionality required
by the POSIX threads interface; namely "pthread_cleanup_push" and
friends. (I know that any feature might get used more widely, but this
is the motivation, as I understand it.) Furthermore, we're trying to
make the POSIX threads cleanups play nice with exception-handling.
Yes, this is right, but your example is not the case we really care
struct A { ~A() { ... } };
{
A foo;
sigpause (); // cancellation point, I hope
}
Here, if 'sigpause' gets cancelled, we want A's destructor to not be
run.
I think you mean you want it *to* be run?

These kinds of cases are easily handled by my #ifdef __cplusplus idea
for the pthread_cleanup_push definition.

As far as I can tell, the only reason to add anything to the compiler
(whether it be try/finally or __builtin_pthread_cleanup_push, or
whatever) is to make it possible to write C code that is linked with
C++ programs, and have exceptions unwinding through the C code run
the cleanups.

Note that this can only happen if the C code calls C++ code, either
explicitly or through a function pointer. In which case it has to
worry about exceptions already, whether they come from cancellation
or an exlicit throw. I'm confused.
--
Mark Mitchell ***@codesourcery.com
CodeSourcery, LLC http://www.codesourcery.com
Geoff Keating
2002-11-07 21:41:02 UTC
Permalink
Date: Thu, 07 Nov 2002 12:29:46 -0800
--On Thursday, November 07, 2002 12:18:33 PM -0800 Geoff Keating
Post by Geoff Keating
Post by Mark Mitchell
We're trying to implement one particular piece of functionality required
by the POSIX threads interface; namely "pthread_cleanup_push" and
friends. (I know that any feature might get used more widely, but this
is the motivation, as I understand it.) Furthermore, we're trying to
make the POSIX threads cleanups play nice with exception-handling.
Yes, this is right, but your example is not the case we really care
struct A { ~A() { ... } };
{
A foo;
sigpause (); // cancellation point, I hope
}
Here, if 'sigpause' gets cancelled, we want A's destructor to not be
run.
I think you mean you want it *to* be run?
Yes.
These kinds of cases are easily handled by my #ifdef __cplusplus idea
for the pthread_cleanup_push definition.
How? There aren't any pthread_cleanup_push calls in the example.
You'd have to have sigpause (and the other cancellable calls)
conditionalised on __cplusplus, and then to permit users to write
programs in a mix of C and C++ you'd have to define some way to
convert the exceptions thrown in C++ to the sj/lj calls in C.
--
- Geoffrey Keating <***@geoffk.org>
Mark Mitchell
2002-11-07 22:03:55 UTC
Permalink
Post by Geoff Keating
You'd have to have sigpause (and the other cancellable calls)
conditionalised on __cplusplus, and then to permit users to write
programs in a mix of C and C++ you'd have to define some way to
convert the exceptions thrown in C++ to the sj/lj calls in C.
OK, I think I see the problem.

Suppose the dynamic stack of your code looks like:

C++ -> C -> C++ -> sigpause

When cancellation occurs, you want to have sigpause throw an exception,
run cleanups in C++, then run the pthread_cleanup_push stuff in C,
the outer C++ cleanups agagin, etc.

If the -> transitions are shared library boundaries, you have no problem;
you can have your personality routines do the right thing. In fact, you
can do that anyhow, if you mark your .o's as being C or C++.

In C, you have pthread_cleanup_push keep a stack of cleanups.
When unwinding through a a C .o, you run the stack. In C++, you use
my pthread_cleanup_push class so that it just happens by itself.

You have sigpause throw an exception, and you have a handler at the top
of the stack, to finish the thread exit. (This last bit is what was
going to happen with try/finally anyhow, I assume.)
--
Mark Mitchell ***@codesourcery.com
CodeSourcery, LLC http://www.codesourcery.com
Richard Henderson
2002-11-07 22:39:02 UTC
Permalink
Post by Mark Mitchell
C++ -> C -> C++ -> sigpause
When cancellation occurs, you want to have sigpause throw an exception,
run cleanups in C++, then run the pthread_cleanup_push stuff in C,
the outer C++ cleanups agagin, etc.
Yes.
Post by Mark Mitchell
In C++, you use
my pthread_cleanup_push class so that it just happens by itself.
Agreed.
Post by Mark Mitchell
In C, you have pthread_cleanup_push keep a stack of cleanups.
When unwinding through a a C .o, you run the stack.
How, exactly, do you do that without having C use EH? Or,
more specifically, how do you interleave the C stack and
the C++ EH?

This is the *exactly* the problem that suggested try/finally
be used in the first place.


r~
Mark Mitchell
2002-11-07 22:50:47 UTC
Permalink
--On Thursday, November 07, 2002 02:39:02 PM -0800 Richard Henderson
Post by Richard Henderson
Post by Mark Mitchell
C++ -> C -> C++ -> sigpause
When cancellation occurs, you want to have sigpause throw an exception,
run cleanups in C++, then run the pthread_cleanup_push stuff in C,
the outer C++ cleanups agagin, etc.
Yes.
Well, I'm catching up, and nobody is throwing up.

We're all trying. Finally.
Post by Richard Henderson
How, exactly, do you do that without having C use EH? Or,
more specifically, how do you interleave the C stack and
the C++ EH?
When in C code, and you hit pthread_cleanup_push, call into the runtime
passing the address of the start and end of the function you're in, and
the frame pointer. When unwinding, notice that you're in that PC range
with that frame pointer, and run the cleanups that correspond to that
range. You're guaranteed to be getting these in the right order; as
you unwind you'll always be pulling stuff off the front of the list.

The call to register the handler needn't be a call; it could be inlined.
You need is to hook a record on the front of a piece of thread-specific
data.

I bet the idea of using the frame pointer to tell function instances
apart doesn't quite work, but I bet some modification of that idea could
work.
--
Mark Mitchell ***@codesourcery.com
CodeSourcery, LLC http://www.codesourcery.com
Geoff Keating
2002-11-07 23:13:58 UTC
Permalink
Post by Mark Mitchell
--On Thursday, November 07, 2002 02:39:02 PM -0800 Richard Henderson
Post by Richard Henderson
How, exactly, do you do that without having C use EH? Or,
more specifically, how do you interleave the C stack and
the C++ EH?
When in C code, and you hit pthread_cleanup_push, call into the runtime
passing the address of the start and end of the function you're in, and
the frame pointer. When unwinding, notice that you're in that PC range
with that frame pointer, and run the cleanups that correspond to that
range. You're guaranteed to be getting these in the right order; as
you unwind you'll always be pulling stuff off the front of the list.
Wouldn't that:

(a) slow down normal C++ unwinding, because you have to keep looking
for runtime-installed cleanups? and
(b) still require unwinding data for the C code?
--
- Geoffrey Keating <***@geoffk.org>
Mark Mitchell
2002-11-07 23:34:53 UTC
Permalink
Post by Geoff Keating
(a) slow down normal C++ unwinding, because you have to keep looking
for runtime-installed cleanups? and
That's pretty much a total non-issue, but it's true. It would be a
quick check, and exceptions are expensive anyhow; as a percentage of
EH-handling cost, let alone as a precentage of program time, this
would be free.
Post by Geoff Keating
(b) still require unwinding data for the C code?
Only a very ittle. I'm not sure you'd even need the typical
data; just enough to figure out what function invocation you're in.
You certainly wouldn't need per-block-including-cleanup information,
which is what would happen with try-finally.

The key difference from try/finally in this respect is that the
pthread_cleanup_push interface takes a function pointer and an argument;
there's noting in there to allow you to access local variables, etc.

So, you don't need to really unwind the frame; you just need to know
that you need to call the function.
--
Mark Mitchell ***@codesourcery.com
CodeSourcery, LLC http://www.codesourcery.com
Richard Henderson
2002-11-07 23:44:53 UTC
Permalink
Post by Mark Mitchell
When in C code, and you hit pthread_cleanup_push, call into the runtime
passing the address of the start and end of the function you're in, and
the frame pointer. When unwinding, notice that you're in that PC range
with that frame pointer, and run the cleanups that correspond to that
range. You're guaranteed to be getting these in the right order; as
you unwind you'll always be pulling stuff off the front of the list.
I don't like the idea of registering such information at runtime.

It is a fact that to unwind through functions at all you have to
compile with -fexceptions, otherwise the data in .eh_frame does
not exist. And if .eh_frame does not exist, we don't have enough
information to find the next call frame.

What you're asking for is that we have .eh_frame data, but that
we go look somewhere else when it comes time to decide if the
frame wants to handle exceptions. This look-aside will have to
happen for each and every call frame, because we don't know which
frames are written in C and which aren't. (And before you mention
the personality routine, you should note that any frame that does
not actually use EH constructs does not have a personality routine
and is thus not an indicator of language.)

Whereas if we add some sort of EH block primitive to C, the compiler
marks which regions need cleanups statically, in read-only data
structures, which are examined as normal during the unwind phase.

I think your option slows down EH for C++ and Java for no good reason.

---

I don't really comprehend the resistance to adding try/finally to C.
I never would have guessed that it would have been greeted with
anything but "hey that's cool". I apologize to Aldy for getting
him into this mess.

I really can't think of any solution to the thread cancelation
problem that works as cleanly and correctly as try/finally. Yes,
I can think of a few hacks that might could be made to work, but
that's all they are -- hacks.

It's not like we're even breaking new ground on the language front.
Microsoft has supported try/finally in their C compiler for years.
I can't imagine WG14 being so bull-headed as to define a similar
construct that is wildly incompatible with prior art. Of course,
Microsoft fails to define their extension in anything even approaching
standards legaleese, but I think that we're on safe ground with the
basic concept, and if we make all the horrible corner cases undefined
we'll still be ok if the future standard does something different.

If you continue to be rock solid dead-set against this extension,
I guess I'll honor that and think of something else (probably with
something akin to longjmp_unwind). That won't happen until I get
back from vacation late next week though.


r~
Mark Mitchell
2002-11-08 00:19:03 UTC
Permalink
Post by Richard Henderson
What you're asking for is that we have .eh_frame data, but that
we go look somewhere else when it comes time to decide if the
frame wants to handle exceptions. This look-aside will have to
happen for each and every call frame, because we don't know which
frames are written in C and which aren't.
Couldn't it get a bit in its frame info to say whether or not it calls
pthread_cleanup_push? That would make the check pretty cheap. (I'm
not nearly as familiar as you with the format of that data; it might
well be that there's no place to put that bit.)
Post by Richard Henderson
It's not like we're even breaking new ground on the language front.
Microsoft has supported try/finally in their C compiler for years.
Yes -- I wrote a pile of code that used it heavily. :-)

It has somewhat fancy semantics. For example, you can catch
floating-point exceptions this way and other things that would be
signals in UNIX. They have a "__leave" keyword; they have exception
handling (as well as cleanups) and they have ways to say whether you
keep unwinding, or stop where you are, when you hit the handler.

The aysnchronous exception aspect of their stuff is particularly
interesting.

It's partly because of Microsoft's stuff that I'm so nervous about
this feature. Their semantics are not always what people expect, but
ours probably won't match theirs, and the next thing that will happen
will be that people will want to port "structured exception-handling"
code to GCC, and then we'll be in trouble.
Post by Richard Henderson
I don't really comprehend the resistance to adding try/finally to C.
I never would have guessed that it would have been greeted with
anything but "hey that's cool".
You won't make that mistake again. :-)

We ought to be talking about new language features before we implement
them, so we can have the arguments up front.

I apologize to Aldy too -- the patches are technically sound, and the
goal of improving interactions between threads and C++ is a good one.
I appreciate the effort, and apologize if I've come across otherwise.
Post by Richard Henderson
If you continue to be rock solid dead-set against this extension,
I guess I'll honor that and think of something else (probably with
something akin to longjmp_unwind). That won't happen until I get
back from vacation late next week though.
I promise not to be a complete jerk. :-)

If everyone else wants to do this, I'll go along.

I would prefer longjmp_unwind; that's something we're supposed to
have anyhow, and it would be very useful in making existing C
libraries play more nicely with C++, so implementing that would be
a definite win.
--
Mark Mitchell ***@codesourcery.com
CodeSourcery, LLC http://www.codesourcery.com
a***@redhat.com
2002-11-08 00:47:26 UTC
Permalink
Post by Mark Mitchell
I apologize to Aldy too -- the patches are technically sound, and the
goal of improving interactions between threads and C++ is a good one.
I appreciate the effort, and apologize if I've come across otherwise.
Just wake me up when all the apolgizing is done and y'all decide
what's going to be implemented. Of course I'm hoping it'll be
try/finally, cuz I *think* I already have a patch for that and I
really don't have many cycles left to work on this ;-))).

Aldy
Richard Henderson
2002-11-08 01:06:29 UTC
Permalink
It has somewhat fancy semantics...
Well, the __finally part of Microsoft's extensions are fairly normal.
It's the __except part of their extensions that are fancy. That's
the one I *don't* want to implement. ;-)
I would prefer longjmp_unwind; that's something we're supposed to
have anyhow, and it would be very useful in making existing C
libraries play more nicely with C++, so implementing that would be
a definite win.
I'll play with this week after next and see how it goes. If it
can be made to interoperate correctly across c/c++, as well as
not absolutely requiring C to be compiled with -fexceptions, then
we'll go ahead with that.


r~
Jakub Jelinek
2002-11-08 01:09:42 UTC
Permalink
Post by Mark Mitchell
Post by Richard Henderson
What you're asking for is that we have .eh_frame data, but that
we go look somewhere else when it comes time to decide if the
frame wants to handle exceptions. This look-aside will have to
happen for each and every call frame, because we don't know which
frames are written in C and which aren't.
Couldn't it get a bit in its frame info to say whether or not it calls
pthread_cleanup_push? That would make the check pretty cheap. (I'm
not nearly as familiar as you with the format of that data; it might
well be that there's no place to put that bit.)
You mean statically into .gcc_except_table, or dynamically?
Statically could be doable through some builtin and some hacks, dynamically
would be way harder.
Post by Mark Mitchell
It has somewhat fancy semantics. For example, you can catch
floating-point exceptions this way and other things that would be
signals in UNIX. They have a "__leave" keyword; they have exception
handling (as well as cleanups) and they have ways to say whether you
keep unwinding, or stop where you are, when you hit the handler.
The aysnchronous exception aspect of their stuff is particularly
interesting.
It's partly because of Microsoft's stuff that I'm so nervous about
this feature. Their semantics are not always what people expect, but
ours probably won't match theirs, and the next thing that will happen
will be that people will want to port "structured exception-handling"
code to GCC, and then we'll be in trouble.
That's why the proposal is about __try/__finally only, not __except
where it is e.g. not clear at all what happens if you throw in C++
and __except control expression returns -1.
At least from what I read in MSFT docs on the web, their __try/__finally
matches Aldy's patch.
Post by Mark Mitchell
I would prefer longjmp_unwind; that's something we're supposed to
have anyhow, and it would be very useful in making existing C
libraries play more nicely with C++, so implementing that would be
a definite win.
longjmp_unwind would be good to have. But __builtin_setjmp has higher
runtime overhead than __try/__finally for pthread_cleanups (especially
on some architectures __builtin_setjmp is really expensive), plus
unless I misunderstood how longjmp_unwind is supposed to work,
longjmp_unwind needs to be passed address to the youngest jmp_buf on
the stack, which means maintaining a chain of nested jmp_buf's on the
stack with the head of the chain in some TLS variable (Richard, please
correct me if you have a better idea).
__try/__finally can well interact with longjmp_unwind - longjmp_unwind
would just call all the __finally blocks while unwinding
to the longjmp_unwind target.

Jakub
Mark Mitchell
2002-11-08 01:26:38 UTC
Permalink
Post by Jakub Jelinek
You mean statically into .gcc_except_table, or dynamically?
Statically could be doable through some builtin and some hacks,
dynamically would be way harder.
I meant statically -- a "you have to check to see if I might have
registered anything" bit.
Post by Jakub Jelinek
That's why the proposal is about __try/__finally only, not __except
where it is e.g. not clear at all what happens if you throw in C++
and __except control expression returns -1.
At least from what I read in MSFT docs on the web, their __try/__finally
matches Aldy's patch.
Except that it also handles asynchronous exceptions, which ours doesn't.
Where we'd get a SIGFPE you get a structured exception in Microsoft's
stuff.
Post by Jakub Jelinek
Post by Mark Mitchell
I would prefer longjmp_unwind; that's something we're supposed to
have anyhow, and it would be very useful in making existing C
libraries play more nicely with C++, so implementing that would be
a definite win.
longjmp_unwind would be good to have.
Well, we all seem to agree on that. So, if we can do it that way,
we get a definitively useful feature required by the ABI, and we
don't open a can of worms on the language side. If it's really too dang
slow, we can do something different. On the other hand, you'll have
avoided adding frame information to every C program, which is a big win
on space, and something our C users have told us they don't want in
the past.

And you can avoid the setjmps if you write your code in C++; there
you can use the resource-acquisition-as-initialization idiom.
--
Mark Mitchell ***@codesourcery.com
CodeSourcery, LLC http://www.codesourcery.com
Richard Henderson
2002-11-08 01:43:28 UTC
Permalink
Post by Mark Mitchell
Except that it also handles asynchronous exceptions, which ours doesn't.
Heh. Not that I've tried, but I bet theirs doesn't *really* handle
asynchronous exceptions any more than ours does. I bet the best they
can actually handle properly are synchronous exceptions -- which we
can also do with -fnon-call-exceptions.

The difference between SIGFPE and SIGALRM is very significant.


r~
Mark Mitchell
2002-11-08 01:52:35 UTC
Permalink
--On Thursday, November 07, 2002 05:43:28 PM -0800 Richard Henderson
Post by Richard Henderson
Post by Mark Mitchell
Except that it also handles asynchronous exceptions, which ours doesn't.
Heh. Not that I've tried, but I bet theirs doesn't *really* handle
asynchronous exceptions any more than ours does. I bet the best they
can actually handle properly are synchronous exceptions -- which we
can also do with -fnon-call-exceptions.
The difference between SIGFPE and SIGALRM is very significant.
You're probably correct. They handle the SIGFPE, SIGSEGV, etc., kind
of stuff -- probably not SIGALRM.
--
Mark Mitchell ***@codesourcery.com
CodeSourcery, LLC http://www.codesourcery.com
Jason Merrill
2002-11-08 01:52:37 UTC
Permalink
Post by Mark Mitchell
Post by Jakub Jelinek
That's why the proposal is about __try/__finally only, not __except
where it is e.g. not clear at all what happens if you throw in C++
and __except control expression returns -1.
At least from what I read in MSFT docs on the web, their __try/__finally
matches Aldy's patch.
Except that it also handles asynchronous exceptions, which ours doesn't.
Where we'd get a SIGFPE you get a structured exception in Microsoft's
stuff.
That has nothing to do with the semantics of try/finally, which just deals
with cleaning up after whatever exceptions do happen to be thrown.

Jason
Mark Mitchell
2002-11-08 01:55:21 UTC
Permalink
--On Thursday, November 07, 2002 08:52:37 PM -0500 Jason Merrill
Post by Jason Merrill
Post by Mark Mitchell
Post by Jakub Jelinek
That's why the proposal is about __try/__finally only, not __except
where it is e.g. not clear at all what happens if you throw in C++
and __except control expression returns -1.
At least from what I read in MSFT docs on the web, their __try/__finally
matches Aldy's patch.
Except that it also handles asynchronous exceptions, which ours doesn't.
Where we'd get a SIGFPE you get a structured exception in Microsoft's
stuff.
That has nothing to do with the semantics of try/finally, which just deals
with cleaning up after whatever exceptions do happen to be thrown.
This horse is pretty dang dead, but the point is that people will *expect*
that the construct behaves the same way as Microsoft's "structured
exception handling" stuff -- in every way. Part of that is handling
SIGFPE type events.
--
Mark Mitchell ***@codesourcery.com
CodeSourcery, LLC http://www.codesourcery.com
Zack Weinberg
2002-11-07 19:38:00 UTC
Permalink
Post by Michael Matz
Hi,
Post by Mark Mitchell
Post by Zack Weinberg
#define pthread_cleanup_push(routine_, arg_) \
__builtin_cleanup_block(routine_, arg_) {
#define pthread_cleanup_pop(doit_) \
__builtin_toggle_do_cleanup(doit_); }
Something like this is a much, much closer to what I think we should
do.
Thanks for working this through.
Uhh, that's exceedingly ugly. Try to think of the proposed semantic and
then try to come up with syntax implementing that semantic. The special
property of this extension is, that it changes control flow in a very
non-trivial (but well defined) way. Every control flow related construct
in C (except calls) are done by "top-level" keywords and syntactic
structure (if, while, for, goto ...). Therefore I'm totally and strictly
against implementing cleanups with the help of builtins, which have
function-call like syntax.
I have to say here that I actually _like_ the idea of adding a
try/finally construct to C. It has a lot of interesting uses
independent of the pthreads cancellation problem. Java and Python
have it already, so there's plenty of experience with the idiom.
Jakub posted some examples of how it makes code more readable and less
error prone. It's explicit, which fits C better than destructors.
Using it directly leads to a more natural coding style than
pthread_cleanup_* do, since the cleanup doesn't have to be broken out
to a callback function.

I made up __builtin_cleanup_block and __builtin_toggle_do_cleanup to
address a specific difficulty with implementing pthread_cleanup_push/pop
in terms of try/finally, which is: when you write out the macros as
they would need to appear, you discover that they're asking to be
broken by user code playing around with the preprocessor. Also it is
impossible to write them in a way that won't produce unavoidable
-Wshadow warnings if you have nested pthread_cleanup_push/pop blocks.

Please note that __builtin_cleanup_block is _not_ a builtin function,
despite the name; it's a control flow keyword with the rough semantic
of 'establish a cleanup for the block immediately following, which
calls ROUTINE with sole argument ARG if (the cleanup was reached
because an exception was thrown || the DOIT flag is true).
__builtin_toggle_do_cleanup is a builtin function, but it doesn't
affect control flow; it just writes the value of its sole argument
into the DOIT flag, which is an invisible variable.

zw
Mike Stump
2002-11-07 17:40:35 UTC
Permalink
Post by Mark Mitchell
Post by Zack Weinberg
#define pthread_cleanup_push(routine_, arg_) \
__builtin_cleanup_block(routine_, arg_) {
#define pthread_cleanup_pop(doit_) \
__builtin_toggle_do_cleanup(doit_); }
Something like this is a much, much closer to what I think we should
do.
The added benefit is that no changes need to be made to programs that
understand and process C source code. Plus, if one doesn't care about
C++ interoperability, those can be implemented in normal C on non-gcc
platforms. This is useful in being able to port code from one platform
to another.
Richard Henderson
2002-11-07 18:05:56 UTC
Permalink
Post by Zack Weinberg
Are there other uses of __try/__finally which y'all have in mind that
wouldn't be covered by this?
Not at present.
Post by Zack Weinberg
p.s. It would be nifty if setjmp/longjmp could be defined in terms of
exception handling. Anyone put thought into that?
See the description of longjmp_unwind in the ia64 eh abi.


r~
Hans-Peter Nilsson
2002-11-07 20:57:56 UTC
Permalink
Post by Zack Weinberg
To clarify, this is about pthread_cleanup_push/pop, right? Which are
used in code that _uses_ libc, not just inside libc itself.
So then you need all -pthread code compiled with -fexceptions,
right? All user code? Perhaps *make it the default for C?*
Argh! Bloat! .eh_frame and friends everywhere!

brgds, H-P
Fergus Henderson
2002-11-07 01:57:57 UTC
Permalink
Post by Richard Henderson
The main problem is that you simply cannot implement cleanups with
a few calls to a runtime library.
This is not true. Cleanups *can* be implemented via a runtime library.
It's just that you need to use callbacks, and this makes the code
harder to read and less efficient.

How performance-critical are the sections of glibc which are affected?
--
Fergus Henderson <***@cs.mu.oz.au> | "I have always known that the pursuit
The University of Melbourne | of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.
Fergus Henderson
2002-11-07 02:09:32 UTC
Permalink
Post by Fergus Henderson
Post by Richard Henderson
The main problem is that you simply cannot implement cleanups with
a few calls to a runtime library.
This is not true. Cleanups *can* be implemented via a runtime library.
It's just that you need to use callbacks, and this makes the code
harder to read and less efficient.
Actually, if you use GNU C nested functions, it's only very slightly harder
to read: instead of

void foo() {
__try {
do_some_stuff();
}
__finally {
do_some_cleanup();
}
}

you have

void foo() {
void stuff(void) {
do_some_stuff();
}
void cleanup(void) {
do_some_cleanup();
}
try_finally(stuff, cleanup);
}

Anyway my guess is that glibc would probably end up using macros for this,

void foo() {
TRY_FINALLY({
do_some_stuff();
},{
do_some_cleanup();
})
}

and the important thing is the readability of the code using the macro,
not the readability of the code which defines the macro. So in that case,
it's just an efficiency issue.
--
Fergus Henderson <***@cs.mu.oz.au> | "I have always known that the pursuit
The University of Melbourne | of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.
Jakub Jelinek
2002-11-07 08:30:14 UTC
Permalink
Post by Fergus Henderson
Post by Richard Henderson
The main problem is that you simply cannot implement cleanups with
a few calls to a runtime library.
This is not true. Cleanups *can* be implemented via a runtime library.
It's just that you need to use callbacks, and this makes the code
harder to read and less efficient.
How performance-critical are the sections of glibc which are affected?
Very.
The sections are e.g. stdio, fast paths in threading library, etc.

Jakub
Geoff Keating
2002-11-07 02:34:44 UTC
Permalink
Post by Richard Henderson
Post by Mark Mitchell
I do not think we should add exception support of any kind to GNU C; use
C++ if you want exceptions.
See elsewhere about resistance compiling libc with a c++ compiler.
It wouldn't work, anyway. User programs can also use
pthread_cleanup_push/pthread_cleanup_pop, in C, so whatever mechanism
is used for that must be accessible to C.
--
- Geoffrey Keating <***@geoffk.org>
Fergus Henderson
2002-11-07 11:29:21 UTC
Permalink
Post by Geoff Keating
Post by Richard Henderson
Post by Mark Mitchell
I do not think we should add exception support of any kind to GNU C; use
C++ if you want exceptions.
See elsewhere about resistance compiling libc with a c++ compiler.
It wouldn't work, anyway. User programs can also use
pthread_cleanup_push/pthread_cleanup_pop, in C, so whatever mechanism
is used for that must be accessible to C.
How about the following?
This uses the C++ compiler's exception support, and is accessible from C.
It makes use of the GNU C nested function extension.
The main drawback that I can see is that it is not as efficient as possible.

/* pthread.h */

void __pthread_try_finally(void (*)(void), void (*)(void *), void *, int);

#define pthread_cleanup_push(__routine,__arg) \
{ \
void (*__cleanup_routine)(void *) = __routine; \
void *__cleanup_arg = __arg; \
void __body(void) {

#define pthread_cleanup_pop(__call_cleanup) \
} \
__pthread_try_finally(__body, __cleanup_routine, __cleanup_arg, \
__call_cleanup); \
}

/* pthread_try_finally.cpp */

extern "C" {

void
__pthread_try_finally(
void (*body)(void),
void (*cleanup_routine)(void *),
void (*cleanup_arg)(void),
int call_cleanup)
{
class S {
void (*cleanup_routine)(void *);
void (*cleanup_arg)(void);
int call_cleanup;
public:
S(void (*cleanup_routine)(void *),
void (*cleanup_arg)(void),
int call_cleanup)
{
this->cleanup_routine = cleanup_routine;
this->cleanup_arg = cleanup_arg;
this->call_cleanup = call_cleanup;
}
~S()
{
if (call_cleanup)
(*cleanup_routine)(cleanup_arg);
}
} s(cleanup_routine, cleanup_arg, call_cleanup);

(*body)();
}

}
--
Fergus Henderson <***@cs.mu.oz.au> | "I have always known that the pursuit
The University of Melbourne | of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.
Jakub Jelinek
2002-11-07 12:22:46 UTC
Permalink
Post by Fergus Henderson
Post by Geoff Keating
Post by Richard Henderson
Post by Mark Mitchell
I do not think we should add exception support of any kind to GNU C; use
C++ if you want exceptions.
See elsewhere about resistance compiling libc with a c++ compiler.
It wouldn't work, anyway. User programs can also use
pthread_cleanup_push/pthread_cleanup_pop, in C, so whatever mechanism
is used for that must be accessible to C.
How about the following?
This uses the C++ compiler's exception support, and is accessible from C.
It makes use of the GNU C nested function extension.
The main drawback that I can see is that it is not as efficient as possible.
s/not efficient as possible/completely inefficient/.
Plus it is incorrect too as pthread_cleanup_p{ush,op} implementation - the
cleanup is to be run if cancellation happens no matter whether __call_cleanup
is zero or non-zero.

This would slow e.g. stdio a lot.
ATM if libpthread is not linked in, __libc_cleanup_region_start
and __libc_cleanup_region_end make no function calls at all, if it is
linked in, it makes functions calls but certainly the body is not forced to
be a separate function, forcing all parent's variables
to stack and accessing them through static chain pointers.

With __try/__finally, there would be no calls at all and no need
to duplicate the cleanup once in a separate function for the unwinder,
once when exiting the block (__libc_cleanup_region_end).

Furthemore, even when no exceptions are used, __try/__finally is a nice
way how to avoid very common goto gem:

x = malloc (somesize);
if (x == NULL)
{
ret = ENOMEM;
goto out_exit;
}
if (fstat (fd, &st) < 0)
{
ret = errno;
goto out_exit;
}
...
out_exit:
free (y);
free (x);
return ret;

One just writes:

__try
{
x = malloc (somesize);
if (x == NULL)
return ENOMEM;
if (fstat (fd, &st) < 0)
return errno;
...
return 0;
}
__finally
{
free (y);
free (x);
}

and magically the code is pthread cancellation aware.
Post by Fergus Henderson
/* pthread.h */
void __pthread_try_finally(void (*)(void), void (*)(void *), void *, int);
#define pthread_cleanup_push(__routine,__arg) \
{ \
void (*__cleanup_routine)(void *) = __routine; \
void *__cleanup_arg = __arg; \
void __body(void) {
#define pthread_cleanup_pop(__call_cleanup) \
} \
__pthread_try_finally(__body, __cleanup_routine, __cleanup_arg, \
__call_cleanup); \
}
/* pthread_try_finally.cpp */
extern "C" {
void
__pthread_try_finally(
void (*body)(void),
void (*cleanup_routine)(void *),
void (*cleanup_arg)(void),
int call_cleanup)
{
class S {
void (*cleanup_routine)(void *);
void (*cleanup_arg)(void);
int call_cleanup;
S(void (*cleanup_routine)(void *),
void (*cleanup_arg)(void),
int call_cleanup)
{
this->cleanup_routine = cleanup_routine;
this->cleanup_arg = cleanup_arg;
this->call_cleanup = call_cleanup;
}
~S()
{
if (call_cleanup)
(*cleanup_routine)(cleanup_arg);
}
} s(cleanup_routine, cleanup_arg, call_cleanup);
(*body)();
}
}
Jakub
Fergus Henderson
2002-11-07 13:13:32 UTC
Permalink
Post by Jakub Jelinek
Post by Fergus Henderson
Post by Geoff Keating
Post by Richard Henderson
Post by Mark Mitchell
I do not think we should add exception support of any kind to GNU C; use
C++ if you want exceptions.
See elsewhere about resistance compiling libc with a c++ compiler.
It wouldn't work, anyway. User programs can also use
pthread_cleanup_push/pthread_cleanup_pop, in C, so whatever mechanism
is used for that must be accessible to C.
How about the following?
This uses the C++ compiler's exception support, and is accessible from C.
It makes use of the GNU C nested function extension.
The main drawback that I can see is that it is not as efficient as possible.
s/not efficient as possible/completely inefficient/.
I agree that this approach is not efficient.

Describing it as "completely inefficient" is going a bit too far, IMHO.
Cleanups are most often used to protect code which acquires locks or
makes system calls, aren't they? Isn't acquiring a lock or making a
system call a fairly expensive operation anyway?

The worst cause of inefficiency for this approach is probably the icache
flush needed for the trampoline creation when you take the address of a
GNU C nested function. (I would like to see a different extension to fix
that: one that allowed you to take the address of a nested function in
a way that gave you two pointers -- a code pointer and a data pointer --
rather than a single pointer to a trampoline.)
Post by Jakub Jelinek
This would slow e.g. stdio a lot.
I was not saying that this code should be used for stdio.
I was just refuting the claim that "it wouldn't work".
Stdio (and other performance-critical routines that need cleanups)
could be implemented in C++. These macros could be implemented
differently (more efficiently) for C++.

Now, all that said, I would like to see support for exception handling
in GNU C (and for glibc to continue to be written in C rather than C++).
But I would like the support for exception handling to include support
for throwing and catching exceptions, not just try/finally. The current
proposal seems too limited to be useful for much else than glibc, IMHO.
Post by Jakub Jelinek
Plus it is incorrect too as pthread_cleanup_p{ush,op} implementation - the
cleanup is to be run if cancellation happens no matter whether __call_cleanup
is zero or non-zero.
Details, details ;-)

Sorry, I misread the spec for pthread_cleanup_{push,pop}. But this
point can easily be remedied. The code I posted was just proof of
concept, really.

"Designing grand concepts is fun, finding nitty little bugs is just work"
-- Brooks, in "The Mythical Man-Month".
--
Fergus Henderson <***@cs.mu.oz.au> | "I have always known that the pursuit
The University of Melbourne | of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.
Michael Matz
2002-11-07 13:46:56 UTC
Permalink
Hi,
Post by Fergus Henderson
Describing it as "completely inefficient" is going a bit too far, IMHO.
Cleanups are most often used to protect code which acquires locks or
makes system calls, aren't they? Isn't acquiring a lock
No it isn't. spinlocks e.g. can be implemented with inline asm, and have
no call in the fast path (which only takes some cycles) at all.
Post by Fergus Henderson
or making a system call a fairly expensive operation anyway?
The worst cause of inefficiency for this approach is probably the icache
flush needed for the trampoline creation when you take the address of a
GNU C nested function.
(I would like to see a different extension to fix that: one that
allowed you to take the address of a nested function in a way that
gave you two pointers -- a code pointer and a data pointer -- rather
than a single pointer to a trampoline.)
So instead of having an extension designed for doing what we want, you
want a) use another extension (which is neither designed, nor implemented
that well) to implement what we want, and b) add another not yet specified
extension to fix one problem with a) ?
Post by Fergus Henderson
Now, all that said, I would like to see support for exception handling
in GNU C
Although the extension only does cleanups not exceptions.
Post by Fergus Henderson
(and for glibc to continue to be written in C rather than C++).
But I would like the support for exception handling to include support
for throwing and catching exceptions, not just try/finally. The current
proposal seems too limited to be useful for much else than glibc, IMHO.
Please? A try/finally would be usefull nearly everywhere in C which deals
with resources. E.g. for something like this:

char *buf;
char *name;
FILE *f;
name = (char *) malloc (size);
if (!name)
return ERROR;
buf = (char *) malloc (size2);
if (!buf)
{
free (name);
return ERROR;
}
f = fopen (name, "r");
if (!f)
{
free (name);
free (buf);
return ERROR;
}
if (fread (buf, size2, 1, f) < 0)
{
fclose (f);
free (name);
free (buf);
return ERROR;
}
...

Well, you get the idea.


Ciao,
Michael.
Aldy Hernandez
2002-11-07 16:55:19 UTC
Permalink
Post by Fergus Henderson
But I would like the support for exception handling to include support
for throwing and catching exceptions, not just try/finally. The current
proposal seems too limited to be useful for much else than glibc, IMHO.
How are you going to handle catching exceptions, given that we have no
way to recognize certain types in C (templates, classes, etc)? See
rth's post about catching exceptions in C.

Besides, this is just a first step. What other compilers have done
(m$oft) is provide a try/finally/__except. Where the __except can
catch exceptions. But they've hand waived over the issue as well and
__except catches *any* exceptions (ala catch(...)) because they've
obviously run into the same problem of type interoperability between
languages.

try/finally is a way to run cleanups, not to implement a full fledged
EH mechanism for C. If you want that, you should be using C++.

Aldy
Mike Stump
2002-11-07 18:02:33 UTC
Permalink
Post by Aldy Hernandez
Post by Fergus Henderson
But I would like the support for exception handling to include support
for throwing and catching exceptions, not just try/finally. The current
proposal seems too limited to be useful for much else than glibc, IMHO.
How are you going to handle catching exceptions, given that we have no
way to recognize certain types in C (templates, classes, etc)? See
rth's post about catching exceptions in C.
This is easy. You conform to the C++ rtti system. Another way to view
that, is as the definition of the language independent runtime type
information. Once you do that, you can catch and throw any C++
compatible type. There are many in gcc, for example, int. Another
example:

struct exception {
int why;
char *description;
};

the possibilities are endless. The fact there might exist an example
of a type that doesn't work in C land isn't that interesting. If we
add the ability to throw and catch to C, we should of course just do it
correctly, the first time.
Alexandre Oliva
2002-11-07 20:36:01 UTC
Permalink
Post by Aldy Hernandez
Post by Fergus Henderson
But I would like the support for exception handling to include support
for throwing and catching exceptions, not just try/finally. The current
proposal seems too limited to be useful for much else than glibc, IMHO.
How are you going to handle catching exceptions, given that we have no
way to recognize certain types in C (templates, classes, etc)?
You don't recognize types. You just catch everything. Which sounds
perfectly reasonable from a C standpoint.

One of the things I liked about the C front-end of GCC was that it
could pretty much exercise any of the features available to all other
languages: nested functions, so that you could do the same as Pascal;
extended inline assembly, such that you didn't have to fallback to
assembly for everything; variable-sized types, like some other
languages support; expression statements, etc, etc. The nice thing
was that C could really be used as a high-level assembly, exercising
most (if not all) of the internal features the back-end would have to
implement for other languages anyway.

This broke down when Ada and C++ introduced exceptions as
language-specific extensions, and then, even though C++ exceptions
were later integrated into the generic back end framework, they were
still unavailable in C.

I personally think cleanups are a step in the right direction of
restoring the ability to exercise all features of the compiler in a
single front end. I don't really care if it's C, C++, treelang or
something else, but clearly to me C++ is not a good candidate since it
fails to support a number of other gcc extensions that the C front end
supports.

So I'm all for bringing back-end-supported exception handling support
features into our higher-level assembly. I wouldn't go as far as
supporting catches and throws right now, given that we don't have a
clean design for this feature, but try/finally looks terrifically
simple, and it's not like it's a new idea: as many others have pointed
out, other compilers already support it, so it's likely to gain
acceptance in the next round of C standardization. Even more so if we
choose to support it exactly like other compilers do.

The other advantage I see in offering not only clean-ups, but actual
exception catching and throwing in our high-level assembly, is that we
can then write glue code to convert exceptions from one language to
another. Currently, there's really no simple way to do it, to the
best of my knowledge. I'm not saying writing such glue code would be
simple, but at least someone wouldn't have to write such glue as a
number of functions that call one-another, each one implemented in a
separate exception-supporting language. Being able to unify the EH
framework and expose it to the user would even enable us to implement
EH supporting routines for languages in our higher-level assembly.

But then, again, for actual exception handling, we still need more
discussion and design, but for clean ups, there's really not much
possibility for variation in the design and syntax, and following
existing practice in other compilers and languages is clearly a plus.
--
Alexandre Oliva Enjoy Guarana', see http://www.ic.unicamp.br/~oliva/
Red Hat GCC Developer aoliva@{redhat.com, gcc.gnu.org}
CS PhD student at IC-Unicamp oliva@{lsd.ic.unicamp.br, gnu.org}
Free Software Evangelist Professional serial bug killer
Aldy Hernandez
2002-11-07 21:08:52 UTC
Permalink
Post by Alexandre Oliva
Post by Aldy Hernandez
How are you going to handle catching exceptions, given that we have no
way to recognize certain types in C (templates, classes, etc)?
You don't recognize types. You just catch everything. Which sounds
perfectly reasonable from a C standpoint.
Which is what try/finally/__except does, and which other C compilers
do. I can implement that. I'm just waiting for all the dust to
settle, and if this an acceptable construct.
Post by Alexandre Oliva
acceptance in the next round of C standardization. Even more so if we
choose to support it exactly like other compilers do.
Which is exactly what I did ;-).

Aldy
Continue reading on narkive:
Loading...