diff options
-rw-r--r-- | gcc/ChangeLog | 17 | ||||
-rw-r--r-- | gcc/c-common.c | 41 | ||||
-rw-r--r-- | gcc/c-common.h | 13 | ||||
-rw-r--r-- | gcc/c-convert.c | 2 | ||||
-rw-r--r-- | gcc/c-decl.c | 23 | ||||
-rw-r--r-- | gcc/c-parse.in | 3 | ||||
-rw-r--r-- | gcc/c-typeck.c | 49 | ||||
-rw-r--r-- | gcc/cp/ChangeLog | 5 | ||||
-rw-r--r-- | gcc/cp/typeck.c | 13 | ||||
-rw-r--r-- | gcc/ginclude/stdbool.h | 16 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 4 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/c99-bool-1.c | 262 |
12 files changed, 404 insertions, 44 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6d026b1..08d2edf 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,22 @@ 2000-11-13 Joseph S. Myers <jsm28@cam.ac.uk> + * c-common.c (boolean_increment): New function. + * c-common.h (enum c_tree_index): Add CTI_C_BOOL_TYPE, + CTI_C_BOOL_TRUE and CTI_C_BOOL_FALSE. + (c_bool_type_node, c_bool_true_node, c_bool_false_node): Define. + (boolean_increment): Declare. + * c-convert.c (convert): Allow for BOOLEAN_TYPE. + * c-decl.c (init_decl_processing): Create boolean nodes. + (finish_struct): Allow for _Bool bitfields. + * c-parse.in (reswords): Add _Bool. + (rid_to_yy): Allow for RID_BOOL. + * c-typeck.c (default_conversion): Make booleans promote to int. + (convert_arguments, build_unary_op, build_modify_expr, + convert_for_assignment): Allow for booleans. + * ginclude/stdbool.h: Make conforming to C99. + +2000-11-13 Joseph S. Myers <jsm28@cam.ac.uk> + * c-parse.in (c99_block_start, c99_block_end, c99_block_lineno_labeled_stmt): New. (simple_if, do_stmt_start): Use c99_block_lineno_labeled_stmt. diff --git a/gcc/c-common.c b/gcc/c-common.c index fdc2875..e0d8a45 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -6147,3 +6147,44 @@ c_expand_builtin_printf (arglist, target, tmode, modifier, ignore) (ignore ? const0_rtx : target), tmode, modifier); } + + +/* Given a boolean expression ARG, return a tree representing an increment + or decrement (as indicated by CODE) of ARG. The front end must check for + invalid cases (e.g., decrement in C++). */ +tree +boolean_increment (code, arg) + enum tree_code code; + tree arg; +{ + tree val; + tree true_res = (c_language == clk_cplusplus + ? boolean_true_node + : c_bool_true_node); + arg = stabilize_reference (arg); + switch (code) + { + case PREINCREMENT_EXPR: + val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, true_res); + break; + case POSTINCREMENT_EXPR: + val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, true_res); + arg = save_expr (arg); + val = build (COMPOUND_EXPR, TREE_TYPE (arg), val, arg); + val = build (COMPOUND_EXPR, TREE_TYPE (arg), arg, val); + break; + case PREDECREMENT_EXPR: + val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, invert_truthvalue (arg)); + break; + case POSTDECREMENT_EXPR: + val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, invert_truthvalue (arg)); + arg = save_expr (arg); + val = build (COMPOUND_EXPR, TREE_TYPE (arg), val, arg); + val = build (COMPOUND_EXPR, TREE_TYPE (arg), arg, val); + break; + default: + abort (); + } + TREE_SIDE_EFFECTS (val) = 1; + return val; +} diff --git a/gcc/c-common.h b/gcc/c-common.h index 41e771c..06c9c61 100644 --- a/gcc/c-common.h +++ b/gcc/c-common.h @@ -130,9 +130,14 @@ enum c_tree_index CTI_STRING_TYPE, CTI_CONST_STRING_TYPE, + /* Type for boolean expressions (bool in C++, int in C). */ CTI_BOOLEAN_TYPE, CTI_BOOLEAN_TRUE, CTI_BOOLEAN_FALSE, + /* C99's _Bool type. */ + CTI_C_BOOL_TYPE, + CTI_C_BOOL_TRUE, + CTI_C_BOOL_FALSE, CTI_DEFAULT_FUNCTION_TYPE, CTI_VOID_LIST, @@ -172,6 +177,10 @@ enum c_tree_index #define boolean_true_node c_global_trees[CTI_BOOLEAN_TRUE] #define boolean_false_node c_global_trees[CTI_BOOLEAN_FALSE] +#define c_bool_type_node c_global_trees[CTI_C_BOOL_TYPE] +#define c_bool_true_node c_global_trees[CTI_C_BOOL_TRUE] +#define c_bool_false_node c_global_trees[CTI_C_BOOL_FALSE] + #define char_array_type_node c_global_trees[CTI_CHAR_ARRAY_TYPE] #define wchar_array_type_node c_global_trees[CTI_WCHAR_ARRAY_TYPE] #define int_array_type_node c_global_trees[CTI_INT_ARRAY_TYPE] @@ -713,6 +722,10 @@ extern tree expand_tree_builtin PARAMS ((tree, tree, tree)); extern tree decl_constant_value PARAMS ((tree)); +/* Handle increment and decrement of boolean types. */ +extern tree boolean_increment PARAMS ((enum tree_code, + tree)); + /* Hook currently used only by the C++ front end to reset internal state after entering or leaving a header file. */ extern void extract_interface_info PARAMS ((void)); diff --git a/gcc/c-convert.c b/gcc/c-convert.c index 5d7ea00..7f57725 100644 --- a/gcc/c-convert.c +++ b/gcc/c-convert.c @@ -88,6 +88,8 @@ convert (type, expr) #endif if (code == INTEGER_TYPE || code == ENUMERAL_TYPE) return fold (convert_to_integer (type, e)); + if (code == BOOLEAN_TYPE) + return fold (build1 (NOP_EXPR, type, truthvalue_conversion (expr))); if (code == POINTER_TYPE || code == REFERENCE_TYPE) return fold (convert_to_pointer (type, e)); if (code == REAL_TYPE) diff --git a/gcc/c-decl.c b/gcc/c-decl.c index 45dbb2c..d9ceac8 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -3113,6 +3113,19 @@ init_decl_processing () boolean_true_node = integer_one_node; boolean_false_node = integer_zero_node; + /* With GCC, C99's _Bool is always of size 1. */ + c_bool_type_node = make_unsigned_type (CHAR_TYPE_SIZE); + TREE_SET_CODE (c_bool_type_node, BOOLEAN_TYPE); + TYPE_MAX_VALUE (c_bool_type_node) = build_int_2 (1, 0); + TREE_TYPE (TYPE_MAX_VALUE (c_bool_type_node)) = c_bool_type_node; + TYPE_PRECISION (c_bool_type_node) = 1; + pushdecl (build_decl (TYPE_DECL, get_identifier ("_Bool"), + c_bool_type_node)); + c_bool_false_node = build_int_2 (0, 0); + TREE_TYPE (c_bool_false_node) = c_bool_type_node; + c_bool_true_node = build_int_2 (1, 0); + TREE_TYPE (c_bool_true_node) = c_bool_type_node; + string_type_node = build_pointer_type (char_type_node); const_string_type_node = build_pointer_type (build_type_variant (char_type_node, 1, 0)); @@ -5431,6 +5444,7 @@ finish_struct (t, fieldlist, attributes) /* Detect invalid bit-field type. */ if (DECL_INITIAL (x) && TREE_CODE (TREE_TYPE (x)) != INTEGER_TYPE + && TREE_CODE (TREE_TYPE (x)) != BOOLEAN_TYPE && TREE_CODE (TREE_TYPE (x)) != ENUMERAL_TYPE) { error_with_decl (x, "bit-field `%s' has invalid type"); @@ -5440,6 +5454,7 @@ finish_struct (t, fieldlist, attributes) if (DECL_INITIAL (x) && pedantic && TYPE_MAIN_VARIANT (TREE_TYPE (x)) != integer_type_node && TYPE_MAIN_VARIANT (TREE_TYPE (x)) != unsigned_type_node + && TYPE_MAIN_VARIANT (TREE_TYPE (x)) != c_bool_type_node /* Accept an enum that's equivalent to int or unsigned int. */ && !(TREE_CODE (TREE_TYPE (x)) == ENUMERAL_TYPE && (TYPE_PRECISION (TREE_TYPE (x)) @@ -5450,10 +5465,14 @@ finish_struct (t, fieldlist, attributes) field widths. */ if (DECL_INITIAL (x)) { + int max_width; + if (TYPE_MAIN_VARIANT (TREE_TYPE (x)) == c_bool_type_node) + max_width = CHAR_TYPE_SIZE; + else + max_width = TYPE_PRECISION (TREE_TYPE (x)); if (tree_int_cst_sgn (DECL_INITIAL (x)) < 0) error_with_decl (x, "negative width in bit-field `%s'"); - else if (0 < compare_tree_int (DECL_INITIAL (x), - TYPE_PRECISION (TREE_TYPE (x)))) + else if (0 < compare_tree_int (DECL_INITIAL (x), max_width)) pedwarn_with_decl (x, "width of `%s' exceeds its type"); else if (integer_zerop (DECL_INITIAL (x)) && DECL_NAME (x) != 0) error_with_decl (x, "zero width for bit-field `%s'"); diff --git a/gcc/c-parse.in b/gcc/c-parse.in index bbce2ca..e4daad6 100644 --- a/gcc/c-parse.in +++ b/gcc/c-parse.in @@ -2832,6 +2832,7 @@ struct resword static const struct resword reswords[] = { + { "_Bool", RID_BOOL, 0 }, { "_Complex", RID_COMPLEX, 0 }, { "__alignof", RID_ALIGNOF, 0 }, { "__alignof__", RID_ALIGNOF, 0 }, @@ -3007,7 +3008,7 @@ static const short rid_to_yy[RID_MAX] = /* RID_PTRVALUE */ PTR_VALUE, /* C++ */ - /* RID_BOOL */ 0, + /* RID_BOOL */ TYPESPEC, /* RID_WCHAR */ 0, /* RID_CLASS */ 0, /* RID_PUBLIC */ 0, diff --git a/gcc/c-typeck.c b/gcc/c-typeck.c index 08523b5..6705440 100644 --- a/gcc/c-typeck.c +++ b/gcc/c-typeck.c @@ -947,6 +947,9 @@ default_conversion (exp) return convert (integer_type_node, exp); } + if (code == BOOLEAN_TYPE) + return convert (integer_type_node, exp); + if (flag_traditional && !flag_allow_single_precision && TYPE_MAIN_VARIANT (type) == float_type_node) return convert (double_type_node, exp); @@ -1733,7 +1736,8 @@ convert_arguments (typelist, values, name, fundecl) if (PROMOTE_PROTOTYPES && (TREE_CODE (type) == INTEGER_TYPE - || TREE_CODE (type) == ENUMERAL_TYPE) + || TREE_CODE (type) == ENUMERAL_TYPE + || TREE_CODE (type) == BOOLEAN_TYPE) && (TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node))) parmval = default_conversion (parmval); } @@ -2790,7 +2794,7 @@ build_unary_op (code, xarg, noconvert) if (typecode == ERROR_MARK) return error_mark_node; - if (typecode == ENUMERAL_TYPE) + if (typecode == ENUMERAL_TYPE || typecode == BOOLEAN_TYPE) typecode = INTEGER_TYPE; switch (code) @@ -2985,18 +2989,23 @@ build_unary_op (code, xarg, noconvert) else { tree incremented, modify, value; - arg = stabilize_reference (arg); - if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR) - value = arg; + if (TREE_CODE (TREE_TYPE (arg)) == BOOLEAN_TYPE) + value = boolean_increment (code, arg); else - value = save_expr (arg); - incremented = build (((code == PREINCREMENT_EXPR - || code == POSTINCREMENT_EXPR) - ? PLUS_EXPR : MINUS_EXPR), - argtype, value, inc); - TREE_SIDE_EFFECTS (incremented) = 1; - modify = build_modify_expr (arg, NOP_EXPR, incremented); - value = build (COMPOUND_EXPR, TREE_TYPE (arg), modify, value); + { + arg = stabilize_reference (arg); + if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR) + value = arg; + else + value = save_expr (arg); + incremented = build (((code == PREINCREMENT_EXPR + || code == POSTINCREMENT_EXPR) + ? PLUS_EXPR : MINUS_EXPR), + argtype, value, inc); + TREE_SIDE_EFFECTS (incremented) = 1; + modify = build_modify_expr (arg, NOP_EXPR, incremented); + value = build (COMPOUND_EXPR, TREE_TYPE (arg), modify, value); + } TREE_USED (value) = 1; return value; } @@ -3021,7 +3030,10 @@ build_unary_op (code, xarg, noconvert) || code == POSTINCREMENT_EXPR) ? "increment" : "decrement")); - val = build (code, TREE_TYPE (arg), arg, inc); + if (TREE_CODE (TREE_TYPE (arg)) == BOOLEAN_TYPE) + val = boolean_increment (code, arg); + else + val = build (code, TREE_TYPE (arg), arg, inc); TREE_SIDE_EFFECTS (val) = 1; val = convert (result_type, val); if (TREE_CODE (val) != code) @@ -3969,6 +3981,7 @@ build_modify_expr (lhs, modifycode, rhs) if (TREE_CODE (lhs) == COMPONENT_REF && (TREE_CODE (lhstype) == INTEGER_TYPE + || TREE_CODE (lhstype) == BOOLEAN_TYPE || TREE_CODE (lhstype) == REAL_TYPE || TREE_CODE (lhstype) == ENUMERAL_TYPE)) lhstype = TREE_TYPE (get_unwidened (lhs, 0)); @@ -4084,9 +4097,11 @@ convert_for_assignment (type, rhs, errtype, fundecl, funname, parmnum) } /* Arithmetic types all interconvert, and enum is treated like int. */ else if ((codel == INTEGER_TYPE || codel == REAL_TYPE - || codel == ENUMERAL_TYPE || codel == COMPLEX_TYPE) + || codel == ENUMERAL_TYPE || codel == COMPLEX_TYPE + || codel == BOOLEAN_TYPE) && (coder == INTEGER_TYPE || coder == REAL_TYPE - || coder == ENUMERAL_TYPE || coder == COMPLEX_TYPE)) + || coder == ENUMERAL_TYPE || coder == COMPLEX_TYPE + || coder == BOOLEAN_TYPE)) return convert_and_check (type, rhs); /* Conversion to a transparent union from its member types. @@ -4266,6 +4281,8 @@ convert_for_assignment (type, rhs, errtype, fundecl, funname, parmnum) errtype, funname, parmnum); return convert (type, rhs); } + else if (codel == BOOLEAN_TYPE && coder == POINTER_TYPE) + return convert (type, rhs); if (!errtype) { diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 1dfd37a..7e6d6f7 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,8 @@ +2000-11-13 Joseph S. Myers <jsm28@cam.ac.uk> + + * typeck.c (build_unary_op): Use boolean_increment from + c-common.c, moving the relevant code there. + 2000-11-11 Jason Merrill <jason@redhat.com> * typeck.c (mark_addressable): Don't call put_var_into_stack. diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 06e6dee..0c628bb 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -4580,18 +4580,7 @@ build_unary_op (code, xarg, noconvert) my patch to expand_increment. (jason) */ val = build (code, TREE_TYPE (arg), arg, inc); #else - if (code == POSTINCREMENT_EXPR) - { - arg = stabilize_reference (arg); - val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, - boolean_true_node); - arg = save_expr (arg); - val = build (COMPOUND_EXPR, TREE_TYPE (arg), val, arg); - val = build (COMPOUND_EXPR, TREE_TYPE (arg), arg, val); - } - else - val = build (MODIFY_EXPR, TREE_TYPE (arg), arg, - boolean_true_node); + val = boolean_increment (code, arg); #endif } else diff --git a/gcc/ginclude/stdbool.h b/gcc/ginclude/stdbool.h index 1e49dc0..33f7d3d4 100644 --- a/gcc/ginclude/stdbool.h +++ b/gcc/ginclude/stdbool.h @@ -32,20 +32,10 @@ Boston, MA 02111-1307, USA. */ #define _STDBOOL_H #ifndef __cplusplus -/* The type `_Bool' must promote to `int' or `unsigned int'. The constants - `true' and `false' must have the value 0 and 1 respectively. */ -typedef enum - { - false = 0, - true = 1 - } _Bool; - -/* The names `true' and `false' must also be made available as macros. */ -#define false false -#define true true -/* The macro `bool', which may be undefined, expands to _Bool. */ -#define bool _Bool +#define bool _Bool +#define true 1 +#define false 0 #else /* __cplusplus */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index cf20ec6..30345b6 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,9 @@ 2000-11-13 Joseph S. Myers <jsm28@cam.ac.uk> + * gcc.dg/c99-bool-1.c: New test. + +2000-11-13 Joseph S. Myers <jsm28@cam.ac.uk> + * gcc.dg/c99-scope-1.c: Remove xfail. * gcc.dg/c99-scope-2.c: New test. diff --git a/gcc/testsuite/gcc.dg/c99-bool-1.c b/gcc/testsuite/gcc.dg/c99-bool-1.c new file mode 100644 index 0000000..632c486 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c99-bool-1.c @@ -0,0 +1,262 @@ +/* Test for _Bool and <stdbool.h> in C99. */ +/* Origin: Joseph Myers <jsm28@cam.ac.uk> */ +/* { dg-do run } */ +/* { dg-options "-std=iso9899:1999 -pedantic-errors" } */ + +/* _Bool must be a builtin type. */ + +_Bool foo; + +#include <stdbool.h> + +/* Three macros must be integer constant expressions suitable for use + in #if. +*/ + +#if !defined(true) || (true != 1) +#error "bad stdbool true" /* { dg-bogus "#error" "bad stdbool.h" } */ +#endif + +#if !defined(false) || (false != 0) +#error "bad stdbool false" /* { dg-bogus "#error" "bad stdbool.h" } */ +#endif + +#if !defined(__bool_true_false_are_defined) || (__bool_true_false_are_defined != 1) +#error "bad stdbool __bool_true_false_are_defined" /* { dg-bogus "#error" "bad stdbool.h" } */ +#endif + +int a = true; +int b = false; +int c = __bool_true_false_are_defined; + +struct foo +{ + _Bool a : 1; + _Bool b : 2; + _Bool c : 7; +} sf; + +#define str(x) xstr(x) +#define xstr(x) #x + + +extern void abort (void); +extern void exit (int); +extern int strcmp (const char *, const char *); + +int +main (void) +{ + /* The macro `bool' must expand to _Bool. */ + const char *t = str (bool); + _Bool u, v; + if (strcmp (t, "_Bool")) + abort (); + if (a != 1 || b != 0 || c != 1) + abort (); + /* Casts to _Bool have a specified behaviour. */ + if ((int)(_Bool)2 != 1) + abort (); + if ((int)(_Bool)0.2 != 1) + abort (); + /* Pointers may be assigned to _Bool. */ + if ((u = t) != 1) + abort (); + /* _Bool may be used to subscript arrays. */ + u = 0; + if (t[u] != '_') + abort (); + if (u[t] != '_') + abort (); + u = 1; + if (t[u] != 'B') + abort (); + if (u[t] != 'B') + abort (); + /* Test increment and decrement operators. */ + u = 0; + if (u++ != 0) + abort (); + if (u != 1) + abort (); + if (u++ != 1) + abort (); + if (u != 1) + abort (); + u = 0; + if (++u != 1) + abort (); + if (u != 1) + abort (); + if (++u != 1) + abort (); + if (u != 1) + abort (); + u = 0; + if (u-- != 0) + abort (); + if (u != 1) + abort (); + if (u-- != 1) + abort (); + if (u != 0) + abort (); + u = 0; + if (--u != 1) + abort (); + if (u != 1) + abort (); + if (--u != 0) + abort (); + if (u != 0) + abort (); + /* Test unary + - ~ !. */ + u = 0; + if (+u != 0) + abort (); + if (-u != 0) + abort (); + u = 1; + if (+u != 1) + abort (); + if (-u != -1) + abort (); + u = 2; + if (+u != 1) + abort (); + if (-u != -1) + abort (); + u = 0; + if (~u != ~(int)0) + abort (); + u = 1; + if (~u != ~(int)1) + abort (); + u = 0; + if (!u != 1) + abort (); + u = 1; + if (!u != 0) + abort (); + /* Test arithmetic * / % + - (which all apply promotions). */ + u = 0; + if (u + 2 != 2) + abort (); + u = 1; + if (u * 4 != 4) + abort (); + if (u % 3 != 1) + abort (); + if (u / 1 != 1) + abort (); + if (4 / u != 4) + abort (); + if (u - 7 != -6) + abort (); + /* Test bitwise shift << >>. */ + u = 1; + if (u << 1 != 2) + abort (); + if (u >> 1 != 0) + abort (); + /* Test relational and equality operators < > <= >= == !=. */ + u = 0; + v = 0; + if (u < v || u > v || !(u <= v) || !(u >= v) || !(u == v) || u != v) + abort (); + u = 0; + v = 1; + if (!(u < v) || u > v || !(u <= v) || u >= v || u == v || !(u != v)) + abort (); + /* Test bitwise operators & ^ |. */ + u = 1; + if ((u | 2) != 3) + abort (); + if ((u ^ 3) != 2) + abort (); + if ((u & 1) != 1) + abort (); + if ((u & 0) != 0) + abort (); + /* Test logical && ||. */ + u = 0; + v = 1; + if (!(u || v)) + abort (); + if (!(v || u)) + abort (); + if (u && v) + abort (); + if (v && u) + abort (); + u = 1; + v = 1; + if (!(u && v)) + abort (); + /* Test conditional ? :. */ + u = 0; + if ((u ? 4 : 7) != 7) + abort (); + u = 1; + v = 0; + if ((1 ? u : v) != 1) + abort (); + if ((1 ? 4 : u) != 4) + abort (); + /* Test assignment operators = *= /= %= += -= <<= >>= &= ^= |=. */ + if ((u = 2) != 1) + abort (); + if (u != 1) + abort (); + if ((u *= -1) != 1) + abort (); + if (u != 1) + abort (); + if ((u /= 2) != 0) + abort (); + if ((u += 3) != 1) + abort (); + if ((u -= 1) != 0) + abort (); + u = 1; + if ((u <<= 4) != 1) + abort (); + if ((u >>= 1) != 0) + abort (); + u = 1; + if ((u &= 0) != 0) + abort (); + if ((u |= 2) != 1) + abort (); + if ((u ^= 3) != 1) + abort (); + /* Test comma expressions. */ + u = 1; + if ((4, u) != 1) + abort (); + /* Test bitfields. */ + { + int i; + for (i = 0; i < sizeof (struct foo); i++) + *((unsigned char *)&sf + i) = (unsigned char) -1; + sf.a = 1; + if (sf.a != 1) + abort (); + sf.b = 1; + if (sf.b != 1) + abort (); + sf.c = 1; + if (sf.c != 1) + abort (); + sf.a = 0; + if (sf.a != 0) + abort (); + sf.b = 0; + if (sf.b != 0) + abort (); + sf.c = 0; + if (sf.c != 0) + abort (); + } + exit (0); +} |