1 | /* Handle exceptional things in C++. |
2 | Copyright (C) 1989-2024 Free Software Foundation, Inc. |
3 | Contributed by Michael Tiemann <tiemann@cygnus.com> |
4 | Rewritten by Mike Stump <mrs@cygnus.com>, based upon an |
5 | initial re-implementation courtesy Tad Hunt. |
6 | |
7 | This file is part of GCC. |
8 | |
9 | GCC is free software; you can redistribute it and/or modify |
10 | it under the terms of the GNU General Public License as published by |
11 | the Free Software Foundation; either version 3, or (at your option) |
12 | any later version. |
13 | |
14 | GCC is distributed in the hope that it will be useful, |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | GNU General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU General Public License |
20 | along with GCC; see the file COPYING3. If not see |
21 | <http://www.gnu.org/licenses/>. */ |
22 | |
23 | |
24 | #include "config.h" |
25 | #include "system.h" |
26 | #include "coretypes.h" |
27 | #include "cp-tree.h" |
28 | #include "stringpool.h" |
29 | #include "trans-mem.h" |
30 | #include "attribs.h" |
31 | #include "tree-iterator.h" |
32 | #include "target.h" |
33 | |
34 | static void push_eh_cleanup (tree); |
35 | static tree prepare_eh_type (tree); |
36 | static tree do_begin_catch (void); |
37 | static int dtor_nothrow (tree); |
38 | static tree do_end_catch (tree); |
39 | static void initialize_handler_parm (tree, tree); |
40 | static tree do_allocate_exception (tree); |
41 | static tree wrap_cleanups_r (tree *, int *, void *); |
42 | static bool is_admissible_throw_operand_or_catch_parameter (tree, bool, |
43 | tsubst_flags_t); |
44 | |
45 | /* Sets up all the global eh stuff that needs to be initialized at the |
46 | start of compilation. */ |
47 | |
48 | void |
49 | init_exception_processing (void) |
50 | { |
51 | tree tmp; |
52 | |
53 | /* void std::terminate (); */ |
54 | push_nested_namespace (std_node); |
55 | tmp = build_function_type_list (void_type_node, NULL_TREE); |
56 | terminate_fn = build_cp_library_fn_ptr ("terminate" , tmp, |
57 | ECF_NOTHROW | ECF_NORETURN |
58 | | ECF_COLD); |
59 | gcc_checking_assert (TREE_THIS_VOLATILE (terminate_fn) |
60 | && TREE_NOTHROW (terminate_fn)); |
61 | pop_nested_namespace (std_node); |
62 | |
63 | /* void __cxa_call_unexpected(void *); */ |
64 | tmp = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); |
65 | call_unexpected_fn |
66 | = push_throw_library_fn (get_identifier ("__cxa_call_unexpected" ), tmp); |
67 | call_terminate_fn |
68 | = push_library_fn (get_identifier ("__cxa_call_terminate" ), tmp, NULL_TREE, |
69 | ECF_NORETURN | ECF_COLD | ECF_NOTHROW); |
70 | } |
71 | |
72 | /* Returns an expression to be executed if an unhandled exception is |
73 | propagated out of a cleanup region. */ |
74 | |
75 | tree |
76 | cp_protect_cleanup_actions (void) |
77 | { |
78 | /* [except.terminate] |
79 | |
80 | When the destruction of an object during stack unwinding exits |
81 | using an exception ... void terminate(); is called. */ |
82 | return call_terminate_fn; |
83 | } |
84 | |
85 | static tree |
86 | prepare_eh_type (tree type) |
87 | { |
88 | if (type == NULL_TREE) |
89 | return type; |
90 | if (type == error_mark_node) |
91 | return error_mark_node; |
92 | |
93 | /* peel back references, so they match. */ |
94 | type = non_reference (type); |
95 | |
96 | /* Peel off cv qualifiers. */ |
97 | type = TYPE_MAIN_VARIANT (type); |
98 | |
99 | /* Functions and arrays decay to pointers. */ |
100 | type = type_decays_to (type); |
101 | |
102 | return type; |
103 | } |
104 | |
105 | /* Return the type info for TYPE as used by EH machinery. */ |
106 | tree |
107 | eh_type_info (tree type) |
108 | { |
109 | if (type == NULL_TREE || type == error_mark_node) |
110 | return type; |
111 | |
112 | return get_tinfo_decl (type); |
113 | } |
114 | |
115 | /* Build the address of a typeinfo decl for use in the runtime |
116 | matching field of the exception model. */ |
117 | |
118 | tree |
119 | build_eh_type_type (tree type) |
120 | { |
121 | tree exp = eh_type_info (type); |
122 | |
123 | if (!exp) |
124 | return NULL; |
125 | |
126 | mark_used (exp); |
127 | |
128 | return convert (ptr_type_node, build_address (exp)); |
129 | } |
130 | |
131 | tree |
132 | build_exc_ptr (void) |
133 | { |
134 | return build_call_n (builtin_decl_explicit (fncode: BUILT_IN_EH_POINTER), |
135 | 1, integer_zero_node); |
136 | } |
137 | |
138 | /* Declare an exception ABI entry point called NAME. |
139 | ECF are the library flags, RTYPE the return type and ARGS[NARGS] |
140 | the parameter types. We return the DECL -- which might be one |
141 | found via the symbol table pushing, if the user already declared |
142 | it. If we pushed a new decl, the user will see it. */ |
143 | |
144 | static tree |
145 | declare_library_fn_1 (const char *name, int ecf, |
146 | tree rtype, int nargs, tree args[]) |
147 | { |
148 | tree ident = get_identifier (name); |
149 | tree except = ecf & ECF_NOTHROW ? empty_except_spec : NULL_TREE; |
150 | |
151 | /* Make a new decl. */ |
152 | tree arg_list = void_list_node; |
153 | for (unsigned ix = nargs; ix--;) |
154 | arg_list = tree_cons (NULL_TREE, args[ix], arg_list); |
155 | tree fntype = build_function_type (rtype, arg_list); |
156 | tree res = push_library_fn (ident, fntype, except, ecf); |
157 | |
158 | return res; |
159 | } |
160 | |
161 | /* Find or declare a function NAME, returning RTYPE, taking a single |
162 | parameter PTYPE, with an empty exception specification. ECF are the |
163 | library fn flags. If TM_ECF is non-zero, also find or create a |
164 | transaction variant and record it as a replacement, when flag_tm is |
165 | in effect. |
166 | |
167 | Note that the C++ ABI document does not have a throw-specifier on |
168 | the routines declared below via this function. The declarations |
169 | are consistent with the actual implementations in libsupc++. */ |
170 | |
171 | static tree |
172 | declare_library_fn (const char *name, tree rtype, tree ptype, |
173 | int ecf, int tm_ecf) |
174 | { |
175 | tree res = declare_library_fn_1 (name, ecf, rtype, nargs: ptype ? 1 : 0, args: &ptype); |
176 | if (res == error_mark_node) |
177 | return res; |
178 | |
179 | if (tm_ecf && flag_tm) |
180 | { |
181 | char *tm_name = concat ("_ITM_" , name + 2, NULL_TREE); |
182 | |
183 | tree tm_fn = declare_library_fn_1 (name: tm_name, ecf: ecf | tm_ecf, rtype, |
184 | nargs: ptype ? 1 : 0, args: &ptype); |
185 | free (ptr: tm_name); |
186 | if (tm_fn != error_mark_node) |
187 | record_tm_replacement (res, tm_fn); |
188 | } |
189 | |
190 | return res; |
191 | } |
192 | |
193 | /* Build up a call to __cxa_get_exception_ptr so that we can build a |
194 | copy constructor for the thrown object. */ |
195 | |
196 | static tree |
197 | do_get_exception_ptr (void) |
198 | { |
199 | if (!get_exception_ptr_fn) |
200 | /* Declare void* __cxa_get_exception_ptr (void *) throw(). */ |
201 | get_exception_ptr_fn |
202 | = declare_library_fn (name: "__cxa_get_exception_ptr" , |
203 | ptr_type_node, ptr_type_node, |
204 | ECF_NOTHROW | ECF_PURE | ECF_LEAF | ECF_TM_PURE, |
205 | tm_ecf: 0); |
206 | |
207 | return cp_build_function_call_nary (get_exception_ptr_fn, |
208 | tf_warning_or_error, |
209 | build_exc_ptr (), NULL_TREE); |
210 | } |
211 | |
212 | /* Build up a call to __cxa_begin_catch, to tell the runtime that the |
213 | exception has been handled. */ |
214 | |
215 | static tree |
216 | do_begin_catch (void) |
217 | { |
218 | if (!begin_catch_fn) |
219 | /* Declare void* __cxa_begin_catch (void *) throw(). */ |
220 | begin_catch_fn |
221 | = declare_library_fn (name: "__cxa_begin_catch" , |
222 | ptr_type_node, ptr_type_node, ECF_NOTHROW, |
223 | ECF_TM_PURE); |
224 | |
225 | return cp_build_function_call_nary (begin_catch_fn, tf_warning_or_error, |
226 | build_exc_ptr (), NULL_TREE); |
227 | } |
228 | |
229 | /* Returns nonzero if cleaning up an exception of type TYPE (which can be |
230 | NULL_TREE for a ... handler) will not throw an exception. */ |
231 | |
232 | static int |
233 | dtor_nothrow (tree type) |
234 | { |
235 | if (type == NULL_TREE || type == error_mark_node) |
236 | return 0; |
237 | |
238 | if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type)) |
239 | return 1; |
240 | |
241 | if (CLASSTYPE_LAZY_DESTRUCTOR (type)) |
242 | lazily_declare_fn (sfk_destructor, type); |
243 | |
244 | return TREE_NOTHROW (CLASSTYPE_DESTRUCTOR (type)); |
245 | } |
246 | |
247 | /* Build up a call to __cxa_end_catch, to destroy the exception object |
248 | for the current catch block if no others are currently using it. */ |
249 | |
250 | static tree |
251 | do_end_catch (tree type) |
252 | { |
253 | if (!end_catch_fn) |
254 | /* Declare void __cxa_end_catch (). |
255 | This can throw if the destructor for the exception throws. */ |
256 | end_catch_fn |
257 | = declare_library_fn (name: "__cxa_end_catch" , void_type_node, |
258 | NULL_TREE, ecf: 0, ECF_TM_PURE); |
259 | |
260 | tree cleanup = cp_build_function_call_vec (end_catch_fn, |
261 | NULL, tf_warning_or_error); |
262 | if (cleanup != error_mark_node) |
263 | TREE_NOTHROW (cleanup) = dtor_nothrow (type); |
264 | |
265 | return cleanup; |
266 | } |
267 | |
268 | /* This routine creates the cleanup for the current exception. */ |
269 | |
270 | static void |
271 | push_eh_cleanup (tree type) |
272 | { |
273 | finish_decl_cleanup (NULL_TREE, do_end_catch (type)); |
274 | } |
275 | |
276 | /* Wrap EXPR in a MUST_NOT_THROW_EXPR expressing that EXPR must |
277 | not throw any exceptions if COND is true. A condition of |
278 | NULL_TREE is treated as 'true'. */ |
279 | |
280 | tree |
281 | build_must_not_throw_expr (tree body, tree cond) |
282 | { |
283 | tree type = body ? TREE_TYPE (body) : void_type_node; |
284 | |
285 | if (!flag_exceptions) |
286 | return body; |
287 | |
288 | if (!cond) |
289 | /* OK, unconditional. */; |
290 | else |
291 | { |
292 | tree conv = NULL_TREE; |
293 | if (!type_dependent_expression_p (cond)) |
294 | conv = perform_implicit_conversion_flags (boolean_type_node, cond, |
295 | tf_warning_or_error, |
296 | LOOKUP_NORMAL); |
297 | if (tree inst = instantiate_non_dependent_or_null (conv)) |
298 | cond = cxx_constant_value (inst); |
299 | else |
300 | require_constant_expression (cond); |
301 | if (integer_zerop (cond)) |
302 | return body; |
303 | else if (integer_onep (cond)) |
304 | cond = NULL_TREE; |
305 | } |
306 | |
307 | return build2 (MUST_NOT_THROW_EXPR, type, body, cond); |
308 | } |
309 | |
310 | |
311 | /* Initialize the catch parameter DECL. */ |
312 | |
313 | static void |
314 | initialize_handler_parm (tree decl, tree exp) |
315 | { |
316 | tree init; |
317 | tree init_type; |
318 | |
319 | /* Make sure we mark the catch param as used, otherwise we'll get a |
320 | warning about an unused ((anonymous)). */ |
321 | TREE_USED (decl) = 1; |
322 | DECL_READ_P (decl) = 1; |
323 | |
324 | /* Figure out the type that the initializer is. Pointers are returned |
325 | adjusted by value from __cxa_begin_catch. Others are returned by |
326 | reference. */ |
327 | init_type = TREE_TYPE (decl); |
328 | if (!INDIRECT_TYPE_P (init_type)) |
329 | init_type = build_reference_type (init_type); |
330 | |
331 | /* Since pointers are passed by value, initialize a reference to |
332 | pointer catch parm with the address of the temporary. */ |
333 | if (TYPE_REF_P (init_type) |
334 | && TYPE_PTR_P (TREE_TYPE (init_type))) |
335 | exp = cp_build_addr_expr (exp, tf_warning_or_error); |
336 | |
337 | exp = ocp_convert (init_type, exp, CONV_IMPLICIT|CONV_FORCE_TEMP, 0, |
338 | tf_warning_or_error); |
339 | |
340 | init = convert_from_reference (exp); |
341 | |
342 | /* If the constructor for the catch parm exits via an exception, we |
343 | must call terminate. See eh23.C. */ |
344 | if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl))) |
345 | { |
346 | /* Generate the copy constructor call directly so we can wrap it. |
347 | See also expand_default_init. */ |
348 | init = ocp_convert (TREE_TYPE (decl), init, |
349 | CONV_IMPLICIT|CONV_FORCE_TEMP, 0, |
350 | tf_warning_or_error); |
351 | /* Force cleanups now to avoid nesting problems with the |
352 | MUST_NOT_THROW_EXPR. */ |
353 | init = fold_build_cleanup_point_expr (TREE_TYPE (init), expr: init); |
354 | init = build_must_not_throw_expr (body: init, NULL_TREE); |
355 | } |
356 | |
357 | decl = pushdecl (decl); |
358 | |
359 | start_decl_1 (decl, true); |
360 | cp_finish_decl (decl, init, /*init_const_expr_p=*/false, NULL_TREE, |
361 | LOOKUP_ONLYCONVERTING|DIRECT_BIND); |
362 | } |
363 | |
364 | |
365 | /* Routine to see if exception handling is turned on. |
366 | DO_WARN is nonzero if we want to inform the user that exception |
367 | handling is turned off. |
368 | |
369 | This is used to ensure that -fexceptions has been specified if the |
370 | compiler tries to use any exception-specific functions. */ |
371 | |
372 | static inline int |
373 | doing_eh (void) |
374 | { |
375 | if (! flag_exceptions) |
376 | { |
377 | static int warned = 0; |
378 | if (! warned) |
379 | { |
380 | error ("exception handling disabled, use %<-fexceptions%> to enable" ); |
381 | warned = 1; |
382 | } |
383 | return 0; |
384 | } |
385 | return 1; |
386 | } |
387 | |
388 | /* Call this to start a catch block. DECL is the catch parameter. */ |
389 | |
390 | tree |
391 | expand_start_catch_block (tree decl) |
392 | { |
393 | tree exp; |
394 | tree type, init; |
395 | |
396 | if (! doing_eh ()) |
397 | return NULL_TREE; |
398 | |
399 | if (decl) |
400 | { |
401 | if (!is_admissible_throw_operand_or_catch_parameter (decl, false, |
402 | tf_warning_or_error)) |
403 | decl = error_mark_node; |
404 | |
405 | type = prepare_eh_type (TREE_TYPE (decl)); |
406 | mark_used (eh_type_info (type)); |
407 | } |
408 | else |
409 | type = NULL_TREE; |
410 | |
411 | /* Call __cxa_end_catch at the end of processing the exception. */ |
412 | push_eh_cleanup (type); |
413 | |
414 | init = do_begin_catch (); |
415 | |
416 | /* If there's no decl at all, then all we need to do is make sure |
417 | to tell the runtime that we've begun handling the exception. */ |
418 | if (decl == NULL || decl == error_mark_node || init == error_mark_node) |
419 | finish_expr_stmt (init); |
420 | |
421 | /* If the C++ object needs constructing, we need to do that before |
422 | calling __cxa_begin_catch, so that std::uncaught_exception gets |
423 | the right value during the copy constructor. */ |
424 | else if (flag_use_cxa_get_exception_ptr |
425 | && TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl))) |
426 | { |
427 | exp = do_get_exception_ptr (); |
428 | if (exp != error_mark_node) |
429 | initialize_handler_parm (decl, exp); |
430 | finish_expr_stmt (init); |
431 | } |
432 | |
433 | /* Otherwise the type uses a bitwise copy, and we don't have to worry |
434 | about the value of std::uncaught_exception and therefore can do the |
435 | copy with the return value of __cxa_end_catch instead. */ |
436 | else |
437 | { |
438 | tree init_type = type; |
439 | |
440 | /* Pointers are passed by values, everything else by reference. */ |
441 | if (!TYPE_PTR_P (type)) |
442 | init_type = build_pointer_type (type); |
443 | if (init_type != TREE_TYPE (init)) |
444 | init = build1 (NOP_EXPR, init_type, init); |
445 | exp = create_temporary_var (init_type); |
446 | cp_finish_decl (exp, init, /*init_const_expr=*/false, |
447 | NULL_TREE, LOOKUP_ONLYCONVERTING); |
448 | DECL_REGISTER (exp) = 1; |
449 | initialize_handler_parm (decl, exp); |
450 | } |
451 | |
452 | return type; |
453 | } |
454 | |
455 | /* True if we are in a catch block within a catch block. Assumes that we are |
456 | in function scope. */ |
457 | |
458 | static bool |
459 | in_nested_catch (void) |
460 | { |
461 | int catches = 0; |
462 | |
463 | /* Scan through the template parameter scopes. */ |
464 | for (cp_binding_level *b = current_binding_level; |
465 | b->kind != sk_function_parms; |
466 | b = b->level_chain) |
467 | if (b->kind == sk_catch |
468 | && ++catches == 2) |
469 | return true; |
470 | return false; |
471 | } |
472 | |
473 | /* Call this to end a catch block. Its responsible for emitting the |
474 | code to handle jumping back to the correct place, and for emitting |
475 | the label to jump to if this catch block didn't match. */ |
476 | |
477 | void |
478 | expand_end_catch_block (void) |
479 | { |
480 | if (! doing_eh ()) |
481 | return; |
482 | |
483 | /* The exception being handled is rethrown if control reaches the end of |
484 | a handler of the function-try-block of a constructor or destructor. */ |
485 | if (in_function_try_handler |
486 | && (DECL_CONSTRUCTOR_P (current_function_decl) |
487 | || DECL_DESTRUCTOR_P (current_function_decl)) |
488 | && !in_nested_catch ()) |
489 | { |
490 | tree rethrow = build_throw (input_location, NULL_TREE, |
491 | tf_warning_or_error); |
492 | /* Disable all warnings for the generated rethrow statement. */ |
493 | suppress_warning (rethrow); |
494 | finish_expr_stmt (rethrow); |
495 | } |
496 | } |
497 | |
498 | tree |
499 | begin_eh_spec_block (void) |
500 | { |
501 | tree r; |
502 | location_t spec_location = DECL_SOURCE_LOCATION (current_function_decl); |
503 | |
504 | /* A noexcept specification (or throw() with -fnothrow-opt) is a |
505 | MUST_NOT_THROW_EXPR. */ |
506 | if (TYPE_NOEXCEPT_P (TREE_TYPE (current_function_decl))) |
507 | { |
508 | r = build_stmt (spec_location, MUST_NOT_THROW_EXPR, |
509 | NULL_TREE, NULL_TREE); |
510 | TREE_SIDE_EFFECTS (r) = 1; |
511 | } |
512 | else |
513 | r = build_stmt (spec_location, EH_SPEC_BLOCK, NULL_TREE, NULL_TREE); |
514 | add_stmt (r); |
515 | TREE_OPERAND (r, 0) = push_stmt_list (); |
516 | return r; |
517 | } |
518 | |
519 | void |
520 | finish_eh_spec_block (tree raw_raises, tree eh_spec_block) |
521 | { |
522 | tree raises; |
523 | |
524 | TREE_OPERAND (eh_spec_block, 0) |
525 | = pop_stmt_list (TREE_OPERAND (eh_spec_block, 0)); |
526 | |
527 | if (TREE_CODE (eh_spec_block) == MUST_NOT_THROW_EXPR) |
528 | return; |
529 | |
530 | /* Strip cv quals, etc, from the specification types. */ |
531 | for (raises = NULL_TREE; |
532 | raw_raises && TREE_VALUE (raw_raises); |
533 | raw_raises = TREE_CHAIN (raw_raises)) |
534 | { |
535 | tree type = prepare_eh_type (TREE_VALUE (raw_raises)); |
536 | tree tinfo = eh_type_info (type); |
537 | |
538 | mark_used (tinfo); |
539 | raises = tree_cons (NULL_TREE, type, raises); |
540 | } |
541 | |
542 | EH_SPEC_RAISES (eh_spec_block) = raises; |
543 | } |
544 | |
545 | /* Return a pointer to a buffer for an exception object of type TYPE. */ |
546 | |
547 | static tree |
548 | do_allocate_exception (tree type) |
549 | { |
550 | if (!allocate_exception_fn) |
551 | /* Declare void *__cxa_allocate_exception(size_t) throw(). */ |
552 | allocate_exception_fn |
553 | = declare_library_fn (name: "__cxa_allocate_exception" , |
554 | ptr_type_node, size_type_node, |
555 | ECF_NOTHROW | ECF_MALLOC | ECF_COLD, ECF_TM_PURE); |
556 | |
557 | return cp_build_function_call_nary (allocate_exception_fn, |
558 | tf_warning_or_error, |
559 | size_in_bytes (t: type), NULL_TREE); |
560 | } |
561 | |
562 | /* Call __cxa_free_exception from a cleanup. This is never invoked |
563 | directly, but see the comment for stabilize_throw_expr. */ |
564 | |
565 | static tree |
566 | do_free_exception (tree ptr) |
567 | { |
568 | if (!free_exception_fn) |
569 | /* Declare void __cxa_free_exception (void *) throw(). */ |
570 | free_exception_fn |
571 | = declare_library_fn (name: "__cxa_free_exception" , |
572 | void_type_node, ptr_type_node, |
573 | ECF_NOTHROW | ECF_LEAF, ECF_TM_PURE); |
574 | |
575 | return cp_build_function_call_nary (free_exception_fn, |
576 | tf_warning_or_error, ptr, NULL_TREE); |
577 | } |
578 | |
579 | /* Wrap all cleanups for TARGET_EXPRs in MUST_NOT_THROW_EXPR. |
580 | Called from build_throw via walk_tree_without_duplicates. */ |
581 | |
582 | static tree |
583 | wrap_cleanups_r (tree *tp, int *walk_subtrees, void * /*data*/) |
584 | { |
585 | tree exp = *tp; |
586 | tree cleanup; |
587 | |
588 | /* Don't walk into types. */ |
589 | if (TYPE_P (exp)) |
590 | { |
591 | *walk_subtrees = 0; |
592 | return NULL_TREE; |
593 | } |
594 | if (TREE_CODE (exp) != TARGET_EXPR) |
595 | return NULL_TREE; |
596 | |
597 | cleanup = TARGET_EXPR_CLEANUP (exp); |
598 | if (cleanup) |
599 | { |
600 | cleanup = build2 (MUST_NOT_THROW_EXPR, void_type_node, cleanup, |
601 | NULL_TREE); |
602 | TARGET_EXPR_CLEANUP (exp) = cleanup; |
603 | } |
604 | |
605 | /* Keep iterating. */ |
606 | return NULL_TREE; |
607 | } |
608 | |
609 | /* Build a throw expression. */ |
610 | |
611 | tree |
612 | build_throw (location_t loc, tree exp, tsubst_flags_t complain) |
613 | { |
614 | if (exp == error_mark_node) |
615 | return exp; |
616 | |
617 | if (processing_template_decl) |
618 | { |
619 | if (cfun) |
620 | current_function_returns_abnormally = 1; |
621 | exp = build_min (THROW_EXPR, void_type_node, exp); |
622 | SET_EXPR_LOCATION (exp, loc); |
623 | return exp; |
624 | } |
625 | |
626 | if (exp && null_node_p (expr: exp) && (complain & tf_warning)) |
627 | warning_at (loc, 0, |
628 | "throwing NULL, which has integral, not pointer type" ); |
629 | |
630 | if (exp && !is_admissible_throw_operand_or_catch_parameter (exp, |
631 | /*is_throw=*/true, |
632 | complain)) |
633 | return error_mark_node; |
634 | |
635 | if (! doing_eh ()) |
636 | return error_mark_node; |
637 | |
638 | if (exp) |
639 | { |
640 | tree throw_type; |
641 | tree temp_type; |
642 | tree cleanup; |
643 | tree object, ptr; |
644 | tree allocate_expr; |
645 | |
646 | /* The CLEANUP_TYPE is the internal type of a destructor. */ |
647 | if (!cleanup_type) |
648 | { |
649 | tree tmp = build_function_type_list (void_type_node, |
650 | ptr_type_node, NULL_TREE); |
651 | cleanup_type = build_pointer_type (tmp); |
652 | } |
653 | |
654 | if (!throw_fn) |
655 | { |
656 | tree args[3] = {ptr_type_node, ptr_type_node, cleanup_type}; |
657 | |
658 | throw_fn = declare_library_fn_1 (name: "__cxa_throw" , |
659 | ECF_NORETURN | ECF_XTHROW | ECF_COLD, |
660 | void_type_node, nargs: 3, args); |
661 | if (flag_tm && throw_fn != error_mark_node) |
662 | { |
663 | tree itm_fn = declare_library_fn_1 (name: "_ITM_cxa_throw" , |
664 | ECF_NORETURN | ECF_XTHROW |
665 | | ECF_COLD, |
666 | void_type_node, nargs: 3, args); |
667 | if (itm_fn != error_mark_node) |
668 | { |
669 | apply_tm_attr (itm_fn, get_identifier ("transaction_pure" )); |
670 | record_tm_replacement (throw_fn, itm_fn); |
671 | } |
672 | } |
673 | } |
674 | |
675 | /* [except.throw] |
676 | |
677 | A throw-expression initializes a temporary object, the type |
678 | of which is determined by removing any top-level |
679 | cv-qualifiers from the static type of the operand of throw |
680 | and adjusting the type from "array of T" or "function return |
681 | T" to "pointer to T" or "pointer to function returning T" |
682 | respectively. */ |
683 | temp_type = is_bitfield_expr_with_lowered_type (exp); |
684 | if (!temp_type) |
685 | temp_type = cv_unqualified (type_decays_to (TREE_TYPE (exp))); |
686 | |
687 | /* OK, this is kind of wacky. The standard says that we call |
688 | terminate when the exception handling mechanism, after |
689 | completing evaluation of the expression to be thrown but |
690 | before the exception is caught (_except.throw_), calls a |
691 | user function that exits via an uncaught exception. |
692 | |
693 | So we have to protect the actual initialization of the |
694 | exception object with terminate(), but evaluate the |
695 | expression first. Since there could be temps in the |
696 | expression, we need to handle that, too. We also expand |
697 | the call to __cxa_allocate_exception first (which doesn't |
698 | matter, since it can't throw). */ |
699 | |
700 | /* Allocate the space for the exception. */ |
701 | allocate_expr = do_allocate_exception (type: temp_type); |
702 | if (allocate_expr == error_mark_node) |
703 | return error_mark_node; |
704 | allocate_expr = get_target_expr (allocate_expr); |
705 | ptr = TARGET_EXPR_SLOT (allocate_expr); |
706 | TARGET_EXPR_CLEANUP (allocate_expr) = do_free_exception (ptr); |
707 | CLEANUP_EH_ONLY (allocate_expr) = 1; |
708 | |
709 | object = build_nop (build_pointer_type (temp_type), ptr); |
710 | object = cp_build_fold_indirect_ref (object); |
711 | |
712 | /* And initialize the exception object. */ |
713 | if (CLASS_TYPE_P (temp_type)) |
714 | { |
715 | int flags = LOOKUP_NORMAL | LOOKUP_ONLYCONVERTING; |
716 | location_t exp_loc = cp_expr_loc_or_loc (t: exp, or_loc: loc); |
717 | |
718 | /* Under C++0x [12.8/16 class.copy], a thrown lvalue is sometimes |
719 | treated as an rvalue for the purposes of overload resolution |
720 | to favor move constructors over copy constructors. */ |
721 | if (tree moved = treat_lvalue_as_rvalue_p (exp, /*return*/false)) |
722 | /* In C++20 we treat the return value as an rvalue that |
723 | can bind to lvalue refs. In C++23, such an expression is just |
724 | an xvalue. */ |
725 | exp = moved; |
726 | |
727 | /* Call the copy constructor. */ |
728 | releasing_vec exp_vec (make_tree_vector_single (exp)); |
729 | exp = build_special_member_call (object, complete_ctor_identifier, |
730 | &exp_vec, TREE_TYPE (object), flags, |
731 | complain); |
732 | if (exp == error_mark_node) |
733 | { |
734 | if (complain & tf_error) |
735 | inform (exp_loc, " in thrown expression" ); |
736 | return error_mark_node; |
737 | } |
738 | } |
739 | else |
740 | { |
741 | tree tmp = decay_conversion (exp, complain); |
742 | if (tmp == error_mark_node) |
743 | return error_mark_node; |
744 | exp = cp_build_init_expr (t: object, i: tmp); |
745 | } |
746 | |
747 | /* Mark any cleanups from the initialization as MUST_NOT_THROW, since |
748 | they are run after the exception object is initialized. */ |
749 | cp_walk_tree_without_duplicates (&exp, wrap_cleanups_r, 0); |
750 | |
751 | /* Prepend the allocation. */ |
752 | exp = build2 (COMPOUND_EXPR, TREE_TYPE (exp), allocate_expr, exp); |
753 | |
754 | /* Force all the cleanups to be evaluated here so that we don't have |
755 | to do them during unwinding. */ |
756 | exp = build1 (CLEANUP_POINT_EXPR, void_type_node, exp); |
757 | |
758 | throw_type = build_eh_type_type (type: prepare_eh_type (TREE_TYPE (object))); |
759 | |
760 | cleanup = NULL_TREE; |
761 | if (type_build_dtor_call (TREE_TYPE (object))) |
762 | { |
763 | tree binfo = TYPE_BINFO (TREE_TYPE (object)); |
764 | tree dtor_fn = lookup_fnfields (binfo, |
765 | complete_dtor_identifier, 0, |
766 | complain); |
767 | dtor_fn = BASELINK_FUNCTIONS (dtor_fn); |
768 | if (!mark_used (dtor_fn) |
769 | || !perform_or_defer_access_check (binfo, dtor_fn, |
770 | dtor_fn, complain)) |
771 | return error_mark_node; |
772 | if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (object))) |
773 | { |
774 | cxx_mark_addressable (dtor_fn); |
775 | /* Pretend it's a normal function. */ |
776 | cleanup = build1 (ADDR_EXPR, cleanup_type, dtor_fn); |
777 | } |
778 | } |
779 | if (cleanup == NULL_TREE) |
780 | cleanup = build_int_cst (cleanup_type, 0); |
781 | |
782 | /* ??? Indicate that this function call throws throw_type. */ |
783 | tree tmp = cp_build_function_call_nary (throw_fn, complain, |
784 | ptr, throw_type, cleanup, |
785 | NULL_TREE); |
786 | |
787 | /* Tack on the initialization stuff. */ |
788 | exp = build2 (COMPOUND_EXPR, TREE_TYPE (tmp), exp, tmp); |
789 | } |
790 | else |
791 | { |
792 | /* Rethrow current exception. */ |
793 | if (!rethrow_fn) |
794 | { |
795 | rethrow_fn = declare_library_fn_1 (name: "__cxa_rethrow" , |
796 | ECF_NORETURN | ECF_XTHROW |
797 | | ECF_COLD, |
798 | void_type_node, nargs: 0, NULL); |
799 | if (flag_tm && rethrow_fn != error_mark_node) |
800 | apply_tm_attr (rethrow_fn, get_identifier ("transaction_pure" )); |
801 | } |
802 | |
803 | /* ??? Indicate that this function call allows exceptions of the type |
804 | of the enclosing catch block (if known). */ |
805 | exp = cp_build_function_call_vec (rethrow_fn, NULL, complain); |
806 | } |
807 | |
808 | exp = build1_loc (loc, code: THROW_EXPR, void_type_node, arg1: exp); |
809 | |
810 | return exp; |
811 | } |
812 | |
813 | /* Make sure TYPE is complete, pointer to complete, reference to |
814 | complete, or pointer to cv void. Issue diagnostic on failure. |
815 | Return the zero on failure and nonzero on success. FROM can be |
816 | the expr or decl from whence TYPE came, if available. */ |
817 | |
818 | static bool |
819 | complete_ptr_ref_or_void_ptr_p (tree type, tree from, tsubst_flags_t complain) |
820 | { |
821 | /* Check complete. */ |
822 | type = complete_type_or_maybe_complain (type, from, complain); |
823 | if (!type) |
824 | return false; |
825 | |
826 | /* Or a pointer or ref to one, or cv void *. */ |
827 | const bool is_ptr = TYPE_PTR_P (type); |
828 | if (is_ptr || TYPE_REF_P (type)) |
829 | { |
830 | tree core = TREE_TYPE (type); |
831 | |
832 | if (is_ptr && VOID_TYPE_P (core)) |
833 | /* OK */; |
834 | else if (!complete_type_or_maybe_complain (core, from, complain)) |
835 | return false; |
836 | } |
837 | return true; |
838 | } |
839 | |
840 | /* If IS_THROW is true return truth-value if T is an expression admissible |
841 | in throw-expression, i.e. if it is not of incomplete type or a pointer/ |
842 | reference to such a type or of an abstract class type. |
843 | If IS_THROW is false, likewise for a catch parameter, same requirements |
844 | for its type plus rvalue reference type is also not admissible. */ |
845 | |
846 | static bool |
847 | is_admissible_throw_operand_or_catch_parameter (tree t, bool is_throw, |
848 | tsubst_flags_t complain) |
849 | { |
850 | tree expr = is_throw ? t : NULL_TREE; |
851 | tree type = TREE_TYPE (t); |
852 | |
853 | /* C++11 [except.handle] The exception-declaration shall not denote |
854 | an incomplete type, an abstract class type, or an rvalue reference |
855 | type. */ |
856 | |
857 | /* 15.1/4 [...] The type of the throw-expression shall not be an |
858 | incomplete type, or a pointer or a reference to an incomplete |
859 | type, other than void*, const void*, volatile void*, or |
860 | const volatile void*. Except for these restriction and the |
861 | restrictions on type matching mentioned in 15.3, the operand |
862 | of throw is treated exactly as a function argument in a call |
863 | (5.2.2) or the operand of a return statement. */ |
864 | if (!complete_ptr_ref_or_void_ptr_p (type, from: expr, complain)) |
865 | return false; |
866 | |
867 | tree nonref_type = non_reference (type); |
868 | if (!verify_type_context (input_location, TCTX_EXCEPTIONS, nonref_type)) |
869 | return false; |
870 | |
871 | /* 10.4/3 An abstract class shall not be used as a parameter type, |
872 | as a function return type or as type of an explicit |
873 | conversion. */ |
874 | else if (abstract_virtuals_error (is_throw ? ACU_THROW : ACU_CATCH, type, |
875 | complain)) |
876 | return false; |
877 | else if (!is_throw |
878 | && TYPE_REF_P (type) |
879 | && TYPE_REF_IS_RVALUE (type)) |
880 | { |
881 | if (complain & tf_error) |
882 | error ("cannot declare %<catch%> parameter to be of rvalue " |
883 | "reference type %qT" , type); |
884 | return false; |
885 | } |
886 | else if (variably_modified_type_p (type, NULL_TREE)) |
887 | { |
888 | if (complain & tf_error) |
889 | { |
890 | if (is_throw) |
891 | error_at (cp_expr_loc_or_input_loc (t: expr), |
892 | "cannot throw expression of type %qT because it involves " |
893 | "types of variable size" , type); |
894 | else |
895 | error ("cannot catch type %qT because it involves types of " |
896 | "variable size" , type); |
897 | } |
898 | return false; |
899 | } |
900 | |
901 | return true; |
902 | } |
903 | |
904 | /* Returns nonzero if FN is a declaration of a standard C library |
905 | function which is known not to throw. |
906 | |
907 | [lib.res.on.exception.handling]: None of the functions from the |
908 | Standard C library shall report an error by throwing an |
909 | exception, unless it calls a program-supplied function that |
910 | throws an exception. */ |
911 | |
912 | #include "cfns.h" |
913 | |
914 | int |
915 | nothrow_libfn_p (const_tree fn) |
916 | { |
917 | tree id; |
918 | |
919 | if (TREE_PUBLIC (fn) |
920 | && DECL_EXTERNAL (fn) |
921 | && DECL_NAMESPACE_SCOPE_P (fn) |
922 | && DECL_EXTERN_C_P (fn)) |
923 | /* OK */; |
924 | else |
925 | /* Can't be a C library function. */ |
926 | return 0; |
927 | |
928 | /* Being a C library function, DECL_ASSEMBLER_NAME == DECL_NAME |
929 | unless the system headers are playing rename tricks, and if |
930 | they are, we don't want to be confused by them. */ |
931 | id = DECL_NAME (fn); |
932 | const struct libc_name_struct *s |
933 | = libc_name::libc_name_p (IDENTIFIER_POINTER (id), IDENTIFIER_LENGTH (id)); |
934 | if (s == NULL) |
935 | return 0; |
936 | switch (s->c_ver) |
937 | { |
938 | case 89: return 1; |
939 | case 99: return !flag_iso || flag_isoc99; |
940 | case 11: return !flag_iso || flag_isoc11; |
941 | default: gcc_unreachable (); |
942 | } |
943 | } |
944 | |
945 | /* Returns nonzero if an exception of type FROM will be caught by a |
946 | handler for type TO, as per [except.handle]. */ |
947 | |
948 | static bool |
949 | can_convert_eh (tree to, tree from) |
950 | { |
951 | to = non_reference (to); |
952 | from = non_reference (from); |
953 | |
954 | if (same_type_ignoring_top_level_qualifiers_p (to, from)) |
955 | return true; |
956 | |
957 | if (TYPE_PTR_P (to) && TYPE_PTR_P (from)) |
958 | { |
959 | to = TREE_TYPE (to); |
960 | from = TREE_TYPE (from); |
961 | |
962 | if (! at_least_as_qualified_p (to, from)) |
963 | return false; |
964 | |
965 | if (VOID_TYPE_P (to)) |
966 | return true; |
967 | |
968 | /* Else fall through. */ |
969 | } |
970 | |
971 | if (CLASS_TYPE_P (to) && CLASS_TYPE_P (from) |
972 | && publicly_uniquely_derived_p (to, from)) |
973 | return true; |
974 | |
975 | return false; |
976 | } |
977 | |
978 | /* Check whether any of the handlers in I are shadowed by another handler |
979 | accepting TYPE. Note that the shadowing may not be complete; even if |
980 | an exception of type B would be caught by a handler for A, there could |
981 | be a derived class C for which A is an ambiguous base but B is not, so |
982 | the handler for B would catch an exception of type C. */ |
983 | |
984 | static void |
985 | check_handlers_1 (tree master, tree_stmt_iterator i) |
986 | { |
987 | tree type = TREE_TYPE (master); |
988 | |
989 | for (; !tsi_end_p (i); tsi_next (i: &i)) |
990 | { |
991 | tree handler = tsi_stmt (i); |
992 | if (TREE_TYPE (handler) && can_convert_eh (to: type, TREE_TYPE (handler))) |
993 | { |
994 | auto_diagnostic_group d; |
995 | if (warning_at (EXPR_LOCATION (handler), OPT_Wexceptions, |
996 | "exception of type %qT will be caught by earlier " |
997 | "handler" , TREE_TYPE (handler))) |
998 | inform (EXPR_LOCATION (master), "for type %qT" , type); |
999 | break; |
1000 | } |
1001 | } |
1002 | } |
1003 | |
1004 | /* Given a STATEMENT_LIST of HANDLERs, make sure that they're OK. */ |
1005 | |
1006 | void |
1007 | check_handlers (tree handlers) |
1008 | { |
1009 | tree_stmt_iterator i; |
1010 | |
1011 | /* If we don't have a STATEMENT_LIST, then we've just got one |
1012 | handler, and thus nothing to warn about. */ |
1013 | if (TREE_CODE (handlers) != STATEMENT_LIST) |
1014 | return; |
1015 | |
1016 | i = tsi_start (t: handlers); |
1017 | if (!tsi_end_p (i)) |
1018 | while (1) |
1019 | { |
1020 | tree handler = tsi_stmt (i); |
1021 | tsi_next (i: &i); |
1022 | |
1023 | /* No more handlers; nothing to shadow. */ |
1024 | if (tsi_end_p (i)) |
1025 | break; |
1026 | if (TREE_TYPE (handler) == NULL_TREE) |
1027 | permerror (EXPR_LOCATION (handler), "%<...%>" |
1028 | " handler must be the last handler for its try block" ); |
1029 | else |
1030 | check_handlers_1 (master: handler, i); |
1031 | } |
1032 | } |
1033 | |
1034 | /* walk_tree helper for finish_noexcept_expr. Returns non-null if the |
1035 | expression *TP causes the noexcept operator to evaluate to false. |
1036 | |
1037 | 5.3.7 [expr.noexcept]: The result of the noexcept operator is false if |
1038 | in a potentially-evaluated context the expression would contain |
1039 | * a potentially evaluated call to a function, member function, |
1040 | function pointer, or member function pointer that does not have a |
1041 | non-throwing exception-specification (15.4), |
1042 | * a potentially evaluated throw-expression (15.1), |
1043 | * a potentially evaluated dynamic_cast expression dynamic_cast<T>(v), |
1044 | where T is a reference type, that requires a run-time check (5.2.7), or |
1045 | * a potentially evaluated typeid expression (5.2.8) applied to a glvalue |
1046 | expression whose type is a polymorphic class type (10.3). */ |
1047 | |
1048 | static tree |
1049 | check_noexcept_r (tree *tp, int *walk_subtrees, void *) |
1050 | { |
1051 | tree t = *tp; |
1052 | enum tree_code code = TREE_CODE (t); |
1053 | |
1054 | if (unevaluated_p (code)) |
1055 | *walk_subtrees = false; |
1056 | else if ((code == CALL_EXPR && CALL_EXPR_FN (t)) |
1057 | || code == AGGR_INIT_EXPR) |
1058 | { |
1059 | /* We can only use the exception specification of the called function |
1060 | for determining the value of a noexcept expression; we can't use |
1061 | TREE_NOTHROW, as it might have a different value in another |
1062 | translation unit, creating ODR problems. |
1063 | |
1064 | We could use TREE_NOTHROW (t) for !TREE_PUBLIC fns, though... */ |
1065 | tree fn = cp_get_callee (t); |
1066 | if (concept_check_p (t: fn)) |
1067 | return NULL_TREE; |
1068 | tree type = TREE_TYPE (fn); |
1069 | gcc_assert (INDIRECT_TYPE_P (type)); |
1070 | type = TREE_TYPE (type); |
1071 | |
1072 | STRIP_NOPS (fn); |
1073 | if (TREE_CODE (fn) == ADDR_EXPR) |
1074 | fn = TREE_OPERAND (fn, 0); |
1075 | if (TREE_CODE (fn) == FUNCTION_DECL) |
1076 | { |
1077 | /* We do use TREE_NOTHROW for ABI internals like __dynamic_cast, |
1078 | and for C library functions known not to throw. */ |
1079 | if (DECL_EXTERN_C_P (fn) |
1080 | && (DECL_ARTIFICIAL (fn) |
1081 | || nothrow_libfn_p (fn))) |
1082 | return TREE_NOTHROW (fn) ? NULL_TREE : fn; |
1083 | /* We used to treat a call to a constexpr function as noexcept if |
1084 | the call was a constant expression (CWG 1129). This has changed |
1085 | in P0003 whereby noexcept has no special rule for constant |
1086 | expressions anymore. Since the current behavior is important for |
1087 | certain library functionality, we treat this as a DR, therefore |
1088 | adjusting the behavior for C++11 and C++14. Previously, we had |
1089 | to evaluate the noexcept-specifier's operand here, but that could |
1090 | cause instantiations that would fail. */ |
1091 | } |
1092 | if (!TYPE_NOTHROW_P (type)) |
1093 | return fn; |
1094 | } |
1095 | |
1096 | return NULL_TREE; |
1097 | } |
1098 | |
1099 | /* If a function that causes a noexcept-expression to be false isn't |
1100 | defined yet, remember it and check it for TREE_NOTHROW again at EOF. */ |
1101 | |
1102 | struct GTY(()) pending_noexcept { |
1103 | tree fn; |
1104 | location_t loc; |
1105 | }; |
1106 | static GTY(()) vec<pending_noexcept, va_gc> *pending_noexcept_checks; |
1107 | |
1108 | /* FN is a FUNCTION_DECL that caused a noexcept-expr to be false. Warn if |
1109 | it can't throw. |
1110 | |
1111 | TODO: Consider extending -Wnoexcept to do something like walk_subtrees in the |
1112 | case of a defaulted function that obtained a noexcept(false) spec. */ |
1113 | |
1114 | static void |
1115 | maybe_noexcept_warning (tree fn) |
1116 | { |
1117 | if (TREE_NOTHROW (fn) |
1118 | && (!DECL_IN_SYSTEM_HEADER (fn) |
1119 | || global_dc->m_warn_system_headers)) |
1120 | { |
1121 | auto s = make_temp_override (var&: global_dc->m_warn_system_headers, overrider: true); |
1122 | auto_diagnostic_group d; |
1123 | if (warning (OPT_Wnoexcept, "noexcept-expression evaluates to %<false%> " |
1124 | "because of a call to %qD" , fn)) |
1125 | inform (DECL_SOURCE_LOCATION (fn), |
1126 | "but %qD does not throw; perhaps " |
1127 | "it should be declared %<noexcept%>" , fn); |
1128 | } |
1129 | } |
1130 | |
1131 | /* Check any functions that weren't defined earlier when they caused a |
1132 | noexcept expression to evaluate to false. */ |
1133 | |
1134 | void |
1135 | perform_deferred_noexcept_checks (void) |
1136 | { |
1137 | int i; |
1138 | pending_noexcept *p; |
1139 | location_t saved_loc = input_location; |
1140 | FOR_EACH_VEC_SAFE_ELT (pending_noexcept_checks, i, p) |
1141 | { |
1142 | input_location = p->loc; |
1143 | maybe_noexcept_warning (fn: p->fn); |
1144 | } |
1145 | input_location = saved_loc; |
1146 | } |
1147 | |
1148 | /* Evaluate noexcept ( EXPR ). */ |
1149 | |
1150 | tree |
1151 | finish_noexcept_expr (tree expr, tsubst_flags_t complain) |
1152 | { |
1153 | if (expr == error_mark_node) |
1154 | return error_mark_node; |
1155 | |
1156 | if (processing_template_decl) |
1157 | return build_min (NOEXCEPT_EXPR, boolean_type_node, expr); |
1158 | |
1159 | return (expr_noexcept_p (expr, complain) |
1160 | ? boolean_true_node : boolean_false_node); |
1161 | } |
1162 | |
1163 | /* Returns whether EXPR is noexcept, possibly warning if allowed by |
1164 | COMPLAIN. */ |
1165 | |
1166 | bool |
1167 | expr_noexcept_p (tree expr, tsubst_flags_t complain) |
1168 | { |
1169 | tree fn; |
1170 | |
1171 | if (expr == error_mark_node) |
1172 | return false; |
1173 | |
1174 | fn = cp_walk_tree_without_duplicates (&expr, check_noexcept_r, 0); |
1175 | if (fn) |
1176 | { |
1177 | if ((complain & tf_warning) && warn_noexcept |
1178 | && TREE_CODE (fn) == FUNCTION_DECL) |
1179 | { |
1180 | if (!DECL_INITIAL (fn)) |
1181 | { |
1182 | /* Not defined yet; check again at EOF. */ |
1183 | pending_noexcept p = {.fn: fn, .loc: input_location}; |
1184 | vec_safe_push (v&: pending_noexcept_checks, obj: p); |
1185 | } |
1186 | else |
1187 | maybe_noexcept_warning (fn); |
1188 | } |
1189 | return false; |
1190 | } |
1191 | else |
1192 | return true; |
1193 | } |
1194 | |
1195 | /* Return true iff SPEC is throw() or noexcept(true). */ |
1196 | |
1197 | bool |
1198 | nothrow_spec_p (const_tree spec) |
1199 | { |
1200 | gcc_assert (!DEFERRED_NOEXCEPT_SPEC_P (spec)); |
1201 | |
1202 | if (spec == empty_except_spec |
1203 | || spec == noexcept_true_spec) |
1204 | return true; |
1205 | |
1206 | gcc_assert (!spec |
1207 | || TREE_VALUE (spec) |
1208 | || spec == noexcept_false_spec |
1209 | || TREE_PURPOSE (spec) == error_mark_node |
1210 | || UNPARSED_NOEXCEPT_SPEC_P (spec) |
1211 | || processing_template_decl); |
1212 | |
1213 | return false; |
1214 | } |
1215 | |
1216 | /* For FUNCTION_TYPE or METHOD_TYPE, true if NODE is noexcept. This is the |
1217 | case for things declared noexcept(true) and, with -fnothrow-opt, for |
1218 | throw() functions. */ |
1219 | |
1220 | bool |
1221 | type_noexcept_p (const_tree type) |
1222 | { |
1223 | tree spec = TYPE_RAISES_EXCEPTIONS (type); |
1224 | gcc_assert (!DEFERRED_NOEXCEPT_SPEC_P (spec)); |
1225 | if (flag_nothrow_opt) |
1226 | return nothrow_spec_p (spec); |
1227 | else |
1228 | return spec == noexcept_true_spec; |
1229 | } |
1230 | |
1231 | /* For FUNCTION_TYPE or METHOD_TYPE, true if NODE can throw any type, |
1232 | i.e. no exception-specification or noexcept(false). */ |
1233 | |
1234 | bool |
1235 | type_throw_all_p (const_tree type) |
1236 | { |
1237 | tree spec = TYPE_RAISES_EXCEPTIONS (type); |
1238 | gcc_assert (!DEFERRED_NOEXCEPT_SPEC_P (spec)); |
1239 | return spec == NULL_TREE || spec == noexcept_false_spec; |
1240 | } |
1241 | |
1242 | /* Create a representation of the noexcept-specification with |
1243 | constant-expression of EXPR. COMPLAIN is as for tsubst. */ |
1244 | |
1245 | tree |
1246 | build_noexcept_spec (tree expr, tsubst_flags_t complain) |
1247 | { |
1248 | if (check_for_bare_parameter_packs (expr)) |
1249 | return error_mark_node; |
1250 | if (TREE_CODE (expr) != DEFERRED_NOEXCEPT |
1251 | && !instantiation_dependent_expression_p (expr)) |
1252 | { |
1253 | expr = build_converted_constant_bool_expr (expr, complain); |
1254 | expr = instantiate_non_dependent_expr (expr, complain); |
1255 | expr = cxx_constant_value (t: expr, complain); |
1256 | } |
1257 | if (TREE_CODE (expr) == INTEGER_CST) |
1258 | { |
1259 | if (operand_equal_p (expr, boolean_true_node, flags: 0)) |
1260 | return noexcept_true_spec; |
1261 | else |
1262 | { |
1263 | gcc_checking_assert (operand_equal_p (expr, boolean_false_node, 0)); |
1264 | return noexcept_false_spec; |
1265 | } |
1266 | } |
1267 | else if (expr == error_mark_node) |
1268 | return error_mark_node; |
1269 | else |
1270 | { |
1271 | gcc_assert (processing_template_decl |
1272 | || TREE_CODE (expr) == DEFERRED_NOEXCEPT); |
1273 | if (TREE_CODE (expr) != DEFERRED_NOEXCEPT) |
1274 | /* Avoid problems with a function type built with a dependent typedef |
1275 | being reused in another scope (c++/84045). */ |
1276 | expr = strip_typedefs_expr (expr); |
1277 | return build_tree_list (expr, NULL_TREE); |
1278 | } |
1279 | } |
1280 | |
1281 | /* If the current function has a cleanup that might throw, and the return value |
1282 | has a non-trivial destructor, return a MODIFY_EXPR to set |
1283 | current_retval_sentinel so that we know that the return value needs to be |
1284 | destroyed on throw. Do the same if the current function might use the |
1285 | named return value optimization, so we don't destroy it on return. |
1286 | Otherwise, returns NULL_TREE. |
1287 | |
1288 | The sentinel is set to indicate that we're in the process of returning, and |
1289 | therefore should destroy a normal return value on throw, and shouldn't |
1290 | destroy a named return value variable on normal scope exit. It is set on |
1291 | return, and cleared either by maybe_splice_retval_cleanup, or when an |
1292 | exception reaches the NRV scope (finalize_nrv_r). Note that once return |
1293 | passes the NRV scope, it's effectively a normal return value, so cleanup |
1294 | past that point is handled by maybe_splice_retval_cleanup. */ |
1295 | |
1296 | tree |
1297 | maybe_set_retval_sentinel () |
1298 | { |
1299 | if (processing_template_decl) |
1300 | return NULL_TREE; |
1301 | tree retval = DECL_RESULT (current_function_decl); |
1302 | if (!TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (retval))) |
1303 | return NULL_TREE; |
1304 | if (!cp_function_chain->throwing_cleanup |
1305 | && (current_function_return_value == error_mark_node |
1306 | || current_function_return_value == NULL_TREE)) |
1307 | return NULL_TREE; |
1308 | |
1309 | if (!current_retval_sentinel) |
1310 | { |
1311 | /* Just create the temporary now, maybe_splice_retval_cleanup |
1312 | will do the rest. */ |
1313 | current_retval_sentinel = create_temporary_var (boolean_type_node); |
1314 | DECL_INITIAL (current_retval_sentinel) = boolean_false_node; |
1315 | pushdecl_outermost_localscope (current_retval_sentinel); |
1316 | } |
1317 | |
1318 | return build2 (MODIFY_EXPR, boolean_type_node, |
1319 | current_retval_sentinel, boolean_true_node); |
1320 | } |
1321 | |
1322 | /* COMPOUND_STMT is the STATEMENT_LIST for some block. If COMPOUND_STMT is the |
1323 | current function body or a try block, and current_retval_sentinel was set in |
1324 | this function, wrap the block in a CLEANUP_STMT to destroy the return value |
1325 | on throw. */ |
1326 | |
1327 | void |
1328 | maybe_splice_retval_cleanup (tree compound_stmt, bool is_try) |
1329 | { |
1330 | if (!current_function_decl || !cfun |
1331 | || DECL_CONSTRUCTOR_P (current_function_decl) |
1332 | || DECL_DESTRUCTOR_P (current_function_decl) |
1333 | || !current_retval_sentinel) |
1334 | return; |
1335 | |
1336 | /* if we need a cleanup for the return value, add it in at the same level as |
1337 | pushdecl_outermost_localscope. And also in try blocks. */ |
1338 | cp_binding_level *b = current_binding_level; |
1339 | const bool function_body = b->kind == sk_function_parms; |
1340 | |
1341 | if (function_body || is_try) |
1342 | { |
1343 | location_t loc = DECL_SOURCE_LOCATION (current_function_decl); |
1344 | tree_stmt_iterator iter = tsi_start (t: compound_stmt); |
1345 | tree retval = DECL_RESULT (current_function_decl); |
1346 | |
1347 | if (function_body) |
1348 | { |
1349 | /* Add a DECL_EXPR for current_retval_sentinel. */ |
1350 | tree decl_expr = build_stmt (loc, DECL_EXPR, current_retval_sentinel); |
1351 | tsi_link_before (&iter, decl_expr, TSI_SAME_STMT); |
1352 | } |
1353 | |
1354 | if (!cp_function_chain->throwing_cleanup) |
1355 | /* We're only using the sentinel for an NRV. */ |
1356 | return; |
1357 | |
1358 | /* Skip past other decls, they can't contain a return. */ |
1359 | while (!tsi_end_p (i: iter) |
1360 | && TREE_CODE (tsi_stmt (iter)) == DECL_EXPR) |
1361 | tsi_next (i: &iter); |
1362 | |
1363 | if (tsi_end_p (i: iter)) |
1364 | /* Nothing to wrap. */ |
1365 | return; |
1366 | |
1367 | /* Wrap the rest of the STATEMENT_LIST in a CLEANUP_STMT. */ |
1368 | tree stmts = NULL_TREE; |
1369 | while (!tsi_end_p (i: iter)) |
1370 | { |
1371 | append_to_statement_list_force (tsi_stmt (i: iter), &stmts); |
1372 | tsi_delink (&iter); |
1373 | } |
1374 | tree dtor = build_cleanup (retval); |
1375 | if (!function_body) |
1376 | { |
1377 | /* Clear the sentinel so we don't try to destroy the retval again on |
1378 | rethrow (c++/112301). */ |
1379 | tree clear = build2 (MODIFY_EXPR, boolean_type_node, |
1380 | current_retval_sentinel, boolean_false_node); |
1381 | dtor = build2 (COMPOUND_EXPR, void_type_node, clear, dtor); |
1382 | } |
1383 | tree cond = build3 (COND_EXPR, void_type_node, current_retval_sentinel, |
1384 | dtor, void_node); |
1385 | tree cleanup = build_stmt (loc, CLEANUP_STMT, |
1386 | stmts, cond, retval); |
1387 | CLEANUP_EH_ONLY (cleanup) = true; |
1388 | append_to_statement_list_force (cleanup, &compound_stmt); |
1389 | } |
1390 | } |
1391 | |
1392 | #include "gt-cp-except.h" |
1393 | |