Line data Source code
1 : /* C++ contracts.
2 :
3 : Copyright (C) 2020-2026 Free Software Foundation, Inc.
4 : Originally by Jeff Chapman II (jchapman@lock3software.com) for proposed
5 : C++20 contracts.
6 : Rewritten for C++26 contracts by:
7 : Nina Ranns (dinka.ranns@googlemail.com)
8 : Iain Sandoe (iain@sandoe.co.uk)
9 : Ville Voutilainen (ville.voutilainen@gmail.com).
10 :
11 : This file is part of GCC.
12 :
13 : GCC is free software; you can redistribute it and/or modify
14 : it under the terms of the GNU General Public License as published by
15 : the Free Software Foundation; either version 3, or (at your option)
16 : any later version.
17 :
18 : GCC is distributed in the hope that it will be useful,
19 : but WITHOUT ANY WARRANTY; without even the implied warranty of
20 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 : GNU General Public License for more details.
22 :
23 : You should have received a copy of the GNU General Public License
24 : along with GCC; see the file COPYING3. If not see
25 : <http://www.gnu.org/licenses/>. */
26 :
27 : #include "config.h"
28 : #include "system.h"
29 : #include "coretypes.h"
30 : #include "cp-tree.h"
31 : #include "stringpool.h"
32 : #include "diagnostic.h"
33 : #include "options.h"
34 : #include "contracts.h"
35 : #include "tree.h"
36 : #include "tree-inline.h"
37 : #include "attribs.h"
38 : #include "tree-iterator.h"
39 : #include "print-tree.h"
40 : #include "stor-layout.h"
41 : #include "intl.h"
42 : #include "cgraph.h"
43 : #include "opts.h"
44 : #include "output.h"
45 :
46 : /* Design notes.
47 :
48 : There are three phases:
49 : 1. Parsing and semantic checks.
50 : Most of the code for this is in the parser, with helpers provided here.
51 : 2. Emitting contract assertion AST nodes into function bodies.
52 : This is initiated from "finish_function ()"
53 : 3. Lowering the contract assertion AST nodes to control flow, constant
54 : data and calls to the violation handler.
55 : This is initiated from "cp_genericize ()".
56 :
57 : The organisation of the code in this file is intended to follow those three
58 : phases where possible.
59 :
60 : Contract Assertion State
61 : ========================
62 :
63 : contract_assert () does not require any special handling and can be
64 : represented directly by AST inserted in the function body.
65 :
66 : 'pre' and 'post' function contract specifiers require most of the special
67 : handling, since they must be tracked across re-declarations of functions and
68 : there are constraints on how such specifiers may change in these cases.
69 :
70 : The contracts specification identifies a "first declaration" of any given
71 : function - which is the first encountered when parsing a given TU.
72 : Subsequent re-declarations may not add or change the function contract
73 : specifiers from any introduced on this first declaration. It is, however,
74 : permitted to omit specifiers on re-declarations.
75 :
76 : Since the implementation of GCC's (re-)declarations is a destructive merge
77 : we need to keep some state on the side to determine whether the re-declaration
78 : rules are met. In this current design we have chosen not to add another tree
79 : to each function decl but, instead, keep a map from function decl to contract
80 : specifier state. In this state we record the 'first declaration' specifiers
81 : which are used to validate re-declaration(s) and to report the initial state
82 : in diagnostics.
83 :
84 : We need (for example) to compare
85 : pre ( x > 2 ) equal to
86 : pre ( z > 2 ) when x and z refer to the same function parameter in a
87 : re-declaration.
88 :
89 : The mechanism used to determine if two contracts are the same is to compare
90 : the folded trees. This makes use of current compiler machinery, rather than
91 : constructing some new AST comparison scheme. However, it does introduce an
92 : additional complexity in that we need to defer such comparison until parsing
93 : is complete - and function contract specifiers in class declarations must be
94 : deferred parses, since it is also permitted for specifiers to refer to class
95 : members.
96 :
97 : When we encounter a definition, the parameter names in a function decl are
98 : re-written to match those of the definition (thus the expected names will
99 : appear in debug information etc). At this point, we also need to re-map
100 : any function parameter names that appear in function contract specifiers
101 : to agree with those of the definition - although we intend to keep the
102 : 'first declaration' record consistent for diagnostics.
103 :
104 : Since we shared some code from the C++2a contracts implementation, pre and
105 : post specifiers are represented by chains of attributes, where the payload
106 : of the attribute is an AST node. However during the parse, these are not
107 : inserted into the function bodies, but kept in the decl-keyed state described
108 : above. A future improvement planned here is to store the specifiers using a
109 : tree vec instead of the attribute list.
110 :
111 : Emitting contract AST
112 : =====================
113 :
114 : When we reach `finish_function ()` and therefore are committed to potentially
115 : emitting code for an instance, we build a new variant of the function body
116 : with the pre-condition AST inserted before the user's function body, and the
117 : post condition AST (if any) linked into the function return.
118 :
119 : Lowering the contract assertion AST
120 : ===================================
121 :
122 : In all cases (pre, post, contract_assert) the AST node is lowered to control
123 : flow and (potentially) calls to the violation handler and/or termination.
124 : This is done during `cp_genericize ()`. In the current implementation, the
125 : decision on the control flow is made on the basis of the setting of a command-
126 : line flag that determines a TU-wide contract evaluation semantic, which has
127 : the following initial set of behaviours:
128 :
129 : 'ignore' : contract assertion AST is lowered to 'nothing',
130 : i.e. omitted.
131 : 'enforce' : contract assertion AST is lowered to a check, if this
132 : fails a violation handler is called, followed by
133 : std::terminate().
134 : 'quick_enforce' : contract assertion AST is lowered to a check, if this
135 : fails, std::terminate () is called.
136 : 'observe' : contract assertion AST is lowered to a check, if this
137 : fails, a violation handler is called, the code then
138 : continues.
139 :
140 : In each case, the "check" might be a simple 'if' (when it is determined that
141 : the assertion condition does not throw) or the condition evaluation will be
142 : wrapped in a try-catch block that treats any exception thrown when evaluating
143 : the check as equivalent to a failed check. It is noted in the violation data
144 : object whether a check failed because of an exception raised in evaluation.
145 :
146 : At present, a simple (but potentially space-inefficient) scheme is used to
147 : store constant data objects that represent the read-only data for the
148 : violation. The exact form of this is subject to revision as it represents
149 : ABI that must be agreed between implementations (as of this point, that
150 : discussion is not yet concluded). */
151 :
152 : /* Contract matching. */
153 :
154 : bool comparing_contracts;
155 :
156 : /* True if the contract is valid. */
157 :
158 : static bool
159 108 : contract_valid_p (tree contract)
160 : {
161 108 : return CONTRACT_CONDITION (contract) != error_mark_node;
162 : }
163 :
164 : /* Compare the contract conditions of OLD_CONTRACT and NEW_CONTRACT.
165 : Returns false if the conditions are equivalent, and true otherwise. */
166 :
167 : static bool
168 54 : mismatched_contracts_p (tree old_contract, tree new_contract)
169 : {
170 : /* Different kinds of contracts do not match. */
171 54 : if (TREE_CODE (old_contract) != TREE_CODE (new_contract))
172 : {
173 0 : auto_diagnostic_group d;
174 0 : error_at (EXPR_LOCATION (new_contract),
175 : "mismatched contract specifier in declaration");
176 0 : inform (EXPR_LOCATION (old_contract), "previous contract here");
177 0 : return true;
178 0 : }
179 :
180 : /* A deferred contract tentatively matches. */
181 54 : if (CONTRACT_CONDITION_DEFERRED_P (new_contract))
182 : return false;
183 :
184 : /* Compare the conditions of the contracts. */
185 54 : tree t1 = cp_fully_fold_init (CONTRACT_CONDITION (old_contract));
186 54 : tree t2 = cp_fully_fold_init (CONTRACT_CONDITION (new_contract));
187 :
188 : /* Compare the contracts. */
189 :
190 54 : bool saved_comparing_contracts = comparing_contracts;
191 54 : comparing_contracts = true;
192 54 : bool matching_p = cp_tree_equal (t1, t2);
193 54 : comparing_contracts = saved_comparing_contracts;
194 :
195 54 : if (!matching_p)
196 : {
197 15 : auto_diagnostic_group d;
198 15 : error_at (EXPR_LOCATION (CONTRACT_CONDITION (new_contract)),
199 : "mismatched contract condition in declaration");
200 15 : inform (EXPR_LOCATION (CONTRACT_CONDITION (old_contract)),
201 : "previous contract here");
202 15 : return true;
203 15 : }
204 :
205 : return false;
206 : }
207 :
208 : /* Compare the contract specifiers of OLDDECL and NEWDECL. Returns true
209 : if the contracts match, and false if they differ. */
210 :
211 : static bool
212 57 : match_contract_specifiers (location_t oldloc, tree old_contracts,
213 : location_t newloc, tree new_contracts)
214 : {
215 : /* Contracts only match if they are both specified. */
216 57 : if (!old_contracts || !new_contracts)
217 : return true;
218 :
219 57 : int old_len = TREE_VEC_LENGTH (old_contracts);
220 57 : int new_len = TREE_VEC_LENGTH (new_contracts);
221 :
222 : /* If we don't have the same number, the contracts don't match. */
223 57 : if (old_len != new_len)
224 : {
225 6 : auto_diagnostic_group d;
226 6 : error_at (newloc,
227 : "declaration has a different number of contracts than "
228 : "previously declared");
229 6 : inform (oldloc,
230 : new_len > old_len
231 : ? "previous declaration with fewer contracts here"
232 : : "previous declaration with more contracts here");
233 6 : return false;
234 6 : }
235 :
236 : /* Compare each contract in turn. */
237 90 : for (int ix = 0; ix < MIN (old_len, new_len); ix++)
238 : {
239 54 : tree old_contract = TREE_VEC_ELT (old_contracts, ix);
240 54 : tree new_contract = TREE_VEC_ELT (new_contracts, ix);
241 :
242 : /* If either contract is ill-formed, skip the rest of the comparison,
243 : since we've already diagnosed an error. */
244 54 : if (!contract_valid_p (new_contract) || !contract_valid_p (old_contract))
245 : return false;
246 :
247 54 : if (mismatched_contracts_p (old_contract, new_contract))
248 : return false;
249 : }
250 :
251 :
252 : return true;
253 : }
254 :
255 : /* Return true if CONTRACT is checked under the current semantic. */
256 :
257 : static bool
258 3497 : contract_active_p (tree contract)
259 : {
260 1224 : return get_evaluation_semantic (contract) != CES_IGNORE;
261 : }
262 :
263 : /* Return true if any contract of FNDECL is checked under the
264 : current semantic. */
265 :
266 : static bool
267 62299337 : contract_any_active_p (tree fndecl)
268 : {
269 62299337 : tree contracts = get_fn_contract_specifiers (fndecl);
270 62299337 : if (!contracts)
271 : return false;
272 :
273 2281 : for (tree contract : tree_vec_range (contracts))
274 2273 : if (contract_active_p (contract))
275 2257 : return true;
276 8 : return false;
277 : }
278 :
279 : /* True if FNDECL has any checked contracts whose TREE_CODE is
280 : C. */
281 :
282 : static bool
283 1080803 : has_active_contract_condition (tree fndecl, tree_code c)
284 : {
285 1080803 : tree contracts = get_fn_contract_specifiers (fndecl);
286 1080803 : if (!contracts)
287 : return false;
288 :
289 3554 : for (tree contract : tree_vec_range (contracts))
290 3822 : if (TREE_CODE (contract) == c && contract_active_p (contract))
291 1224 : return true;
292 956 : return false;
293 : }
294 :
295 : /* True if FNDECL has any checked or assumed preconditions. */
296 :
297 : static bool
298 1041 : has_active_preconditions (tree fndecl)
299 : {
300 0 : return has_active_contract_condition (fndecl, PRECONDITION_STMT);
301 : }
302 :
303 : /* True if FNDECL has any checked or assumed postconditions. */
304 :
305 : static bool
306 1079762 : has_active_postconditions (tree fndecl)
307 : {
308 0 : return has_active_contract_condition (fndecl, POSTCONDITION_STMT);
309 : }
310 :
311 : /* Return true if any contract in CONTRACTS is not yet parsed. */
312 :
313 : bool
314 1395 : contract_any_deferred_p (tree contracts)
315 : {
316 1395 : if (!contracts)
317 : return false;
318 :
319 2560 : for (tree contract : tree_vec_range (contracts))
320 1722 : if (CONTRACT_CONDITION_DEFERRED_P (contract))
321 466 : return true;
322 838 : return false;
323 : }
324 :
325 : /* Returns true if function decl FNDECL has contracts and we need to
326 : process them for the purposes of either building caller or definition
327 : contract checks.
328 : This function does not take into account whether caller or definition
329 : side checking is enabled. Those checks will be done from the calling
330 : function which will be able to determine whether it is doing caller
331 : or definition contract handling. */
332 :
333 : static bool
334 656186079 : handle_contracts_p (tree fndecl)
335 : {
336 656186079 : return (flag_contracts
337 97586474 : && !processing_template_decl
338 62299421 : && (CONTRACT_HELPER (fndecl) == ldf_contract_none)
339 718485416 : && contract_any_active_p (fndecl));
340 : }
341 :
342 : /* For use with the tree inliner. This preserves non-mapped local variables,
343 : such as postcondition result variables, during remapping. */
344 :
345 : static tree
346 764 : retain_decl (tree decl, copy_body_data *)
347 : {
348 764 : return decl;
349 : }
350 :
351 : /* Lookup a name in std::, or inject it. */
352 :
353 : static tree
354 192 : lookup_std_type (tree name_id)
355 : {
356 192 : tree res_type = lookup_qualified_name
357 192 : (std_node, name_id, LOOK_want::TYPE | LOOK_want::HIDDEN_FRIEND);
358 :
359 192 : if (TREE_CODE (res_type) == TYPE_DECL)
360 49 : res_type = TREE_TYPE (res_type);
361 : else
362 : {
363 143 : push_nested_namespace (std_node);
364 143 : res_type = make_class_type (RECORD_TYPE);
365 143 : create_implicit_typedef (name_id, res_type);
366 143 : DECL_SOURCE_LOCATION (TYPE_NAME (res_type)) = BUILTINS_LOCATION;
367 143 : DECL_CONTEXT (TYPE_NAME (res_type)) = current_namespace;
368 143 : pushdecl_namespace_level (TYPE_NAME (res_type), /*hidden*/true);
369 143 : pop_nested_namespace (std_node);
370 : }
371 192 : return res_type;
372 : }
373 :
374 : /* Get constract_assertion_kind of the specified contract. Used when building
375 : contract_violation object. */
376 :
377 : static contract_assertion_kind
378 1052 : get_contract_assertion_kind (tree contract)
379 : {
380 1052 : if (CONTRACT_ASSERTION_KIND (contract))
381 : {
382 1052 : tree s = CONTRACT_ASSERTION_KIND (contract);
383 1052 : tree i = (TREE_CODE (s) == INTEGER_CST) ? s
384 0 : : DECL_INITIAL (STRIP_NOPS (s));
385 1052 : gcc_checking_assert (!type_dependent_expression_p (s) && i);
386 1052 : return (contract_assertion_kind) tree_to_uhwi (i);
387 : }
388 :
389 0 : switch (TREE_CODE (contract))
390 : {
391 : case ASSERTION_STMT: return CAK_ASSERT;
392 : case PRECONDITION_STMT: return CAK_PRE;
393 : case POSTCONDITION_STMT: return CAK_POST;
394 0 : default: break;
395 : }
396 :
397 0 : gcc_unreachable ();
398 : }
399 :
400 : /* Get contract_evaluation_semantic of the specified contract. */
401 :
402 : contract_evaluation_semantic
403 5529 : get_evaluation_semantic (const_tree contract)
404 : {
405 5529 : if (CONTRACT_EVALUATION_SEMANTIC (contract))
406 : {
407 5529 : tree s = CONTRACT_EVALUATION_SEMANTIC (contract);
408 5529 : tree i = (TREE_CODE (s) == INTEGER_CST) ? s
409 0 : : DECL_INITIAL (STRIP_NOPS (s));
410 5529 : gcc_checking_assert (!type_dependent_expression_p (s) && i);
411 5529 : switch (contract_evaluation_semantic ev =
412 5529 : (contract_evaluation_semantic) tree_to_uhwi (i))
413 : {
414 : /* This needs to be kept in step with any added semantics. */
415 5529 : case CES_IGNORE:
416 5529 : case CES_OBSERVE:
417 5529 : case CES_ENFORCE:
418 5529 : case CES_QUICK:
419 5529 : return ev;
420 : default:
421 : break;
422 : }
423 : }
424 :
425 0 : gcc_unreachable ();
426 : }
427 :
428 : /* Get location of the last contract in CONTRACTS. */
429 :
430 : static location_t
431 1475 : get_contract_end_loc (tree contracts)
432 : {
433 2950 : gcc_checking_assert (contracts && TREE_VEC_LENGTH (contracts) > 0);
434 1475 : tree last = TREE_VEC_ELT (contracts, TREE_VEC_LENGTH (contracts) - 1);
435 1475 : return EXPR_LOCATION (last);
436 : }
437 :
438 : /* Build the contract specifiers for a function from CONTRACTS, which are in
439 : source order. Returns NULL_TREE when there are none. */
440 :
441 : tree
442 22748120 : build_contract_specifiers (vec<tree, va_gc> *contracts)
443 : {
444 22748120 : unsigned len = vec_safe_length (contracts);
445 22748120 : if (!len)
446 : return NULL_TREE;
447 :
448 936 : tree specs = make_tree_vec (len);
449 3379 : for (unsigned ix = 0; ix < len; ix++)
450 1507 : TREE_VEC_ELT (specs, ix) = (*contracts)[ix];
451 : return specs;
452 : }
453 :
454 : /* Append the contract specifiers in SECOND to those in FIRST, either of
455 : which may be NULL_TREE. Neither input is modified. */
456 :
457 : tree
458 22903512 : contract_specifiers_concat (tree first, tree second)
459 : {
460 22903512 : if (!first)
461 : return second;
462 0 : if (!second)
463 : return first;
464 :
465 0 : int flen = TREE_VEC_LENGTH (first);
466 0 : int slen = TREE_VEC_LENGTH (second);
467 0 : tree specs = make_tree_vec (flen + slen);
468 0 : for (int ix = 0; ix < flen; ix++)
469 0 : TREE_VEC_ELT (specs, ix) = TREE_VEC_ELT (first, ix);
470 0 : for (int ix = 0; ix < slen; ix++)
471 0 : TREE_VEC_ELT (specs, flen + ix) = TREE_VEC_ELT (second, ix);
472 : return specs;
473 : }
474 :
475 : struct GTY(()) contract_decl
476 : {
477 : tree contract_specifiers;
478 : location_t note_loc;
479 : };
480 :
481 : static GTY(()) hash_map<tree, contract_decl> *contract_decl_map;
482 :
483 : /* Converts a contract condition to bool and ensures it has a location. */
484 :
485 : tree
486 2227 : finish_contract_condition (cp_expr condition)
487 : {
488 2227 : if (!condition || error_operand_p (condition))
489 : return condition;
490 :
491 : /* Ensure we have the condition location saved in case we later need to
492 : emit a conversion error during template instantiation and wouldn't
493 : otherwise have it. This differs from maybe_wrap_with_location in that
494 : it allows wrappers on EXCEPTIONAL_CLASS_P which includes CONSTRUCTORs. */
495 2207 : if (!CAN_HAVE_LOCATION_P (condition)
496 121 : && condition.get_location () != UNKNOWN_LOCATION)
497 : {
498 121 : tree_code code
499 117 : = (((CONSTANT_CLASS_P (condition) && TREE_CODE (condition) != STRING_CST)
500 4 : || (TREE_CODE (condition) == CONST_DECL && !TREE_STATIC (condition)))
501 121 : ? NON_LVALUE_EXPR : VIEW_CONVERT_EXPR);
502 121 : condition = build1_loc (condition.get_location (), code,
503 121 : TREE_TYPE (condition), condition);
504 121 : EXPR_LOCATION_WRAPPER_P (condition) = true;
505 : }
506 :
507 2207 : if (type_dependent_expression_p (condition))
508 : return condition;
509 :
510 1659 : return condition_conversion (condition);
511 : }
512 :
513 : /* Wrap the DECL into VIEW_CONVERT_EXPR representing const qualified version
514 : of the declaration. */
515 :
516 : tree
517 3471 : view_as_const (tree decl)
518 : {
519 3471 : if (decl
520 3471 : && !CP_TYPE_CONST_P (TREE_TYPE (decl)))
521 : {
522 2457 : gcc_checking_assert (!contract_const_wrapper_p (decl));
523 2457 : tree ctype = TREE_TYPE (decl);
524 2457 : location_t loc =
525 2457 : EXPR_P (decl) ? EXPR_LOCATION (decl) : DECL_SOURCE_LOCATION (decl);
526 2457 : ctype = cp_build_qualified_type (ctype, (cp_type_quals (ctype)
527 : | TYPE_QUAL_CONST));
528 2457 : decl = build1 (VIEW_CONVERT_EXPR, ctype, decl);
529 2457 : SET_EXPR_LOCATION (decl, loc);
530 : /* Mark the VCE as contract const wrapper. */
531 2457 : CONST_WRAPPER_P (decl) = true;
532 : }
533 3471 : return decl;
534 : }
535 :
536 : /* Constify access to DECL from within the contract condition. */
537 :
538 : tree
539 2696 : constify_contract_access (tree decl)
540 : {
541 : /* We check if we have a variable, a parameter, a variable of reference type,
542 : * or a parameter of reference type
543 : */
544 2696 : if (!TREE_READONLY (decl)
545 2696 : && (VAR_P (decl)
546 2233 : || (TREE_CODE (decl) == PARM_DECL)
547 781 : || (REFERENCE_REF_P (decl)
548 140 : && (VAR_P (TREE_OPERAND (decl, 0))
549 132 : || (TREE_CODE (TREE_OPERAND (decl, 0)) == PARM_DECL)
550 9 : || (TREE_CODE (TREE_OPERAND (decl, 0))
551 : == TEMPLATE_PARM_INDEX)))))
552 1776 : decl = view_as_const (decl);
553 :
554 2696 : return decl;
555 : }
556 :
557 : /* Indicate that PARM_DECL DECL is ODR used in a postcondition. */
558 :
559 : static void
560 728 : set_parm_used_in_post (tree decl, bool constify = true)
561 : {
562 728 : gcc_checking_assert (TREE_CODE (decl) == PARM_DECL);
563 728 : DECL_LANG_FLAG_4 (decl) = constify;
564 728 : }
565 :
566 : /* Test if PARM_DECL is ODR used in a postcondition. */
567 :
568 : static bool
569 797 : parm_used_in_post_p (const_tree decl)
570 : {
571 : /* Check if this parameter is odr used within a function's postcondition */
572 797 : return ((TREE_CODE (decl) == PARM_DECL) && DECL_LANG_FLAG_4 (decl));
573 : }
574 :
575 : /* If declaration DECL is a PARM_DECL and it appears in a postcondition, then
576 : check that it is not a non-const by-value param. LOCATION is where the
577 : expression was found and is used for diagnostic purposes. */
578 :
579 : void
580 755509051 : check_param_in_postcondition (tree decl, location_t location)
581 : {
582 755509051 : if (processing_postcondition
583 1543 : && TREE_CODE (decl) == PARM_DECL
584 : /* TREE_CODE (decl) == PARM_DECL only holds for non-reference
585 : parameters. */
586 907 : && !cp_unevaluated_operand
587 : /* Return value parameter has DECL_ARTIFICIAL flag set. The flag
588 : presence of the flag should be sufficient to distinguish the
589 : return value parameter in this context. */
590 755509742 : && !(DECL_ARTIFICIAL (decl)))
591 : {
592 481 : set_parm_used_in_post (decl);
593 :
594 481 : if (!dependent_type_p (TREE_TYPE (decl))
595 481 : && !CP_TYPE_CONST_P (TREE_TYPE (decl)))
596 : {
597 114 : auto_diagnostic_group d;
598 114 : error_at (location,
599 : "a value parameter used in a postcondition must be const");
600 114 : inform (DECL_SOURCE_LOCATION (decl), "parameter declared here");
601 114 : }
602 : }
603 755509051 : }
604 :
605 : /* Check if parameters used in postconditions are const qualified on
606 : a redeclaration that does not specify contracts or on an instantiation
607 : of a function template. */
608 :
609 : void
610 148208653 : check_postconditions_in_redecl (tree olddecl, tree newdecl)
611 : {
612 148208653 : tree contract_spec = get_fn_contract_specifiers (olddecl);
613 148208653 : if (!contract_spec)
614 : return;
615 :
616 490 : tree t1 = FUNCTION_FIRST_USER_PARM (olddecl);
617 490 : tree t2 = FUNCTION_FIRST_USER_PARM (newdecl);
618 :
619 1777 : for (; t1 && t1 != void_list_node;
620 797 : t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2))
621 : {
622 797 : if (parm_used_in_post_p (t1))
623 : {
624 247 : set_parm_used_in_post (t2);
625 247 : if (!dependent_type_p (TREE_TYPE (t2))
626 193 : && !CP_TYPE_CONST_P (TREE_TYPE (t2))
627 340 : && !TREE_READONLY (t2))
628 : {
629 93 : auto_diagnostic_group d;
630 93 : error_at (DECL_SOURCE_LOCATION (t2),
631 : "value parameter %qE used in a postcondition must be "
632 : "const", t2);
633 93 : inform (DECL_SOURCE_LOCATION (olddecl),
634 : "previous declaration here");
635 93 : }
636 : }
637 : }
638 : }
639 :
640 : /* Map from FUNCTION_DECL to a FUNCTION_DECL for either the PRE_FN or POST_FN.
641 : These are used to parse contract conditions and are called inside the body
642 : of the guarded function. */
643 : static GTY(()) hash_map<tree, tree> *decl_pre_fn;
644 : static GTY(()) hash_map<tree, tree> *decl_post_fn;
645 :
646 : /* Given a pre or post function decl (for an outlined check function) return
647 : the decl for the function for which the outlined checks are being
648 : performed. */
649 : static GTY(()) hash_map<tree, tree> *orig_from_outlined;
650 :
651 : /* Makes PRE the precondition function for FNDECL. */
652 :
653 : static void
654 14 : set_precondition_function (tree fndecl, tree pre)
655 : {
656 14 : gcc_assert (pre);
657 14 : hash_map_maybe_create<hm_ggc> (decl_pre_fn);
658 14 : gcc_checking_assert (!decl_pre_fn->get (fndecl));
659 14 : decl_pre_fn->put (fndecl, pre);
660 :
661 14 : hash_map_maybe_create<hm_ggc> (orig_from_outlined);
662 14 : gcc_checking_assert (!orig_from_outlined->get (pre));
663 14 : orig_from_outlined->put (pre, fndecl);
664 14 : }
665 :
666 : /* Makes POST the postcondition function for FNDECL. */
667 :
668 : static void
669 45 : set_postcondition_function (tree fndecl, tree post)
670 : {
671 45 : gcc_checking_assert (post);
672 45 : hash_map_maybe_create<hm_ggc> (decl_post_fn);
673 45 : gcc_checking_assert (!decl_post_fn->get (fndecl));
674 45 : decl_post_fn->put (fndecl, post);
675 :
676 45 : hash_map_maybe_create<hm_ggc> (orig_from_outlined);
677 45 : gcc_checking_assert (!orig_from_outlined->get (post));
678 45 : orig_from_outlined->put (post, fndecl);
679 45 : }
680 :
681 : /* For a given pre or post condition function, find the checked function. */
682 : tree
683 32 : get_orig_for_outlined (tree fndecl)
684 : {
685 32 : gcc_checking_assert (fndecl);
686 32 : tree *result = hash_map_safe_get (orig_from_outlined, fndecl);
687 32 : return result ? *result : NULL_TREE ;
688 : }
689 :
690 : /* For a given function OLD_FN set suitable names for NEW_FN (which is an
691 : outlined contract check) usually by appending '.pre' or '.post'.
692 :
693 : For functions with special meaning names (i.e. main and cdtors) we need to
694 : make special provisions and therefore handle all the contracts function
695 : name changes here, rather than requiring a separate update to mangle.cc.
696 :
697 : PRE specifies if we need an identifier for a pre or post contract check. */
698 :
699 : static void
700 87 : contracts_fixup_names (tree new_fn, tree old_fn, bool pre, bool wrapper)
701 : {
702 87 : bool cdtor = DECL_CXX_CONSTRUCTOR_P (old_fn)
703 87 : || DECL_CXX_DESTRUCTOR_P (old_fn);
704 87 : const char *fname = IDENTIFIER_POINTER (DECL_NAME (old_fn));
705 132 : const char *append = wrapper ? "contract_wrapper"
706 59 : : (pre ? "pre" : "post");
707 87 : size_t len = strlen (fname);
708 : /* Cdtor names have a space at the end. We need to remove that space
709 : when forming the new identifier. */
710 87 : char *nn = xasprintf ("%.*s%s%s",
711 0 : cdtor ? (int)len-1 : int(len),
712 : fname,
713 : JOIN_STR,
714 : append);
715 87 : DECL_NAME (new_fn) = get_identifier (nn);
716 87 : free (nn);
717 :
718 : /* Now do the mangled version. */
719 87 : fname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (old_fn));
720 87 : nn = xasprintf ("%s%s%s", fname, JOIN_STR, append);
721 87 : SET_DECL_ASSEMBLER_NAME (new_fn, get_identifier (nn));
722 87 : free (nn);
723 87 : }
724 :
725 : /* Build a declaration for the pre- or postcondition of a guarded FNDECL. */
726 :
727 : static tree
728 59 : build_contract_condition_function (tree fndecl, bool pre)
729 : {
730 59 : if (error_operand_p (fndecl))
731 0 : return error_mark_node;
732 :
733 : /* Start the copy. */
734 59 : tree fn = copy_decl (fndecl);
735 :
736 : /* Don't propagate declaration attributes to the checking function,
737 : including the original contracts. */
738 59 : DECL_ATTRIBUTES (fn) = NULL_TREE;
739 :
740 : /* If requested, disable optimisation of checking functions; this can, in
741 : some cases, prevent UB from eliding the checks themselves. */
742 59 : if (flag_contract_disable_optimized_checks)
743 0 : DECL_ATTRIBUTES (fn)
744 0 : = tree_cons (get_identifier ("optimize"),
745 : build_tree_list (NULL_TREE, build_string (3, "-O0")),
746 : NULL_TREE);
747 :
748 : /* Now parse and add any internal representation of these attrs to the
749 : decl. */
750 59 : if (DECL_ATTRIBUTES (fn))
751 0 : cplus_decl_attributes (&fn, DECL_ATTRIBUTES (fn), 0);
752 :
753 : /* A possible later optimization may delete unused args to prevent extra arg
754 : passing. */
755 : /* Handle the args list. */
756 59 : tree arg_types = NULL_TREE;
757 59 : tree *last = &arg_types;
758 59 : for (tree arg_type = TYPE_ARG_TYPES (TREE_TYPE (fn));
759 151 : arg_type && arg_type != void_list_node;
760 92 : arg_type = TREE_CHAIN (arg_type))
761 : {
762 92 : if (DECL_IOBJ_MEMBER_FUNCTION_P (fndecl)
763 92 : && TYPE_ARG_TYPES (TREE_TYPE (fn)) == arg_type)
764 36 : continue;
765 56 : *last = build_tree_list (TREE_PURPOSE (arg_type), TREE_VALUE (arg_type));
766 56 : last = &TREE_CHAIN (*last);
767 : }
768 :
769 : /* Copy the function parameters, if present. Disable warnings for them. */
770 59 : DECL_ARGUMENTS (fn) = NULL_TREE;
771 59 : if (DECL_ARGUMENTS (fndecl))
772 : {
773 54 : tree *last_a = &DECL_ARGUMENTS (fn);
774 146 : for (tree p = DECL_ARGUMENTS (fndecl); p; p = TREE_CHAIN (p))
775 : {
776 92 : *last_a = copy_decl (p);
777 92 : suppress_warning (*last_a);
778 92 : DECL_CONTEXT (*last_a) = fn;
779 92 : last_a = &TREE_CHAIN (*last_a);
780 : }
781 : }
782 :
783 59 : tree orig_fn_value_type = TREE_TYPE (TREE_TYPE (fn));
784 59 : if (!pre && !VOID_TYPE_P (orig_fn_value_type))
785 : {
786 : /* For post contracts that deal with a non-void function, append a
787 : parameter to pass the return value. */
788 35 : tree name = get_identifier ("__r");
789 35 : tree parm = build_lang_decl (PARM_DECL, name, orig_fn_value_type);
790 35 : DECL_CONTEXT (parm) = fn;
791 35 : DECL_ARTIFICIAL (parm) = true;
792 35 : suppress_warning (parm);
793 35 : DECL_ARGUMENTS (fn) = chainon (DECL_ARGUMENTS (fn), parm);
794 35 : *last = build_tree_list (NULL_TREE, orig_fn_value_type);
795 35 : last = &TREE_CHAIN (*last);
796 : }
797 :
798 59 : *last = void_list_node;
799 :
800 59 : tree adjusted_type = NULL_TREE;
801 :
802 : /* The handlers are void fns. */
803 59 : if (DECL_IOBJ_MEMBER_FUNCTION_P (fndecl))
804 36 : adjusted_type = build_method_type_directly (DECL_CONTEXT (fndecl),
805 : void_type_node,
806 : arg_types);
807 : else
808 23 : adjusted_type = build_function_type (void_type_node, arg_types);
809 :
810 : /* If the original function is noexcept, build a noexcept function. */
811 59 : if (flag_exceptions && type_noexcept_p (TREE_TYPE (fndecl)))
812 8 : adjusted_type = build_exception_variant (adjusted_type, noexcept_true_spec);
813 :
814 59 : TREE_TYPE (fn) = adjusted_type;
815 59 : DECL_RESULT (fn) = NULL_TREE; /* Let the start function code fill it in. */
816 :
817 : /* The contract check functions are never a cdtor, nor virtual. */
818 59 : DECL_CXX_DESTRUCTOR_P (fn) = DECL_CXX_CONSTRUCTOR_P (fn) = 0;
819 59 : DECL_VIRTUAL_P (fn) = false;
820 :
821 : /* Append .pre / .post to a usable name for the original function. */
822 59 : contracts_fixup_names (fn, fndecl, pre, /*wrapper*/false);
823 :
824 59 : DECL_INITIAL (fn) = NULL_TREE;
825 104 : CONTRACT_HELPER (fn) = pre ? ldf_contract_pre : ldf_contract_post;
826 : /* We might have a pre/post for a wrapper. */
827 59 : DECL_CONTRACT_WRAPPER (fn) = DECL_CONTRACT_WRAPPER (fndecl);
828 :
829 : /* Make these functions internal if we can, i.e. if the guarded function is
830 : not vague linkage, or if we can put them in a comdat group with the
831 : guarded function. */
832 59 : if (!DECL_WEAK (fndecl) || HAVE_COMDAT_GROUP)
833 : {
834 59 : TREE_PUBLIC (fn) = false;
835 59 : DECL_EXTERNAL (fn) = false;
836 59 : DECL_WEAK (fn) = false;
837 59 : DECL_COMDAT (fn) = false;
838 :
839 : /* We may not have set the comdat group on the guarded function yet.
840 : If we haven't, we'll add this to the same group in comdat_linkage
841 : later. Otherwise, add it to the same comdat group now. */
842 59 : if (DECL_ONE_ONLY (fndecl))
843 : {
844 0 : symtab_node *n = symtab_node::get (fndecl);
845 0 : cgraph_node::get_create (fn)->add_to_same_comdat_group (n);
846 : }
847 :
848 : }
849 :
850 59 : DECL_INTERFACE_KNOWN (fn) = true;
851 59 : DECL_ARTIFICIAL (fn) = true;
852 59 : suppress_warning (fn);
853 :
854 59 : return fn;
855 : }
856 :
857 : /* Build the precondition checking function for FNDECL. */
858 :
859 : static tree
860 26 : build_precondition_function (tree fndecl)
861 : {
862 26 : if (!has_active_preconditions (fndecl))
863 : return NULL_TREE;
864 :
865 14 : return build_contract_condition_function (fndecl, /*pre=*/true);
866 : }
867 :
868 : /* Build the postcondition checking function for FNDECL. If the return
869 : type is undeduced, don't build the function yet. We do that in
870 : apply_deduced_return_type. */
871 :
872 : static tree
873 57 : build_postcondition_function (tree fndecl)
874 : {
875 57 : if (!has_active_postconditions (fndecl))
876 : return NULL_TREE;
877 :
878 45 : tree type = TREE_TYPE (TREE_TYPE (fndecl));
879 45 : if (is_auto (type))
880 : return NULL_TREE;
881 :
882 45 : return build_contract_condition_function (fndecl, /*pre=*/false);
883 : }
884 :
885 : /* If we're outlining the contract, build the functions to do the
886 : precondition and postcondition checks, and associate them with
887 : the function decl FNDECL.
888 : */
889 :
890 : static void
891 26 : build_contract_function_decls (tree fndecl)
892 : {
893 : /* Build the pre/post functions (or not). */
894 26 : if (!get_precondition_function (fndecl))
895 26 : if (tree pre = build_precondition_function (fndecl))
896 14 : set_precondition_function (fndecl, pre);
897 :
898 26 : if (!get_postcondition_function (fndecl))
899 26 : if (tree post = build_postcondition_function (fndecl))
900 14 : set_postcondition_function (fndecl, post);
901 26 : }
902 :
903 : /* Map from FUNCTION_DECL to a FUNCTION_DECL for contract wrapper. */
904 :
905 : static GTY(()) hash_map<tree, tree> *decl_wrapper_fn = nullptr;
906 :
907 : /* Map from the function decl of a wrapper to the function that it wraps. */
908 :
909 : static GTY(()) hash_map<tree, tree> *decl_for_wrapper = nullptr;
910 :
911 : /* Makes wrapper the precondition function for FNDECL. */
912 :
913 : static void
914 28 : set_contract_wrapper_function (tree fndecl, tree wrapper)
915 : {
916 28 : gcc_checking_assert (wrapper && fndecl);
917 28 : hash_map_maybe_create<hm_ggc> (decl_wrapper_fn);
918 28 : gcc_checking_assert (decl_wrapper_fn && !decl_wrapper_fn->get (fndecl));
919 28 : decl_wrapper_fn->put (fndecl, wrapper);
920 :
921 : /* We need to know the wrapped function when composing the diagnostic. */
922 28 : hash_map_maybe_create<hm_ggc> (decl_for_wrapper);
923 28 : gcc_checking_assert (decl_for_wrapper && !decl_for_wrapper->get (wrapper));
924 28 : decl_for_wrapper->put (wrapper, fndecl);
925 28 : }
926 :
927 : /* Returns the wrapper function decl for FNDECL, or null if not set. */
928 :
929 : static tree
930 28 : get_contract_wrapper_function (tree fndecl)
931 : {
932 28 : gcc_checking_assert (fndecl);
933 28 : tree *result = hash_map_safe_get (decl_wrapper_fn, fndecl);
934 13 : return result ? *result : NULL_TREE;
935 : }
936 :
937 : /* Given a wrapper function WRAPPER, find the original function decl. */
938 :
939 : static tree
940 40 : get_orig_func_for_wrapper (tree wrapper)
941 : {
942 40 : gcc_checking_assert (wrapper);
943 40 : tree *result = hash_map_safe_get (decl_for_wrapper, wrapper);
944 40 : return result ? *result : NULL_TREE;
945 : }
946 :
947 : /* Build a declaration for the contract wrapper of a caller FNDECL.
948 : We're making a caller side contract check wrapper. For caller side contract
949 : checks, postconditions are only checked if check_post is true.
950 : Defer the attachment of the contracts to this function until the callee
951 : is non-dependent, or we get cases where the conditions can be non-dependent
952 : but still need tsubst-ing. */
953 :
954 : static tree
955 28 : build_contract_wrapper_function (tree fndecl)
956 : {
957 28 : if (error_operand_p (fndecl))
958 0 : return error_mark_node;
959 :
960 : /* We should not be trying to build wrappers for templates or functions that
961 : are still dependent. */
962 28 : gcc_checking_assert (!processing_template_decl
963 : && !TYPE_DEPENDENT_P (TREE_TYPE (fndecl)));
964 :
965 28 : location_t loc = DECL_SOURCE_LOCATION (fndecl);
966 :
967 : /* Fill in the names later. */
968 28 : tree wrapdecl
969 28 : = build_lang_decl_loc (loc, FUNCTION_DECL, NULL_TREE, TREE_TYPE (fndecl));
970 :
971 : /* Put the wrapper in the same context as the callee. */
972 28 : DECL_CONTEXT (wrapdecl) = DECL_CONTEXT (fndecl);
973 :
974 : /* This declaration is a contract wrapper function. */
975 28 : DECL_CONTRACT_WRAPPER (wrapdecl) = true;
976 :
977 28 : contracts_fixup_names (wrapdecl, fndecl, /*pre*/false, /*wrapper*/true);
978 :
979 28 : DECL_SOURCE_LOCATION (wrapdecl) = loc;
980 : /* The declaration was implicitly generated by the compiler. */
981 28 : DECL_ARTIFICIAL (wrapdecl) = true;
982 : /* Declaration, no definition yet. */
983 28 : DECL_INITIAL (wrapdecl) = NULL_TREE;
984 :
985 : /* Let the start function code fill in the result decl. */
986 28 : DECL_RESULT (wrapdecl) = NULL_TREE;
987 :
988 : /* Copy the function parameters, if present. Suppress (e.g. unused)
989 : warnings on them. */
990 28 : DECL_ARGUMENTS (wrapdecl) = NULL_TREE;
991 28 : if (tree p = DECL_ARGUMENTS (fndecl))
992 : {
993 28 : tree *last_a = &DECL_ARGUMENTS (wrapdecl);
994 87 : for (; p; p = TREE_CHAIN (p))
995 : {
996 59 : *last_a = copy_decl (p);
997 59 : suppress_warning (*last_a);
998 59 : DECL_CONTEXT (*last_a) = wrapdecl;
999 59 : last_a = &TREE_CHAIN (*last_a);
1000 : }
1001 : }
1002 :
1003 : /* Copy selected attributes from the original function. */
1004 28 : TREE_USED (wrapdecl) = TREE_USED (fndecl);
1005 :
1006 : /* Copy any alignment added. */
1007 28 : if (DECL_ALIGN (fndecl))
1008 28 : SET_DECL_ALIGN (wrapdecl, DECL_ALIGN (fndecl));
1009 28 : DECL_USER_ALIGN (wrapdecl) = DECL_USER_ALIGN (fndecl);
1010 :
1011 : /* Make this function internal. */
1012 28 : TREE_PUBLIC (wrapdecl) = false;
1013 28 : DECL_EXTERNAL (wrapdecl) = false;
1014 28 : DECL_WEAK (wrapdecl) = false;
1015 :
1016 : /* We know this is an internal function. */
1017 28 : DECL_INTERFACE_KNOWN (wrapdecl) = true;
1018 28 : return wrapdecl;
1019 : }
1020 :
1021 : static tree
1022 28 : get_or_create_contract_wrapper_function (tree fndecl)
1023 : {
1024 28 : tree wrapdecl = get_contract_wrapper_function (fndecl);
1025 28 : if (!wrapdecl)
1026 : {
1027 28 : wrapdecl = build_contract_wrapper_function (fndecl);
1028 28 : set_contract_wrapper_function (fndecl, wrapdecl);
1029 : }
1030 28 : return wrapdecl;
1031 : }
1032 :
1033 : void
1034 169405640 : start_function_contracts (tree fndecl)
1035 : {
1036 169405640 : if (error_operand_p (fndecl))
1037 : return;
1038 :
1039 169405640 : if (!handle_contracts_p (fndecl))
1040 : return;
1041 :
1042 : /* If this is not a client side check and definition side checks are
1043 : disabled, do nothing. */
1044 620 : if (!flag_contracts_definition_check
1045 620 : && !DECL_CONTRACT_WRAPPER (fndecl))
1046 : return;
1047 :
1048 : /* Check that the postcondition result name, if any, does not shadow a
1049 : function parameter. */
1050 618 : if (tree specs = get_fn_contract_specifiers (fndecl))
1051 1546 : for (tree ca : tree_vec_range (specs))
1052 928 : if (POSTCONDITION_P (ca))
1053 377 : if (tree id = POSTCONDITION_IDENTIFIER (ca))
1054 : {
1055 168 : if (id == error_mark_node)
1056 : {
1057 3 : CONTRACT_CONDITION (ca) = error_mark_node;
1058 3 : continue;
1059 : }
1060 165 : tree r_name = tree_strip_any_location_wrapper (id);
1061 165 : if (TREE_CODE (id) == PARM_DECL)
1062 165 : r_name = DECL_NAME (id);
1063 165 : gcc_checking_assert (r_name
1064 : && TREE_CODE (r_name) == IDENTIFIER_NODE);
1065 165 : tree seen = lookup_name (r_name);
1066 165 : if (seen
1067 3 : && TREE_CODE (seen) == PARM_DECL
1068 168 : && DECL_CONTEXT (seen) == fndecl)
1069 : {
1070 3 : auto_diagnostic_group d;
1071 3 : location_t id_l = location_wrapper_p (id)
1072 3 : ? EXPR_LOCATION (id)
1073 3 : : DECL_SOURCE_LOCATION (id);
1074 3 : location_t co_l = EXPR_LOCATION (ca);
1075 3 : if (id_l != UNKNOWN_LOCATION)
1076 3 : co_l = make_location (id_l, co_l, co_l);
1077 3 : error_at (co_l, "contract postcondition result name shadows a"
1078 : " function parameter");
1079 3 : inform (DECL_SOURCE_LOCATION (seen),
1080 : "parameter declared here");
1081 3 : POSTCONDITION_IDENTIFIER (ca) = error_mark_node;
1082 3 : CONTRACT_CONDITION (ca) = error_mark_node;
1083 3 : }
1084 : }
1085 :
1086 : /* If we are expanding contract assertions inline then no need to declare
1087 : the outline function decls. */
1088 618 : if (!flag_contract_checks_outlined)
1089 : return;
1090 :
1091 : /* Contracts may have just been added without a chance to parse them, though
1092 : we still need the PRE_FN available to generate a call to it. */
1093 : /* Do we already have declarations generated ? */
1094 26 : if (!DECL_PRE_FN (fndecl) && !DECL_POST_FN (fndecl))
1095 26 : build_contract_function_decls (fndecl);
1096 : }
1097 :
1098 : void
1099 1078690 : maybe_update_postconditions (tree fndecl)
1100 : {
1101 : /* Update any postconditions and the postcondition checking function
1102 : as needed. If there are postconditions, we'll use those to rewrite
1103 : return statements to check postconditions. */
1104 1078690 : if (has_active_postconditions (fndecl))
1105 : {
1106 31 : rebuild_postconditions (fndecl);
1107 31 : tree post = build_postcondition_function (fndecl);
1108 31 : set_postcondition_function (fndecl, post);
1109 : }
1110 1078690 : }
1111 :
1112 : /* Build and return an argument list containing all the parameters of the
1113 : (presumably guarded) function decl FNDECL. This can be used to forward
1114 : all of FNDECL arguments to a function taking the same list of arguments
1115 : -- namely the unchecked form of FNDECL.
1116 :
1117 : We use CALL_FROM_THUNK_P instead of forward_parm for forwarding
1118 : semantics. */
1119 :
1120 : static vec<tree, va_gc> *
1121 56 : build_arg_list (tree fndecl)
1122 : {
1123 56 : vec<tree, va_gc> *args = make_tree_vector ();
1124 155 : for (tree t = DECL_ARGUMENTS (fndecl); t; t = DECL_CHAIN (t))
1125 99 : vec_safe_push (args, t);
1126 56 : return args;
1127 : }
1128 :
1129 : /* Build and return a thunk like call to FUNC from CALLER using the supplied
1130 : arguments. The call is like a thunk call in the fact that we do not
1131 : want to create additional copies of the arguments. We can not simply reuse
1132 : the thunk machinery as it does more than we want. More specifically, we
1133 : don't want to mark the calling function as `DECL_THUNK_P` for this
1134 : particular purpose, we only want the special treatment for the parameters
1135 : of the call we are about to generate. We temporarily mark the calling
1136 : function as DECL_THUNK_P so build_call_a does the right thing. */
1137 :
1138 : static tree
1139 56 : build_thunk_like_call (tree func, int n, tree *argarray)
1140 : {
1141 56 : bool old_decl_thunk_p = DECL_THUNK_P (current_function_decl);
1142 56 : LANG_DECL_FN_CHECK (current_function_decl)->thunk_p = true;
1143 :
1144 56 : tree call = build_call_a (func, n, argarray);
1145 :
1146 : /* Revert the `DECL_THUNK_P` flag. */
1147 56 : LANG_DECL_FN_CHECK (current_function_decl)->thunk_p = old_decl_thunk_p;
1148 :
1149 : /* Mark the call as a thunk call to allow for correct gimplification
1150 : of the arguments. */
1151 56 : CALL_FROM_THUNK_P (call) = true;
1152 :
1153 56 : return call;
1154 : }
1155 :
1156 : /* If we have a precondition function and it's valid, call it. */
1157 :
1158 : static void
1159 14 : add_pre_condition_fn_call (tree fndecl)
1160 : {
1161 : /* If we're starting a guarded function with valid contracts, we need to
1162 : insert a call to the pre function. */
1163 14 : gcc_checking_assert (DECL_PRE_FN (fndecl)
1164 : && DECL_PRE_FN (fndecl) != error_mark_node);
1165 :
1166 14 : releasing_vec args = build_arg_list (fndecl);
1167 14 : tree call = build_thunk_like_call (DECL_PRE_FN (fndecl),
1168 14 : args->length (), args->address ());
1169 :
1170 14 : finish_expr_stmt (call);
1171 14 : }
1172 :
1173 : /* Returns the parameter corresponding to the return value of a guarded
1174 : function FNDECL. Returns NULL_TREE if FNDECL has no postconditions or
1175 : is void. */
1176 :
1177 : static tree
1178 14 : get_postcondition_result_parameter (tree fndecl)
1179 : {
1180 14 : if (!fndecl || fndecl == error_mark_node)
1181 : return NULL_TREE;
1182 :
1183 14 : if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (fndecl))))
1184 : return NULL_TREE;
1185 :
1186 4 : tree post = DECL_POST_FN (fndecl);
1187 4 : if (!post || post == error_mark_node)
1188 : return NULL_TREE;
1189 :
1190 : /* The last param is the return value. */
1191 4 : return tree_last (DECL_ARGUMENTS (post));
1192 : }
1193 :
1194 : /* Build and add a call to the post-condition checking function, when that
1195 : is in use. */
1196 :
1197 : static void
1198 14 : add_post_condition_fn_call (tree fndecl)
1199 : {
1200 14 : gcc_checking_assert (DECL_POST_FN (fndecl)
1201 : && DECL_POST_FN (fndecl) != error_mark_node);
1202 :
1203 14 : releasing_vec args = build_arg_list (fndecl);
1204 14 : if (get_postcondition_result_parameter (fndecl))
1205 4 : vec_safe_push (args, DECL_RESULT (fndecl));
1206 14 : tree call = build_thunk_like_call (DECL_POST_FN (fndecl),
1207 14 : args->length (), args->address ());
1208 14 : finish_expr_stmt (call);
1209 14 : }
1210 :
1211 : /* Copy (possibly a sub-set of) contracts from CONTRACTS on FNDECL. */
1212 :
1213 : static tree
1214 665 : copy_contracts_list (tree contracts, tree fndecl,
1215 : contract_match_kind remap_kind = cmk_all)
1216 : {
1217 665 : if (!contracts)
1218 : return NULL_TREE;
1219 :
1220 665 : auto_vec<tree> copies (TREE_VEC_LENGTH (contracts));
1221 1764 : for (tree contract : tree_vec_range (contracts))
1222 : {
1223 1099 : if ((remap_kind == cmk_pre
1224 631 : && TREE_CODE (contract) == POSTCONDITION_STMT)
1225 1003 : || (remap_kind == cmk_post
1226 468 : && TREE_CODE (contract) == PRECONDITION_STMT))
1227 203 : continue;
1228 :
1229 896 : tree c = copy_node (contract);
1230 :
1231 896 : copy_body_data id;
1232 896 : hash_map<tree, tree> decl_map;
1233 :
1234 896 : memset (&id, 0, sizeof (id));
1235 :
1236 896 : id.src_fn = fndecl;
1237 896 : id.dst_fn = fndecl;
1238 896 : id.src_cfun = DECL_STRUCT_FUNCTION (fndecl);
1239 896 : id.decl_map = &decl_map;
1240 :
1241 896 : id.copy_decl = retain_decl;
1242 :
1243 896 : id.transform_call_graph_edges = CB_CGE_DUPLICATE;
1244 896 : id.transform_new_cfg = false;
1245 896 : id.transform_return_to_modify = false;
1246 896 : id.transform_parameter = true;
1247 :
1248 : /* Make sure not to unshare trees behind the front-end's back
1249 : since front-end specific mechanisms may rely on sharing. */
1250 896 : id.regimplify = false;
1251 896 : id.do_not_unshare = true;
1252 896 : id.do_not_fold = true;
1253 :
1254 : /* We're not inside any EH region. */
1255 896 : id.eh_lp_nr = 0;
1256 896 : walk_tree (&CONTRACT_CONDITION (c), copy_tree_body_r, &id, NULL);
1257 :
1258 896 : CONTRACT_COMMENT (c) = copy_node (CONTRACT_COMMENT (c));
1259 :
1260 896 : copies.quick_push (c);
1261 896 : }
1262 :
1263 665 : if (copies.is_empty ())
1264 : return NULL_TREE;
1265 :
1266 665 : tree new_contracts = make_tree_vec (copies.length ());
1267 2226 : for (unsigned ix = 0; ix < copies.length (); ix++)
1268 896 : TREE_VEC_ELT (new_contracts, ix) = copies[ix];
1269 : return new_contracts;
1270 665 : }
1271 :
1272 : /* Returns a copy of FNDECL contracts. This is used when emitting a contract.
1273 : If we were to emit the original contract tree, any folding of the contract
1274 : condition would affect the original contract too. The original contract
1275 : tree needs to be preserved in case it is used to apply to a different
1276 : function (for inheritance or wrapping reasons). */
1277 :
1278 : static tree
1279 665 : copy_contracts (tree fndecl, contract_match_kind remap_kind = cmk_all)
1280 : {
1281 665 : tree contracts = get_fn_contract_specifiers (fndecl);
1282 665 : return copy_contracts_list (contracts, fndecl, remap_kind);
1283 : }
1284 :
1285 : /* Add the contract statement CONTRACT to the current block if valid. */
1286 :
1287 : static bool
1288 928 : emit_contract_statement (tree contract)
1289 : {
1290 : /* Only add valid contracts. */
1291 928 : if (contract == error_mark_node
1292 928 : || CONTRACT_CONDITION (contract) == error_mark_node)
1293 : return false;
1294 :
1295 911 : if (get_evaluation_semantic (contract) == CES_INVALID)
1296 : return false;
1297 :
1298 911 : add_stmt (contract);
1299 911 : return true;
1300 : }
1301 :
1302 : /* Add a call or a direct evaluation of the pre checks. */
1303 :
1304 : static void
1305 439 : apply_preconditions (tree fndecl)
1306 : {
1307 439 : if (flag_contract_checks_outlined)
1308 14 : add_pre_condition_fn_call (fndecl);
1309 : else
1310 : {
1311 425 : if (tree contract_copy = copy_contracts (fndecl, cmk_pre))
1312 960 : for (tree contract : tree_vec_range (contract_copy))
1313 535 : emit_contract_statement (contract);
1314 : }
1315 439 : }
1316 :
1317 : /* Add a call or a direct evaluation of the post checks. */
1318 :
1319 : static void
1320 254 : apply_postconditions (tree fndecl)
1321 : {
1322 254 : if (flag_contract_checks_outlined)
1323 14 : add_post_condition_fn_call (fndecl);
1324 : else
1325 : {
1326 240 : if (tree contract_copy = copy_contracts (fndecl, cmk_post))
1327 601 : for (tree contract : tree_vec_range (contract_copy))
1328 361 : emit_contract_statement (contract);
1329 : }
1330 254 : }
1331 :
1332 : /* Add contract handling to the function in FNDECL.
1333 :
1334 : When we have only pre-conditions, this simply prepends a call (or a direct
1335 : evaluation, for cdtors) to the existing function body.
1336 :
1337 : When we have post conditions we build a try-finally block.
1338 : If the function might throw then the handler in the try-finally is an
1339 : EH_ELSE expression, where the post condition check is applied to the
1340 : non-exceptional path, and an empty statement is added to the EH path. If
1341 : the function has a non-throwing eh spec, then the handler is simply the
1342 : post-condition checker. */
1343 :
1344 : void
1345 152618777 : maybe_apply_function_contracts (tree fndecl)
1346 : {
1347 152618777 : if (!handle_contracts_p (fndecl))
1348 : /* We did nothing and the original function body statement list will be
1349 : popped by our caller. */
1350 : return;
1351 :
1352 : /* If this is not a client side check and definition side checks are
1353 : disabled, do nothing. */
1354 620 : if (!flag_contracts_definition_check
1355 620 : && !DECL_CONTRACT_WRAPPER (fndecl))
1356 : return;
1357 :
1358 618 : bool do_pre = has_active_preconditions (fndecl);
1359 618 : bool do_post = has_active_postconditions (fndecl);
1360 : /* We should not have reached here with nothing to do... */
1361 618 : gcc_checking_assert (do_pre || do_post);
1362 :
1363 : /* If the function is noexcept, the user's written body will be wrapped in a
1364 : MUST_NOT_THROW expression. In that case we leave the MUST_NOT_THROW in
1365 : place and do our replacement inside it. */
1366 618 : tree fnbody;
1367 618 : if (TYPE_NOEXCEPT_P (TREE_TYPE (fndecl)))
1368 : {
1369 42 : tree m_n_t_expr = expr_first (DECL_SAVED_TREE (fndecl));
1370 42 : gcc_checking_assert (TREE_CODE (m_n_t_expr) == MUST_NOT_THROW_EXPR);
1371 42 : fnbody = TREE_OPERAND (m_n_t_expr, 0);
1372 42 : TREE_OPERAND (m_n_t_expr, 0) = push_stmt_list ();
1373 : }
1374 : else
1375 : {
1376 576 : fnbody = DECL_SAVED_TREE (fndecl);
1377 576 : DECL_SAVED_TREE (fndecl) = push_stmt_list ();
1378 : }
1379 :
1380 : /* If we have a lambda with captures, ensure that those captures are in-
1381 : scope for pre and post conditions. */
1382 640 : if (LAMBDA_FUNCTION_P (fndecl)
1383 640 : && TREE_CODE (fnbody) == BIND_EXPR)
1384 : {
1385 0 : tree extract = BIND_EXPR_BODY (fnbody);
1386 0 : BIND_EXPR_BODY (fnbody) = NULL_TREE;
1387 0 : add_stmt (fnbody);
1388 0 : BIND_EXPR_BODY (fnbody) = push_stmt_list ();
1389 0 : fnbody = extract;
1390 : }
1391 :
1392 : /* Now add the pre and post conditions to the existing function body.
1393 : This approach mostly copies that of function try blocks, but also stops
1394 : maybe_splice_retval_cleanup from acting on sk_function_parms again and
1395 : introducing another DECL_EXPR for the same sentinel, or a second cleanup
1396 : statement, both of which would be broken.
1397 :
1398 : FIXME: This means no cleanup statement will be emitted to clean up the
1399 : return object if the postcondition evaluations themselves throw an
1400 : exception (from the contract-violation handler), and this suppression
1401 : might need to be removed when that is addressed (PR c++/127414). */
1402 618 : auto retval_sentinel_ovr = make_temp_override (current_retval_sentinel,
1403 618 : NULL_TREE);
1404 :
1405 618 : tree compound_stmt = begin_compound_stmt (0);
1406 618 : current_binding_level->artificial = true;
1407 :
1408 : /* Do not add locations for the synthesised code. */
1409 618 : location_t loc = UNKNOWN_LOCATION;
1410 :
1411 : /* For other cases, we call a function to process the check. */
1412 :
1413 : /* If we have a pre, but not a post, then just emit that and we are done. */
1414 618 : if (!do_post)
1415 : {
1416 364 : apply_preconditions (fndecl);
1417 364 : add_stmt (fnbody);
1418 364 : finish_compound_stmt (compound_stmt);
1419 364 : return;
1420 : }
1421 :
1422 254 : if (do_pre)
1423 : /* Add a precondition call, if we have one. */
1424 75 : apply_preconditions (fndecl);
1425 254 : tree try_fin = build_stmt (loc, TRY_FINALLY_EXPR, fnbody, NULL_TREE);
1426 254 : add_stmt (try_fin);
1427 254 : TREE_OPERAND (try_fin, 1) = push_stmt_list ();
1428 : /* If we have exceptions, and a function that might throw, then add
1429 : an EH_ELSE clause that allows the exception to propagate upwards
1430 : without encountering the post-condition checks. */
1431 254 : if (flag_exceptions && !type_noexcept_p (TREE_TYPE (fndecl)))
1432 : {
1433 240 : tree eh_else = build_stmt (loc, EH_ELSE_EXPR, NULL_TREE, NULL_TREE);
1434 240 : add_stmt (eh_else);
1435 240 : TREE_OPERAND (eh_else, 0) = push_stmt_list ();
1436 240 : apply_postconditions (fndecl);
1437 240 : TREE_OPERAND (eh_else, 0) = pop_stmt_list (TREE_OPERAND (eh_else, 0));
1438 240 : TREE_OPERAND (eh_else, 1) = void_node;
1439 : }
1440 : else
1441 14 : apply_postconditions (fndecl);
1442 254 : TREE_OPERAND (try_fin, 1) = pop_stmt_list (TREE_OPERAND (try_fin, 1));
1443 254 : finish_compound_stmt (compound_stmt);
1444 : /* The DECL_SAVED_TREE stmt list will be popped by our caller. */
1445 618 : }
1446 :
1447 : /* Rewrite the condition of contract in place, so that references to SRC's
1448 : parameters are updated to refer to DST's parameters. The postcondition
1449 : result variable is left unchanged.
1450 :
1451 : When declarations are merged, we sometimes need to update contracts to
1452 : refer to new parameters.
1453 :
1454 : If DUPLICATE_P is true, this is called by duplicate_decls to rewrite
1455 : contracts in terms of a new set of parameters. This also preserves the
1456 : references to postcondition results, which are not replaced during
1457 : merging. */
1458 :
1459 : static void
1460 356 : remap_contract (tree src, tree dst, tree contract, bool duplicate_p)
1461 : {
1462 356 : copy_body_data id;
1463 356 : hash_map<tree, tree> decl_map;
1464 :
1465 356 : memset (&id, 0, sizeof (id));
1466 356 : id.src_fn = src;
1467 356 : id.dst_fn = dst;
1468 356 : id.src_cfun = DECL_STRUCT_FUNCTION (src);
1469 356 : id.decl_map = &decl_map;
1470 :
1471 : /* If we're merging contracts, don't copy local variables. */
1472 356 : id.copy_decl = duplicate_p ? retain_decl : copy_decl_no_change;
1473 :
1474 356 : id.transform_call_graph_edges = CB_CGE_DUPLICATE;
1475 356 : id.transform_new_cfg = false;
1476 356 : id.transform_return_to_modify = false;
1477 356 : id.transform_parameter = true;
1478 :
1479 : /* Make sure not to unshare trees behind the front-end's back
1480 : since front-end specific mechanisms may rely on sharing. */
1481 356 : id.regimplify = false;
1482 356 : id.do_not_unshare = true;
1483 356 : id.do_not_fold = true;
1484 :
1485 : /* We're not inside any EH region. */
1486 356 : id.eh_lp_nr = 0;
1487 :
1488 356 : bool do_remap = false;
1489 :
1490 : /* Insert parameter remappings. */
1491 356 : gcc_checking_assert (TREE_CODE (src) == FUNCTION_DECL);
1492 356 : gcc_checking_assert (TREE_CODE (dst) == FUNCTION_DECL);
1493 :
1494 356 : int src_num_artificial_args = num_artificial_parms_for (src);
1495 356 : int dst_num_artificial_args = num_artificial_parms_for (dst);
1496 :
1497 356 : for (tree sp = DECL_ARGUMENTS (src), dp = DECL_ARGUMENTS (dst);
1498 1038 : sp || dp;
1499 682 : sp = DECL_CHAIN (sp), dp = DECL_CHAIN (dp))
1500 : {
1501 688 : if (!sp && dp
1502 6 : && TREE_CODE (contract) == POSTCONDITION_STMT
1503 694 : && DECL_CHAIN (dp) == NULL_TREE)
1504 : {
1505 6 : gcc_assert (!duplicate_p);
1506 6 : if (tree result = POSTCONDITION_IDENTIFIER (contract))
1507 : {
1508 6 : gcc_assert (DECL_P (result));
1509 6 : insert_decl_map (&id, result, dp);
1510 6 : do_remap = true;
1511 : }
1512 : break;
1513 : }
1514 682 : gcc_assert (sp && dp);
1515 :
1516 682 : if (sp == dp)
1517 304 : continue;
1518 :
1519 378 : insert_decl_map (&id, sp, dp);
1520 378 : do_remap = true;
1521 :
1522 : /* First artificial arg is *this. We want to remap that. However, we
1523 : want to skip _in_charge param and __vtt_parm. Do so now. */
1524 378 : if (src_num_artificial_args > 0)
1525 : {
1526 113 : while (--src_num_artificial_args,src_num_artificial_args > 0)
1527 0 : sp = DECL_CHAIN (sp);
1528 : }
1529 378 : if (dst_num_artificial_args > 0)
1530 : {
1531 113 : while (--dst_num_artificial_args,dst_num_artificial_args > 0)
1532 0 : dp = DECL_CHAIN (dp);
1533 : }
1534 : }
1535 :
1536 356 : if (!do_remap)
1537 162 : return;
1538 :
1539 194 : walk_tree (&CONTRACT_CONDITION (contract), copy_tree_body_r, &id, NULL);
1540 356 : }
1541 :
1542 : /* Returns a copy of SOURCE contracts where any references to SOURCE's
1543 : PARM_DECLs have been rewritten to the corresponding PARM_DECL in DEST. */
1544 :
1545 : tree
1546 243 : copy_and_remap_contracts (tree dest, tree source,
1547 : contract_match_kind remap_kind)
1548 : {
1549 243 : tree contracts = get_fn_contract_specifiers (source);
1550 243 : if (!contracts)
1551 : return NULL_TREE;
1552 :
1553 243 : auto_vec<tree> copies (TREE_VEC_LENGTH (contracts));
1554 571 : for (tree contract : tree_vec_range (contracts))
1555 : {
1556 328 : if ((remap_kind == cmk_pre
1557 10 : && TREE_CODE (contract) == POSTCONDITION_STMT)
1558 324 : || (remap_kind == cmk_post
1559 0 : && TREE_CODE (contract) == PRECONDITION_STMT))
1560 4 : continue;
1561 :
1562 324 : tree stmt = copy_node (contract);
1563 :
1564 : /* If we have an erroneous postcondition identifier, we also mark the
1565 : condition as invalid so only need to check that. */
1566 324 : if (CONTRACT_CONDITION (stmt) != error_mark_node)
1567 324 : remap_contract (source, dest, stmt, /*duplicate_p=*/true);
1568 :
1569 324 : if (TREE_CODE (stmt) == POSTCONDITION_STMT)
1570 : {
1571 : /* If we have a postcondition return value placeholder, then
1572 : ensure the copied one has the correct context. */
1573 110 : tree var = POSTCONDITION_IDENTIFIER (stmt);
1574 110 : if (var && var != error_mark_node)
1575 24 : DECL_CONTEXT (var) = dest;
1576 : }
1577 :
1578 324 : if (CONTRACT_COMMENT (stmt) != error_mark_node)
1579 324 : CONTRACT_COMMENT (stmt) = copy_node (CONTRACT_COMMENT (stmt));
1580 :
1581 324 : copies.quick_push (stmt);
1582 : }
1583 :
1584 243 : if (copies.is_empty ())
1585 : return NULL_TREE;
1586 :
1587 243 : tree contracts_copy = make_tree_vec (copies.length ());
1588 810 : for (unsigned ix = 0; ix < copies.length (); ix++)
1589 324 : TREE_VEC_ELT (contracts_copy, ix) = copies[ix];
1590 :
1591 : return contracts_copy;
1592 243 : }
1593 :
1594 : /* Set the (maybe) parsed contract specifiers CONTRACTS for DECL.
1595 : CONTRACTS is either NULL_TREE or a TREE_VEC of contract statements. */
1596 :
1597 : void
1598 1818 : set_fn_contract_specifiers (tree decl, tree contracts)
1599 : {
1600 1818 : if (!decl || error_operand_p (decl))
1601 0 : return;
1602 :
1603 1818 : gcc_checking_assert (!contracts || TREE_CODE (contracts) == TREE_VEC);
1604 :
1605 1818 : bool existed = false;
1606 1818 : contract_decl& rd
1607 1818 : = hash_map_safe_get_or_insert<hm_ggc> (contract_decl_map, decl, &existed);
1608 1818 : if (!existed)
1609 : {
1610 : /* This is the first time we encountered this decl, save the location
1611 : for error messages. This will ensure all error messages refer to the
1612 : contracts used for the function. */
1613 1403 : location_t decl_loc = DECL_SOURCE_LOCATION (decl);
1614 1403 : location_t cont_end = decl_loc;
1615 1403 : if (contracts)
1616 1403 : cont_end = get_contract_end_loc (contracts);
1617 1403 : rd.note_loc = make_location (decl_loc, decl_loc, cont_end);
1618 : }
1619 1818 : rd.contract_specifiers = contracts;
1620 : }
1621 :
1622 : /* Update the entry for DECL in the map of contract specifiers with the
1623 : contracts in CONTRACTS. */
1624 :
1625 : void
1626 448 : update_fn_contract_specifiers (tree decl, tree contracts)
1627 : {
1628 448 : if (!decl || error_operand_p (decl))
1629 0 : return;
1630 :
1631 448 : bool existed = false;
1632 448 : contract_decl& rd
1633 448 : = hash_map_safe_get_or_insert<hm_ggc> (contract_decl_map, decl, &existed);
1634 448 : gcc_checking_assert (existed);
1635 :
1636 : /* We should only get here when we parse deferred contracts. */
1637 448 : gcc_checking_assert (!contract_any_deferred_p (contracts));
1638 :
1639 448 : rd.contract_specifiers = contracts;
1640 : }
1641 :
1642 : /* When a decl is about to be removed, then we need to release its content and
1643 : then take it out of the map. */
1644 :
1645 : void
1646 1539794 : remove_decl_with_fn_contracts_specifiers (tree decl)
1647 : {
1648 1539997 : if (contract_decl *p = hash_map_safe_get (contract_decl_map, decl))
1649 : {
1650 164 : p->contract_specifiers = NULL_TREE;
1651 164 : contract_decl_map->remove (decl);
1652 : }
1653 1539794 : }
1654 :
1655 : /* If this function has contract specifiers, then remove them, but leave the
1656 : function registered. */
1657 :
1658 : void
1659 481505 : remove_fn_contract_specifiers (tree decl)
1660 : {
1661 481556 : if (contract_decl *p = hash_map_safe_get (contract_decl_map, decl))
1662 : {
1663 49 : p->contract_specifiers = NULL_TREE;
1664 : }
1665 481505 : }
1666 :
1667 : /* Get the contract specifier list for this DECL if there is one. */
1668 :
1669 : tree
1670 557936731 : get_fn_contract_specifiers (tree decl)
1671 : {
1672 558441505 : if (contract_decl *p = hash_map_safe_get (contract_decl_map, decl))
1673 9350 : return p->contract_specifiers;
1674 : return NULL_TREE;
1675 : }
1676 :
1677 : /* A subroutine of duplicate_decls. Diagnose issues in the redeclaration of
1678 : guarded functions. */
1679 :
1680 : void
1681 20058292 : check_redecl_contract (tree newdecl, tree olddecl)
1682 : {
1683 20058292 : if (!flag_contracts)
1684 : return;
1685 :
1686 1893816 : if (TREE_CODE (newdecl) == TEMPLATE_DECL)
1687 429403 : newdecl = DECL_TEMPLATE_RESULT (newdecl);
1688 1893816 : if (TREE_CODE (olddecl) == TEMPLATE_DECL)
1689 429403 : olddecl = DECL_TEMPLATE_RESULT (olddecl);
1690 :
1691 1893816 : tree new_contracts = get_fn_contract_specifiers (newdecl);
1692 1893816 : tree old_contracts = get_fn_contract_specifiers (olddecl);
1693 :
1694 1893816 : if (!old_contracts && !new_contracts)
1695 : return;
1696 :
1697 : /* We should always be comparing with the 'first' declaration which should
1698 : have been recorded already (if it has contract specifiers). However
1699 : if the new decl is trying to add contracts, that is an error and we do
1700 : not want to create a map entry yet. */
1701 199 : contract_decl *rdp = hash_map_safe_get (contract_decl_map, olddecl);
1702 199 : gcc_checking_assert(rdp || !old_contracts);
1703 :
1704 199 : location_t new_loc = DECL_SOURCE_LOCATION (newdecl);
1705 199 : if (new_contracts && !old_contracts)
1706 : {
1707 15 : auto_diagnostic_group d;
1708 : /* If a re-declaration has contracts, they must be the same as those
1709 : that appear on the first declaration seen (they cannot be added). */
1710 15 : location_t cont_end = get_contract_end_loc (new_contracts);
1711 15 : cont_end = make_location (new_loc, new_loc, cont_end);
1712 15 : error_at (cont_end, "declaration adds contracts to %q#D", olddecl);
1713 15 : inform (DECL_SOURCE_LOCATION (olddecl), "first declared here");
1714 15 : return;
1715 15 : }
1716 :
1717 184 : if (old_contracts && !new_contracts)
1718 : /* We allow re-declarations to omit contracts declared on the initial decl.
1719 : In fact, this is required if the conditions contain lambdas. Check if
1720 : all the parameters are correctly const qualified. */
1721 121 : check_postconditions_in_redecl (olddecl, newdecl);
1722 63 : else if (old_contracts && new_contracts
1723 63 : && !contract_any_deferred_p (old_contracts)
1724 57 : && contract_any_deferred_p (new_contracts)
1725 63 : && DECL_UNIQUE_FRIEND_P (newdecl))
1726 : {
1727 : /* Put the deferred contracts on the olddecl so we parse it when
1728 : we can. */
1729 0 : set_fn_contract_specifiers (olddecl, old_contracts);
1730 : }
1731 63 : else if (contract_any_deferred_p (old_contracts)
1732 63 : || contract_any_deferred_p (new_contracts))
1733 : {
1734 : /* TODO: ignore these and figure out how to process them later. */
1735 : /* Note that a friend declaration has deferred contracts, but the
1736 : declaration of the same function outside the class definition
1737 : doesn't. */
1738 : }
1739 : else
1740 : {
1741 57 : gcc_checking_assert (old_contracts);
1742 57 : location_t cont_end = get_contract_end_loc (new_contracts);
1743 57 : cont_end = make_location (new_loc, new_loc, cont_end);
1744 : /* We have two sets - they should match or we issue a diagnostic. */
1745 57 : match_contract_specifiers (rdp->note_loc, old_contracts,
1746 : cont_end, new_contracts);
1747 : }
1748 :
1749 : return;
1750 : }
1751 :
1752 : /* Update the contracts of DEST to match the argument names from contracts
1753 : of SRC. When we merge two declarations in duplicate_decls, we preserve the
1754 : arguments from the new declaration, if the new declaration is a
1755 : definition. We need to update the contracts accordingly. */
1756 :
1757 : void
1758 12407102 : update_contract_arguments (tree srcdecl, tree destdecl)
1759 : {
1760 12407102 : tree src_contracts = get_fn_contract_specifiers (srcdecl);
1761 12407102 : tree dest_contracts = get_fn_contract_specifiers (destdecl);
1762 :
1763 12407102 : if (!src_contracts && !dest_contracts)
1764 : return;
1765 :
1766 : /* Check if src even has contracts. It is possible that a redeclaration
1767 : does not have contracts. Is this is the case, first apply contracts
1768 : to src. */
1769 130 : if (!src_contracts)
1770 : {
1771 91 : if (contract_any_deferred_p (dest_contracts))
1772 : {
1773 0 : set_fn_contract_specifiers (srcdecl, dest_contracts);
1774 : /* Nothing more to do here. */
1775 0 : return;
1776 : }
1777 : else
1778 91 : set_fn_contract_specifiers
1779 91 : (srcdecl, copy_and_remap_contracts (srcdecl, destdecl));
1780 : }
1781 :
1782 : /* For deferred contracts, we currently copy the tokens from the redeclaration
1783 : onto the decl that will be preserved. This is not ideal because the
1784 : redeclaration may have erroneous contracts.
1785 : For non deferred contracts we currently do copy and remap, which is doing
1786 : more than we need. */
1787 130 : if (contract_any_deferred_p (src_contracts))
1788 6 : set_fn_contract_specifiers (destdecl, src_contracts);
1789 : else
1790 : {
1791 : /* Temporarily rename the arguments to get the right mapping. */
1792 124 : tree tmp_arguments = DECL_ARGUMENTS (destdecl);
1793 124 : DECL_ARGUMENTS (destdecl) = DECL_ARGUMENTS (srcdecl);
1794 124 : set_fn_contract_specifiers (destdecl,
1795 : copy_and_remap_contracts (destdecl, srcdecl));
1796 124 : DECL_ARGUMENTS (destdecl) = tmp_arguments;
1797 : }
1798 : }
1799 :
1800 : /* Checks if a contract check wrapper is needed for fndecl. */
1801 :
1802 : static bool
1803 397 : should_contract_wrap_call (bool do_pre, bool do_post)
1804 : {
1805 : /* Only if the target function actually has any contracts. */
1806 0 : if (!do_pre && !do_post)
1807 : return false;
1808 :
1809 :
1810 397 : return ((flag_contract_client_check > 1)
1811 397 : || ((flag_contract_client_check > 0)
1812 : && do_pre));
1813 : }
1814 :
1815 : /* Possibly replace call with a call to a wrapper function which
1816 : will do the contracts check required around a CALL to FNDECL. */
1817 :
1818 : tree
1819 171477924 : maybe_contract_wrap_call (tree fndecl, tree call)
1820 : {
1821 : /* We can be called from build_cxx_call without a known callee. */
1822 171477924 : if (!fndecl)
1823 : return call;
1824 :
1825 164756151 : if (error_operand_p (fndecl) || !call || call == error_mark_node)
1826 0 : return error_mark_node;
1827 :
1828 164756151 : if (!handle_contracts_p (fndecl))
1829 : return call;
1830 :
1831 397 : bool do_pre = has_active_preconditions (fndecl);
1832 397 : bool do_post = has_active_postconditions (fndecl);
1833 :
1834 : /* Check if we need a wrapper. */
1835 405 : if (!should_contract_wrap_call (do_pre, do_post))
1836 : return call;
1837 :
1838 : /* Build the declaration of the wrapper, if we need to. */
1839 28 : tree wrapdecl = get_or_create_contract_wrapper_function (fndecl);
1840 :
1841 28 : unsigned nargs = call_expr_nargs (call);
1842 28 : vec<tree, va_gc> *argwrap;
1843 28 : vec_alloc (argwrap, nargs);
1844 :
1845 28 : tree arg;
1846 28 : call_expr_arg_iterator iter;
1847 115 : FOR_EACH_CALL_EXPR_ARG (arg, iter, call)
1848 59 : argwrap->quick_push (arg);
1849 :
1850 28 : tree wrapcall = build_call_expr_loc_vec (DECL_SOURCE_LOCATION (wrapdecl),
1851 : wrapdecl, argwrap);
1852 :
1853 28 : return wrapcall;
1854 : }
1855 :
1856 : /* Map traversal callback to define a wrapper function.
1857 : This generates code for client-side contract check wrappers and the
1858 : noexcept wrapper around the contract violation handler. */
1859 :
1860 : bool
1861 64 : define_contract_wrapper_func (const tree& fndecl, const tree& wrapdecl, void*)
1862 : {
1863 : /* If we already built this function on a previous pass, then do nothing. */
1864 64 : if (DECL_INITIAL (wrapdecl) && DECL_INITIAL (wrapdecl) != error_mark_node)
1865 : return true;
1866 :
1867 28 : gcc_checking_assert (!DECL_HAS_CONTRACTS_P (wrapdecl));
1868 : /* We check postconditions if postcondition checks are enabled for clients.
1869 : We should not get here unless there are some checks to make. */
1870 28 : bool check_post = flag_contract_client_check > 1;
1871 : /* For wrappers on CDTORs we need to refer to the original contracts,
1872 : when the wrapper is around a clone. */
1873 56 : set_fn_contract_specifiers ( wrapdecl,
1874 28 : copy_and_remap_contracts (wrapdecl, DECL_ORIGIN (fndecl),
1875 : check_post? cmk_all : cmk_pre));
1876 :
1877 28 : start_preparsed_function (wrapdecl, /*DECL_ATTRIBUTES*/NULL_TREE,
1878 : SF_DEFAULT | SF_PRE_PARSED);
1879 28 : tree body = begin_function_body ();
1880 28 : tree compound_stmt = begin_compound_stmt (BCS_FN_BODY);
1881 :
1882 28 : vec<tree, va_gc> * args = build_arg_list (wrapdecl);
1883 :
1884 : /* We do not support contracts on virtual functions yet. */
1885 28 : gcc_checking_assert (!DECL_IOBJ_MEMBER_FUNCTION_P (fndecl)
1886 : || !DECL_VIRTUAL_P (fndecl));
1887 :
1888 28 : tree call = build_thunk_like_call (fndecl, args->length (), args->address ());
1889 :
1890 28 : finish_return_stmt (call);
1891 :
1892 28 : finish_compound_stmt (compound_stmt);
1893 28 : finish_function_body (body);
1894 28 : expand_or_defer_fn (finish_function (/*inline_p=*/false));
1895 28 : return true;
1896 : }
1897 :
1898 : /* If any wrapper functions have been declared, emit their definition.
1899 : This might be called multiple times, as we instantiate functions. When
1900 : the processing here adds more wrappers, then flag to the caller that
1901 : possible additional instantiations should be considered.
1902 : Once instantiations are complete, this will be called with done == true. */
1903 :
1904 : bool
1905 57488 : emit_contract_wrapper_func (bool done)
1906 : {
1907 57488 : if (!decl_wrapper_fn || decl_wrapper_fn->is_empty ())
1908 : return false;
1909 38 : size_t start_elements = decl_wrapper_fn->elements ();
1910 102 : decl_wrapper_fn->traverse<void *, define_contract_wrapper_func>(NULL);
1911 38 : bool more = decl_wrapper_fn->elements () > start_elements;
1912 38 : if (done)
1913 15 : decl_wrapper_fn->empty ();
1914 15 : gcc_checking_assert (!done || !more);
1915 : return more;
1916 : }
1917 :
1918 : /* Mark most of a contract as being invalid. */
1919 :
1920 : tree
1921 15 : invalidate_contract (tree contract)
1922 : {
1923 15 : if (TREE_CODE (contract) == POSTCONDITION_STMT
1924 15 : && POSTCONDITION_IDENTIFIER (contract))
1925 15 : POSTCONDITION_IDENTIFIER (contract) = error_mark_node;
1926 15 : CONTRACT_CONDITION (contract) = error_mark_node;
1927 15 : CONTRACT_COMMENT (contract) = error_mark_node;
1928 15 : return contract;
1929 : }
1930 :
1931 : /* Returns an invented parameter declaration of the form 'TYPE ID' for the
1932 : purpose of parsing the postcondition.
1933 :
1934 : We use a PARM_DECL instead of a VAR_DECL so that tsubst forces a lookup
1935 : in local specializations when we instantiate these things later. */
1936 :
1937 : tree
1938 202 : make_postcondition_variable (cp_expr id, tree type)
1939 : {
1940 202 : if (id == error_mark_node)
1941 : return id;
1942 202 : gcc_checking_assert (scope_chain && scope_chain->bindings
1943 : && scope_chain->bindings->kind == sk_contract);
1944 :
1945 202 : tree decl = build_lang_decl (PARM_DECL, id, type);
1946 202 : DECL_ARTIFICIAL (decl) = true;
1947 202 : DECL_SOURCE_LOCATION (decl) = id.get_location ();
1948 202 : return pushdecl (decl);
1949 : }
1950 :
1951 : /* As above, except that the type is unknown. */
1952 :
1953 : tree
1954 124 : make_postcondition_variable (cp_expr id)
1955 : {
1956 124 : return make_postcondition_variable (id, make_auto ());
1957 : }
1958 :
1959 : /* Check that the TYPE is valid for a named postcondition variable on
1960 : function decl FNDECL. Emit a diagnostic if it is not. Returns TRUE if
1961 : the result is OK and false otherwise. */
1962 :
1963 : bool
1964 254 : check_postcondition_result (tree fndecl, tree type, location_t loc)
1965 : {
1966 : /* Do not be confused by targetm.cxx.cdtor_return_this ();
1967 : conceptually, cdtors have no return value. */
1968 254 : if (VOID_TYPE_P (type)
1969 478 : || DECL_CONSTRUCTOR_P (fndecl)
1970 493 : || DECL_DESTRUCTOR_P (fndecl))
1971 : {
1972 45 : error_at (loc,
1973 30 : DECL_CONSTRUCTOR_P (fndecl)
1974 : ? G_("constructor does not return a value to test")
1975 12 : : DECL_DESTRUCTOR_P (fndecl)
1976 12 : ? G_("destructor does not return a value to test")
1977 : : G_("function does not return a value to test"));
1978 15 : return false;
1979 : }
1980 :
1981 : return true;
1982 : }
1983 :
1984 : /* Instantiate each postcondition with the return type to finalize the
1985 : contract specifiers on a function decl FNDECL. */
1986 :
1987 : void
1988 943 : rebuild_postconditions (tree fndecl)
1989 : {
1990 943 : if (!fndecl || fndecl == error_mark_node || processing_template_decl)
1991 : return;
1992 :
1993 578 : tree type = TREE_TYPE (TREE_TYPE (fndecl));
1994 :
1995 : /* If the return type is undeduced, defer until later. */
1996 578 : if (type_uses_auto (type))
1997 : return;
1998 :
1999 542 : tree contract_spec = get_fn_contract_specifiers (fndecl);
2000 542 : if (!contract_spec)
2001 : return;
2002 :
2003 1185 : for (tree contract : tree_vec_range (contract_spec))
2004 : {
2005 719 : if (TREE_CODE (contract) != POSTCONDITION_STMT)
2006 523 : continue;
2007 266 : tree condition = CONTRACT_CONDITION (contract);
2008 266 : if (!condition || condition == error_mark_node)
2009 0 : continue;
2010 :
2011 : /* If any conditions are deferred, they're all deferred. Note that
2012 : we don't have to instantiate postconditions in that case because
2013 : the type is available through the declaration. */
2014 266 : if (TREE_CODE (condition) == DEFERRED_PARSE)
2015 76 : return;
2016 :
2017 190 : tree oldvar = POSTCONDITION_IDENTIFIER (contract);
2018 190 : if (!oldvar)
2019 67 : continue;
2020 :
2021 123 : gcc_checking_assert (!DECL_CONTEXT (oldvar)
2022 : || DECL_CONTEXT (oldvar) == fndecl);
2023 123 : DECL_CONTEXT (oldvar) = fndecl;
2024 :
2025 : /* Check the postcondition variable. */
2026 123 : location_t loc = DECL_SOURCE_LOCATION (oldvar);
2027 123 : if (!check_postcondition_result (fndecl, type, loc))
2028 : {
2029 3 : invalidate_contract (contract);
2030 3 : continue;
2031 : }
2032 :
2033 : /* A concrete late-parsed result variable still needs validation, but
2034 : not rebuilding. Rebuild only one whose type was undeduced. */
2035 120 : if (!type_uses_auto (TREE_TYPE (oldvar)))
2036 0 : continue;
2037 :
2038 : /* "Instantiate" the result variable using the known type. */
2039 120 : tree newvar = copy_node (oldvar);
2040 120 : TREE_TYPE (newvar) = type;
2041 :
2042 : /* Make parameters and result available for substitution. */
2043 120 : local_specialization_stack stack (lss_copy);
2044 278 : for (tree t = DECL_ARGUMENTS (fndecl); t != NULL_TREE; t = TREE_CHAIN (t))
2045 158 : register_local_identity (t);
2046 120 : register_local_specialization (newvar, oldvar);
2047 :
2048 120 : begin_scope (sk_contract, fndecl);
2049 120 : bool old_pc = processing_postcondition;
2050 120 : processing_postcondition = true;
2051 :
2052 120 : condition = tsubst_expr (condition, make_tree_vec (0),
2053 : tf_warning_or_error, fndecl);
2054 :
2055 : /* Update the contract condition and result. */
2056 120 : POSTCONDITION_IDENTIFIER (contract) = newvar;
2057 120 : CONTRACT_CONDITION (contract) = finish_contract_condition (condition);
2058 120 : processing_postcondition = old_pc;
2059 120 : gcc_checking_assert (scope_chain && scope_chain->bindings
2060 : && scope_chain->bindings->kind == sk_contract);
2061 120 : pop_bindings_and_leave_scope ();
2062 120 : }
2063 : }
2064 :
2065 : /* Make a string of the contract condition, if it is available. */
2066 :
2067 : static tree
2068 1662 : build_comment (cp_expr condition)
2069 : {
2070 : /* Try to get the actual source text for the condition; if that fails pretty
2071 : print the resulting tree. */
2072 1662 : char *str = get_source_text_between (global_dc->get_file_cache (),
2073 : condition.get_start (),
2074 : condition.get_finish ());
2075 1662 : if (!str)
2076 : {
2077 2 : const char *str = expr_to_string (condition);
2078 2 : return build_string_literal (strlen (str) + 1, str);
2079 : }
2080 :
2081 1660 : tree t = build_string_literal (strlen (str) + 1, str);
2082 1660 : free (str);
2083 1660 : return t;
2084 : }
2085 :
2086 : /* Build a contract statement. */
2087 :
2088 : tree
2089 1710 : grok_contract (tree contract_spec, tree mode, tree result, cp_expr condition,
2090 : location_t loc)
2091 : {
2092 1710 : if (condition == error_mark_node)
2093 : return error_mark_node;
2094 :
2095 1677 : tree_code code;
2096 1677 : contract_assertion_kind kind = CAK_INVALID;
2097 1677 : if (IDENTIFIER_KEYWORD_P (contract_spec)
2098 168 : && C_RID_CODE (contract_spec) == RID_CONTASSERT)
2099 : {
2100 : code = ASSERTION_STMT;
2101 : kind = CAK_ASSERT;
2102 : }
2103 1509 : else if (id_equal (contract_spec, "pre"))
2104 : {
2105 : code = PRECONDITION_STMT;
2106 : kind = CAK_PRE;
2107 : }
2108 802 : else if (id_equal (contract_spec,"post"))
2109 : {
2110 : code = POSTCONDITION_STMT;
2111 : kind = CAK_POST;
2112 : }
2113 : else
2114 0 : gcc_unreachable ();
2115 :
2116 : /* Build the contract. The condition is added later. In the case that
2117 : the contract is deferred, result an plain identifier, not a result
2118 : variable. */
2119 802 : tree contract;
2120 802 : if (code != POSTCONDITION_STMT)
2121 875 : contract = build5_loc (loc, code, void_type_node, mode,
2122 : NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
2123 : else
2124 : {
2125 802 : contract = build_nt (code, mode, NULL_TREE, NULL_TREE,
2126 : NULL_TREE, NULL_TREE, result);
2127 802 : TREE_TYPE (contract) = void_type_node;
2128 802 : SET_EXPR_LOCATION (contract, loc);
2129 : }
2130 :
2131 : /* Determine the assertion kind. */
2132 1677 : CONTRACT_ASSERTION_KIND (contract) = build_int_cst (uint16_type_node, kind);
2133 :
2134 : /* Determine the evaluation semantic. This is now an override, so that if
2135 : not set we will get the default (currently enforce). */
2136 1677 : CONTRACT_EVALUATION_SEMANTIC (contract)
2137 3354 : = build_int_cst (uint16_type_node, (uint16_t)
2138 1677 : flag_contract_evaluation_semantic);
2139 :
2140 : /* If the contract is deferred, don't do anything with the condition. */
2141 1677 : if (TREE_CODE (condition) == DEFERRED_PARSE)
2142 : {
2143 806 : CONTRACT_CONDITION (contract) = condition;
2144 806 : return contract;
2145 : }
2146 :
2147 : /* Generate the comment from the original condition. */
2148 871 : CONTRACT_COMMENT (contract) = build_comment (condition);
2149 :
2150 : /* The condition is converted to bool. */
2151 871 : condition = finish_contract_condition (condition);
2152 :
2153 871 : if (condition == error_mark_node)
2154 : return error_mark_node;
2155 :
2156 867 : CONTRACT_CONDITION (contract) = condition;
2157 :
2158 867 : return contract;
2159 : }
2160 :
2161 : /* Update condition of a late-parsed contract and postcondition variable,
2162 : if any. */
2163 :
2164 : void
2165 791 : update_late_contract (tree contract, tree result, cp_expr condition)
2166 : {
2167 791 : if (TREE_CODE (contract) == POSTCONDITION_STMT)
2168 507 : POSTCONDITION_IDENTIFIER (contract) = result;
2169 :
2170 : /* Generate the comment from the original condition. */
2171 791 : CONTRACT_COMMENT (contract) = build_comment (condition);
2172 :
2173 : /* The condition is converted to bool. */
2174 791 : condition = finish_contract_condition (condition);
2175 791 : CONTRACT_CONDITION (contract) = condition;
2176 791 : }
2177 :
2178 : /* Returns the precondition function for FNDECL, or null if not set. */
2179 :
2180 : tree
2181 1932041 : get_precondition_function (tree fndecl)
2182 : {
2183 1932041 : gcc_checking_assert (fndecl);
2184 1932041 : tree *result = hash_map_safe_get (decl_pre_fn, fndecl);
2185 90 : return result ? *result : NULL_TREE;
2186 : }
2187 :
2188 : /* Returns the postcondition function for FNDECL, or null if not set. */
2189 :
2190 : tree
2191 1932045 : get_postcondition_function (tree fndecl)
2192 : {
2193 1932045 : gcc_checking_assert (fndecl);
2194 1932045 : tree *result = hash_map_safe_get (decl_post_fn, fndecl);
2195 70 : return result ? *result : NULL_TREE;
2196 : }
2197 :
2198 : /* Set the PRE and POST functions for FNDECL. Note that PRE and POST can
2199 : be null in this case. If so the functions are not recorded. Used by the
2200 : modules code. */
2201 :
2202 : void
2203 473467 : set_contract_functions (tree fndecl, tree pre, tree post)
2204 : {
2205 473467 : if (pre)
2206 0 : set_precondition_function (fndecl, pre);
2207 :
2208 473467 : if (post)
2209 0 : set_postcondition_function (fndecl, post);
2210 473467 : }
2211 :
2212 :
2213 : /* We're compiling the pre/postcondition function CONDFN; remap any FN
2214 : contracts that match CODE and emit them. */
2215 :
2216 : static void
2217 28 : remap_and_emit_conditions (tree fn, tree condfn, tree_code code)
2218 : {
2219 28 : gcc_assert (code == PRECONDITION_STMT || code == POSTCONDITION_STMT);
2220 28 : tree contract_spec = get_fn_contract_specifiers (fn);
2221 28 : if (!contract_spec)
2222 : return;
2223 :
2224 68 : for (tree contract : tree_vec_range (contract_spec))
2225 40 : if (TREE_CODE (contract) == code)
2226 : {
2227 32 : contract = copy_node (contract);
2228 32 : if (CONTRACT_CONDITION (contract) != error_mark_node)
2229 32 : remap_contract (fn, condfn, contract, /*duplicate_p=*/false);
2230 32 : emit_contract_statement (contract);
2231 : }
2232 : }
2233 :
2234 : /* Finish up the pre & post function definitions for a guarded FNDECL,
2235 : and compile those functions all the way to assembler language output. */
2236 :
2237 : void
2238 169405604 : finish_function_outlined_contracts (tree fndecl)
2239 : {
2240 : /* If the guarded func is either already decided to be ill-formed or is
2241 : not yet complete return early. */
2242 169405604 : if (error_operand_p (fndecl)
2243 169405604 : || !DECL_INITIAL (fndecl)
2244 338811208 : || DECL_INITIAL (fndecl) == error_mark_node)
2245 : return;
2246 :
2247 : /* If there are no contracts here, or we're building them in-line then we
2248 : do not need to build the outlined functions. */
2249 169405511 : if (!handle_contracts_p (fndecl)
2250 169405511 : || !flag_contract_checks_outlined)
2251 : return;
2252 :
2253 : /* If this is not a client side check and definition side checks are
2254 : disabled, do nothing. */
2255 26 : if (!flag_contracts_definition_check
2256 26 : && !DECL_CONTRACT_WRAPPER (fndecl))
2257 : return;
2258 :
2259 : /* If either the pre or post functions are bad, don't bother emitting
2260 : any contracts. The program is already ill-formed. */
2261 26 : tree pre = DECL_PRE_FN (fndecl);
2262 26 : tree post = DECL_POST_FN (fndecl);
2263 26 : if (pre == error_mark_node || post == error_mark_node)
2264 : return;
2265 :
2266 : /* We are generating code, deferred parses should be complete. */
2267 26 : tree contract_spec = get_fn_contract_specifiers (fndecl);
2268 26 : gcc_checking_assert (!contract_any_deferred_p (contract_spec));
2269 :
2270 26 : int flags = SF_DEFAULT | SF_PRE_PARSED;
2271 :
2272 26 : if (pre && !DECL_INITIAL (pre))
2273 : {
2274 14 : DECL_PENDING_INLINE_P (pre) = false;
2275 14 : start_preparsed_function (pre, DECL_ATTRIBUTES (pre), flags);
2276 14 : remap_and_emit_conditions (fndecl, pre, PRECONDITION_STMT);
2277 14 : finish_return_stmt (NULL_TREE);
2278 14 : pre = finish_function (false);
2279 14 : expand_or_defer_fn (pre);
2280 : }
2281 :
2282 26 : if (post && !DECL_INITIAL (post))
2283 : {
2284 14 : DECL_PENDING_INLINE_P (post) = false;
2285 14 : start_preparsed_function (post, DECL_ATTRIBUTES (post), flags);
2286 14 : remap_and_emit_conditions (fndecl, post, POSTCONDITION_STMT);
2287 14 : gcc_checking_assert (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (post))));
2288 14 : finish_return_stmt (NULL_TREE);
2289 14 : post = finish_function (false);
2290 14 : expand_or_defer_fn (post);
2291 : }
2292 : }
2293 :
2294 : /* ===== Code generation ===== */
2295 :
2296 : /* Insert a BUILT_IN_OBSERVABLE_CHECKPOINT epoch marker. */
2297 :
2298 : static void
2299 542 : emit_builtin_observable_checkpoint ()
2300 : {
2301 542 : tree fn = builtin_decl_explicit (BUILT_IN_OBSERVABLE_CHKPT);
2302 542 : releasing_vec vec;
2303 542 : fn = finish_call_expr (fn, &vec, false, false, tf_warning_or_error);
2304 542 : finish_expr_stmt (fn);
2305 542 : }
2306 :
2307 : /* Shared code between TU-local wrappers for the violation handler. */
2308 :
2309 : static tree
2310 384 : declare_one_violation_handler_wrapper (tree fn_name, tree fn_type,
2311 : tree p1_type, tree p2_type)
2312 : {
2313 384 : location_t loc = BUILTINS_LOCATION;
2314 384 : tree fn_decl = build_lang_decl_loc (loc, FUNCTION_DECL, fn_name, fn_type);
2315 384 : DECL_CONTEXT (fn_decl) = FROB_CONTEXT (global_namespace);
2316 384 : DECL_ARTIFICIAL (fn_decl) = true;
2317 384 : DECL_INITIAL (fn_decl) = error_mark_node;
2318 : /* Let the start function code fill in the result decl. */
2319 384 : DECL_RESULT (fn_decl) = NULL_TREE;
2320 : /* Two args violation ref, dynamic info. */
2321 384 : tree parms = cp_build_parm_decl (fn_decl, NULL_TREE, p1_type);
2322 384 : TREE_USED (parms) = true;
2323 384 : DECL_READ_P (parms) = true;
2324 384 : tree p2 = cp_build_parm_decl (fn_decl, NULL_TREE, p2_type);
2325 384 : TREE_USED (p2) = true;
2326 384 : DECL_READ_P (p2) = true;
2327 384 : DECL_CHAIN (parms) = p2;
2328 384 : DECL_ARGUMENTS (fn_decl) = parms;
2329 : /* Make this function internal. */
2330 384 : TREE_PUBLIC (fn_decl) = false;
2331 384 : DECL_EXTERNAL (fn_decl) = false;
2332 384 : DECL_WEAK (fn_decl) = false;
2333 384 : return fn_decl;
2334 : }
2335 :
2336 : static GTY(()) tree tu_has_violation = NULL_TREE;
2337 : static GTY(()) tree tu_has_violation_exception = NULL_TREE;
2338 :
2339 : static void
2340 1052 : declare_violation_handler_wrappers ()
2341 : {
2342 1052 : if (tu_has_violation && tu_has_violation_exception)
2343 1052 : return;
2344 :
2345 192 : iloc_sentinel ils (input_location);
2346 192 : input_location = BUILTINS_LOCATION;
2347 192 : tree v_obj_type = builtin_contract_violation_type;
2348 192 : v_obj_type = cp_build_qualified_type (v_obj_type, TYPE_QUAL_CONST);
2349 192 : v_obj_type = cp_build_reference_type (v_obj_type, /*rval*/false);
2350 192 : tree fn_type = build_function_type_list (void_type_node, v_obj_type,
2351 : uint16_type_node, NULL_TREE);
2352 192 : tree fn_name = get_identifier ("__tu_has_violation_exception");
2353 192 : tu_has_violation_exception
2354 192 : = declare_one_violation_handler_wrapper (fn_name, fn_type, v_obj_type,
2355 : uint16_type_node);
2356 192 : fn_name = get_identifier ("__tu_has_violation");
2357 192 : tu_has_violation
2358 192 : = declare_one_violation_handler_wrapper (fn_name, fn_type, v_obj_type,
2359 : uint16_type_node);
2360 192 : }
2361 :
2362 : static GTY(()) tree tu_terminate_wrapper = NULL_TREE;
2363 :
2364 : /* Declare a noipa wrapper around the call to std::terminate */
2365 :
2366 : static tree
2367 1054 : declare_terminate_wrapper ()
2368 : {
2369 1054 : if (tu_terminate_wrapper)
2370 : return tu_terminate_wrapper;
2371 :
2372 194 : iloc_sentinel ils (input_location);
2373 194 : input_location = BUILTINS_LOCATION;
2374 :
2375 194 : tree fn_type = build_function_type_list (void_type_node, NULL_TREE);
2376 194 : if (!TREE_NOTHROW (terminate_fn))
2377 0 : fn_type = build_exception_variant (fn_type, noexcept_true_spec);
2378 194 : tree fn_name = get_identifier ("__tu_terminate_wrapper");
2379 :
2380 194 : tu_terminate_wrapper
2381 194 : = build_lang_decl_loc (input_location, FUNCTION_DECL, fn_name, fn_type);
2382 194 : DECL_CONTEXT (tu_terminate_wrapper) = FROB_CONTEXT(global_namespace);
2383 194 : DECL_ARTIFICIAL (tu_terminate_wrapper) = true;
2384 194 : DECL_INITIAL (tu_terminate_wrapper) = error_mark_node;
2385 : /* Let the start function code fill in the result decl. */
2386 194 : DECL_RESULT (tu_terminate_wrapper) = NULL_TREE;
2387 :
2388 : /* Make this function internal. */
2389 194 : TREE_PUBLIC (tu_terminate_wrapper) = false;
2390 194 : DECL_EXTERNAL (tu_terminate_wrapper) = false;
2391 194 : DECL_WEAK (tu_terminate_wrapper) = false;
2392 :
2393 194 : DECL_ATTRIBUTES (tu_terminate_wrapper)
2394 194 : = tree_cons (get_identifier ("noipa"), NULL, NULL_TREE);
2395 194 : cplus_decl_attributes (&tu_terminate_wrapper,
2396 194 : DECL_ATTRIBUTES (tu_terminate_wrapper), 0);
2397 194 : return tu_terminate_wrapper;
2398 194 : }
2399 :
2400 : /* Define a noipa wrapper around the call to std::terminate */
2401 :
2402 : static void
2403 194 : build_terminate_wrapper ()
2404 : {
2405 : /* We should not be trying to build this if we never used it. */
2406 194 : gcc_checking_assert (tu_terminate_wrapper);
2407 :
2408 194 : start_preparsed_function (tu_terminate_wrapper,
2409 194 : DECL_ATTRIBUTES(tu_terminate_wrapper),
2410 : SF_DEFAULT | SF_PRE_PARSED);
2411 194 : tree body = begin_function_body ();
2412 194 : tree compound_stmt = begin_compound_stmt (BCS_FN_BODY);
2413 194 : finish_expr_stmt (build_call_a (terminate_fn, 0, nullptr));
2414 194 : finish_return_stmt (NULL_TREE);
2415 194 : finish_compound_stmt (compound_stmt);
2416 194 : finish_function_body (body);
2417 194 : tu_terminate_wrapper = finish_function (false);
2418 194 : expand_or_defer_fn (tu_terminate_wrapper);
2419 194 : }
2420 :
2421 : /* Lookup a name in std::contracts, or inject it. */
2422 :
2423 : static tree
2424 150 : lookup_std_contracts_type (tree name_id)
2425 : {
2426 150 : tree id_ns = get_identifier ("contracts");
2427 150 : tree ns = lookup_qualified_name (std_node, id_ns);
2428 :
2429 150 : tree res_type = error_mark_node;
2430 150 : if (TREE_CODE (ns) == NAMESPACE_DECL)
2431 8 : res_type = lookup_qualified_name
2432 8 : (ns, name_id, LOOK_want::TYPE | LOOK_want::HIDDEN_FRIEND);
2433 :
2434 150 : if (TREE_CODE (res_type) == TYPE_DECL)
2435 8 : res_type = TREE_TYPE (res_type);
2436 : else
2437 : {
2438 142 : push_nested_namespace (std_node);
2439 142 : push_namespace (id_ns, /*inline*/false);
2440 142 : res_type = make_class_type (RECORD_TYPE);
2441 142 : create_implicit_typedef (name_id, res_type);
2442 142 : DECL_SOURCE_LOCATION (TYPE_NAME (res_type)) = BUILTINS_LOCATION;
2443 142 : DECL_CONTEXT (TYPE_NAME (res_type)) = current_namespace;
2444 142 : pushdecl_namespace_level (TYPE_NAME (res_type), /*hidden*/true);
2445 142 : pop_namespace ();
2446 142 : pop_nested_namespace (std_node);
2447 : }
2448 150 : return res_type;
2449 : }
2450 :
2451 : /* Return handle_contract_violation (), declaring it if needed. */
2452 :
2453 : static tree
2454 384 : declare_handle_contract_violation ()
2455 : {
2456 : /* We may need to declare new types, ensure they are not considered
2457 : attached to a named module. */
2458 384 : auto module_kind_override = make_temp_override
2459 384 : (module_kind, module_kind & ~(MK_PURVIEW | MK_ATTACH | MK_EXPORTING));
2460 384 : tree fnname = get_identifier ("handle_contract_violation");
2461 384 : tree viol_name = get_identifier ("contract_violation");
2462 384 : tree l = lookup_qualified_name (global_namespace, fnname,
2463 : LOOK_want::HIDDEN_FRIEND);
2464 918 : for (tree f: lkp_range (l))
2465 384 : if (TREE_CODE (f) == FUNCTION_DECL)
2466 : {
2467 234 : tree parms = TYPE_ARG_TYPES (TREE_TYPE (f));
2468 234 : if (remaining_arguments (parms) != 1)
2469 0 : continue;
2470 234 : tree parmtype = non_reference (TREE_VALUE (parms));
2471 234 : if (CLASS_TYPE_P (parmtype)
2472 468 : && TYPE_IDENTIFIER (parmtype) == viol_name)
2473 234 : return f;
2474 : }
2475 :
2476 150 : tree violation = lookup_std_contracts_type (viol_name);
2477 150 : tree fntype = NULL_TREE;
2478 150 : tree v_obj_ref = cp_build_qualified_type (violation, TYPE_QUAL_CONST);
2479 150 : v_obj_ref = cp_build_reference_type (v_obj_ref, /*rval*/false);
2480 150 : fntype = build_function_type_list (void_type_node, v_obj_ref, NULL_TREE);
2481 :
2482 150 : push_nested_namespace (global_namespace);
2483 150 : tree fndecl
2484 150 : = build_cp_library_fn_ptr ("handle_contract_violation", fntype, ECF_COLD);
2485 150 : pushdecl_namespace_level (fndecl, /*hiding*/true);
2486 150 : pop_nested_namespace (global_namespace);
2487 :
2488 : /* Build the parameter(s). */
2489 150 : tree parms = cp_build_parm_decl (fndecl, NULL_TREE, v_obj_ref);
2490 150 : TREE_USED (parms) = true;
2491 150 : DECL_READ_P (parms) = true;
2492 150 : DECL_ARGUMENTS (fndecl) = parms;
2493 150 : return fndecl;
2494 384 : }
2495 :
2496 : /* Build the call to handle_contract_violation for VIOLATION. */
2497 :
2498 : static void
2499 384 : build_contract_handler_call (tree violation)
2500 : {
2501 384 : tree violation_fn = declare_handle_contract_violation ();
2502 384 : tree call = build_call_n (violation_fn, 1, violation);
2503 384 : finish_expr_stmt (call);
2504 384 : }
2505 :
2506 : /* If we have emitted any contracts in this TU that will call a violation
2507 : handler, then emit the wrappers for the handler. */
2508 :
2509 : void
2510 25208 : maybe_emit_violation_handler_wrappers ()
2511 : {
2512 : /* We might need the terminate wrapper, even if we do not use the violation
2513 : handler wrappers. */
2514 25208 : if (tu_terminate_wrapper && flag_contracts_conservative_ipa)
2515 194 : build_terminate_wrapper ();
2516 :
2517 25208 : if (!tu_has_violation && !tu_has_violation_exception)
2518 : return;
2519 :
2520 192 : tree terminate_wrapper = terminate_fn;
2521 192 : if (flag_contracts_conservative_ipa)
2522 192 : terminate_wrapper = tu_terminate_wrapper;
2523 :
2524 : /* tu_has_violation */
2525 192 : start_preparsed_function (tu_has_violation, NULL_TREE,
2526 : SF_DEFAULT | SF_PRE_PARSED);
2527 192 : tree body = begin_function_body ();
2528 192 : tree compound_stmt = begin_compound_stmt (BCS_FN_BODY);
2529 192 : tree v = DECL_ARGUMENTS (tu_has_violation);
2530 192 : tree semantic = DECL_CHAIN (v);
2531 :
2532 : /* We are going to call the handler. */
2533 192 : build_contract_handler_call (v);
2534 :
2535 192 : tree if_observe = begin_if_stmt ();
2536 : /* if (observe) return; */
2537 192 : tree cond = build2 (EQ_EXPR, uint16_type_node, semantic,
2538 : build_int_cst (uint16_type_node, (uint16_t)CES_OBSERVE));
2539 192 : finish_if_stmt_cond (cond, if_observe);
2540 192 : emit_builtin_observable_checkpoint ();
2541 192 : finish_then_clause (if_observe);
2542 192 : begin_else_clause (if_observe);
2543 : /* else terminate. */
2544 192 : finish_expr_stmt (build_call_a (terminate_wrapper, 0, nullptr));
2545 192 : finish_else_clause (if_observe);
2546 192 : finish_if_stmt (if_observe);
2547 192 : finish_return_stmt (NULL_TREE);
2548 :
2549 192 : finish_compound_stmt (compound_stmt);
2550 192 : finish_function_body (body);
2551 192 : tu_has_violation = finish_function (false);
2552 192 : expand_or_defer_fn (tu_has_violation);
2553 :
2554 : /* tu_has_violation_exception */
2555 192 : start_preparsed_function (tu_has_violation_exception, NULL_TREE,
2556 : SF_DEFAULT | SF_PRE_PARSED);
2557 192 : body = begin_function_body ();
2558 192 : compound_stmt = begin_compound_stmt (BCS_FN_BODY);
2559 192 : v = DECL_ARGUMENTS (tu_has_violation_exception);
2560 192 : semantic = DECL_CHAIN (v);
2561 192 : location_t loc = DECL_SOURCE_LOCATION (tu_has_violation_exception);
2562 :
2563 192 : tree a_type = strip_top_quals (non_reference (TREE_TYPE (v)));
2564 192 : tree v2 = build_decl (loc, VAR_DECL, NULL_TREE, a_type);
2565 192 : DECL_SOURCE_LOCATION (v2) = loc;
2566 192 : DECL_CONTEXT (v2) = current_function_decl;
2567 192 : DECL_ARTIFICIAL (v2) = true;
2568 192 : layout_decl (v2, 0);
2569 192 : v2 = pushdecl (v2);
2570 192 : add_decl_expr (v2);
2571 192 : tree r = cp_build_init_expr (v2, convert_from_reference (v));
2572 192 : finish_expr_stmt (r);
2573 192 : tree memb = lookup_member (a_type, get_identifier ("_M_detection_mode"),
2574 : /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
2575 192 : r = build_class_member_access_expr (v2, memb, NULL_TREE, false,
2576 : tf_warning_or_error);
2577 192 : r = cp_build_modify_expr
2578 192 : (loc, r, NOP_EXPR,
2579 : build_int_cst (uint16_type_node, (uint16_t)CDM_EVAL_EXCEPTION),
2580 : tf_warning_or_error);
2581 192 : finish_expr_stmt (r);
2582 : /* We are going to call the handler. */
2583 192 : build_contract_handler_call (v);
2584 :
2585 192 : if_observe = begin_if_stmt ();
2586 : /* if (observe) return; */
2587 192 : cond = build2 (EQ_EXPR, uint16_type_node, semantic,
2588 : build_int_cst (uint16_type_node, (uint16_t)CES_OBSERVE));
2589 192 : finish_if_stmt_cond (cond, if_observe);
2590 192 : emit_builtin_observable_checkpoint ();
2591 192 : finish_then_clause (if_observe);
2592 192 : begin_else_clause (if_observe);
2593 : /* else terminate. */
2594 192 : finish_expr_stmt (build_call_a (terminate_wrapper, 0, nullptr));
2595 192 : finish_else_clause (if_observe);
2596 192 : finish_if_stmt (if_observe);
2597 192 : finish_return_stmt (NULL_TREE);
2598 192 : finish_compound_stmt (compound_stmt);
2599 192 : finish_function_body (body);
2600 192 : tu_has_violation_exception = finish_function (false);
2601 192 : expand_or_defer_fn (tu_has_violation_exception);
2602 : }
2603 :
2604 : /* Build a layout-compatible internal version of contract_violation type. */
2605 :
2606 : static tree
2607 25530 : get_contract_violation_fields ()
2608 : {
2609 25530 : tree fields = NULL_TREE;
2610 : /* Must match <contracts>:
2611 : class contract_violation {
2612 : uint16_t _M_version;
2613 : assertion_kind _M_assertion_kind;
2614 : evaluation_semantic _M_evaluation_semantic;
2615 : detection_mode _M_detection_mode;
2616 : const char* _M_comment;
2617 : void *_M_src_loc_ptr;
2618 : __vendor_ext* _M_ext;
2619 : };
2620 : If this changes, also update the initializer in
2621 : build_contract_violation. */
2622 25530 : const tree types[] = { uint16_type_node,
2623 : uint16_type_node,
2624 : uint16_type_node,
2625 : uint16_type_node,
2626 25530 : const_string_type_node,
2627 25530 : ptr_type_node,
2628 : ptr_type_node
2629 25530 : };
2630 25530 : const char *names[] = { "_M_version",
2631 : "_M_assertion_kind",
2632 : "_M_evaluation_semantic",
2633 : "_M_detection_mode",
2634 : "_M_comment",
2635 : "_M_src_loc_ptr",
2636 : "_M_ext",
2637 : };
2638 25530 : unsigned n = 0;
2639 204240 : for (tree type : types)
2640 : {
2641 : /* finish_builtin_struct wants fields chained in reverse. */
2642 178710 : tree next = build_decl (BUILTINS_LOCATION, FIELD_DECL,
2643 178710 : get_identifier(names[n++]), type);
2644 178710 : DECL_CHAIN (next) = fields;
2645 178710 : fields = next;
2646 : }
2647 25530 : return fields;
2648 : }
2649 :
2650 : /* Build a type to represent contract violation objects. */
2651 :
2652 : static tree
2653 25530 : init_builtin_contract_violation_type ()
2654 : {
2655 25530 : if (builtin_contract_violation_type)
2656 : return builtin_contract_violation_type;
2657 :
2658 25530 : tree fields = get_contract_violation_fields ();
2659 :
2660 25530 : iloc_sentinel ils (input_location);
2661 25530 : input_location = BUILTINS_LOCATION;
2662 25530 : builtin_contract_violation_type = make_class_type (RECORD_TYPE);
2663 25530 : finish_builtin_struct (builtin_contract_violation_type,
2664 : "__builtin_contract_violation_type", fields, NULL_TREE);
2665 51060 : CLASSTYPE_AS_BASE (builtin_contract_violation_type)
2666 25530 : = builtin_contract_violation_type;
2667 25530 : DECL_CONTEXT (TYPE_NAME (builtin_contract_violation_type))
2668 25530 : = FROB_CONTEXT (global_namespace);
2669 25530 : CLASSTYPE_LITERAL_P (builtin_contract_violation_type) = true;
2670 25530 : CLASSTYPE_LAZY_COPY_CTOR (builtin_contract_violation_type) = true;
2671 25530 : xref_basetypes (builtin_contract_violation_type, /*bases=*/NULL_TREE);
2672 25530 : DECL_CONTEXT (TYPE_NAME (builtin_contract_violation_type))
2673 25530 : = FROB_CONTEXT (global_namespace);
2674 25530 : DECL_ARTIFICIAL (TYPE_NAME (builtin_contract_violation_type)) = true;
2675 25530 : TYPE_ARTIFICIAL (builtin_contract_violation_type) = true;
2676 25530 : builtin_contract_violation_type
2677 25530 : = cp_build_qualified_type (builtin_contract_violation_type,
2678 : TYPE_QUAL_CONST);
2679 25530 : return builtin_contract_violation_type;
2680 25530 : }
2681 :
2682 : /* Early initialisation of types and functions we will use. */
2683 : void
2684 25530 : init_contracts ()
2685 : {
2686 25530 : init_terminate_fn ();
2687 25530 : init_builtin_contract_violation_type ();
2688 25530 : }
2689 :
2690 : static GTY(()) tree contracts_source_location_impl_type;
2691 :
2692 : /* Build a layout-compatible internal version of source location __impl
2693 : type. */
2694 :
2695 : static tree
2696 192 : get_contracts_source_location_impl_type (tree context = NULL_TREE)
2697 : {
2698 192 : if (contracts_source_location_impl_type)
2699 : return contracts_source_location_impl_type;
2700 :
2701 : /* First see if we have a declaration that we can use. */
2702 192 : tree contracts_source_location_type
2703 192 : = lookup_std_type (get_identifier ("source_location"));
2704 :
2705 192 : if (contracts_source_location_type
2706 192 : && contracts_source_location_type != error_mark_node
2707 384 : && TYPE_FIELDS (contracts_source_location_type))
2708 : {
2709 49 : contracts_source_location_impl_type = get_source_location_impl_type ();
2710 49 : return contracts_source_location_impl_type;
2711 : }
2712 :
2713 : /* We do not, so build the __impl layout equivalent type, which must
2714 : match <source_location>:
2715 : struct __impl
2716 : {
2717 : const char* _M_file_name;
2718 : const char* _M_function_name;
2719 : unsigned _M_line;
2720 : unsigned _M_column;
2721 : }; */
2722 143 : const tree types[] = { const_string_type_node,
2723 : const_string_type_node,
2724 143 : uint_least32_type_node,
2725 143 : uint_least32_type_node };
2726 :
2727 143 : const char *names[] = { "_M_file_name",
2728 : "_M_function_name",
2729 : "_M_line",
2730 : "_M_column",
2731 : };
2732 143 : tree fields = NULL_TREE;
2733 143 : unsigned n = 0;
2734 715 : for (tree type : types)
2735 : {
2736 : /* finish_builtin_struct wants fields chained in reverse. */
2737 572 : tree next = build_decl (BUILTINS_LOCATION, FIELD_DECL,
2738 572 : get_identifier (names[n++]), type);
2739 572 : DECL_CHAIN (next) = fields;
2740 572 : fields = next;
2741 : }
2742 :
2743 143 : iloc_sentinel ils (input_location);
2744 143 : input_location = BUILTINS_LOCATION;
2745 143 : contracts_source_location_impl_type = cxx_make_type (RECORD_TYPE);
2746 143 : finish_builtin_struct (contracts_source_location_impl_type,
2747 : "__impl", fields, NULL_TREE);
2748 143 : DECL_CONTEXT (TYPE_NAME (contracts_source_location_impl_type)) = context;
2749 143 : DECL_ARTIFICIAL (TYPE_NAME (contracts_source_location_impl_type)) = true;
2750 143 : TYPE_ARTIFICIAL (contracts_source_location_impl_type) = true;
2751 143 : contracts_source_location_impl_type
2752 143 : = cp_build_qualified_type (contracts_source_location_impl_type,
2753 : TYPE_QUAL_CONST);
2754 :
2755 143 : return contracts_source_location_impl_type;
2756 143 : }
2757 :
2758 : static tree
2759 1052 : get_src_loc_impl_ptr (location_t loc)
2760 : {
2761 1052 : if (!contracts_source_location_impl_type)
2762 192 : get_contracts_source_location_impl_type ();
2763 :
2764 1052 : tree fndecl = current_function_decl;
2765 : /* We might be an outlined function. */
2766 1052 : if (DECL_IS_PRE_FN_P (fndecl) || DECL_IS_POST_FN_P (fndecl))
2767 32 : fndecl = get_orig_for_outlined (fndecl);
2768 : /* We might be a wrapper. */
2769 1052 : if (DECL_IS_WRAPPER_FN_P (fndecl))
2770 40 : fndecl = get_orig_func_for_wrapper (fndecl);
2771 :
2772 1052 : gcc_checking_assert (fndecl);
2773 1052 : tree impl__
2774 1052 : = build_source_location_impl (loc, fndecl,
2775 : contracts_source_location_impl_type);
2776 1052 : tree p = build_pointer_type (contracts_source_location_impl_type);
2777 1052 : return build_fold_addr_expr_with_type_loc (loc, impl__, p);
2778 : }
2779 :
2780 : /* Build a contract_violation layout compatible object. */
2781 :
2782 : /* Constructor. At present, this should always be constant. */
2783 :
2784 : static tree
2785 1052 : build_contract_violation_ctor (tree contract)
2786 : {
2787 1052 : bool can_be_const = true;
2788 1052 : uint16_t version = 1;
2789 : /* Default CDM_PREDICATE_FALSE. */
2790 1052 : uint16_t detection_mode = CDM_PREDICATE_FALSE;
2791 :
2792 1052 : tree assertion_kind = CONTRACT_ASSERTION_KIND (contract);
2793 1052 : if (!assertion_kind || really_constant_p (assertion_kind))
2794 : {
2795 1052 : contract_assertion_kind kind = get_contract_assertion_kind (contract);
2796 1052 : assertion_kind = build_int_cst (uint16_type_node, kind);
2797 : }
2798 : else
2799 : can_be_const = false;
2800 :
2801 1052 : tree eval_semantic = CONTRACT_EVALUATION_SEMANTIC (contract);
2802 1052 : gcc_checking_assert (eval_semantic);
2803 1052 : if (!really_constant_p (eval_semantic))
2804 0 : can_be_const = false;
2805 :
2806 1052 : tree comment = CONTRACT_COMMENT (contract);
2807 1052 : if (comment && !really_constant_p (comment))
2808 : can_be_const = false;
2809 :
2810 1052 : tree std_src_loc_impl_ptr = CONTRACT_STD_SOURCE_LOC (contract);
2811 1052 : if (std_src_loc_impl_ptr)
2812 : {
2813 0 : std_src_loc_impl_ptr = convert_from_reference (std_src_loc_impl_ptr);
2814 0 : if (!really_constant_p (std_src_loc_impl_ptr))
2815 0 : can_be_const = false;
2816 : }
2817 : else
2818 1052 : std_src_loc_impl_ptr = get_src_loc_impl_ptr (EXPR_LOCATION (contract));
2819 :
2820 : /* Must match the type layout in builtin_contract_violation_type. */
2821 1052 : tree f0 = next_aggregate_field (TYPE_FIELDS (builtin_contract_violation_type));
2822 1052 : tree f1 = next_aggregate_field (DECL_CHAIN (f0));
2823 1052 : tree f2 = next_aggregate_field (DECL_CHAIN (f1));
2824 1052 : tree f3 = next_aggregate_field (DECL_CHAIN (f2));
2825 1052 : tree f4 = next_aggregate_field (DECL_CHAIN (f3));
2826 1052 : tree f5 = next_aggregate_field (DECL_CHAIN (f4));
2827 1052 : tree f6 = next_aggregate_field (DECL_CHAIN (f5));
2828 1052 : tree ctor = build_constructor_va
2829 1052 : (builtin_contract_violation_type, 7,
2830 1052 : f0, build_int_cst (uint16_type_node, version),
2831 : f1, assertion_kind,
2832 : f2, eval_semantic,
2833 1052 : f3, build_int_cst (uint16_type_node, detection_mode),
2834 : f4, comment,
2835 : f5, std_src_loc_impl_ptr,
2836 : f6, build_zero_cst (nullptr_type_node)); // __vendor_ext
2837 :
2838 1052 : TREE_READONLY (ctor) = true;
2839 1052 : if (can_be_const)
2840 1052 : TREE_CONSTANT (ctor) = true;
2841 :
2842 1052 : return ctor;
2843 : }
2844 :
2845 : /* Build a named TU-local constant of TYPE. */
2846 :
2847 : static tree
2848 1052 : contracts_tu_local_named_var (location_t loc, const char *name, tree type)
2849 : {
2850 1052 : tree var_ = build_decl (loc, VAR_DECL, NULL, type);
2851 1052 : DECL_NAME (var_) = generate_internal_label (name);
2852 1052 : TREE_PUBLIC (var_) = false;
2853 1052 : DECL_EXTERNAL (var_) = false;
2854 1052 : TREE_STATIC (var_) = true;
2855 : /* Compiler-generated. */
2856 1052 : DECL_ARTIFICIAL (var_) = true;
2857 1052 : TREE_CONSTANT (var_) = true;
2858 1052 : layout_decl (var_, 0);
2859 1052 : return var_;
2860 : }
2861 :
2862 : /* Create a read-only violation object. */
2863 :
2864 : static tree
2865 1052 : build_contract_violation_constant (tree ctor, tree contract)
2866 : {
2867 1052 : tree viol_ = contracts_tu_local_named_var
2868 1052 : (EXPR_LOCATION (contract), "Lcontract_violation",
2869 : builtin_contract_violation_type);
2870 :
2871 1052 : TREE_CONSTANT (viol_) = true;
2872 1052 : DECL_INITIAL (viol_) = ctor;
2873 1052 : varpool_node::finalize_decl (viol_);
2874 :
2875 1052 : return viol_;
2876 : }
2877 :
2878 : /* Helper to replace references to dummy this parameters with references to
2879 : the first argument of the FUNCTION_DECL DATA. */
2880 :
2881 : static tree
2882 5427 : remap_dummy_this_1 (tree *tp, int *, void *data)
2883 : {
2884 5427 : if (!is_this_parameter (*tp))
2885 : return NULL_TREE;
2886 49 : tree fn = (tree)data;
2887 49 : *tp = DECL_ARGUMENTS (fn);
2888 49 : return NULL_TREE;
2889 : }
2890 :
2891 : /* Replace all references to dummy this parameters in EXPR with references to
2892 : the first argument of the FUNCTION_DECL FNDECL. */
2893 :
2894 : static void
2895 1054 : remap_dummy_this (tree fndecl, tree *expr)
2896 : {
2897 0 : walk_tree (expr, remap_dummy_this_1, fndecl, NULL);
2898 0 : }
2899 :
2900 : /* Replace uses of user's placeholder var with the actual return value. */
2901 :
2902 : struct replace_tree
2903 : {
2904 : tree from, to;
2905 : };
2906 :
2907 : static tree
2908 2018 : remap_retval_1 (tree *here, int *do_subtree, void *d)
2909 : {
2910 2018 : replace_tree *data = (replace_tree *) d;
2911 :
2912 2018 : if (*here == data->from)
2913 : {
2914 84 : *here = data->to;
2915 84 : *do_subtree = 0;
2916 : }
2917 : else
2918 1934 : *do_subtree = 1;
2919 2018 : return NULL_TREE;
2920 : }
2921 :
2922 : static void
2923 355 : remap_retval (tree fndecl, tree contract)
2924 : {
2925 355 : struct replace_tree data;
2926 355 : data.from = POSTCONDITION_IDENTIFIER (contract);
2927 355 : gcc_checking_assert (DECL_RESULT (fndecl));
2928 355 : data.to = DECL_RESULT (fndecl);
2929 355 : walk_tree (&CONTRACT_CONDITION (contract), remap_retval_1, &data, NULL);
2930 355 : }
2931 :
2932 :
2933 : /* Genericize a CONTRACT tree, but do not attach it to the current context,
2934 : the caller is responsible for that.
2935 : This is called during genericization. */
2936 :
2937 : tree
2938 1056 : build_contract_check (tree contract)
2939 : {
2940 1056 : contract_evaluation_semantic semantic = get_evaluation_semantic (contract);
2941 1056 : bool quick = false;
2942 1056 : bool calls_handler = false;
2943 1056 : switch (semantic)
2944 : {
2945 2 : case CES_IGNORE:
2946 2 : return void_node;
2947 : case CES_ENFORCE:
2948 : case CES_OBSERVE:
2949 : calls_handler = true;
2950 : break;
2951 2 : case CES_QUICK:
2952 2 : quick = true;
2953 2 : break;
2954 0 : default:
2955 0 : gcc_unreachable ();
2956 : }
2957 :
2958 1054 : location_t loc = EXPR_LOCATION (contract);
2959 :
2960 1054 : remap_dummy_this (current_function_decl, &CONTRACT_CONDITION (contract));
2961 1054 : tree condition = CONTRACT_CONDITION (contract);
2962 1054 : if (condition == error_mark_node)
2963 : return NULL_TREE;
2964 :
2965 1054 : if (!flag_contract_checks_outlined && POSTCONDITION_P (contract))
2966 : {
2967 355 : remap_retval (current_function_decl, contract);
2968 355 : condition = CONTRACT_CONDITION (contract);
2969 355 : if (condition == error_mark_node)
2970 : return NULL_TREE;
2971 : }
2972 :
2973 1054 : tree terminate_wrapper = terminate_fn;
2974 1054 : if (flag_contracts_conservative_ipa)
2975 1054 : terminate_wrapper = declare_terminate_wrapper ();
2976 1054 : if (calls_handler)
2977 1052 : declare_violation_handler_wrappers ();
2978 :
2979 1054 : bool check_might_throw = (flag_exceptions
2980 1054 : && !expr_noexcept_p (condition, tf_none));
2981 :
2982 : /* Build a statement expression to hold a contract check, with the check
2983 : potentially wrapped in a try-catch expr. */
2984 1054 : tree cc_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
2985 1054 : BIND_EXPR_BODY (cc_bind) = push_stmt_list ();
2986 :
2987 1054 : if (TREE_CODE (contract) == ASSERTION_STMT)
2988 158 : emit_builtin_observable_checkpoint ();
2989 1054 : tree cond = build_x_unary_op (loc, TRUTH_NOT_EXPR, condition, NULL_TREE,
2990 : tf_warning_or_error);
2991 1054 : tree violation;
2992 1054 : bool viol_is_var = false;
2993 1054 : if (quick)
2994 : /* We will not be calling a handler. */
2995 2 : violation = build_zero_cst (nullptr_type_node);
2996 : else
2997 : {
2998 : /* Build a violation object, with the contract settings. */
2999 1052 : tree ctor = build_contract_violation_ctor (contract);
3000 1052 : gcc_checking_assert (TREE_CONSTANT (ctor));
3001 1052 : violation = build_contract_violation_constant (ctor, contract);
3002 1052 : violation = build_address (violation);
3003 : }
3004 :
3005 1054 : tree s_const = build_int_cst (uint16_type_node, semantic);
3006 : /* So now do we need a try-catch? */
3007 1054 : if (check_might_throw)
3008 : {
3009 : /* This will hold the computed condition. */
3010 252 : tree check_failed = build_decl (loc, VAR_DECL, NULL, boolean_type_node);
3011 252 : DECL_ARTIFICIAL (check_failed) = true;
3012 252 : DECL_IGNORED_P (check_failed) = true;
3013 252 : DECL_CONTEXT (check_failed) = current_function_decl;
3014 252 : layout_decl (check_failed, 0);
3015 252 : add_decl_expr (check_failed);
3016 252 : DECL_CHAIN (check_failed) = BIND_EXPR_VARS (cc_bind);
3017 252 : BIND_EXPR_VARS (cc_bind) = check_failed;
3018 252 : tree check_try = begin_try_block ();
3019 252 : finish_expr_stmt (cp_build_init_expr (check_failed, cond));
3020 252 : finish_try_block (check_try);
3021 :
3022 252 : tree handler = begin_handler ();
3023 252 : finish_handler_parms (NULL_TREE, handler); /* catch (...) */
3024 252 : if (quick)
3025 0 : finish_expr_stmt (build_call_a (terminate_wrapper, 0, nullptr));
3026 : else
3027 : {
3028 252 : if (viol_is_var)
3029 : {
3030 : /* We can update the detection mode here. */
3031 : tree memb
3032 : = lookup_member (builtin_contract_violation_type,
3033 : get_identifier ("_M_detection_mode"),
3034 : 1, 0, tf_warning_or_error);
3035 : tree r = cp_build_indirect_ref (loc, violation, RO_UNARY_STAR,
3036 : tf_warning_or_error);
3037 : r = build_class_member_access_expr (r, memb, NULL_TREE, false,
3038 : tf_warning_or_error);
3039 : r = cp_build_modify_expr
3040 : (loc, r, NOP_EXPR,
3041 : build_int_cst (uint16_type_node, (uint16_t)CDM_EVAL_EXCEPTION),
3042 : tf_warning_or_error);
3043 : finish_expr_stmt (r);
3044 : finish_expr_stmt (build_call_n (tu_has_violation, 2,
3045 : violation, s_const));
3046 : }
3047 : else
3048 : /* We need to make a copy of the violation object to update. */
3049 252 : finish_expr_stmt (build_call_n (tu_has_violation_exception, 2,
3050 : violation, s_const));
3051 : /* If we reach here, we have handled the exception thrown and do not
3052 : need further action. */
3053 252 : tree e = cp_build_modify_expr (loc, check_failed, NOP_EXPR,
3054 : boolean_false_node,
3055 : tf_warning_or_error);
3056 252 : finish_expr_stmt (e);
3057 : }
3058 252 : finish_handler (handler);
3059 252 : finish_handler_sequence (check_try);
3060 252 : cond = check_failed;
3061 252 : BIND_EXPR_VARS (cc_bind) = nreverse (BIND_EXPR_VARS (cc_bind));
3062 : }
3063 :
3064 1054 : tree do_check = begin_if_stmt ();
3065 1054 : finish_if_stmt_cond (cond, do_check);
3066 1054 : if (quick)
3067 2 : finish_expr_stmt (build_call_a (terminate_wrapper, 0, nullptr));
3068 : else
3069 1052 : finish_expr_stmt (build_call_n (tu_has_violation, 2, violation, s_const));
3070 1054 : finish_then_clause (do_check);
3071 1054 : finish_if_stmt (do_check);
3072 :
3073 1054 : TREE_SIDE_EFFECTS (cc_bind) = true;
3074 1054 : BIND_EXPR_BODY (cc_bind) = pop_stmt_list (BIND_EXPR_BODY (cc_bind));
3075 1054 : return cc_bind;
3076 : }
3077 :
3078 : #include "gt-cp-contracts.h"
|