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