1 | /* Definitions for C++ contract levels |
2 | Copyright (C) 2020-2024 Free Software Foundation, Inc. |
3 | Contributed by Jeff Chapman II (jchapman@lock3software.com) |
4 | |
5 | This file is part of GCC. |
6 | |
7 | GCC is free software; you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by |
9 | the Free Software Foundation; either version 3, or (at your option) |
10 | any later version. |
11 | |
12 | GCC is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | GNU General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU General Public License |
18 | along with GCC; see the file COPYING3. If not see |
19 | <http://www.gnu.org/licenses/>. */ |
20 | |
21 | /* Design Notes |
22 | |
23 | A function is called a "guarded" function if it has pre or post contract |
24 | attributes. A contract is considered an "active" contract if runtime code is |
25 | needed for the contract under the current contract configuration. |
26 | |
27 | pre and post contract attributes are parsed and stored in DECL_ATTRIBUTES. |
28 | assert contracts are parsed and wrapped in statements. When genericizing, all |
29 | active and assumed contracts are transformed into an if block. An observed |
30 | contract: |
31 | |
32 | [[ pre: v > 0 ]] |
33 | |
34 | is transformed into: |
35 | |
36 | if (!(v > 0)) { |
37 | handle_contract_violation(__pseudo_contract_violation{ |
38 | 5, // line_number, |
39 | "main.cpp", // file_name, |
40 | "fun", // function_name, |
41 | "v > 0", // comment, |
42 | "default", // assertion_level, |
43 | "default", // assertion_role, |
44 | maybe_continue, // continuation_mode |
45 | }); |
46 | terminate (); // if never_continue |
47 | } |
48 | |
49 | We use an internal type with the same layout as contract_violation rather |
50 | than try to define the latter internally and somehow deal with its actual |
51 | definition in a TU that includes <contract>. |
52 | |
53 | ??? is it worth factoring out the calls to handle_contract_violation and |
54 | terminate into a local function? |
55 | |
56 | Assumed contracts use the same implementation as C++23 [[assume]]. |
57 | |
58 | Parsing of pre and post contract conditions need to be deferred when the |
59 | contracts are attached to a member function. The postcondition identifier |
60 | cannot be used before the deduced return type of an auto function is used, |
61 | except when used in a defining declaration in which case they conditions are |
62 | fully parsed once the body is finished (see cpp2a/contracts-deduced{1,2}.C). |
63 | |
64 | A list of pre and post contracts can either be repeated in their entirety or |
65 | completely absent in subsequent declarations. If contract lists appear on two |
66 | matching declarations, their contracts have to be equivalent. In general this |
67 | means that anything before the colon have to be token equivalent and the |
68 | condition must be cp_tree_equal (primarily to allow for parameter renaming). |
69 | |
70 | Contracts on overrides must match those present on (all of) the overridee(s). |
71 | |
72 | Template specializations may have their own contracts. If no contracts are |
73 | specified on the initial specialization they're assumed to be the same as |
74 | the primary template. Specialization redeclarations must then match either |
75 | the primary template (if they were unspecified originally), or those |
76 | specified on the specialization. |
77 | |
78 | |
79 | For non-cdtors two functions are generated for ease of implementation and to |
80 | avoid some cases where code bloat may occurr. These are the DECL_PRE_FN and |
81 | DECL_POST_FN. Each handles checking either the set of pre or post contracts |
82 | of a guarded function. |
83 | |
84 | int fun(int v) |
85 | [[ pre: v > 0 ]] |
86 | [[ post r: r < 0 ]] |
87 | { |
88 | return -v; |
89 | } |
90 | |
91 | The original decl is left alone and instead calls are generated to pre/post |
92 | functions within the body: |
93 | |
94 | void fun.pre(int v) |
95 | { |
96 | [[ assert: v > 0 ]]; |
97 | } |
98 | int fun.post(int v, int __r) |
99 | { |
100 | [[ assert: __r < 0 ]]; |
101 | return __r; |
102 | } |
103 | int fun(int v) |
104 | { |
105 | fun.pre(v); |
106 | return fun.post(v, -v); |
107 | } |
108 | |
109 | If fun returns in memory, the return value is not passed through the post |
110 | function; instead, the return object is initialized directly and then passed |
111 | to the post function by invisible reference. |
112 | |
113 | This sides steps a number of issues with having to rewrite the bodies or |
114 | rewrite the parsed conditions as the parameters to the original function |
115 | changes (as happens during redeclaration). The ultimate goal is to get |
116 | something that optimizes well along the lines of |
117 | |
118 | int fun(int v) |
119 | { |
120 | [[ assert: v > 0 ]]; |
121 | auto &&__r = -v; |
122 | goto out; |
123 | out: |
124 | [[ assert: __r < 0 ]]; |
125 | return __r; |
126 | } |
127 | |
128 | With the idea being that multiple return statements could collapse the |
129 | function epilogue after inlining the pre/post functions. clang is able |
130 | to collapse common function epilogues, while gcc needs -O3 -Os combined. |
131 | |
132 | Directly laying the pre contracts down in the function body doesn't have |
133 | many issues. The post contracts may need to be repeated multiple times, once |
134 | for each return, or a goto epilogue would need to be generated. |
135 | For this initial implementation, generating function calls and letting |
136 | later optimizations decide whether to inline and duplicate the actual |
137 | checks or whether to collapse the shared epilogue was chosen. |
138 | |
139 | For cdtors a post contract is implemented using a CLEANUP_STMT. |
140 | |
141 | FIXME the compiler already shores cleanup code on multiple exit paths, so |
142 | this outlining seems unnecessary if we represent the postcondition as a |
143 | cleanup for all functions. |
144 | |
145 | More helpful for optimization might be to make the contracts a wrapper |
146 | function (for non-variadic functions), that could be inlined into a |
147 | caller while preserving the call to the actual function? Either that or |
148 | mirror a never-continue post contract with an assume in the caller. */ |
149 | |
150 | #include "config.h" |
151 | #include "system.h" |
152 | #include "coretypes.h" |
153 | #include "cp-tree.h" |
154 | #include "stringpool.h" |
155 | #include "diagnostic.h" |
156 | #include "options.h" |
157 | #include "contracts.h" |
158 | #include "tree.h" |
159 | #include "tree-inline.h" |
160 | #include "attribs.h" |
161 | #include "tree-iterator.h" |
162 | #include "print-tree.h" |
163 | #include "stor-layout.h" |
164 | #include "intl.h" |
165 | |
166 | const int max_custom_roles = 32; |
167 | static contract_role contract_build_roles[max_custom_roles] = { |
168 | }; |
169 | |
170 | bool valid_configs[CCS_MAYBE + 1][CCS_MAYBE + 1] = { |
171 | { 0, 0, 0, 0, 0, }, |
172 | { 0, 1, 0, 0, 0, }, |
173 | { 0, 1, 1, 1, 1, }, |
174 | { 0, 1, 1, 1, 1, }, |
175 | { 0, 1, 0, 0, 1, }, |
176 | }; |
177 | |
178 | void |
179 | validate_contract_role (contract_role *role) |
180 | { |
181 | gcc_assert (role); |
182 | if (!unchecked_contract_p (cs: role->axiom_semantic)) |
183 | error ("axiom contract semantic must be %<assume%> or %<ignore%>" ); |
184 | |
185 | if (!valid_configs[role->default_semantic][role->audit_semantic] ) |
186 | warning (0, "the %<audit%> semantic should be at least as strong as " |
187 | "the %<default%> semantic" ); |
188 | } |
189 | |
190 | contract_semantic |
191 | lookup_concrete_semantic (const char *name) |
192 | { |
193 | if (strcmp (s1: name, s2: "ignore" ) == 0) |
194 | return CCS_IGNORE; |
195 | if (strcmp (s1: name, s2: "assume" ) == 0) |
196 | return CCS_ASSUME; |
197 | if (strcmp (s1: name, s2: "check_never_continue" ) == 0 |
198 | || strcmp (s1: name, s2: "never" ) == 0 |
199 | || strcmp (s1: name, s2: "abort" ) == 0) |
200 | return CCS_NEVER; |
201 | if (strcmp (s1: name, s2: "check_maybe_continue" ) == 0 |
202 | || strcmp (s1: name, s2: "maybe" ) == 0) |
203 | return CCS_MAYBE; |
204 | error ("'%s' is not a valid explicit concrete semantic" , name); |
205 | return CCS_INVALID; |
206 | } |
207 | |
208 | /* Compare role and name up to either the NUL terminator or the first |
209 | occurrence of colon. */ |
210 | |
211 | static bool |
212 | role_name_equal (const char *role, const char *name) |
213 | { |
214 | size_t role_len = strcspn (s: role, reject: ":" ); |
215 | size_t name_len = strcspn (s: name, reject: ":" ); |
216 | if (role_len != name_len) |
217 | return false; |
218 | return strncmp (s1: role, s2: name, n: role_len) == 0; |
219 | } |
220 | |
221 | static bool |
222 | role_name_equal (contract_role *role, const char *name) |
223 | { |
224 | if (role->name == NULL) |
225 | return false; |
226 | return role_name_equal (role: role->name, name); |
227 | } |
228 | |
229 | contract_role * |
230 | get_contract_role (const char *name) |
231 | { |
232 | for (int i = 0; i < max_custom_roles; ++i) |
233 | { |
234 | contract_role *potential = contract_build_roles + i; |
235 | if (role_name_equal (role: potential, name)) |
236 | return potential; |
237 | } |
238 | if (role_name_equal (role: name, name: "default" ) || role_name_equal (role: name, name: "review" )) |
239 | { |
240 | setup_default_contract_role (false); |
241 | return get_contract_role (name); |
242 | } |
243 | return NULL; |
244 | } |
245 | |
246 | contract_role * |
247 | add_contract_role (const char *name, |
248 | contract_semantic des, |
249 | contract_semantic aus, |
250 | contract_semantic axs, |
251 | bool update) |
252 | { |
253 | for (int i = 0; i < max_custom_roles; ++i) |
254 | { |
255 | contract_role *potential = contract_build_roles + i; |
256 | if (potential->name != NULL |
257 | && !role_name_equal (role: potential, name)) |
258 | continue; |
259 | if (potential->name != NULL && !update) |
260 | return potential; |
261 | potential->name = name; |
262 | potential->default_semantic = des; |
263 | potential->audit_semantic = aus; |
264 | potential->axiom_semantic = axs; |
265 | return potential; |
266 | } |
267 | return NULL; |
268 | } |
269 | |
270 | enum contract_build_level { OFF, DEFAULT, AUDIT }; |
271 | static bool flag_contract_continuation_mode = false; |
272 | static bool flag_contract_assumption_mode = true; |
273 | static int flag_contract_build_level = DEFAULT; |
274 | |
275 | static bool contracts_p1332_default = false, contracts_p1332_review = false, |
276 | contracts_std = false, contracts_p1429 = false; |
277 | |
278 | static contract_semantic |
279 | get_concrete_check () |
280 | { |
281 | return flag_contract_continuation_mode ? CCS_MAYBE : CCS_NEVER; |
282 | } |
283 | |
284 | static contract_semantic |
285 | get_concrete_axiom_semantic () |
286 | { |
287 | return flag_contract_assumption_mode ? CCS_ASSUME : CCS_IGNORE; |
288 | } |
289 | |
290 | void |
291 | setup_default_contract_role (bool update) |
292 | { |
293 | contract_semantic check = get_concrete_check (); |
294 | contract_semantic axiom = get_concrete_axiom_semantic (); |
295 | switch (flag_contract_build_level) |
296 | { |
297 | case OFF: |
298 | add_contract_role (name: "default" , des: CCS_IGNORE, aus: CCS_IGNORE, axs: axiom, update); |
299 | add_contract_role (name: "review" , des: CCS_IGNORE, aus: CCS_IGNORE, axs: CCS_IGNORE, update); |
300 | break; |
301 | case DEFAULT: |
302 | add_contract_role (name: "default" , des: check, aus: CCS_IGNORE, axs: axiom, update); |
303 | add_contract_role (name: "review" , des: check, aus: CCS_IGNORE, axs: CCS_IGNORE, update); |
304 | break; |
305 | case AUDIT: |
306 | add_contract_role (name: "default" , des: check, aus: check, axs: axiom, update); |
307 | add_contract_role (name: "review" , des: check, aus: check, axs: CCS_IGNORE, update); |
308 | break; |
309 | } |
310 | } |
311 | |
312 | contract_semantic |
313 | map_contract_semantic (const char *ident) |
314 | { |
315 | if (strcmp (s1: ident, s2: "ignore" ) == 0) |
316 | return CCS_IGNORE; |
317 | else if (strcmp (s1: ident, s2: "assume" ) == 0) |
318 | return CCS_ASSUME; |
319 | else if (strcmp (s1: ident, s2: "check_never_continue" ) == 0) |
320 | return CCS_NEVER; |
321 | else if (strcmp (s1: ident, s2: "check_maybe_continue" ) == 0) |
322 | return CCS_MAYBE; |
323 | return CCS_INVALID; |
324 | } |
325 | |
326 | contract_level |
327 | map_contract_level (const char *ident) |
328 | { |
329 | if (strcmp (s1: ident, s2: "default" ) == 0) |
330 | return CONTRACT_DEFAULT; |
331 | else if (strcmp (s1: ident, s2: "audit" ) == 0) |
332 | return CONTRACT_AUDIT; |
333 | else if (strcmp (s1: ident, s2: "axiom" ) == 0) |
334 | return CONTRACT_AXIOM; |
335 | return CONTRACT_INVALID; |
336 | } |
337 | |
338 | |
339 | void |
340 | handle_OPT_fcontract_build_level_ (const char *arg) |
341 | { |
342 | if (contracts_p1332_default || contracts_p1332_review || contracts_p1429) |
343 | { |
344 | error ("%<-fcontract-build-level=%> cannot be mixed with p1332/p1429" ); |
345 | return; |
346 | } |
347 | else |
348 | contracts_std = true; |
349 | |
350 | if (strcmp (s1: arg, s2: "off" ) == 0) |
351 | flag_contract_build_level = OFF; |
352 | else if (strcmp (s1: arg, s2: "default" ) == 0) |
353 | flag_contract_build_level = DEFAULT; |
354 | else if (strcmp (s1: arg, s2: "audit" ) == 0) |
355 | flag_contract_build_level = AUDIT; |
356 | else |
357 | error ("%<-fcontract-build-level=%> must be off|default|audit" ); |
358 | |
359 | setup_default_contract_role (); |
360 | } |
361 | |
362 | void |
363 | handle_OPT_fcontract_assumption_mode_ (const char *arg) |
364 | { |
365 | if (contracts_p1332_default || contracts_p1332_review || contracts_p1429) |
366 | { |
367 | error ("%<-fcontract-assumption-mode=%> cannot be mixed with p1332/p1429" ); |
368 | return; |
369 | } |
370 | else |
371 | contracts_std = true; |
372 | |
373 | if (strcmp (s1: arg, s2: "on" ) == 0) |
374 | flag_contract_assumption_mode = true; |
375 | else if (strcmp (s1: arg, s2: "off" ) == 0) |
376 | flag_contract_assumption_mode = false; |
377 | else |
378 | error ("%<-fcontract-assumption-mode=%> must be %<on%> or %<off%>" ); |
379 | |
380 | setup_default_contract_role (); |
381 | } |
382 | |
383 | void |
384 | handle_OPT_fcontract_continuation_mode_ (const char *arg) |
385 | { |
386 | if (contracts_p1332_default || contracts_p1332_review || contracts_p1429) |
387 | { |
388 | error ("%<-fcontract-continuation-mode=%> cannot be mixed with p1332/p1429" ); |
389 | return; |
390 | } |
391 | else |
392 | contracts_std = true; |
393 | |
394 | if (strcmp (s1: arg, s2: "on" ) == 0) |
395 | flag_contract_continuation_mode = true; |
396 | else if (strcmp (s1: arg, s2: "off" ) == 0) |
397 | flag_contract_continuation_mode = false; |
398 | else |
399 | error ("%<-fcontract-continuation-mode=%> must be %<on%> or %<off%>" ); |
400 | |
401 | setup_default_contract_role (); |
402 | } |
403 | |
404 | void |
405 | handle_OPT_fcontract_role_ (const char *arg) |
406 | { |
407 | const char *name = arg; |
408 | const char *vals = strchr (s: name, c: ':'); |
409 | if (vals == NULL) |
410 | { |
411 | error ("%<-fcontract-role=%> must be in the form role:semantics" ); |
412 | return; |
413 | } |
414 | |
415 | contract_semantic dess = CCS_INVALID, auss = CCS_INVALID, axss = CCS_INVALID; |
416 | char *des = NULL, *aus = NULL, *axs = NULL; |
417 | des = xstrdup (vals + 1); |
418 | |
419 | aus = strchr (s: des, c: ','); |
420 | if (aus == NULL) |
421 | { |
422 | error ("%<-fcontract-role=%> semantics must include default,audit,axiom values" ); |
423 | goto validate; |
424 | } |
425 | *aus = '\0'; // null terminate des |
426 | aus = aus + 1; // move past null |
427 | |
428 | axs = strchr (s: aus, c: ','); |
429 | if (axs == NULL) |
430 | { |
431 | error ("%<-fcontract-role=%> semantics must include default,audit,axiom values" ); |
432 | goto validate; |
433 | } |
434 | *axs = '\0'; // null terminate aus |
435 | axs = axs + 1; // move past null |
436 | |
437 | dess = lookup_concrete_semantic (name: des); |
438 | auss = lookup_concrete_semantic (name: aus); |
439 | axss = lookup_concrete_semantic (name: axs); |
440 | validate: |
441 | free (ptr: des); |
442 | if (dess == CCS_INVALID || auss == CCS_INVALID || axss == CCS_INVALID) |
443 | return; |
444 | |
445 | bool is_defalult_role = role_name_equal (role: name, name: "default" ); |
446 | bool is_review_role = role_name_equal (role: name, name: "review" ); |
447 | bool is_std_role = is_defalult_role || is_review_role; |
448 | if ((contracts_std && is_std_role) || (contracts_p1429 && is_defalult_role)) |
449 | { |
450 | error ("%<-fcontract-role=%> cannot be mixed with std/p1429 contract flags" ); |
451 | return; |
452 | } |
453 | else if (is_std_role) |
454 | { |
455 | contracts_p1332_default |= is_defalult_role; |
456 | contracts_p1332_review |= is_review_role; |
457 | } |
458 | |
459 | contract_role *role = add_contract_role (name, des: dess, aus: auss, axs: axss); |
460 | |
461 | if (role == NULL) |
462 | { |
463 | // TODO: not enough space? |
464 | error ("%<-fcontract-level=%> too many custom roles" ); |
465 | return; |
466 | } |
467 | else |
468 | validate_contract_role (role); |
469 | } |
470 | |
471 | void |
472 | handle_OPT_fcontract_semantic_ (const char *arg) |
473 | { |
474 | if (!strchr (s: arg, c: ':')) |
475 | { |
476 | error ("%<-fcontract-semantic=%> must be in the form level:semantic" ); |
477 | return; |
478 | } |
479 | |
480 | if (contracts_std || contracts_p1332_default) |
481 | { |
482 | error ("%<-fcontract-semantic=%> cannot be mixed with std/p1332 contract flags" ); |
483 | return; |
484 | } |
485 | contracts_p1429 = true; |
486 | |
487 | contract_role *role = get_contract_role (name: "default" ); |
488 | if (!role) |
489 | { |
490 | error ("%<-fcontract-semantic=%> cannot find default role" ); |
491 | return; |
492 | } |
493 | |
494 | const char *semantic = strchr (s: arg, c: ':') + 1; |
495 | contract_semantic sem = lookup_concrete_semantic (name: semantic); |
496 | if (sem == CCS_INVALID) |
497 | return; |
498 | |
499 | if (strncmp (s1: "default:" , s2: arg, n: 8) == 0) |
500 | role->default_semantic = sem; |
501 | else if (strncmp (s1: "audit:" , s2: arg, n: 6) == 0) |
502 | role->audit_semantic = sem; |
503 | else if (strncmp (s1: "axiom:" , s2: arg, n: 6) == 0) |
504 | role->axiom_semantic = sem; |
505 | else |
506 | error ("%<-fcontract-semantic=%> level must be default, audit, or axiom" ); |
507 | validate_contract_role (role); |
508 | } |
509 | |
510 | /* Convert a contract CONFIG into a contract_mode. */ |
511 | |
512 | static contract_mode |
513 | contract_config_to_mode (tree config) |
514 | { |
515 | if (config == NULL_TREE) |
516 | return contract_mode (CONTRACT_DEFAULT, get_default_contract_role ()); |
517 | |
518 | /* TREE_LIST has TREE_VALUE is a level and TREE_PURPOSE is role. */ |
519 | if (TREE_CODE (config) == TREE_LIST) |
520 | { |
521 | contract_role *role = NULL; |
522 | if (TREE_PURPOSE (config)) |
523 | role = get_contract_role (IDENTIFIER_POINTER (TREE_PURPOSE (config))); |
524 | if (!role) |
525 | role = get_default_contract_role (); |
526 | |
527 | contract_level level = |
528 | map_contract_level (IDENTIFIER_POINTER (TREE_VALUE (config))); |
529 | return contract_mode (level, role); |
530 | } |
531 | |
532 | /* Literal semantic. */ |
533 | gcc_assert (TREE_CODE (config) == IDENTIFIER_NODE); |
534 | contract_semantic semantic = |
535 | map_contract_semantic (IDENTIFIER_POINTER (config)); |
536 | return contract_mode (semantic); |
537 | } |
538 | |
539 | /* Convert a contract's config into a concrete semantic using the current |
540 | contract semantic mapping. */ |
541 | |
542 | static contract_semantic |
543 | compute_concrete_semantic (tree contract) |
544 | { |
545 | contract_mode mode = contract_config_to_mode (CONTRACT_MODE (contract)); |
546 | /* Compute the concrete semantic for the contract. */ |
547 | if (!flag_contract_mode) |
548 | /* If contracts are off, treat all contracts as ignore. */ |
549 | return CCS_IGNORE; |
550 | else if (mode.kind == contract_mode::cm_invalid) |
551 | return CCS_INVALID; |
552 | else if (mode.kind == contract_mode::cm_explicit) |
553 | return mode.get_semantic (); |
554 | else |
555 | { |
556 | gcc_assert (mode.get_role ()); |
557 | gcc_assert (mode.get_level () != CONTRACT_INVALID); |
558 | contract_level level = mode.get_level (); |
559 | contract_role *role = mode.get_role (); |
560 | if (level == CONTRACT_DEFAULT) |
561 | return role->default_semantic; |
562 | else if (level == CONTRACT_AUDIT) |
563 | return role->audit_semantic; |
564 | else if (level == CONTRACT_AXIOM) |
565 | return role->axiom_semantic; |
566 | } |
567 | gcc_assert (false); |
568 | } |
569 | |
570 | /* Return true if any contract in CONTRACT_ATTRs is not yet parsed. */ |
571 | |
572 | bool |
573 | contract_any_deferred_p (tree contract_attr) |
574 | { |
575 | for (; contract_attr; contract_attr = CONTRACT_CHAIN (contract_attr)) |
576 | if (CONTRACT_CONDITION_DEFERRED_P (CONTRACT_STATEMENT (contract_attr))) |
577 | return true; |
578 | return false; |
579 | } |
580 | |
581 | /* Returns true if all attributes are contracts. */ |
582 | |
583 | bool |
584 | all_attributes_are_contracts_p (tree attributes) |
585 | { |
586 | for (; attributes; attributes = TREE_CHAIN (attributes)) |
587 | if (!cxx_contract_attribute_p (attributes)) |
588 | return false; |
589 | return true; |
590 | } |
591 | |
592 | /* Mark most of a contract as being invalid. */ |
593 | |
594 | tree |
595 | invalidate_contract (tree t) |
596 | { |
597 | if (TREE_CODE (t) == POSTCONDITION_STMT && POSTCONDITION_IDENTIFIER (t)) |
598 | POSTCONDITION_IDENTIFIER (t) = error_mark_node; |
599 | CONTRACT_CONDITION (t) = error_mark_node; |
600 | CONTRACT_COMMENT (t) = error_mark_node; |
601 | return t; |
602 | } |
603 | |
604 | /* Returns an invented parameter declration of the form 'TYPE ID' for the |
605 | purpose of parsing the postcondition. |
606 | |
607 | We use a PARM_DECL instead of a VAR_DECL so that tsubst forces a lookup |
608 | in local specializations when we instantiate these things later. */ |
609 | |
610 | tree |
611 | make_postcondition_variable (cp_expr id, tree type) |
612 | { |
613 | if (id == error_mark_node) |
614 | return id; |
615 | |
616 | tree decl = build_lang_decl (PARM_DECL, id, type); |
617 | DECL_ARTIFICIAL (decl) = true; |
618 | DECL_SOURCE_LOCATION (decl) = id.get_location (); |
619 | |
620 | pushdecl (decl); |
621 | return decl; |
622 | } |
623 | |
624 | /* As above, except that the type is unknown. */ |
625 | |
626 | tree |
627 | make_postcondition_variable (cp_expr id) |
628 | { |
629 | return make_postcondition_variable (id, type: make_auto ()); |
630 | } |
631 | |
632 | /* Check that the TYPE is valid for a named postcondition variable. Emit a |
633 | diagnostic if it is not. Returns TRUE if the result is OK and false |
634 | otherwise. */ |
635 | |
636 | bool |
637 | check_postcondition_result (tree decl, tree type, location_t loc) |
638 | { |
639 | /* Do not be confused by targetm.cxx.cdtor_return_this (); |
640 | conceptually, cdtors have no return value. */ |
641 | if (VOID_TYPE_P (type) |
642 | || DECL_CONSTRUCTOR_P (decl) |
643 | || DECL_DESTRUCTOR_P (decl)) |
644 | { |
645 | error_at (loc, |
646 | DECL_CONSTRUCTOR_P (decl) |
647 | ? G_("constructor does not return a value to test" ) |
648 | : DECL_DESTRUCTOR_P (decl) |
649 | ? G_("destructor does not return a value to test" ) |
650 | : G_("function does not return a value to test" )); |
651 | return false; |
652 | } |
653 | |
654 | return true; |
655 | } |
656 | |
657 | /* Instantiate each postcondition with the return type to finalize the |
658 | attribute. */ |
659 | |
660 | void |
661 | rebuild_postconditions (tree decl) |
662 | { |
663 | tree type = TREE_TYPE (TREE_TYPE (decl)); |
664 | tree attributes = DECL_CONTRACTS (decl); |
665 | |
666 | for (; attributes ; attributes = TREE_CHAIN (attributes)) |
667 | { |
668 | if (!cxx_contract_attribute_p (attributes)) |
669 | continue; |
670 | tree contract = TREE_VALUE (TREE_VALUE (attributes)); |
671 | if (TREE_CODE (contract) != POSTCONDITION_STMT) |
672 | continue; |
673 | tree condition = CONTRACT_CONDITION (contract); |
674 | |
675 | /* If any conditions are deferred, they're all deferred. Note that |
676 | we don't have to instantiate postconditions in that case because |
677 | the type is available through the declaration. */ |
678 | if (TREE_CODE (condition) == DEFERRED_PARSE) |
679 | return; |
680 | |
681 | tree oldvar = POSTCONDITION_IDENTIFIER (contract); |
682 | if (!oldvar) |
683 | continue; |
684 | |
685 | /* Always update the context of the result variable so that it can |
686 | be remapped by remap_contracts. */ |
687 | DECL_CONTEXT (oldvar) = decl; |
688 | |
689 | /* If the return type is undeduced, defer until later. */ |
690 | if (TREE_CODE (type) == TEMPLATE_TYPE_PARM) |
691 | return; |
692 | |
693 | /* Check the postcondition variable. */ |
694 | location_t loc = DECL_SOURCE_LOCATION (oldvar); |
695 | if (!check_postcondition_result (decl, type, loc)) |
696 | { |
697 | invalidate_contract (t: contract); |
698 | continue; |
699 | } |
700 | |
701 | /* "Instantiate" the result variable using the known type. Also update |
702 | the context so the inliner will actually remap this the parameter when |
703 | generating contract checks. */ |
704 | tree newvar = copy_node (oldvar); |
705 | TREE_TYPE (newvar) = type; |
706 | |
707 | /* Make parameters and result available for substitution. */ |
708 | local_specialization_stack stack (lss_copy); |
709 | for (tree t = DECL_ARGUMENTS (decl); t != NULL_TREE; t = TREE_CHAIN (t)) |
710 | register_local_identity (t); |
711 | register_local_specialization (newvar, oldvar); |
712 | |
713 | ++processing_contract_condition; |
714 | condition = tsubst_expr (condition, make_tree_vec (0), |
715 | tf_warning_or_error, decl); |
716 | --processing_contract_condition; |
717 | |
718 | /* Update the contract condition and result. */ |
719 | POSTCONDITION_IDENTIFIER (contract) = newvar; |
720 | CONTRACT_CONDITION (contract) = finish_contract_condition (condition); |
721 | } |
722 | } |
723 | |
724 | static tree |
725 | (cp_expr condition) |
726 | { |
727 | /* Try to get the actual source text for the condition; if that fails pretty |
728 | print the resulting tree. */ |
729 | char *str = get_source_text_between (global_dc->get_file_cache (), |
730 | condition.get_start (), |
731 | condition.get_finish ()); |
732 | if (!str) |
733 | { |
734 | /* FIXME cases where we end up here |
735 | #line macro usage (oof) |
736 | contracts10.C |
737 | contracts11.C */ |
738 | const char *str = expr_to_string (condition); |
739 | return build_string_literal (strlen (s: str) + 1, str); |
740 | } |
741 | |
742 | tree t = build_string_literal (strlen (s: str) + 1, str); |
743 | free (ptr: str); |
744 | return t; |
745 | } |
746 | |
747 | /* Build a contract statement. */ |
748 | |
749 | tree |
750 | grok_contract (tree attribute, tree mode, tree result, cp_expr condition, |
751 | location_t loc) |
752 | { |
753 | tree_code code; |
754 | if (is_attribute_p (attr_name: "assert" , ident: attribute)) |
755 | code = ASSERTION_STMT; |
756 | else if (is_attribute_p (attr_name: "pre" , ident: attribute)) |
757 | code = PRECONDITION_STMT; |
758 | else if (is_attribute_p (attr_name: "post" , ident: attribute)) |
759 | code = POSTCONDITION_STMT; |
760 | else |
761 | gcc_unreachable (); |
762 | |
763 | /* Build the contract. The condition is added later. In the case that |
764 | the contract is deferred, result an plain identifier, not a result |
765 | variable. */ |
766 | tree contract; |
767 | tree type = void_type_node; |
768 | if (code != POSTCONDITION_STMT) |
769 | contract = build3_loc (loc, code, type, arg0: mode, NULL_TREE, NULL_TREE); |
770 | else |
771 | contract = build4_loc (loc, code, type, arg0: mode, NULL_TREE, NULL_TREE, arg3: result); |
772 | |
773 | /* Determine the concrete semantic. */ |
774 | set_contract_semantic (t: contract, semantic: compute_concrete_semantic (contract)); |
775 | |
776 | /* If the contract is deferred, don't do anything with the condition. */ |
777 | if (TREE_CODE (condition) == DEFERRED_PARSE) |
778 | { |
779 | CONTRACT_CONDITION (contract) = condition; |
780 | return contract; |
781 | } |
782 | |
783 | /* Generate the comment from the original condition. */ |
784 | CONTRACT_COMMENT (contract) = build_comment (condition); |
785 | |
786 | /* The condition is converted to bool. */ |
787 | condition = finish_contract_condition (condition); |
788 | CONTRACT_CONDITION (contract) = condition; |
789 | |
790 | return contract; |
791 | } |
792 | |
793 | /* Build the contract attribute specifier where IDENTIFIER is one of 'pre', |
794 | 'post' or 'assert' and CONTRACT is the underlying statement. */ |
795 | tree |
796 | finish_contract_attribute (tree identifier, tree contract) |
797 | { |
798 | if (contract == error_mark_node) |
799 | return error_mark_node; |
800 | |
801 | tree attribute = build_tree_list (build_tree_list (NULL_TREE, identifier), |
802 | build_tree_list (NULL_TREE, contract)); |
803 | |
804 | |
805 | /* Mark the attribute as dependent if the condition is dependent. |
806 | |
807 | TODO: I'm not sure this is strictly necessary. It's going to be marked as |
808 | such by a subroutine of cplus_decl_attributes. */ |
809 | tree condition = CONTRACT_CONDITION (contract); |
810 | if (TREE_CODE (condition) == DEFERRED_PARSE |
811 | || value_dependent_expression_p (condition)) |
812 | ATTR_IS_DEPENDENT (attribute) = true; |
813 | |
814 | return attribute; |
815 | } |
816 | |
817 | /* Update condition of a late-parsed contract and postcondition variable, |
818 | if any. */ |
819 | |
820 | void |
821 | update_late_contract (tree contract, tree result, tree condition) |
822 | { |
823 | if (TREE_CODE (contract) == POSTCONDITION_STMT) |
824 | POSTCONDITION_IDENTIFIER (contract) = result; |
825 | |
826 | /* Generate the comment from the original condition. */ |
827 | CONTRACT_COMMENT (contract) = build_comment (condition); |
828 | |
829 | /* The condition is converted to bool. */ |
830 | condition = finish_contract_condition (condition); |
831 | CONTRACT_CONDITION (contract) = condition; |
832 | } |
833 | |
834 | /* Return TRUE iff ATTR has been parsed by the front-end as a c++2a contract |
835 | attribute. */ |
836 | |
837 | bool |
838 | cxx_contract_attribute_p (const_tree attr) |
839 | { |
840 | if (attr == NULL_TREE |
841 | || TREE_CODE (attr) != TREE_LIST) |
842 | return false; |
843 | |
844 | if (!TREE_PURPOSE (attr) || TREE_CODE (TREE_PURPOSE (attr)) != TREE_LIST) |
845 | return false; |
846 | if (!TREE_VALUE (attr) || TREE_CODE (TREE_VALUE (attr)) != TREE_LIST) |
847 | return false; |
848 | if (!TREE_VALUE (TREE_VALUE (attr))) |
849 | return false; |
850 | |
851 | return (TREE_CODE (TREE_VALUE (TREE_VALUE (attr))) == PRECONDITION_STMT |
852 | || TREE_CODE (TREE_VALUE (TREE_VALUE (attr))) == POSTCONDITION_STMT |
853 | || TREE_CODE (TREE_VALUE (TREE_VALUE (attr))) == ASSERTION_STMT); |
854 | } |
855 | |
856 | /* True if ATTR is an assertion. */ |
857 | |
858 | bool |
859 | cp_contract_assertion_p (const_tree attr) |
860 | { |
861 | /* This is only an assertion if it is a valid cxx contract attribute and the |
862 | statement is an ASSERTION_STMT. */ |
863 | return cxx_contract_attribute_p (attr) |
864 | && TREE_CODE (CONTRACT_STATEMENT (attr)) == ASSERTION_STMT; |
865 | } |
866 | |
867 | /* Remove all c++2a style contract attributes from the DECL_ATTRIBUTEs of the |
868 | FUNCTION_DECL FNDECL. */ |
869 | |
870 | void |
871 | remove_contract_attributes (tree fndecl) |
872 | { |
873 | tree list = NULL_TREE; |
874 | for (tree p = DECL_ATTRIBUTES (fndecl); p; p = TREE_CHAIN (p)) |
875 | if (!cxx_contract_attribute_p (attr: p)) |
876 | list = tree_cons (TREE_PURPOSE (p), TREE_VALUE (p), list); |
877 | DECL_ATTRIBUTES (fndecl) = nreverse (list); |
878 | } |
879 | |
880 | static tree find_first_non_contract (tree attributes) |
881 | { |
882 | tree head = attributes; |
883 | tree p = find_contract (attrs: attributes); |
884 | |
885 | /* There are no contracts. */ |
886 | if (!p) |
887 | return head; |
888 | |
889 | /* There are leading contracts. */ |
890 | if (p == head) |
891 | { |
892 | while (cxx_contract_attribute_p (attr: p)) |
893 | p = TREE_CHAIN (p); |
894 | head = p; |
895 | } |
896 | |
897 | return head; |
898 | } |
899 | |
900 | /* Remove contracts from ATTRIBUTES. */ |
901 | |
902 | tree splice_out_contracts (tree attributes) |
903 | { |
904 | tree head = find_first_non_contract (attributes); |
905 | if (!head) |
906 | return NULL_TREE; |
907 | |
908 | /* Splice out remaining contracts. */ |
909 | tree p = TREE_CHAIN (head); |
910 | tree q = head; |
911 | while (p) |
912 | { |
913 | if (cxx_contract_attribute_p (attr: p)) |
914 | { |
915 | /* Skip a sequence of contracts and then link q to the next |
916 | non-contract attribute. */ |
917 | do |
918 | p = TREE_CHAIN (p); |
919 | while (cxx_contract_attribute_p (attr: p)); |
920 | TREE_CHAIN (q) = p; |
921 | } |
922 | else |
923 | p = TREE_CHAIN (p); |
924 | } |
925 | |
926 | return head; |
927 | } |
928 | |
929 | /* Copy contract attributes from NEWDECL onto the attribute list of OLDDECL. */ |
930 | |
931 | void copy_contract_attributes (tree olddecl, tree newdecl) |
932 | { |
933 | tree attrs = NULL_TREE; |
934 | for (tree c = DECL_CONTRACTS (newdecl); c; c = TREE_CHAIN (c)) |
935 | { |
936 | if (!cxx_contract_attribute_p (attr: c)) |
937 | continue; |
938 | attrs = tree_cons (TREE_PURPOSE (c), TREE_VALUE (c), attrs); |
939 | } |
940 | attrs = chainon (DECL_ATTRIBUTES (olddecl), nreverse (attrs)); |
941 | DECL_ATTRIBUTES (olddecl) = attrs; |
942 | |
943 | /* And update DECL_CONTEXT of the postcondition result identifier. */ |
944 | rebuild_postconditions (decl: olddecl); |
945 | } |
946 | |
947 | /* Returns the parameter corresponding to the return value of a guarded |
948 | function D. Returns NULL_TREE if D has no postconditions or is void. */ |
949 | |
950 | static tree |
951 | get_postcondition_result_parameter (tree d) |
952 | { |
953 | if (!d || d == error_mark_node) |
954 | return NULL_TREE; |
955 | |
956 | if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (d)))) |
957 | return NULL_TREE; |
958 | |
959 | tree post = DECL_POST_FN (d); |
960 | if (!post || post == error_mark_node) |
961 | return NULL_TREE; |
962 | |
963 | for (tree arg = DECL_ARGUMENTS (post); arg; arg = TREE_CHAIN (arg)) |
964 | if (!TREE_CHAIN (arg)) |
965 | return arg; |
966 | |
967 | return NULL_TREE; |
968 | } |
969 | |
970 | |
971 | /* For use with the tree inliner. This preserves non-mapped local variables, |
972 | such as postcondition result variables, during remapping. */ |
973 | |
974 | static tree |
975 | retain_decl (tree decl, copy_body_data *) |
976 | { |
977 | return decl; |
978 | } |
979 | |
980 | /* Rewrite the condition of contract in place, so that references to SRC's |
981 | parameters are updated to refer to DST's parameters. The postcondition |
982 | result variable is left unchanged. |
983 | |
984 | This, along with remap_contracts, are subroutines of duplicate_decls. |
985 | When declarations are merged, we sometimes need to update contracts to |
986 | refer to new parameters. |
987 | |
988 | If DUPLICATE_P is true, this is called by duplicate_decls to rewrite contacts |
989 | in terms of a new set of parameters. In this case, we can retain local |
990 | variables appearing in the contract because the contract is not being |
991 | prepared for insertion into a new function. Importantly, this preserves the |
992 | references to postcondition results, which are not replaced during merging. |
993 | |
994 | If false, we're preparing to emit the contract condition into the body |
995 | of a new function, so we need to make copies of all local variables |
996 | appearing in the contract (e.g., if it includes a lambda expression). Note |
997 | that in this case, postcondition results are mapped to the last parameter |
998 | of DST. |
999 | |
1000 | This is also used to reuse a parent type's contracts on virtual methods. */ |
1001 | |
1002 | static void |
1003 | remap_contract (tree src, tree dst, tree contract, bool duplicate_p) |
1004 | { |
1005 | copy_body_data id; |
1006 | hash_map<tree, tree> decl_map; |
1007 | |
1008 | memset (s: &id, c: 0, n: sizeof (id)); |
1009 | id.src_fn = src; |
1010 | id.dst_fn = dst; |
1011 | id.src_cfun = DECL_STRUCT_FUNCTION (src); |
1012 | id.decl_map = &decl_map; |
1013 | |
1014 | /* If we're merging contracts, don't copy local variables. */ |
1015 | id.copy_decl = duplicate_p ? retain_decl : copy_decl_no_change; |
1016 | |
1017 | id.transform_call_graph_edges = CB_CGE_DUPLICATE; |
1018 | id.transform_new_cfg = false; |
1019 | id.transform_return_to_modify = false; |
1020 | id.transform_parameter = true; |
1021 | |
1022 | /* Make sure not to unshare trees behind the front-end's back |
1023 | since front-end specific mechanisms may rely on sharing. */ |
1024 | id.regimplify = false; |
1025 | id.do_not_unshare = true; |
1026 | id.do_not_fold = true; |
1027 | |
1028 | /* We're not inside any EH region. */ |
1029 | id.eh_lp_nr = 0; |
1030 | |
1031 | bool do_remap = false; |
1032 | |
1033 | /* Insert parameter remappings. */ |
1034 | if (TREE_CODE (src) == FUNCTION_DECL) |
1035 | src = DECL_ARGUMENTS (src); |
1036 | if (TREE_CODE (dst) == FUNCTION_DECL) |
1037 | dst = DECL_ARGUMENTS (dst); |
1038 | |
1039 | for (tree sp = src, dp = dst; |
1040 | sp || dp; |
1041 | sp = DECL_CHAIN (sp), dp = DECL_CHAIN (dp)) |
1042 | { |
1043 | if (!sp && dp |
1044 | && TREE_CODE (contract) == POSTCONDITION_STMT |
1045 | && DECL_CHAIN (dp) == NULL_TREE) |
1046 | { |
1047 | gcc_assert (!duplicate_p); |
1048 | if (tree result = POSTCONDITION_IDENTIFIER (contract)) |
1049 | { |
1050 | gcc_assert (DECL_P (result)); |
1051 | insert_decl_map (&id, result, dp); |
1052 | do_remap = true; |
1053 | } |
1054 | break; |
1055 | } |
1056 | gcc_assert (sp && dp); |
1057 | |
1058 | if (sp == dp) |
1059 | continue; |
1060 | |
1061 | insert_decl_map (&id, sp, dp); |
1062 | do_remap = true; |
1063 | } |
1064 | if (!do_remap) |
1065 | return; |
1066 | |
1067 | walk_tree (&CONTRACT_CONDITION (contract), copy_tree_body_r, &id, NULL); |
1068 | } |
1069 | |
1070 | /* Rewrite any references to SRC's PARM_DECLs to the corresponding PARM_DECL in |
1071 | DST in all of the contract attributes in CONTRACTS by calling remap_contract |
1072 | on each. |
1073 | |
1074 | This is used for two purposes: to rewrite contract attributes during |
1075 | duplicate_decls, and to prepare contracts for emission into a function's |
1076 | respective precondition and postcondition functions. DUPLICATE_P is used |
1077 | to determine the context in which this function is called. See above for |
1078 | the behavior described by this flag. */ |
1079 | |
1080 | void |
1081 | remap_contracts (tree src, tree dst, tree contracts, bool duplicate_p) |
1082 | { |
1083 | for (tree attr = contracts; attr; attr = CONTRACT_CHAIN (attr)) |
1084 | { |
1085 | if (!cxx_contract_attribute_p (attr)) |
1086 | continue; |
1087 | tree contract = CONTRACT_STATEMENT (attr); |
1088 | if (TREE_CODE (CONTRACT_CONDITION (contract)) != DEFERRED_PARSE) |
1089 | remap_contract (src, dst, contract, duplicate_p); |
1090 | } |
1091 | } |
1092 | |
1093 | /* Helper to replace references to dummy this parameters with references to |
1094 | the first argument of the FUNCTION_DECL DATA. */ |
1095 | |
1096 | static tree |
1097 | remap_dummy_this_1 (tree *tp, int *, void *data) |
1098 | { |
1099 | if (!is_this_parameter (*tp)) |
1100 | return NULL_TREE; |
1101 | tree fn = (tree)data; |
1102 | *tp = DECL_ARGUMENTS (fn); |
1103 | return NULL_TREE; |
1104 | } |
1105 | |
1106 | /* Replace all references to dummy this parameters in EXPR with references to |
1107 | the first argument of the FUNCTION_DECL FN. */ |
1108 | |
1109 | static void |
1110 | remap_dummy_this (tree fn, tree *expr) |
1111 | { |
1112 | walk_tree (expr, remap_dummy_this_1, fn, NULL); |
1113 | } |
1114 | |
1115 | /* Contract matching. */ |
1116 | |
1117 | /* True if the contract is valid. */ |
1118 | |
1119 | static bool |
1120 | contract_valid_p (tree contract) |
1121 | { |
1122 | return CONTRACT_CONDITION (contract) != error_mark_node; |
1123 | } |
1124 | |
1125 | /* True if the contract attribute is valid. */ |
1126 | |
1127 | static bool |
1128 | contract_attribute_valid_p (tree attribute) |
1129 | { |
1130 | return contract_valid_p (TREE_VALUE (TREE_VALUE (attribute))); |
1131 | } |
1132 | |
1133 | /* Compare the contract conditions of OLD_ATTR and NEW_ATTR. Returns false |
1134 | if the conditions are equivalent, and true otherwise. */ |
1135 | |
1136 | static bool |
1137 | check_for_mismatched_contracts (tree old_attr, tree new_attr, |
1138 | contract_matching_context ctx) |
1139 | { |
1140 | tree old_contract = CONTRACT_STATEMENT (old_attr); |
1141 | tree new_contract = CONTRACT_STATEMENT (new_attr); |
1142 | |
1143 | /* Different kinds of contracts do not match. */ |
1144 | if (TREE_CODE (old_contract) != TREE_CODE (new_contract)) |
1145 | { |
1146 | auto_diagnostic_group d; |
1147 | error_at (EXPR_LOCATION (new_contract), |
1148 | ctx == cmc_declaration |
1149 | ? "mismatched contract attribute in declaration" |
1150 | : "mismatched contract attribute in override" ); |
1151 | inform (EXPR_LOCATION (old_contract), "previous contract here" ); |
1152 | return true; |
1153 | } |
1154 | |
1155 | /* A deferred contract tentatively matches. */ |
1156 | if (CONTRACT_CONDITION_DEFERRED_P (new_contract)) |
1157 | return false; |
1158 | |
1159 | /* Compare the conditions of the contracts. We fold immediately to avoid |
1160 | issues comparing contracts on overrides that use parameters -- see |
1161 | contracts-pre3. */ |
1162 | tree t1 = cp_fully_fold_init (CONTRACT_CONDITION (old_contract)); |
1163 | tree t2 = cp_fully_fold_init (CONTRACT_CONDITION (new_contract)); |
1164 | |
1165 | /* Compare the contracts. The fold doesn't eliminate conversions to members. |
1166 | Set the comparing_override_contracts flag to ensure that references |
1167 | through 'this' are equal if they designate the same member, regardless of |
1168 | the path those members. */ |
1169 | bool saved_comparing_contracts = comparing_override_contracts; |
1170 | comparing_override_contracts = (ctx == cmc_override); |
1171 | bool matching_p = cp_tree_equal (t1, t2); |
1172 | comparing_override_contracts = saved_comparing_contracts; |
1173 | |
1174 | if (!matching_p) |
1175 | { |
1176 | auto_diagnostic_group d; |
1177 | error_at (EXPR_LOCATION (CONTRACT_CONDITION (new_contract)), |
1178 | ctx == cmc_declaration |
1179 | ? "mismatched contract condition in declaration" |
1180 | : "mismatched contract condition in override" ); |
1181 | inform (EXPR_LOCATION (CONTRACT_CONDITION (old_contract)), |
1182 | "previous contract here" ); |
1183 | return true; |
1184 | } |
1185 | |
1186 | return false; |
1187 | } |
1188 | |
1189 | /* Compare the contract attributes of OLDDECL and NEWDECL. Returns true |
1190 | if the contracts match, and false if they differ. */ |
1191 | |
1192 | bool |
1193 | match_contract_conditions (location_t oldloc, tree old_attrs, |
1194 | location_t newloc, tree new_attrs, |
1195 | contract_matching_context ctx) |
1196 | { |
1197 | /* Contracts only match if they are both specified. */ |
1198 | if (!old_attrs || !new_attrs) |
1199 | return true; |
1200 | |
1201 | /* Compare each contract in turn. */ |
1202 | while (old_attrs && new_attrs) |
1203 | { |
1204 | /* If either contract is ill-formed, skip the rest of the comparison, |
1205 | since we've already diagnosed an error. */ |
1206 | if (!contract_attribute_valid_p (attribute: new_attrs) |
1207 | || !contract_attribute_valid_p (attribute: old_attrs)) |
1208 | return false; |
1209 | |
1210 | if (check_for_mismatched_contracts (old_attr: old_attrs, new_attr: new_attrs, ctx)) |
1211 | return false; |
1212 | old_attrs = CONTRACT_CHAIN (old_attrs); |
1213 | new_attrs = CONTRACT_CHAIN (new_attrs); |
1214 | } |
1215 | |
1216 | /* If we didn't compare all attributes, the contracts don't match. */ |
1217 | if (old_attrs || new_attrs) |
1218 | { |
1219 | auto_diagnostic_group d; |
1220 | error_at (newloc, |
1221 | ctx == cmc_declaration |
1222 | ? "declaration has a different number of contracts than " |
1223 | "previously declared" |
1224 | : "override has a different number of contracts than " |
1225 | "previously declared" ); |
1226 | inform (oldloc, |
1227 | new_attrs |
1228 | ? "original declaration with fewer contracts here" |
1229 | : "original declaration with more contracts here" ); |
1230 | return false; |
1231 | } |
1232 | |
1233 | return true; |
1234 | } |
1235 | |
1236 | /* Deferred contract mapping. |
1237 | |
1238 | This is used to compare late-parsed contracts on overrides with their |
1239 | base class functions. |
1240 | |
1241 | TODO: It seems like this could be replaced by a simple list that maps from |
1242 | overrides to their base functions. It's not clear that we really need |
1243 | a map to a function + a list of contracts. */ |
1244 | |
1245 | /* Map from FNDECL to a tree list of contracts that have not been matched or |
1246 | diagnosed yet. The TREE_PURPOSE is the basefn we're overriding, and the |
1247 | TREE_VALUE is the list of contract attrs for BASEFN. */ |
1248 | |
1249 | static hash_map<tree_decl_hash, tree> pending_guarded_decls; |
1250 | |
1251 | void |
1252 | defer_guarded_contract_match (tree fndecl, tree fn, tree contracts) |
1253 | { |
1254 | if (!pending_guarded_decls.get (k: fndecl)) |
1255 | { |
1256 | pending_guarded_decls.put (k: fndecl, v: build_tree_list (fn, contracts)); |
1257 | return; |
1258 | } |
1259 | for (tree pending = *pending_guarded_decls.get (k: fndecl); |
1260 | pending; |
1261 | pending = TREE_CHAIN (pending)) |
1262 | { |
1263 | if (TREE_VALUE (pending) == contracts) |
1264 | return; |
1265 | if (TREE_CHAIN (pending) == NULL_TREE) |
1266 | TREE_CHAIN (pending) = build_tree_list (fn, contracts); |
1267 | } |
1268 | } |
1269 | |
1270 | /* If the FUNCTION_DECL DECL has any contracts that had their matching |
1271 | deferred earlier, do that checking now. */ |
1272 | |
1273 | void |
1274 | match_deferred_contracts (tree decl) |
1275 | { |
1276 | tree *tp = pending_guarded_decls.get (k: decl); |
1277 | if (!tp) |
1278 | return; |
1279 | |
1280 | gcc_assert(!contract_any_deferred_p (DECL_CONTRACTS (decl))); |
1281 | |
1282 | processing_template_decl_sentinel ptds; |
1283 | processing_template_decl = uses_template_parms (decl); |
1284 | |
1285 | /* Do late contract matching. */ |
1286 | for (tree pending = *tp; pending; pending = TREE_CHAIN (pending)) |
1287 | { |
1288 | tree new_contracts = TREE_VALUE (pending); |
1289 | location_t new_loc = CONTRACT_SOURCE_LOCATION (new_contracts); |
1290 | tree old_contracts = DECL_CONTRACTS (decl); |
1291 | location_t old_loc = CONTRACT_SOURCE_LOCATION (old_contracts); |
1292 | tree base = TREE_PURPOSE (pending); |
1293 | match_contract_conditions (oldloc: new_loc, old_attrs: new_contracts, |
1294 | newloc: old_loc, new_attrs: old_contracts, |
1295 | ctx: base ? cmc_override : cmc_declaration); |
1296 | } |
1297 | |
1298 | /* Clear out deferred match list so we don't check it twice. */ |
1299 | pending_guarded_decls.remove (k: decl); |
1300 | } |
1301 | |
1302 | /* Map from FUNCTION_DECL to a FUNCTION_DECL for either the PRE_FN or POST_FN. |
1303 | These are used to parse contract conditions and are called inside the body |
1304 | of the guarded function. */ |
1305 | static GTY(()) hash_map<tree, tree> *decl_pre_fn; |
1306 | static GTY(()) hash_map<tree, tree> *decl_post_fn; |
1307 | |
1308 | /* Returns the precondition funtion for D, or null if not set. */ |
1309 | |
1310 | tree |
1311 | get_precondition_function (tree d) |
1312 | { |
1313 | hash_map_maybe_create<hm_ggc> (h&: decl_pre_fn); |
1314 | tree *result = decl_pre_fn->get (k: d); |
1315 | return result ? *result : NULL_TREE; |
1316 | } |
1317 | |
1318 | /* Returns the postcondition funtion for D, or null if not set. */ |
1319 | |
1320 | tree |
1321 | get_postcondition_function (tree d) |
1322 | { |
1323 | hash_map_maybe_create<hm_ggc> (h&: decl_post_fn); |
1324 | tree *result = decl_post_fn->get (k: d); |
1325 | return result ? *result : NULL_TREE; |
1326 | } |
1327 | |
1328 | /* Makes PRE the precondition function for D. */ |
1329 | |
1330 | void |
1331 | set_precondition_function (tree d, tree pre) |
1332 | { |
1333 | gcc_assert (pre); |
1334 | hash_map_maybe_create<hm_ggc> (h&: decl_pre_fn); |
1335 | gcc_assert (!decl_pre_fn->get (d)); |
1336 | decl_pre_fn->put (k: d, v: pre); |
1337 | } |
1338 | |
1339 | /* Makes POST the postcondition function for D. */ |
1340 | |
1341 | void |
1342 | set_postcondition_function (tree d, tree post) |
1343 | { |
1344 | gcc_assert (post); |
1345 | hash_map_maybe_create<hm_ggc> (h&: decl_post_fn); |
1346 | gcc_assert (!decl_post_fn->get (d)); |
1347 | decl_post_fn->put (k: d, v: post); |
1348 | } |
1349 | |
1350 | /* Set the PRE and POST functions for D. Note that PRE and POST can be |
1351 | null in this case. If so the functions are not recorded. */ |
1352 | |
1353 | void |
1354 | set_contract_functions (tree d, tree pre, tree post) |
1355 | { |
1356 | if (pre) |
1357 | set_precondition_function (d, pre); |
1358 | if (post) |
1359 | set_postcondition_function (d, post); |
1360 | } |
1361 | |
1362 | /* Return a copy of the FUNCTION_DECL IDECL with its own unshared |
1363 | PARM_DECL and DECL_ATTRIBUTEs. */ |
1364 | |
1365 | static tree |
1366 | copy_fn_decl (tree idecl) |
1367 | { |
1368 | tree decl = copy_decl (idecl); |
1369 | DECL_ATTRIBUTES (decl) = copy_list (DECL_ATTRIBUTES (idecl)); |
1370 | |
1371 | if (DECL_RESULT (idecl)) |
1372 | { |
1373 | DECL_RESULT (decl) = copy_decl (DECL_RESULT (idecl)); |
1374 | DECL_CONTEXT (DECL_RESULT (decl)) = decl; |
1375 | } |
1376 | if (!DECL_ARGUMENTS (idecl) || VOID_TYPE_P (DECL_ARGUMENTS (idecl))) |
1377 | return decl; |
1378 | |
1379 | tree last = DECL_ARGUMENTS (decl) = copy_decl (DECL_ARGUMENTS (decl)); |
1380 | DECL_CONTEXT (last) = decl; |
1381 | for (tree p = TREE_CHAIN (DECL_ARGUMENTS (idecl)); p; p = TREE_CHAIN (p)) |
1382 | { |
1383 | if (VOID_TYPE_P (p)) |
1384 | { |
1385 | TREE_CHAIN (last) = void_list_node; |
1386 | break; |
1387 | } |
1388 | last = TREE_CHAIN (last) = copy_decl (p); |
1389 | DECL_CONTEXT (last) = decl; |
1390 | } |
1391 | return decl; |
1392 | } |
1393 | |
1394 | /* Build a declaration for the pre- or postcondition of a guarded FNDECL. */ |
1395 | |
1396 | static tree |
1397 | build_contract_condition_function (tree fndecl, bool pre) |
1398 | { |
1399 | if (TREE_TYPE (fndecl) == error_mark_node) |
1400 | return error_mark_node; |
1401 | if (DECL_IOBJ_MEMBER_FUNCTION_P (fndecl) |
1402 | && !TYPE_METHOD_BASETYPE (TREE_TYPE (fndecl))) |
1403 | return error_mark_node; |
1404 | |
1405 | /* Create and rename the unchecked function and give an internal name. */ |
1406 | tree fn = copy_fn_decl (idecl: fndecl); |
1407 | DECL_RESULT (fn) = NULL_TREE; |
1408 | tree value_type = pre ? void_type_node : TREE_TYPE (TREE_TYPE (fn)); |
1409 | |
1410 | /* Don't propagate declaration attributes to the checking function, |
1411 | including the original contracts. */ |
1412 | DECL_ATTRIBUTES (fn) = NULL_TREE; |
1413 | |
1414 | tree arg_types = NULL_TREE; |
1415 | tree *last = &arg_types; |
1416 | |
1417 | /* FIXME will later optimizations delete unused args to prevent extra arg |
1418 | passing? do we care? */ |
1419 | tree class_type = NULL_TREE; |
1420 | for (tree arg_type = TYPE_ARG_TYPES (TREE_TYPE (fn)); |
1421 | arg_type && arg_type != void_list_node; |
1422 | arg_type = TREE_CHAIN (arg_type)) |
1423 | { |
1424 | if (DECL_IOBJ_MEMBER_FUNCTION_P (fndecl) |
1425 | && TYPE_ARG_TYPES (TREE_TYPE (fn)) == arg_type) |
1426 | { |
1427 | class_type = TREE_TYPE (TREE_VALUE (arg_type)); |
1428 | continue; |
1429 | } |
1430 | *last = build_tree_list (TREE_PURPOSE (arg_type), TREE_VALUE (arg_type)); |
1431 | last = &TREE_CHAIN (*last); |
1432 | } |
1433 | |
1434 | if (pre || VOID_TYPE_P (value_type)) |
1435 | *last = void_list_node; |
1436 | else |
1437 | { |
1438 | tree name = get_identifier ("__r" ); |
1439 | tree parm = build_lang_decl (PARM_DECL, name, value_type); |
1440 | DECL_CONTEXT (parm) = fn; |
1441 | DECL_ARTIFICIAL (parm) = true; |
1442 | DECL_ARGUMENTS (fn) = chainon (DECL_ARGUMENTS (fn), parm); |
1443 | |
1444 | *last = build_tree_list (NULL_TREE, value_type); |
1445 | TREE_CHAIN (*last) = void_list_node; |
1446 | |
1447 | if (aggregate_value_p (value_type, fndecl)) |
1448 | /* If FNDECL returns in memory, don't return the value from the |
1449 | postcondition. */ |
1450 | value_type = void_type_node; |
1451 | } |
1452 | |
1453 | TREE_TYPE (fn) = build_function_type (value_type, arg_types); |
1454 | if (DECL_IOBJ_MEMBER_FUNCTION_P (fndecl)) |
1455 | TREE_TYPE (fn) = build_method_type (class_type, TREE_TYPE (fn)); |
1456 | |
1457 | DECL_NAME (fn) = copy_node (DECL_NAME (fn)); |
1458 | DECL_INITIAL (fn) = error_mark_node; |
1459 | DECL_ABSTRACT_ORIGIN (fn) = fndecl; |
1460 | |
1461 | IDENTIFIER_VIRTUAL_P (DECL_NAME (fn)) = false; |
1462 | DECL_VIRTUAL_P (fn) = false; |
1463 | |
1464 | /* Make these functions internal if we can, i.e. if the guarded function is |
1465 | not vague linkage, or if we can put them in a comdat group with the |
1466 | guarded function. */ |
1467 | if (!DECL_WEAK (fndecl) || HAVE_COMDAT_GROUP) |
1468 | { |
1469 | TREE_PUBLIC (fn) = false; |
1470 | DECL_EXTERNAL (fn) = false; |
1471 | DECL_WEAK (fn) = false; |
1472 | DECL_COMDAT (fn) = false; |
1473 | |
1474 | /* We haven't set the comdat group on the guarded function yet, we'll add |
1475 | this to the same group in comdat_linkage later. */ |
1476 | gcc_assert (!DECL_ONE_ONLY (fndecl)); |
1477 | |
1478 | DECL_INTERFACE_KNOWN (fn) = true; |
1479 | } |
1480 | |
1481 | DECL_ARTIFICIAL (fn) = true; |
1482 | |
1483 | /* Update various inline related declaration properties. */ |
1484 | //DECL_DECLARED_INLINE_P (fn) = true; |
1485 | DECL_DISREGARD_INLINE_LIMITS (fn) = true; |
1486 | TREE_NO_WARNING (fn) = 1; |
1487 | |
1488 | return fn; |
1489 | } |
1490 | |
1491 | /* Return true if CONTRACT is checked or assumed under the current build |
1492 | configuration. */ |
1493 | |
1494 | bool |
1495 | contract_active_p (tree contract) |
1496 | { |
1497 | return get_contract_semantic (t: contract) != CCS_IGNORE; |
1498 | } |
1499 | |
1500 | static bool |
1501 | has_active_contract_condition (tree d, tree_code c) |
1502 | { |
1503 | for (tree as = DECL_CONTRACTS (d) ; as != NULL_TREE; as = TREE_CHAIN (as)) |
1504 | { |
1505 | tree contract = TREE_VALUE (TREE_VALUE (as)); |
1506 | if (TREE_CODE (contract) == c && contract_active_p (contract)) |
1507 | return true; |
1508 | } |
1509 | return false; |
1510 | } |
1511 | |
1512 | /* True if D has any checked or assumed preconditions. */ |
1513 | |
1514 | static bool |
1515 | has_active_preconditions (tree d) |
1516 | { |
1517 | return has_active_contract_condition (d, c: PRECONDITION_STMT); |
1518 | } |
1519 | |
1520 | /* True if D has any checked or assumed postconditions. */ |
1521 | |
1522 | static bool |
1523 | has_active_postconditions (tree d) |
1524 | { |
1525 | return has_active_contract_condition (d, c: POSTCONDITION_STMT); |
1526 | } |
1527 | |
1528 | /* Return true if any contract in the CONTRACT list is checked or assumed |
1529 | under the current build configuration. */ |
1530 | |
1531 | bool |
1532 | contract_any_active_p (tree contract) |
1533 | { |
1534 | for (; contract != NULL_TREE; contract = CONTRACT_CHAIN (contract)) |
1535 | if (contract_active_p (TREE_VALUE (TREE_VALUE (contract)))) |
1536 | return true; |
1537 | return false; |
1538 | } |
1539 | |
1540 | /* Do we need to mess with contracts for DECL1? */ |
1541 | |
1542 | static bool |
1543 | handle_contracts_p (tree decl1) |
1544 | { |
1545 | return (flag_contracts |
1546 | && !processing_template_decl |
1547 | && DECL_ABSTRACT_ORIGIN (decl1) == NULL_TREE |
1548 | && contract_any_active_p (DECL_CONTRACTS (decl1))); |
1549 | } |
1550 | |
1551 | /* Should we break out DECL1's pre/post contracts into separate functions? |
1552 | FIXME I'd like this to default to 0, but that will need an overhaul to the |
1553 | return identifier handling to just refer to the RESULT_DECL. */ |
1554 | |
1555 | static bool |
1556 | outline_contracts_p (tree decl1) |
1557 | { |
1558 | return (!DECL_CONSTRUCTOR_P (decl1) |
1559 | && !DECL_DESTRUCTOR_P (decl1)); |
1560 | } |
1561 | |
1562 | /* Build the precondition checking function for D. */ |
1563 | |
1564 | static tree |
1565 | build_precondition_function (tree d) |
1566 | { |
1567 | if (!has_active_preconditions (d)) |
1568 | return NULL_TREE; |
1569 | |
1570 | return build_contract_condition_function (fndecl: d, /*pre=*/true); |
1571 | } |
1572 | |
1573 | /* Build the postcondition checking function for D. If the return |
1574 | type is undeduced, don't build the function yet. We do that in |
1575 | apply_deduced_return_type. */ |
1576 | |
1577 | static tree |
1578 | build_postcondition_function (tree d) |
1579 | { |
1580 | if (!has_active_postconditions (d)) |
1581 | return NULL_TREE; |
1582 | |
1583 | tree type = TREE_TYPE (TREE_TYPE (d)); |
1584 | if (is_auto (type)) |
1585 | return NULL_TREE; |
1586 | |
1587 | return build_contract_condition_function (fndecl: d, /*pre=*/false); |
1588 | } |
1589 | |
1590 | static void |
1591 | build_contract_function_decls (tree d) |
1592 | { |
1593 | /* Constructors and destructors have their contracts inserted inline. */ |
1594 | if (!outline_contracts_p (decl1: d)) |
1595 | return; |
1596 | |
1597 | /* Build the pre/post functions (or not). */ |
1598 | tree pre = build_precondition_function (d); |
1599 | tree post = build_postcondition_function (d); |
1600 | set_contract_functions (d, pre, post); |
1601 | } |
1602 | |
1603 | static const char * |
1604 | get_contract_level_name (tree contract) |
1605 | { |
1606 | if (CONTRACT_LITERAL_MODE_P (contract)) |
1607 | return "" ; |
1608 | if (tree mode = CONTRACT_MODE (contract)) |
1609 | if (tree level = TREE_VALUE (mode)) |
1610 | return IDENTIFIER_POINTER (level); |
1611 | return "default" ; |
1612 | } |
1613 | |
1614 | static const char * |
1615 | get_contract_role_name (tree contract) |
1616 | { |
1617 | if (CONTRACT_LITERAL_MODE_P (contract)) |
1618 | return "" ; |
1619 | if (tree mode = CONTRACT_MODE (contract)) |
1620 | if (tree role = TREE_PURPOSE (mode)) |
1621 | return IDENTIFIER_POINTER (role); |
1622 | return "default" ; |
1623 | } |
1624 | |
1625 | /* Build a layout-compatible internal version of std::contract_violation. */ |
1626 | |
1627 | static tree |
1628 | get_pseudo_contract_violation_type () |
1629 | { |
1630 | if (!pseudo_contract_violation_type) |
1631 | { |
1632 | /* Must match <contract>: |
1633 | class contract_violation { |
1634 | const char* _M_file; |
1635 | const char* _M_function; |
1636 | const char* _M_comment; |
1637 | const char* _M_level; |
1638 | const char* _M_role; |
1639 | uint_least32_t _M_line; |
1640 | signed char _M_continue; |
1641 | If this changes, also update the initializer in |
1642 | build_contract_violation. */ |
1643 | const tree types[] = { const_string_type_node, |
1644 | const_string_type_node, |
1645 | const_string_type_node, |
1646 | const_string_type_node, |
1647 | const_string_type_node, |
1648 | uint_least32_type_node, |
1649 | signed_char_type_node }; |
1650 | tree fields = NULL_TREE; |
1651 | for (tree type : types) |
1652 | { |
1653 | /* finish_builtin_struct wants fieldss chained in reverse. */ |
1654 | tree next = build_decl (BUILTINS_LOCATION, FIELD_DECL, |
1655 | NULL_TREE, type); |
1656 | DECL_CHAIN (next) = fields; |
1657 | fields = next; |
1658 | } |
1659 | iloc_sentinel ils (input_location); |
1660 | input_location = BUILTINS_LOCATION; |
1661 | pseudo_contract_violation_type = make_class_type (RECORD_TYPE); |
1662 | finish_builtin_struct (pseudo_contract_violation_type, |
1663 | "__pseudo_contract_violation" , |
1664 | fields, NULL_TREE); |
1665 | CLASSTYPE_AS_BASE (pseudo_contract_violation_type) |
1666 | = pseudo_contract_violation_type; |
1667 | DECL_CONTEXT (TYPE_NAME (pseudo_contract_violation_type)) |
1668 | = FROB_CONTEXT (global_namespace); |
1669 | TREE_PUBLIC (TYPE_NAME (pseudo_contract_violation_type)) = true; |
1670 | CLASSTYPE_LITERAL_P (pseudo_contract_violation_type) = true; |
1671 | CLASSTYPE_LAZY_COPY_CTOR (pseudo_contract_violation_type) = true; |
1672 | xref_basetypes (pseudo_contract_violation_type, /*bases=*/NULL_TREE); |
1673 | pseudo_contract_violation_type |
1674 | = cp_build_qualified_type (pseudo_contract_violation_type, |
1675 | TYPE_QUAL_CONST); |
1676 | } |
1677 | return pseudo_contract_violation_type; |
1678 | } |
1679 | |
1680 | /* Return a VAR_DECL to pass to handle_contract_violation. */ |
1681 | |
1682 | static tree |
1683 | build_contract_violation (tree contract, contract_continuation cmode) |
1684 | { |
1685 | expanded_location loc = expand_location (EXPR_LOCATION (contract)); |
1686 | const char *function = fndecl_name (DECL_ORIGIN (current_function_decl)); |
1687 | const char *level = get_contract_level_name (contract); |
1688 | const char *role = get_contract_role_name (contract); |
1689 | |
1690 | /* Must match the type layout in get_pseudo_contract_violation_type. */ |
1691 | tree ctor = build_constructor_va |
1692 | (init_list_type_node, 7, |
1693 | NULL_TREE, build_string_literal (p: loc.file), |
1694 | NULL_TREE, build_string_literal (p: function), |
1695 | NULL_TREE, CONTRACT_COMMENT (contract), |
1696 | NULL_TREE, build_string_literal (p: level), |
1697 | NULL_TREE, build_string_literal (p: role), |
1698 | NULL_TREE, build_int_cst (uint_least32_type_node, loc.line), |
1699 | NULL_TREE, build_int_cst (signed_char_type_node, cmode)); |
1700 | |
1701 | ctor = finish_compound_literal (get_pseudo_contract_violation_type (), |
1702 | ctor, tf_none); |
1703 | protected_set_expr_location (ctor, EXPR_LOCATION (contract)); |
1704 | return ctor; |
1705 | } |
1706 | |
1707 | /* Return handle_contract_violation(), declaring it if needed. */ |
1708 | |
1709 | static tree |
1710 | declare_handle_contract_violation () |
1711 | { |
1712 | tree fnname = get_identifier ("handle_contract_violation" ); |
1713 | tree viol_name = get_identifier ("contract_violation" ); |
1714 | tree l = lookup_qualified_name (global_namespace, name: fnname, |
1715 | LOOK_want::HIDDEN_FRIEND); |
1716 | for (tree f: lkp_range (l)) |
1717 | if (TREE_CODE (f) == FUNCTION_DECL) |
1718 | { |
1719 | tree parms = TYPE_ARG_TYPES (TREE_TYPE (f)); |
1720 | if (remaining_arguments (parms) != 1) |
1721 | continue; |
1722 | tree parmtype = non_reference (TREE_VALUE (parms)); |
1723 | if (CLASS_TYPE_P (parmtype) |
1724 | && TYPE_IDENTIFIER (parmtype) == viol_name) |
1725 | return f; |
1726 | } |
1727 | |
1728 | tree id_exp = get_identifier ("experimental" ); |
1729 | tree ns_exp = lookup_qualified_name (std_node, name: id_exp); |
1730 | |
1731 | tree violation = error_mark_node; |
1732 | if (TREE_CODE (ns_exp) == NAMESPACE_DECL) |
1733 | violation = lookup_qualified_name (scope: ns_exp, name: viol_name, |
1734 | LOOK_want::TYPE |
1735 | |LOOK_want::HIDDEN_FRIEND); |
1736 | |
1737 | if (TREE_CODE (violation) == TYPE_DECL) |
1738 | violation = TREE_TYPE (violation); |
1739 | else |
1740 | { |
1741 | push_nested_namespace (std_node); |
1742 | push_namespace (id_exp, /*inline*/make_inline: false); |
1743 | violation = make_class_type (RECORD_TYPE); |
1744 | create_implicit_typedef (viol_name, violation); |
1745 | DECL_SOURCE_LOCATION (TYPE_NAME (violation)) = BUILTINS_LOCATION; |
1746 | DECL_CONTEXT (TYPE_NAME (violation)) = current_namespace; |
1747 | pushdecl_namespace_level (TYPE_NAME (violation), /*hidden*/hiding: true); |
1748 | pop_namespace (); |
1749 | pop_nested_namespace (std_node); |
1750 | } |
1751 | |
1752 | tree argtype = cp_build_qualified_type (violation, TYPE_QUAL_CONST); |
1753 | argtype = cp_build_reference_type (argtype, /*rval*/false); |
1754 | tree fntype = build_function_type_list (void_type_node, argtype, NULL_TREE); |
1755 | |
1756 | push_nested_namespace (global_namespace); |
1757 | tree fn = build_cp_library_fn_ptr ("handle_contract_violation" , fntype, |
1758 | ECF_COLD); |
1759 | pushdecl_namespace_level (fn, /*hiding*/true); |
1760 | pop_nested_namespace (global_namespace); |
1761 | |
1762 | return fn; |
1763 | } |
1764 | |
1765 | /* Build the call to handle_contract_violation for CONTRACT. */ |
1766 | |
1767 | static void |
1768 | build_contract_handler_call (tree contract, |
1769 | contract_continuation cmode) |
1770 | { |
1771 | tree violation = build_contract_violation (contract, cmode); |
1772 | tree violation_fn = declare_handle_contract_violation (); |
1773 | tree call = build_call_n (violation_fn, 1, build_address (violation)); |
1774 | finish_expr_stmt (call); |
1775 | } |
1776 | |
1777 | /* Generate the code that checks or assumes a contract, but do not attach |
1778 | it to the current context. This is called during genericization. */ |
1779 | |
1780 | tree |
1781 | build_contract_check (tree contract) |
1782 | { |
1783 | contract_semantic semantic = get_contract_semantic (t: contract); |
1784 | if (semantic == CCS_INVALID) |
1785 | return NULL_TREE; |
1786 | |
1787 | /* Ignored contracts are never checked or assumed. */ |
1788 | if (semantic == CCS_IGNORE) |
1789 | return void_node; |
1790 | |
1791 | remap_dummy_this (fn: current_function_decl, expr: &CONTRACT_CONDITION (contract)); |
1792 | tree condition = CONTRACT_CONDITION (contract); |
1793 | if (condition == error_mark_node) |
1794 | return NULL_TREE; |
1795 | |
1796 | location_t loc = EXPR_LOCATION (contract); |
1797 | |
1798 | if (semantic == CCS_ASSUME) |
1799 | return build_assume_call (loc, condition); |
1800 | |
1801 | tree if_stmt = begin_if_stmt (); |
1802 | tree cond = build_x_unary_op (loc, |
1803 | TRUTH_NOT_EXPR, |
1804 | condition, NULL_TREE, |
1805 | tf_warning_or_error); |
1806 | finish_if_stmt_cond (cond, if_stmt); |
1807 | |
1808 | /* Get the continuation mode. */ |
1809 | contract_continuation cmode; |
1810 | switch (semantic) |
1811 | { |
1812 | case CCS_NEVER: cmode = NEVER_CONTINUE; break; |
1813 | case CCS_MAYBE: cmode = MAYBE_CONTINUE; break; |
1814 | default: gcc_unreachable (); |
1815 | } |
1816 | |
1817 | build_contract_handler_call (contract, cmode); |
1818 | if (cmode == NEVER_CONTINUE) |
1819 | finish_expr_stmt (build_call_a (terminate_fn, 0, nullptr)); |
1820 | |
1821 | finish_then_clause (if_stmt); |
1822 | tree scope = IF_SCOPE (if_stmt); |
1823 | IF_SCOPE (if_stmt) = NULL; |
1824 | return do_poplevel (scope); |
1825 | } |
1826 | |
1827 | /* Add the contract statement CONTRACT to the current block if valid. */ |
1828 | |
1829 | static void |
1830 | emit_contract_statement (tree contract) |
1831 | { |
1832 | /* Only add valid contracts. */ |
1833 | if (get_contract_semantic (t: contract) != CCS_INVALID |
1834 | && CONTRACT_CONDITION (contract) != error_mark_node) |
1835 | add_stmt (contract); |
1836 | } |
1837 | |
1838 | /* Generate the statement for the given contract attribute by adding the |
1839 | statement to the current block. Returns the next contract in the chain. */ |
1840 | |
1841 | static tree |
1842 | emit_contract_attr (tree attr) |
1843 | { |
1844 | gcc_assert (TREE_CODE (attr) == TREE_LIST); |
1845 | |
1846 | emit_contract_statement (CONTRACT_STATEMENT (attr)); |
1847 | |
1848 | return CONTRACT_CHAIN (attr); |
1849 | } |
1850 | |
1851 | /* Add the statements of contract attributes ATTRS to the current block. */ |
1852 | |
1853 | static void |
1854 | emit_contract_conditions (tree attrs, tree_code code) |
1855 | { |
1856 | if (!attrs) return; |
1857 | gcc_assert (TREE_CODE (attrs) == TREE_LIST); |
1858 | gcc_assert (code == PRECONDITION_STMT || code == POSTCONDITION_STMT); |
1859 | while (attrs) |
1860 | { |
1861 | tree contract = CONTRACT_STATEMENT (attrs); |
1862 | if (TREE_CODE (contract) == code) |
1863 | attrs = emit_contract_attr (attr: attrs); |
1864 | else |
1865 | attrs = CONTRACT_CHAIN (attrs); |
1866 | } |
1867 | } |
1868 | |
1869 | /* Emit the statement for an assertion attribute. */ |
1870 | |
1871 | void |
1872 | emit_assertion (tree attr) |
1873 | { |
1874 | emit_contract_attr (attr); |
1875 | } |
1876 | |
1877 | /* Emit statements for precondition attributes. */ |
1878 | |
1879 | static void |
1880 | emit_preconditions (tree attr) |
1881 | { |
1882 | return emit_contract_conditions (attrs: attr, code: PRECONDITION_STMT); |
1883 | } |
1884 | |
1885 | /* Emit statements for postcondition attributes. */ |
1886 | |
1887 | static void |
1888 | emit_postconditions_cleanup (tree contracts) |
1889 | { |
1890 | tree stmts = push_stmt_list (); |
1891 | emit_contract_conditions (attrs: contracts, code: POSTCONDITION_STMT); |
1892 | stmts = pop_stmt_list (stmts); |
1893 | push_cleanup (NULL_TREE, stmts, /*eh_only*/false); |
1894 | } |
1895 | |
1896 | /* We're compiling the pre/postcondition function CONDFN; remap any FN |
1897 | attributes that match CODE and emit them. */ |
1898 | |
1899 | static void |
1900 | remap_and_emit_conditions (tree fn, tree condfn, tree_code code) |
1901 | { |
1902 | gcc_assert (code == PRECONDITION_STMT || code == POSTCONDITION_STMT); |
1903 | for (tree attr = DECL_CONTRACTS (fn); attr; |
1904 | attr = CONTRACT_CHAIN (attr)) |
1905 | { |
1906 | tree contract = CONTRACT_STATEMENT (attr); |
1907 | if (TREE_CODE (contract) == code) |
1908 | { |
1909 | contract = copy_node (contract); |
1910 | remap_contract (src: fn, dst: condfn, contract, /*duplicate_p=*/false); |
1911 | emit_contract_statement (contract); |
1912 | } |
1913 | } |
1914 | } |
1915 | |
1916 | /* Converts a contract condition to bool and ensures it has a locaiton. */ |
1917 | |
1918 | tree |
1919 | finish_contract_condition (cp_expr condition) |
1920 | { |
1921 | /* Ensure we have the condition location saved in case we later need to |
1922 | emit a conversion error during template instantiation and wouldn't |
1923 | otherwise have it. */ |
1924 | if (!CAN_HAVE_LOCATION_P (condition) || EXCEPTIONAL_CLASS_P (condition)) |
1925 | { |
1926 | condition = build1_loc (loc: condition.get_location (), code: VIEW_CONVERT_EXPR, |
1927 | TREE_TYPE (condition), arg1: condition); |
1928 | EXPR_LOCATION_WRAPPER_P (condition) = 1; |
1929 | } |
1930 | |
1931 | if (condition == error_mark_node || type_dependent_expression_p (condition)) |
1932 | return condition; |
1933 | |
1934 | return condition_conversion (condition); |
1935 | } |
1936 | |
1937 | void |
1938 | maybe_update_postconditions (tree fco) |
1939 | { |
1940 | /* Update any postconditions and the postcondition checking function |
1941 | as needed. If there are postconditions, we'll use those to rewrite |
1942 | return statements to check postconditions. */ |
1943 | if (has_active_postconditions (d: fco)) |
1944 | { |
1945 | rebuild_postconditions (decl: fco); |
1946 | tree post = build_postcondition_function (d: fco); |
1947 | set_postcondition_function (d: fco, post); |
1948 | } |
1949 | } |
1950 | |
1951 | /* Called on attribute lists that must not contain contracts. If any |
1952 | contracts are present, issue an error diagnostic and return true. */ |
1953 | |
1954 | bool |
1955 | diagnose_misapplied_contracts (tree attributes) |
1956 | { |
1957 | if (attributes == NULL_TREE) |
1958 | return false; |
1959 | |
1960 | tree contract_attr = find_contract (attrs: attributes); |
1961 | if (!contract_attr) |
1962 | return false; |
1963 | |
1964 | error_at (EXPR_LOCATION (CONTRACT_STATEMENT (contract_attr)), |
1965 | "contracts must appertain to a function type" ); |
1966 | |
1967 | /* Invalidate the contract so we don't treat it as valid later on. */ |
1968 | invalidate_contract (TREE_VALUE (TREE_VALUE (contract_attr))); |
1969 | |
1970 | return true; |
1971 | } |
1972 | |
1973 | /* Build and return an argument list containing all the parameters of the |
1974 | (presumably guarded) FUNCTION_DECL FN. This can be used to forward all of |
1975 | FN's arguments to a function taking the same list of arguments -- namely |
1976 | the unchecked form of FN. |
1977 | |
1978 | We use CALL_FROM_THUNK_P instead of forward_parm for forwarding |
1979 | semantics. */ |
1980 | |
1981 | static vec<tree, va_gc> * |
1982 | build_arg_list (tree fn) |
1983 | { |
1984 | vec<tree, va_gc> *args = make_tree_vector (); |
1985 | for (tree t = DECL_ARGUMENTS (fn); t; t = DECL_CHAIN (t)) |
1986 | vec_safe_push (v&: args, obj: t); |
1987 | return args; |
1988 | } |
1989 | |
1990 | void |
1991 | start_function_contracts (tree decl1) |
1992 | { |
1993 | if (!handle_contracts_p (decl1)) |
1994 | return; |
1995 | |
1996 | if (!outline_contracts_p (decl1)) |
1997 | { |
1998 | emit_preconditions (DECL_CONTRACTS (current_function_decl)); |
1999 | emit_postconditions_cleanup (DECL_CONTRACTS (current_function_decl)); |
2000 | return; |
2001 | } |
2002 | |
2003 | /* Contracts may have just been added without a chance to parse them, though |
2004 | we still need the PRE_FN available to generate a call to it. */ |
2005 | if (!DECL_PRE_FN (decl1)) |
2006 | build_contract_function_decls (d: decl1); |
2007 | |
2008 | /* If we're starting a guarded function with valid contracts, we need to |
2009 | insert a call to the pre function. */ |
2010 | if (DECL_PRE_FN (decl1) |
2011 | && DECL_PRE_FN (decl1) != error_mark_node) |
2012 | { |
2013 | releasing_vec args = build_arg_list (fn: decl1); |
2014 | tree call = build_call_a (DECL_PRE_FN (decl1), |
2015 | args->length (), |
2016 | args->address ()); |
2017 | CALL_FROM_THUNK_P (call) = true; |
2018 | finish_expr_stmt (call); |
2019 | } |
2020 | } |
2021 | |
2022 | /* Finish up the pre & post function definitions for a guarded FNDECL, |
2023 | and compile those functions all the way to assembler language output. */ |
2024 | |
2025 | void |
2026 | finish_function_contracts (tree fndecl) |
2027 | { |
2028 | if (!handle_contracts_p (decl1: fndecl) |
2029 | || !outline_contracts_p (decl1: fndecl)) |
2030 | return; |
2031 | |
2032 | for (tree ca = DECL_CONTRACTS (fndecl); ca; ca = CONTRACT_CHAIN (ca)) |
2033 | { |
2034 | tree contract = CONTRACT_STATEMENT (ca); |
2035 | if (!CONTRACT_CONDITION (contract) |
2036 | || CONTRACT_CONDITION_DEFERRED_P (contract) |
2037 | || CONTRACT_CONDITION (contract) == error_mark_node) |
2038 | return; |
2039 | } |
2040 | |
2041 | int flags = SF_DEFAULT | SF_PRE_PARSED; |
2042 | |
2043 | /* If either the pre or post functions are bad, don't bother emitting |
2044 | any contracts. The program is already ill-formed. */ |
2045 | tree pre = DECL_PRE_FN (fndecl); |
2046 | tree post = DECL_POST_FN (fndecl); |
2047 | if (pre == error_mark_node || post == error_mark_node) |
2048 | return; |
2049 | |
2050 | if (pre && DECL_INITIAL (fndecl) != error_mark_node) |
2051 | { |
2052 | DECL_PENDING_INLINE_P (pre) = false; |
2053 | start_preparsed_function (pre, DECL_ATTRIBUTES (pre), flags); |
2054 | remap_and_emit_conditions (fn: fndecl, condfn: pre, code: PRECONDITION_STMT); |
2055 | tree finished_pre = finish_function (false); |
2056 | expand_or_defer_fn (finished_pre); |
2057 | } |
2058 | |
2059 | if (post && DECL_INITIAL (fndecl) != error_mark_node) |
2060 | { |
2061 | DECL_PENDING_INLINE_P (post) = false; |
2062 | start_preparsed_function (post, |
2063 | DECL_ATTRIBUTES (post), |
2064 | flags); |
2065 | remap_and_emit_conditions (fn: fndecl, condfn: post, code: POSTCONDITION_STMT); |
2066 | if (!VOID_TYPE_P (TREE_TYPE (TREE_TYPE (post)))) |
2067 | finish_return_stmt (get_postcondition_result_parameter (d: fndecl)); |
2068 | |
2069 | tree finished_post = finish_function (false); |
2070 | expand_or_defer_fn (finished_post); |
2071 | } |
2072 | } |
2073 | |
2074 | /* Rewrite the expression of a returned expression so that it invokes the |
2075 | postcondition function as needed. */ |
2076 | |
2077 | tree |
2078 | apply_postcondition_to_return (tree expr) |
2079 | { |
2080 | tree fn = current_function_decl; |
2081 | tree post = DECL_POST_FN (fn); |
2082 | if (!post) |
2083 | return NULL_TREE; |
2084 | |
2085 | /* If FN returns in memory, POST has a void return type and we call it when |
2086 | EXPR is DECL_RESULT (fn). If FN returns a scalar, POST has the same |
2087 | return type and we call it when EXPR is the value being returned. */ |
2088 | if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (post))) |
2089 | != (expr == DECL_RESULT (fn))) |
2090 | return NULL_TREE; |
2091 | |
2092 | releasing_vec args = build_arg_list (fn); |
2093 | if (get_postcondition_result_parameter (d: fn)) |
2094 | vec_safe_push (r&: args, t: expr); |
2095 | tree call = build_call_a (post, |
2096 | args->length (), |
2097 | args->address ()); |
2098 | CALL_FROM_THUNK_P (call) = true; |
2099 | |
2100 | return call; |
2101 | } |
2102 | |
2103 | /* A subroutine of duplicate_decls. Diagnose issues in the redeclaration of |
2104 | guarded functions. */ |
2105 | |
2106 | void |
2107 | duplicate_contracts (tree newdecl, tree olddecl) |
2108 | { |
2109 | if (TREE_CODE (newdecl) == TEMPLATE_DECL) |
2110 | newdecl = DECL_TEMPLATE_RESULT (newdecl); |
2111 | if (TREE_CODE (olddecl) == TEMPLATE_DECL) |
2112 | olddecl = DECL_TEMPLATE_RESULT (olddecl); |
2113 | |
2114 | /* Compare contracts to see if they match. */ |
2115 | tree old_contracts = DECL_CONTRACTS (olddecl); |
2116 | tree new_contracts = DECL_CONTRACTS (newdecl); |
2117 | |
2118 | if (!old_contracts && !new_contracts) |
2119 | return; |
2120 | |
2121 | location_t old_loc = DECL_SOURCE_LOCATION (olddecl); |
2122 | location_t new_loc = DECL_SOURCE_LOCATION (newdecl); |
2123 | |
2124 | /* If both declarations specify contracts, ensure they match. |
2125 | |
2126 | TODO: This handles a potential error a little oddly. Consider: |
2127 | |
2128 | struct B { |
2129 | virtual void f(int n) [[pre: n == 0]]; |
2130 | }; |
2131 | struct D : B { |
2132 | void f(int n) override; // inherits contracts |
2133 | }; |
2134 | void D::f(int n) [[pre: n == 0]] // OK |
2135 | { } |
2136 | |
2137 | It's okay because we're explicitly restating the inherited contract. |
2138 | Changing the precondition on the definition D::f causes match_contracts |
2139 | to complain about the mismatch. |
2140 | |
2141 | This would previously have been diagnosed as adding contracts to an |
2142 | override, but this seems like it should be well-formed. */ |
2143 | if (old_contracts && new_contracts) |
2144 | { |
2145 | if (!match_contract_conditions (oldloc: old_loc, old_attrs: old_contracts, |
2146 | newloc: new_loc, new_attrs: new_contracts, |
2147 | ctx: cmc_declaration)) |
2148 | return; |
2149 | if (DECL_UNIQUE_FRIEND_P (newdecl)) |
2150 | /* Newdecl's contracts are still DEFERRED_PARSE, and we're about to |
2151 | collapse it into olddecl, so stash away olddecl's contracts for |
2152 | later comparison. */ |
2153 | defer_guarded_contract_match (fndecl: olddecl, fn: olddecl, contracts: old_contracts); |
2154 | } |
2155 | |
2156 | /* Handle cases where contracts are omitted in one or the other |
2157 | declaration. */ |
2158 | if (old_contracts) |
2159 | { |
2160 | /* Contracts have been previously specified by are no omitted. The |
2161 | new declaration inherits the existing contracts. */ |
2162 | if (!new_contracts) |
2163 | copy_contract_attributes (olddecl: newdecl, newdecl: olddecl); |
2164 | |
2165 | /* In all cases, remove existing contracts from OLDDECL to prevent the |
2166 | attribute merging function from adding excess contracts. */ |
2167 | remove_contract_attributes (fndecl: olddecl); |
2168 | } |
2169 | else if (!old_contracts) |
2170 | { |
2171 | /* We are adding contracts to a declaration. */ |
2172 | if (new_contracts) |
2173 | { |
2174 | /* We can't add to a previously defined function. */ |
2175 | if (DECL_INITIAL (olddecl)) |
2176 | { |
2177 | auto_diagnostic_group d; |
2178 | error_at (new_loc, "cannot add contracts after definition" ); |
2179 | inform (DECL_SOURCE_LOCATION (olddecl), "original definition here" ); |
2180 | return; |
2181 | } |
2182 | |
2183 | /* We can't add to an unguarded virtual function declaration. */ |
2184 | if (DECL_VIRTUAL_P (olddecl) && new_contracts) |
2185 | { |
2186 | auto_diagnostic_group d; |
2187 | error_at (new_loc, "cannot add contracts to a virtual function" ); |
2188 | inform (DECL_SOURCE_LOCATION (olddecl), "original declaration here" ); |
2189 | return; |
2190 | } |
2191 | |
2192 | /* Depending on the "first declaration" rule, we may not be able |
2193 | to add contracts to a function after the fact. */ |
2194 | if (flag_contract_strict_declarations) |
2195 | { |
2196 | warning_at (new_loc, |
2197 | OPT_fcontract_strict_declarations_, |
2198 | "declaration adds contracts to %q#D" , |
2199 | olddecl); |
2200 | return; |
2201 | } |
2202 | |
2203 | /* Copy the contracts from NEWDECL to OLDDECL. We shouldn't need to |
2204 | remap them because NEWDECL's parameters will replace those of |
2205 | OLDDECL. Remove the contracts from NEWDECL so they aren't |
2206 | cloned when merging. */ |
2207 | copy_contract_attributes (olddecl, newdecl); |
2208 | remove_contract_attributes (fndecl: newdecl); |
2209 | } |
2210 | } |
2211 | } |
2212 | |
2213 | /* Replace the any contract attributes on OVERRIDER with a copy where any |
2214 | references to BASEFN's PARM_DECLs have been rewritten to the corresponding |
2215 | PARM_DECL in OVERRIDER. */ |
2216 | |
2217 | void |
2218 | inherit_base_contracts (tree overrider, tree basefn) |
2219 | { |
2220 | tree last = NULL_TREE, contract_attrs = NULL_TREE; |
2221 | for (tree a = DECL_CONTRACTS (basefn); |
2222 | a != NULL_TREE; |
2223 | a = CONTRACT_CHAIN (a)) |
2224 | { |
2225 | tree c = copy_node (a); |
2226 | TREE_VALUE (c) = build_tree_list (TREE_PURPOSE (TREE_VALUE (c)), |
2227 | copy_node (CONTRACT_STATEMENT (c))); |
2228 | |
2229 | tree src = basefn; |
2230 | tree dst = overrider; |
2231 | remap_contract (src, dst, CONTRACT_STATEMENT (c), /*duplicate_p=*/true); |
2232 | |
2233 | CONTRACT_COMMENT (CONTRACT_STATEMENT (c)) = |
2234 | copy_node (CONTRACT_COMMENT (CONTRACT_STATEMENT (c))); |
2235 | |
2236 | chainon (last, c); |
2237 | last = c; |
2238 | if (!contract_attrs) |
2239 | contract_attrs = c; |
2240 | } |
2241 | |
2242 | set_decl_contracts (decl: overrider, contract_attrs); |
2243 | } |
2244 | |
2245 | #include "gt-cp-contracts.h" |
2246 | |