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