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