Compare commits

...

48 Commits

Author SHA1 Message Date
Jason Merrill
df3709d167 tweak get_source_text_between comment 2022-11-04 13:05:06 -04:00
Jason Merrill
a01c9dfe60 test predicate output 2022-11-04 10:08:26 -04:00
Jason Merrill
7817afdb87 update get_source_text_between 2022-11-04 10:08:26 -04:00
Jason Merrill
43ff4caa5c remove remove_contracts_from_specialization 2022-11-03 21:16:50 -04:00
Jason Merrill
061f0031b2 doc: add contracts flags 2022-11-03 18:26:29 -04:00
Jason Merrill
eaee89c99c comment tweaks 2022-11-03 17:33:27 -04:00
Jason Merrill
7dbd1db4df input: rename get_source to get_source_text_between
Let's use a more informative name.

gcc/ChangeLog:

	* input.h:
	* input.cc (get_source):
	Rename to get_source_text_between.

gcc/cp/ChangeLog:

	* contracts.cc (build_comment): Adjust.
2022-11-03 15:44:25 -04:00
Jason Merrill
e378b7f825 c++: friend contracts are in complete-class context
Comparing friend contracts to a previous declaration was awkward
because at the point of duplicate_decls we haven't parsed the new ones yet,
and we're about to throw away one of the decls.  But conveniently, there's
already defer_guarded_contract_match to handle this.

This reverts commit 9c0d8bfebc32d5e2c35ed61753440fb175a87dcf.

gcc/cp/ChangeLog:

	* contracts.cc (check_for_mismatched_contracts):
	Only check new_contract for deferred.
	(match_deferred_contracts): Set processing_template_decl.
	(duplicate_contracts): Call defer_guarded_contract_match
	for friend decl.  Handle templates.
	* decl.cc (duplicate_decls): Use it for templates.
	* parser.h (struct cp_parser): Remove declaring_friend_p.
	* parser.cc (cp_parser_new): Don't clear it.
	(cp_parser_direct_declarator): Don't set it.
	(cp_parser_contract_attribute_spec): Don't check it.
	* contracts.h (match_contract_conditions): Remove.

gcc/testsuite/ChangeLog:

	* g++.dg/contracts/contracts-friend1.C: Revert.
	* g++.dg/contracts/contracts-nested-class1.C: Revert.
	* g++.dg/contracts/contracts-redecl7.C: Revert.
	* g++.dg/contracts/contracts-redecl8.C: Revert.
2022-11-02 16:23:20 -04:00
Jason Merrill
a05f106baf c++: fix assume test 2022-11-02 16:23:16 -04:00
Jason Merrill
4333447720 c++: more tidying
Move more macros to contracts.h, remove unnecessary extern decls, tweak
comments, remove DECL_ORIGINAL_FN.
2022-11-02 11:06:25 -04:00
Jason Merrill
0c073c813d c++: small refactor
Reducing the number of places that check DECL_{CONS,DES}TRUCTOR_P to decide
whether to outline the contracts.
2022-11-02 11:04:57 -04:00
Jason Merrill
4f428e8b3f libstdc++: reorder contract_violation
Moving the int after the pointers makes the object one word smaller.
2022-11-02 11:04:00 -04:00
Jason Merrill
bfcb6e7373 Merge remote-tracking branch 'origin/master' into devel/c++-contracts 2022-10-31 22:18:58 -04:00
Jason Merrill
2031dff9d9 c++: more tidying
gcc/cp/ChangeLog:

	* contracts.h: Move lots of decls from...
	* cp-tree.h: ...here.
	* contracts.cc (cp_contract_assertion_p): Move from parser.cc.
	(inherit_base_contracts): Move from search.cc.
	* decl.cc (grokdeclarator): Don't use function_declarator_p.
	* decl2.cc (cp_tree_defined_p_r)
	(cp_tree_defined_p): Remove.
	* module.cc (trees_out::fn_parms_init): Tweak comment.
	* parser.cc (cp_contract_assertion_p): Moved.
	(find_innermost_function_declarator)
	(function_declarator_p): Revert.
	(cp_parser_function_definition_after_declarator): Remove dead assignment.
	(cp_parser_save_default_args): Tweak formatting.
	* search.cc (inherit_base_contracts): Moved.

gcc/ChangeLog:

	* input.cc (get_source): Tweak comments.
2022-10-31 22:13:08 -04:00
Jason Merrill
f5a360ff9b c++: don't add contracted fns to unparsed_funs.
They get their own late parse.

gcc/cp/ChangeLog:

	* parser.cc (cp_parser_member_declaration): Don't add
	fn with contracts to unparsed_funs_with_definitions.
2022-10-31 12:00:30 -04:00
Jason Merrill
5080aec5f3 c++: move duplicate_contracts to contracts.cc
gcc/cp/ChangeLog:

	* cp-tree.h (duplicate_contracts): Add prototype.
	* contracts.cc (duplicate_contracts): Move from...
	* decl.cc (duplicate_contracts): ...here.
	* parser.cc (cp_parser_contract_attribute_spec): Fix typo.
2022-10-28 16:36:10 -04:00
Jason Merrill
51c5fc25b3 c++: remove friend_attributes
Instead of the friend_attributes hack, we can apply friend attributes before
calling do_friend.

gcc/cp/ChangeLog:

	* decl.cc (friend_attributes): Remove.
	(duplicate_contracts): Adjust.
	(duplicate_decls): Adjust.
	(grokdeclarator): Call cplus_decl_attributes before do_friend.
2022-10-28 16:36:01 -04:00
Jason Merrill
6f047a4540 c++: build contract_violation object directly
The __on_contract_violation function was an awkward library dependency;
instead, let's build up a temporary contract_violation and call
handle_contract_violation directly.

gcc/cp/ChangeLog:

	* cp-tree.h (enum cp_tree_index): Remove
	CPTI_ON_CONTRACT_VIOLATION*, add CPTI_PSEUDO_CONTRACT_VIOLATION.
	(pseudo_contract_violation_type): New.
	(on_contract_violation_fn)
	(on_contract_violation_never_fn): Remove.
	* contracts.cc (get_pseudo_contract_violation_type): New.
	(build_contract_violation): New.
	(declare_handle_contract_violation): New.
 	(build_contract_handler_call): Use them.
	(build_contract_check): Call terminate here.
	(init_contract_processing): Remove.
	* decl.cc (cxx_init_decl_processing): Don't call it.

libstdc++-v3/ChangeLog:

	* src/experimental/contract.cc (__on_contract_violation): Remove.
	* include/experimental/contract: Remove its comment.

gcc/testsuite/ChangeLog:

	* g++.dg/contracts/contracts14.C
	* g++.dg/contracts/contracts15.C: Remove __on_contract_violation.
2022-10-28 16:36:00 -04:00
Jason Merrill
ce5faeb1f5 c++: pass dg-options to module link
The link command needs to see -fcontracts for it to automatically add
-lstdc++exp.

gcc/testsuite/ChangeLog:

	* g++.dg/modules/modules.exp: Pass dg-options to link command.
2022-10-28 16:36:00 -04:00
Jason Merrill
a7a4a4ea95 c++: move contract_violation to std::experimental
libstdc++-v3/ChangeLog:

	* include/experimental/contract: Move into std::experimental
	* src/experimental/contract.cc: Adjust.

gcc/testsuite/ChangeLog:

	* g++.dg/contracts/contracts14.C
	* g++.dg/contracts/contracts15.C
	* g++.dg/contracts/contracts16.C
	* g++.dg/contracts/contracts17.C: contract_violation is now in
	std::experimental.
2022-10-28 16:36:00 -04:00
Jason Merrill
3a3e900ba6 c++: comment tweaks 2022-10-28 16:35:42 -04:00
Jason Merrill
d7cb97b26d c++: share code between [[assert]] and contracts
gcc/cp/ChangeLog:

	* constexpr.cc (cxx_eval_assert): Factor out from...
	(cxx_eval_internal_function): ...here.
	(cxx_eval_constant_expression): Also use it for contracts.
	* contracts.cc (build_contract_check): Use build_assume_call.
2022-10-26 17:00:24 -04:00
Jason Merrill
0cda8a7e83 Merge branch 'master' into devel/c++-contracts 2022-10-25 13:50:28 -04:00
Jason Merrill
598a58d553 c++: minor tweaks
gcc/cp/ChangeLog:

	* cp-tree.h (struct saved_scope): Move non-bitfield
	out from between bitfields.
	(DECL_CDTOR_NEEDS_LABLED_EXIT_P): Remove.
	* contracts.cc: Add FIXMEs.
	* pt.cc (register_specialization): Revert reformatting.
2022-10-25 13:50:00 -04:00
Jason Merrill
2492743871 c++: move contracts support to libstdc++exp.a
To avoid ABI compatibility issues with contracts support in libstdc++.so, we
can move it to a separate .a, to be added automatically with -fcontracts.
In the future, other experimental symbols could also be added to
libstdc++exp.a.

gcc/cp/ChangeLog:

	* g++spec.cc (EXPERIMENTAL): New bitmask.
	(lang_specific_driver): Implement it.

libstdc++-v3/ChangeLog:

	* src/c++17/contract.cc: Moved to...
	* src/experimental/contract.cc: ...here.
	* acinclude.m4: Add src/experimental.
	* src/Makefile.am: Likewise.
	* src/Makefile.in: Regenerate.
	* config/abi/pre/gnu.ver: Remove contracts symbols.
	* src/c++17/Makefile.am: Remove contract.cc.
	* src/c++17/Makefile.in: Regenerate.
	* configure: Regenerate.
	* src/experimental/Makefile.am: New file.
	* src/experimental/Makefile.in: New file.

gcc/testsuite/ChangeLog:

	* lib/g++.exp: Add path for libstdc++exp.a.
2022-10-20 21:14:25 -04:00
Jason Merrill
de497a4f4b c++: move contracts init to contracts.cc
gcc/cp/ChangeLog:

	* cp-tree.h (init_contract_processing): Declare.
	* decl.cc (cxx_init_decl_processing): Call it.
	* contracts.cc (init_contract_processing): Move from...
	* except.cc (init_exception_processing): ...here.
2022-10-20 13:33:08 -04:00
Jason Merrill
b141fc304e c++: comments
gcc/cp/ChangeLog:

	* contracts.h (enum contract_continuation): Add comment.

libstdc++-v3/ChangeLog:

	* include/experimental/contract: Add comments.
2022-10-18 17:23:56 -04:00
Jason Merrill
fcce3a45f0 libstdc++: contracts uint_least32_t lineno
As specified in N4820.

libstdc++-v3/ChangeLog:

	* include/experimental/contract: line_number() returns
	uint_least32_t.

gcc/testsuite/ChangeLog:

	* g++.dg/contracts/contracts14.C: Cast line_number to int.
	* g++.dg/contracts/contracts15.C
	* g++.dg/contracts/contracts16.C: Likewise.
2022-10-18 13:21:13 -04:00
Jason Merrill
704294a049 libstdc++: contracts support is experimental
Since it got dropped from the standard, the header should move to
experimental/.

libstdc++-v3/ChangeLog:

	* include/std/contract: Moved to...
	* include/experimental/contract: ...here.
	* src/c++17/contract.cc
	* include/Makefile.am
	* include/Makefile.in: Adjust.

gcc/testsuite/ChangeLog:

	* g++.dg/contracts/contracts14.C
	* g++.dg/contracts/contracts15.C
	* g++.dg/contracts/contracts16.C
	* g++.dg/contracts/contracts17.C: Header now experimental/contract.
2022-10-18 12:51:04 -04:00
Jason Merrill
c436179eec c++: fix module attachment in contract tests
handle_contract_violation needs to be attached to the global module.

gcc/testsuite/ChangeLog:

	* g++.dg/modules/contracts-1_a.C
	* g++.dg/modules/contracts-2_a.C
	* g++.dg/modules/contracts-3_a.C
	* g++.dg/modules/contracts-4_a.C: Add extern "C++".
2022-10-17 17:08:56 -04:00
Jason Merrill
0a35364554 c++: fix option quoting for -Wformat
gcc/cp/ChangeLog:

	* contracts.cc (handle_OPT_fcontract_build_level_)
	(handle_OPT_fcontract_assumption_mode_)
	(handle_OPT_fcontract_continuation_mode_)
	(handle_OPT_fcontract_semantic_)
	(handle_OPT_fcontract_role_): Fix option quoting.
2022-10-17 16:34:09 -04:00
Jason Merrill
99e1cabe02 c++: use cleanup for cdtor postconditions
The contracts branch used the cdtor label for emitting postconditions, but
that's been removed on trunk, so let's use a CLEANUP_STMT instead.  And tell
set_cleanup_locs to leave its location alone.

gcc/cp/ChangeLog:

	* cp-tree.h (emit_postconditions): Rename...
	(emit_postconditions_cleanup): ...to this.
	* contracts.cc: Likewise.
	* decl.cc (maybe_return_this): Don't emit_postconditions.
	(begin_destructor_body): Do it here.
	* semantics.cc (finish_mem_initializers): And here.
	(set_cleanup_locs): Don't mess with POSTCONDITION_STMT.
2022-10-17 16:11:29 -04:00
Jason Merrill
61b1ec945d c++: disable constexpr assume contract special handling
This code was trying to avoid instantiating for an assumed contract, but the
instantiation in contracts-assume6.C was happening earlier.  Now it doesn't,
but the testcase still expects it.  So disable this code for now.

gcc/cp/ChangeLog:

	* constexpr.cc (cxx_eval_constant_expression): Don't try to avoid
	instantiation for CCS_ASSUME.
2022-10-17 15:58:03 -04:00
Jason Merrill
0199003d90 Merge branch 'master' into c++-contracts 2022-10-17 15:55:01 -04:00
Jason Merrill
493164dddd c++: contract condition function linkage
Since the .pre/post functions are implementation details, not ABI artifacts,
we should make them internal when possible, including putting them in the
same COMDAT group with a vague linkage guarded function.

gcc/cp/ChangeLog:

	* contracts.cc (build_contract_condition_function): Set linkage.
	* semantics.c (expand_or_defer_fn_1): Call finish_function_contracts
	here.
	* decl.c (finish_function): Not here.
	* decl2.c (comdat_linkage): Add pre/post fns to same comdat group.

gcc/testsuite/ChangeLog:

	* g++.dg/contracts/contracts-comdat1.C: New test.
2021-07-06 16:42:23 -04:00
Jason Merrill
a70e678aec libstdc++: contracts support changes
libstdc++-v3/ChangeLog:

	* src/c++17/contract.cc: Remove member functions.
	* config/abi/pre/gnu.ver: Remove their symbols, move to latest
	version.
	* include/std/contract: Make them inline.
	Change string_view data members to const char *.
	Use libstdc++ naming pattern.
2021-07-06 16:42:23 -04:00
Jason Merrill
d5c78dacbf c++: GC problem emitting contract functions
Here, the copied list of contracts was getting garbage collected between
compilation of the .pre and .post functions.  Instead of copying the list,
let's remap the conditions when we are ready to emit them.

gcc/cp/ChangeLog:

	* contracts.cc (emit_contract_statement): Just add_stmt.
	(emit_contract_attr): Renamed from above.
	(emit_contract_conditions, emit_assertion): Adjust.
	(remap_and_emit_conditions): New.
	(finish_function_contracts): Use it.

gcc/testsuite/ChangeLog:

	* g++.dg/contracts/contracts-deduced1.C: Use aggressive GC.
2021-07-06 10:29:15 -04:00
Jason Merrill
9160ebeefb c++: use DECL_ABSTRACT_ORIGIN on contract fns
DECL_ABSTRACT_ORIGIN represents the relationship between the condition
functions and the function they are split from, so we can drop one of the
hash tables.

gcc/cp/ChangeLog:

	* cp-tree.h (DECL_ORIGINAL_FN): Use DECL_ABSTRACT_ORIGIN.
	* contracts.cc (get_contracts_original_fn): Remove.
	(set_contracts_original_fn): Remove.
	(build_contract_condition_function): Set DECL_ABSTRACT_ORIGIN.
	* module.cc (trees_in::fn_parms_init): Don't
	set_contracts_original_fn.
2021-07-06 10:29:15 -04:00
Jason Merrill
e7d3169ccd c++: violation handler helper is not constexpr
I can't think of a reason why these should be marked constexpr.

gcc/cp/ChangeLog:

	* except.c (init_exception_processing): Don't set
	DECL_DECLARED_CONSTEXPR_P on violation handler.
2021-07-06 10:29:14 -04:00
Jason Merrill
52587776b6 c++: contracts and aggregate parm/return types
The code for forwarding function parameters to the condition functions was
naively making copies of by-value parameters, which is undesirable.  Use
CALL_FROM_THUNK_P to avoid copying, and don't try to return a class from the
.post function.

gcc/cp/ChangeLog:

	* contracts.cc (build_contract_condition_function): Return
	void from .post if the function returns in memory.
	(build_arg_list): Don't forward_parm.
	(start_function_contracts): Use build_call_a, CALL_FROM_THUNK_P.
	(finish_function_contracts): Check for void return.
	(apply_postcondition_to_return): Handle scalar and aggregate
	return differently.
	* typeck.c (check_return_expr): Likewise.
	* cp-tree.h: Adjust.

gcc/testsuite/ChangeLog:

	* g++.dg/contracts/contracts-nocopy1.C: New test.
2021-07-06 10:29:14 -04:00
Jason Merrill
270966b506 c++: non-constant assume contract on constexpr fn
gcc/cp/ChangeLog:

	* constexpr.c (cxx_eval_constant_expression): An assume
	contract being non-constant doesn't make the whole evaluation
	non-constant.
	(potential_constant_expression_1): Only consider checked contracts.

gcc/testsuite/ChangeLog:

	* g++.dg/contracts/contracts-constexpr3.C: New test.
2021-07-06 10:29:14 -04:00
Jason Merrill
58aa1b5f57 c++: adjust violation call handling
We shouldn't need to touch TREE_NOTHROW in cp_genericize_r; the call should
go through set_flags_from_callee and then we'll see that the current
function might throw.  For this to work I needed to move the TREE_NOTHROW
setting in finish_function to after cp_genericize.

gcc/cp/ChangeLog:

	* contracts.cc (build_contract_handler_fn): Rename to...
	(build_contract_handler_call): ...this.  Use build_call_n.
	(build_contract_check): Use void_node.
	* cp-gimplify.c (cp_genericize_r): Don't mess with TREE_NOTHROW
	or current_function_returns_abnormally.
	* decl.c (finish_function): Set TREE_NOTHROW after genericize.
2021-07-06 10:29:14 -04:00
Jason Merrill
6e2be2d051 c++: contracts postcondition id redeclaration
We were crashing because the variable for the postcondition identifier was
referring to the second declaration after it was ggc_freed.  When we copy
the contracts from newdecl back to olddecl, we need to update the dummy
return variable's DECL_CONTEXT.

And the use of newdecl/olddecl here didn't match either the comment or the
use of those names in the call from duplicate_decls.

gcc/cp/ChangeLog:

	* contracts.cc (copy_contract_attributes): Swap parameter names.
	Call rebuild_postconditions.
2021-07-06 10:29:10 -04:00
Jason Merrill
7e03bf10f4 c++: trivial contracts adjustments
gcc/cp/ChangeLog:

	* contracts.h: Tweak comment.
	* contracts.cc: Tweak comment.
	* cp-gimplify.c (cp_genericize_r): Remove commented-out code.
	* decl.c (duplicate_decls): Fix formatting.
	* mangle.c (write_encoding): Update comment.
	* parser.c (cp_parser_conditional_expression): Expand comment.

libstdc++-v3/ChangeLog:

	* src/c++17/contract.cc (__on_contract_violation): Fix formatting.
2021-07-06 05:45:07 -04:00
Jason Merrill
d1ee78da00 c++: move more contracts code to contracts.cc
gcc/cp/ChangeLog:

	* cp-tree.h (rebuild_postconditions): Adjust prototype.
	(build_arg_list): Remove prototype.
	(maybe_update_postconditions, start_function_contracts)
	(finish_function_contracts)
	(apply_postcondition_to_return): Declare.
	* contracts.cc: Move lots from other files.
	(rebuild_postconditions): Remove type parm.
	(start_function_contracts): Factor from start_preparsed_function.
	(maybe_update_postconditions): Factor from
	apply_deduced_return_type.
	* decl.c: Move lots to contracts.cc.
	* pt.c (tsubst_copy_and_build): Remove FLOAT_EXPR handling.
	* semantics.c: Move lots to contracts.cc.
	* typeck.c (apply_postcondition_to_return): Move to contracts.cc.
	* config-lang.in: Add contracts.cc to gtfiles.

gcc/testsuite/ChangeLog:

	* contracts/backtrace_handler/assert_fail.cpp: Moved to...
	* g++.dg/contracts/backtrace_handler/assert_fail.cpp: ...here.
	* contracts/backtrace_handler/handle_contract_violation.cpp: Moved to...
	* g++.dg/contracts/backtrace_handler/handle_contract_violation.cpp: ...here.
	* g++.dg/cpp2a/contracts-access1.C: Moved to...
	* g++.dg/contracts/contracts-access1.C: ...here.
	* g++.dg/cpp2a/contracts-assume1.C: Moved to...
	* g++.dg/contracts/contracts-assume1.C: ...here.
	* g++.dg/cpp2a/contracts-assume2.C: Moved to...
	* g++.dg/contracts/contracts-assume2.C: ...here.
	* g++.dg/cpp2a/contracts-assume3.C: Moved to...
	* g++.dg/contracts/contracts-assume3.C: ...here.
	* g++.dg/cpp2a/contracts-assume4.C: Moved to...
	* g++.dg/contracts/contracts-assume4.C: ...here.
	* g++.dg/cpp2a/contracts-assume5.C: Moved to...
	* g++.dg/contracts/contracts-assume5.C: ...here.
	* g++.dg/cpp2a/contracts-assume6.C: Moved to...
	* g++.dg/contracts/contracts-assume6.C: ...here.
	* g++.dg/cpp2a/contracts-config1.C: Moved to...
	* g++.dg/contracts/contracts-config1.C: ...here.
	* g++.dg/cpp2a/contracts-constexpr1.C: Moved to...
	* g++.dg/contracts/contracts-constexpr1.C: ...here.
	* g++.dg/cpp2a/contracts-constexpr2.C: Moved to...
	* g++.dg/contracts/contracts-constexpr2.C: ...here.
	* g++.dg/cpp2a/contracts-conversion1.C: Moved to...
	* g++.dg/contracts/contracts-conversion1.C: ...here.
	* g++.dg/cpp2a/contracts-ctor-dtor1.C: Moved to...
	* g++.dg/contracts/contracts-ctor-dtor1.C: ...here.
	* g++.dg/cpp2a/contracts-ctor-dtor2.C: Moved to...
	* g++.dg/contracts/contracts-ctor-dtor2.C: ...here.
	* g++.dg/cpp2a/contracts-cv1.C: Moved to...
	* g++.dg/contracts/contracts-cv1.C: ...here.
	* g++.dg/cpp2a/contracts-deduced1.C: Moved to...
	* g++.dg/contracts/contracts-deduced1.C: ...here.
	* g++.dg/cpp2a/contracts-deduced2.C: Moved to...
	* g++.dg/contracts/contracts-deduced2.C: ...here.
	* g++.dg/cpp2a/contracts-friend1.C: Moved to...
	* g++.dg/contracts/contracts-friend1.C: ...here.
	* g++.dg/cpp2a/contracts-ft1.C: Moved to...
	* g++.dg/contracts/contracts-ft1.C: ...here.
	* g++.dg/cpp2a/contracts-ignore1.C: Moved to...
	* g++.dg/contracts/contracts-ignore1.C: ...here.
	* g++.dg/cpp2a/contracts-ignore2.C: Moved to...
	* g++.dg/contracts/contracts-ignore2.C: ...here.
	* g++.dg/cpp2a/contracts-large-return.C: Moved to...
	* g++.dg/contracts/contracts-large-return.C: ...here.
	* g++.dg/cpp2a/contracts-multiline1.C: Moved to...
	* g++.dg/contracts/contracts-multiline1.C: ...here.
	* g++.dg/cpp2a/contracts-multiple-inheritance1.C: Moved to...
	* g++.dg/contracts/contracts-multiple-inheritance1.C: ...here.
	* g++.dg/cpp2a/contracts-multiple-inheritance2.C: Moved to...
	* g++.dg/contracts/contracts-multiple-inheritance2.C: ...here.
	* g++.dg/cpp2a/contracts-nested-class1.C: Moved to...
	* g++.dg/contracts/contracts-nested-class1.C: ...here.
	* g++.dg/cpp2a/contracts-nested-class2.C: Moved to...
	* g++.dg/contracts/contracts-nested-class2.C: ...here.
	* g++.dg/cpp2a/contracts-override.C: Moved to...
	* g++.dg/contracts/contracts-override.C: ...here.
	* g++.dg/cpp2a/contracts-post1.C: Moved to...
	* g++.dg/contracts/contracts-post1.C: ...here.
	* g++.dg/cpp2a/contracts-post2.C: Moved to...
	* g++.dg/contracts/contracts-post2.C: ...here.
	* g++.dg/cpp2a/contracts-post3.C: Moved to...
	* g++.dg/contracts/contracts-post3.C: ...here.
	* g++.dg/cpp2a/contracts-post4.C: Moved to...
	* g++.dg/contracts/contracts-post4.C: ...here.
	* g++.dg/cpp2a/contracts-post5.C: Moved to...
	* g++.dg/contracts/contracts-post5.C: ...here.
	* g++.dg/cpp2a/contracts-post6.C: Moved to...
	* g++.dg/contracts/contracts-post6.C: ...here.
	* g++.dg/cpp2a/contracts-pre1.C: Moved to...
	* g++.dg/contracts/contracts-pre1.C: ...here.
	* g++.dg/cpp2a/contracts-pre10.C: Moved to...
	* g++.dg/contracts/contracts-pre10.C: ...here.
	* g++.dg/cpp2a/contracts-pre2.C: Moved to...
	* g++.dg/contracts/contracts-pre2.C: ...here.
	* g++.dg/cpp2a/contracts-pre2a1.C: Moved to...
	* g++.dg/contracts/contracts-pre2a1.C: ...here.
	* g++.dg/cpp2a/contracts-pre2a2.C: Moved to...
	* g++.dg/contracts/contracts-pre2a2.C: ...here.
	* g++.dg/cpp2a/contracts-pre3.C: Moved to...
	* g++.dg/contracts/contracts-pre3.C: ...here.
	* g++.dg/cpp2a/contracts-pre4.C: Moved to...
	* g++.dg/contracts/contracts-pre4.C: ...here.
	* g++.dg/cpp2a/contracts-pre5.C: Moved to...
	* g++.dg/contracts/contracts-pre5.C: ...here.
	* g++.dg/cpp2a/contracts-pre6.C: Moved to...
	* g++.dg/contracts/contracts-pre6.C: ...here.
	* g++.dg/cpp2a/contracts-pre7.C: Moved to...
	* g++.dg/contracts/contracts-pre7.C: ...here.
	* g++.dg/cpp2a/contracts-pre9.C: Moved to...
	* g++.dg/contracts/contracts-pre9.C: ...here.
	* g++.dg/cpp2a/contracts-redecl1.C: Moved to...
	* g++.dg/contracts/contracts-redecl1.C: ...here.
	* g++.dg/cpp2a/contracts-redecl2.C: Moved to...
	* g++.dg/contracts/contracts-redecl2.C: ...here.
	* g++.dg/cpp2a/contracts-redecl3.C: Moved to...
	* g++.dg/contracts/contracts-redecl3.C: ...here.
	* g++.dg/cpp2a/contracts-redecl4.C: Moved to...
	* g++.dg/contracts/contracts-redecl4.C: ...here.
	* g++.dg/cpp2a/contracts-redecl5.C: Moved to...
	* g++.dg/contracts/contracts-redecl5.C: ...here.
	* g++.dg/cpp2a/contracts-redecl6.C: Moved to...
	* g++.dg/contracts/contracts-redecl6.C: ...here.
	* g++.dg/cpp2a/contracts-redecl7.C: Moved to...
	* g++.dg/contracts/contracts-redecl7.C: ...here.
	* g++.dg/cpp2a/contracts-redecl8.C: Moved to...
	* g++.dg/contracts/contracts-redecl8.C: ...here.
	* g++.dg/cpp2a/contracts-tmpl-attr1.C: Moved to...
	* g++.dg/contracts/contracts-tmpl-attr1.C: ...here.
	* g++.dg/cpp2a/contracts-tmpl-spec1.C: Moved to...
	* g++.dg/contracts/contracts-tmpl-spec1.C: ...here.
	* g++.dg/cpp2a/contracts-tmpl-spec2.C: Moved to...
	* g++.dg/contracts/contracts-tmpl-spec2.C: ...here.
	* g++.dg/cpp2a/contracts-tmpl-spec3.C: Moved to...
	* g++.dg/contracts/contracts-tmpl-spec3.C: ...here.
	* g++.dg/cpp2a/contracts1.C: Moved to...
	* g++.dg/contracts/contracts1.C: ...here.
	* g++.dg/cpp2a/contracts10.C: Moved to...
	* g++.dg/contracts/contracts10.C: ...here.
	* g++.dg/cpp2a/contracts11.C: Moved to...
	* g++.dg/contracts/contracts11.C: ...here.
	* g++.dg/cpp2a/contracts12.C: Moved to...
	* g++.dg/contracts/contracts12.C: ...here.
	* g++.dg/cpp2a/contracts13.C: Moved to...
	* g++.dg/contracts/contracts13.C: ...here.
	* g++.dg/cpp2a/contracts14.C: Moved to...
	* g++.dg/contracts/contracts14.C: ...here.
	* g++.dg/cpp2a/contracts15.C: Moved to...
	* g++.dg/contracts/contracts15.C: ...here.
	* g++.dg/cpp2a/contracts16.C: Moved to...
	* g++.dg/contracts/contracts16.C: ...here.
	* g++.dg/cpp2a/contracts17.C: Moved to...
	* g++.dg/contracts/contracts17.C: ...here.
	* g++.dg/cpp2a/contracts18.C: Moved to...
	* g++.dg/contracts/contracts18.C: ...here.
	* g++.dg/cpp2a/contracts19.C: Moved to...
	* g++.dg/contracts/contracts19.C: ...here.
	* g++.dg/cpp2a/contracts2.C: Moved to...
	* g++.dg/contracts/contracts2.C: ...here.
	* g++.dg/cpp2a/contracts20.C: Moved to...
	* g++.dg/contracts/contracts20.C: ...here.
	* g++.dg/cpp2a/contracts22.C: Moved to...
	* g++.dg/contracts/contracts22.C: ...here.
	* g++.dg/cpp2a/contracts24.C: Moved to...
	* g++.dg/contracts/contracts24.C: ...here.
	* g++.dg/cpp2a/contracts25.C: Moved to...
	* g++.dg/contracts/contracts25.C: ...here.
	* g++.dg/cpp2a/contracts3.C: Moved to...
	* g++.dg/contracts/contracts3.C: ...here.
	* g++.dg/cpp2a/contracts35.C: Moved to...
	* g++.dg/contracts/contracts35.C: ...here.
	* g++.dg/cpp2a/contracts4.C: Moved to...
	* g++.dg/contracts/contracts4.C: ...here.
	* g++.dg/cpp2a/contracts5.C: Moved to...
	* g++.dg/contracts/contracts5.C: ...here.
	* g++.dg/cpp2a/contracts6.C: Moved to...
	* g++.dg/contracts/contracts6.C: ...here.
	* g++.dg/cpp2a/contracts7.C: Moved to...
	* g++.dg/contracts/contracts7.C: ...here.
	* g++.dg/cpp2a/contracts8.C: Moved to...
	* g++.dg/contracts/contracts8.C: ...here.
	* g++.dg/cpp2a/contracts9.C: Moved to...
	* g++.dg/contracts/contracts9.C: ...here.
	* contracts/except_preload_handler/assert_fail.cpp: Moved to...
	* g++.dg/contracts/except_preload_handler/assert_fail.cpp: ...here.
	* contracts/except_preload_handler/handle_contract_violation.cpp: Moved to...
	* g++.dg/contracts/except_preload_handler/handle_contract_violation.cpp: ...here.
	* contracts/noexcept_preload_handler/assert_fail.cpp: Moved to...
	* g++.dg/contracts/noexcept_preload_handler/assert_fail.cpp: ...here.
	* contracts/noexcept_preload_handler/handle_contract_violation.cpp: Moved to...
	* g++.dg/contracts/noexcept_preload_handler/handle_contract_violation.cpp: ...here.
	* contracts/preload_handler/assert_fail.cpp: Moved to...
	* g++.dg/contracts/preload_handler/assert_fail.cpp: ...here.
	* contracts/preload_handler/handle_contract_violation.cpp: Moved to...
	* g++.dg/contracts/preload_handler/handle_contract_violation.cpp: ...here.
	* contracts/preload_nocontinue_handler/assert_fail.cpp: Moved to...
	* g++.dg/contracts/preload_nocontinue_handler/assert_fail.cpp: ...here.
	* contracts/preload_nocontinue_handler/handle_contract_violation.cpp: Moved to...
	* g++.dg/contracts/preload_nocontinue_handler/handle_contract_violation.cpp: ...here.
	* contracts/preload_nocontinue_handler/nocontinue.cpp: Moved to...
	* g++.dg/contracts/preload_nocontinue_handler/nocontinue.cpp: ...here.
	* contracts/backtrace_handler/Makefile: Moved to...
	* g++.dg/contracts/backtrace_handler/Makefile: ...here.
	* contracts/backtrace_handler/README: Moved to...
	* g++.dg/contracts/backtrace_handler/README: ...here.
	* contracts/backtrace_handler/example_out.txt: Moved to...
	* g++.dg/contracts/backtrace_handler/example_out.txt: ...here.
	* contracts/backtrace_handler/example_pretty.txt: Moved to...
	* g++.dg/contracts/backtrace_handler/example_pretty.txt: ...here.
	* contracts/backtrace_handler/prettytrace.sh: Moved to...
	* g++.dg/contracts/backtrace_handler/prettytrace.sh: ...here.
	* contracts/except_preload_handler/Makefile: Moved to...
	* g++.dg/contracts/except_preload_handler/Makefile: ...here.
	* contracts/except_preload_handler/README: Moved to...
	* g++.dg/contracts/except_preload_handler/README: ...here.
	* contracts/noexcept_preload_handler/Makefile: Moved to...
	* g++.dg/contracts/noexcept_preload_handler/Makefile: ...here.
	* contracts/noexcept_preload_handler/README: Moved to...
	* g++.dg/contracts/noexcept_preload_handler/README: ...here.
	* contracts/preload_handler/Makefile: Moved to...
	* g++.dg/contracts/preload_handler/Makefile: ...here.
	* contracts/preload_handler/README: Moved to...
	* g++.dg/contracts/preload_handler/README: ...here.
	* contracts/preload_nocontinue_handler/Makefile: Moved to...
	* g++.dg/contracts/preload_nocontinue_handler/Makefile: ...here.
	* contracts/preload_nocontinue_handler/README: Moved to...
	* g++.dg/contracts/preload_nocontinue_handler/README: ...here.
2021-07-06 05:45:07 -04:00
Jeff Chapman II
fd571461f8 c++: Cleanup get_source; use obstack
gcc/
	* input.c (get_source): Simplify; use obstack instead of a fixed
	sized static buffer.

gcc/testsuite/
	* g++.dg/cpp2a/contracts-multiline1.C: New.
2021-07-06 05:45:06 -04:00
Jeff Chapman II
3920778fb8 c++: implement P1492 contracts
Implement the P1492 versions of contracts, along with extensions that
support the emulation of N4820 and other proposals. This implementation
assigns a concrete semantic (one of: ignore, assume, enforce, or
observe) to each contract attribute depending on its labels and
configuration options.

gcc/c-family/ChangeLog:

	* c-cppbuiltin.c (c_cpp_builtins): Add feature test macros
	for contracts features.
	* c.opt (contracts): Enable contracts.
	(contract-assumption-mode): Allow assumptions to be assumed.
	(contract-strict-declarations): Require contracts on the first
	declaration. Also, allow out-of-class redeclaration of members.
	(contract-mode): Enable or disable contracts.
	(contract-continuation-mode): Allow contracts to resume after
	invoking the violation handler.
	(contract-role): Specifies semantics for a role label applied to
	contracts.
	(contract-semantic): Specifies the concrete semantic for a level.

gcc/cp/ChangeLog:

	* Make-lang.in: Add contracts.cc to the build.
	* constexpr.c (cxx_eval_constant_expression): Support constexpr
	checking of contracts.
	(potential_constant_expression_1): Add cases for contracts.
	* contracts.h: New.
	* contracts.cc: New. Implements much of the semantic support for
	contracts.
	* cp-gimplify.c (cp_genericize_r): Emit code for contract checking
	statements.
	* cp-objcp-common.c (cp_tree_size): Add contract nodes.
	(cp_handle_option): Handle contracts-related options.
	* cp-tree.def: Add new statement nodes for contract attributes.
	* cp-tree.h: Add a bunch of new macros and functions for working
	with contracts.
	(saved_scope): Add a processing to specify that the parser is working
	with contract attributes outside of a function's parameter list.
	(comparing_override_contracts): Add a global to alter the behavior
	of cp_tree_equal when comparing contracts of overrides.
	* decl.c (diagnose_misapplied_contracts): New.
	(friend_attributes): Implicitly pass attributes from grokdeclarator to
	duplicate_decls.
	(duplicate_contracts): New.
	(duplicate_decls): Compare contracts when duplicating. Diagnose various
	mismatching errors.
	(start_decl): Support redeclarations of members outside of classes.
	(grokfndecl): Rebuild postconditions after the return type is likely
	known (except when it is deduced).
	(grokdeclarator): Diagnose the application of contracts outside of
	functions. Also, make sure to attach contracts to the function
	declaration, not its type. Suppress warnings about contracts on friends
	since they're potentially used for declaration matching.
	(start_preparsed_function): Build contract checking functions at the
	start of a function definition.
	(finish_constructor_body): Emit postconditions directly into the
	body of a constructor.
	(begin_destructor_body, finish_destructor_body): Emit
	pre/postconditions directly into the bodyof a destructor.
	(finish_function_contracts): Synthesize functions to check
	preconditions and postconditions.
	* decl2.c (cp_check_const_attributes): Skip contracts.
	(cp_tree_defined_r, cp_tree_defined): New. Ensure that functions and
	variables used in pre/post are defined.
	* error.c (dump_type): Prevent crashes when the type is unnamed.
	* except.c (init_exception_processing): Build contract violation
	handlers.
	* mangle.c (write_encoding): Add .pre and .post to checking
	functions.
	* module.cc (fn_parms_init): Serialize contracts and pre/post
	checking functions.
	(check_mergeable_decl): Reject functions with invalid contracts.
	* parser.c (make_call_declarator): Pass standard attributes to the
	call declarator.
	(cp_parser_constant_expression): Cleanup redundant code by calling
	cp_parser_conditional_expression.
	(unparsed_contracts): New queue for late-parsed contracts.
	(push_unparsed_function_queues): Update initializer.
	(cp_parser_new): Initialize new declaring_friend_p flag.
	(cp_parser_statement): Check for invalid contract attributes and handle
	assertions.
	(cp_parser_decl_specifier_seq, cp_parser_class_head): Check for
	misapplied contracts.
	(cp_parser_direct_declarator) Note when we're parsing a friend
	declaration. This is needed to prevent deferred parsing of contract
	attributes.
	(cp_parser_class_specifier_1): Handle late-parsed contracts.
	(cp_parser_std_attribute_spec): Parse contracts.
	(cp_parser_member_declaration, cp_parser_late_parsing_for_member): Push
	deferred contracts.
	(cp_contract_assertion_p, function_declarator_p,
	find_innermost_function_declarator, cp_parser_late_contract_condition,
	cp_parser_skip_up_to_closing_square_bracket, contract_attribute_p,
	cp_parser_conditional_expression, cp_parser_contract_role,
	cp_parser_contract_mode_opt, find_error, contains_error_p,
	cp_parser_contract_attribute_spec): New. Support the parsing contracs.
	* parser.h (cp_unparsed_functions_entry): Add queue for contracts.
	(cp_parser): Add flag for processing friend declarations.
	* pt.c (register_parameter_specializations): Make non-static.
	(register_local_identity): New. Used when rebuilding postconditions.
	(remove_contracts_from_specialization): New. Ensure that
	specializations don't inherit contracts from their more general
	template.
	(tsubst_contract, tsubst_contract_attribute,
	tsubst_contract_attributes): New.
	(tsubst_copy): Allow parameters outside of parameter lists.
	(tsubst_expr): Substitute through assertions.
	(tsubst_copy_and_build): Re-enable handling FLOAT_EXPRs since
	they still occur in the contract test suite.
	(regenerate_decl_from_template): Instantiate contract attributes
	as needed.
	* search.c (inherit_base_contracts): New. Copy base function
	contracts onto an override.
	(check_final_overrider): Check that contracts match by pushing a
	deferred check.
	* semantics.c (decl_pre_fn, decl_post_fn, decl_original_fn,
	get_precondition_function, get_postcondition_function,
	set_precondition_function, set_postcondition_function,
	set_contract_functions, get_contracts_original_fn,
	set_contracts_original_fn): New. Manage associations between guarded
	functions and their pre/post checking functions.
	(build_arg_list, copy_fn_decl, build_contract_condition_function,
	build_contract_condition_function, has_active_contract_condition,
	has_active_preconditions, has_active_postconditions,
	build_precondition_function, build_postcondition_function,
	build_contract_function_decls): New. Create pre/post checking functions
	for a guarded function.
	(start_postcondition_statement, finish_postcondition_statement): New.
	Build postconditions.
	(get_contract_level_name, get_contract_role_name,
	build_contract_handler_fn, contract_active_p, contract_any_active_p,
	build_contract_check, emit_contract_statement,
	emit_contract_conditions, emit_assertion, emit_preconditions,
	emit_postconditions, finish_contract_condition): New. Emit checking
	for individual contract	attributes.
	(finish_return_stmt): Simplify checks. Emit postconditions directly.
	(finish_non_static_data_member): Add checks for contracts referring
	to members in constructors and destructors.
	(process_outer_var_ref): Allow parameter usage in contract attributes.
	(finish_id_expression_1): Similarly.
	(apply_deduced_return_type): Rebuild postconditions once the return
	type has been deduced.
	* tree.c (comparing_override_contracts, get_innermost_component,
	is_this_expression, comparing_this_references,
	equivalent_member_references): New. Define expression equivalence for
	contract conditions that refer to the same members, possibly through
	different paths. Needed for contract matching with overrides.
	(cp_tree_equal): Compare expressions differently when comparing
	override and base function contracts.
	(std_attribute_table): Add entries for pre and post.
	(handle_contract_attribute): New.
	* typeck.c (apply_postcondition_to_return): New. Wrap a return a call
	to the postcondition.
	(check_return_expr): Use the function above.

gcc/ChangeLog:

	* input.c (get_source): New. Return the source text for a
	particular location in a file.
	* input.h (get_source): Declare.

libstdc++-v3/ChangeLog:

	* config/abi/pre/gnu.ver: List symbols exported for contracts.
	* include/Makefile.am: Add the contract header to the build.
	* include/std/contract: New standard header.
	* src/c++17/Makefile.am: Add contracts.cc.
	* src/c++17/contract.cc: New implementation file for contracts.
	* include/Makefile.in: Regenerate.
	* src/c++17/Makefile.in: Regenerate.

gcc/testsuite/ChangeLog:

	* contracts/backtrace_handler/assert_fail.cpp: New test.
	* contracts/backtrace_handler/handle_contract_violation.cpp: New test.
	* contracts/except_preload_handler/assert_fail.cpp: New test.
	* contracts/except_preload_handler/handle_contract_violation.cpp:
	New test.
	* contracts/noexcept_preload_handler/assert_fail.cpp: New test.
	* contracts/noexcept_preload_handler/handle_contract_violation.cpp:
	New test.
	* contracts/preload_handler/assert_fail.cpp: New test.
	* contracts/preload_handler/handle_contract_violation.cpp: New test.
	* contracts/preload_nocontinue_handler/assert_fail.cpp: New test.
	* contracts/preload_nocontinue_handler/handle_contract_violation.cpp:
	New test.
	* contracts/preload_nocontinue_handler/nocontinue.cpp: New test.
	* g++.dg/cpp2a/contracts-access1.C: New test.
	* g++.dg/cpp2a/contracts-assume1.C: New test.
	* g++.dg/cpp2a/contracts-assume2.C: New test.
	* g++.dg/cpp2a/contracts-assume3.C: New test.
	* g++.dg/cpp2a/contracts-assume4.C: New test.
	* g++.dg/cpp2a/contracts-assume5.C: New test.
	* g++.dg/cpp2a/contracts-assume6.C: New test.
	* g++.dg/cpp2a/contracts-config1.C: New test.
	* g++.dg/cpp2a/contracts-constexpr1.C: New test.
	* g++.dg/cpp2a/contracts-constexpr2.C: New test.
	* g++.dg/cpp2a/contracts-conversion1.C: New test.
	* g++.dg/cpp2a/contracts-ctor-dtor1.C: New test.
	* g++.dg/cpp2a/contracts-ctor-dtor2.C: New test.
	* g++.dg/cpp2a/contracts-cv1.C: New test.
	* g++.dg/cpp2a/contracts-deduced1.C: New test.
	* g++.dg/cpp2a/contracts-deduced2.C: New test.
	* g++.dg/cpp2a/contracts-friend1.C: New test.
	* g++.dg/cpp2a/contracts-ft1.C: New test.
	* g++.dg/cpp2a/contracts-ignore1.C: New test.
	* g++.dg/cpp2a/contracts-ignore2.C: New test.
	* g++.dg/cpp2a/contracts-large-return.C: New test.
	* g++.dg/cpp2a/contracts-multiple-inheritance1.C: New test.
	* g++.dg/cpp2a/contracts-multiple-inheritance2.C: New test.
	* g++.dg/cpp2a/contracts-nested-class1.C: New test.
	* g++.dg/cpp2a/contracts-nested-class2.C: New test.
	* g++.dg/cpp2a/contracts-override.C: New test.
	* g++.dg/cpp2a/contracts-post1.C: New test.
	* g++.dg/cpp2a/contracts-post2.C: New test.
	* g++.dg/cpp2a/contracts-post3.C: New test.
	* g++.dg/cpp2a/contracts-post4.C: New test.
	* g++.dg/cpp2a/contracts-post5.C: New test.
	* g++.dg/cpp2a/contracts-post6.C: New test.
	* g++.dg/cpp2a/contracts-pre1.C: New test.
	* g++.dg/cpp2a/contracts-pre10.C: New test.
	* g++.dg/cpp2a/contracts-pre2.C: New test.
	* g++.dg/cpp2a/contracts-pre2a1.C: New test.
	* g++.dg/cpp2a/contracts-pre2a2.C: New test.
	* g++.dg/cpp2a/contracts-pre3.C: New test.
	* g++.dg/cpp2a/contracts-pre4.C: New test.
	* g++.dg/cpp2a/contracts-pre5.C: New test.
	* g++.dg/cpp2a/contracts-pre6.C: New test.
	* g++.dg/cpp2a/contracts-pre7.C: New test.
	* g++.dg/cpp2a/contracts-pre9.C: New test.
	* g++.dg/cpp2a/contracts-redecl1.C: New test.
	* g++.dg/cpp2a/contracts-redecl2.C: New test.
	* g++.dg/cpp2a/contracts-redecl3.C: New test.
	* g++.dg/cpp2a/contracts-redecl4.C: New test.
	* g++.dg/cpp2a/contracts-redecl5.C: New test.
	* g++.dg/cpp2a/contracts-redecl6.C: New test.
	* g++.dg/cpp2a/contracts-redecl7.C: New test.
	* g++.dg/cpp2a/contracts-redecl8.C: New test.
	* g++.dg/cpp2a/contracts-tmpl-attr1.C: New test.
	* g++.dg/cpp2a/contracts-tmpl-spec1.C: New test.
	* g++.dg/cpp2a/contracts-tmpl-spec2.C: New test.
	* g++.dg/cpp2a/contracts-tmpl-spec3.C: New test.
	* g++.dg/cpp2a/contracts1.C: New test.
	* g++.dg/cpp2a/contracts10.C: New test.
	* g++.dg/cpp2a/contracts11.C: New test.
	* g++.dg/cpp2a/contracts12.C: New test.
	* g++.dg/cpp2a/contracts13.C: New test.
	* g++.dg/cpp2a/contracts14.C: New test.
	* g++.dg/cpp2a/contracts15.C: New test.
	* g++.dg/cpp2a/contracts16.C: New test.
	* g++.dg/cpp2a/contracts17.C: New test.
	* g++.dg/cpp2a/contracts18.C: New test.
	* g++.dg/cpp2a/contracts19.C: New test.
	* g++.dg/cpp2a/contracts2.C: New test.
	* g++.dg/cpp2a/contracts20.C: New test.
	* g++.dg/cpp2a/contracts22.C: New test.
	* g++.dg/cpp2a/contracts24.C: New test.
	* g++.dg/cpp2a/contracts25.C: New test.
	* g++.dg/cpp2a/contracts3.C: New test.
	* g++.dg/cpp2a/contracts35.C: New test.
	* g++.dg/cpp2a/contracts4.C: New test.
	* g++.dg/cpp2a/contracts5.C: New test.
	* g++.dg/cpp2a/contracts6.C: New test.
	* g++.dg/cpp2a/contracts7.C: New test.
	* g++.dg/cpp2a/contracts8.C: New test.
	* g++.dg/cpp2a/contracts9.C: New test.
	* g++.dg/modules/contracts-1_a.C: New test.
	* g++.dg/modules/contracts-1_b.C: New test.
	* g++.dg/modules/contracts-2_a.C: New test.
	* g++.dg/modules/contracts-2_b.C: New test.
	* g++.dg/modules/contracts-2_c.C: New test.
	* g++.dg/modules/contracts-3_a.C: New test.
	* g++.dg/modules/contracts-3_b.C: New test.
	* g++.dg/modules/contracts-4_a.C: New test.
	* g++.dg/modules/contracts-4_b.C: New test.
	* g++.dg/modules/contracts-4_c.C: New test.
	* g++.dg/modules/contracts-4_d.C: New test.
	* g++.dg/modules/contracts-tpl-friend-1_a.C: New test.
	* g++.dg/modules/contracts-tpl-friend-1_b.C: New test.
	* contracts/backtrace_handler/example_out.txt: New test.
	* contracts/backtrace_handler/example_pretty.txt: New test.
	* contracts/backtrace_handler/prettytrace.sh: New test.
	* contracts/backtrace_handler/Makefile: New test.
	* contracts/backtrace_handler/README: New test.
	* contracts/except_preload_handler/Makefile: New test.
	* contracts/except_preload_handler/README: New test.
	* contracts/noexcept_preload_handler/Makefile: New test.
	* contracts/noexcept_preload_handler/README: New test.
	* contracts/preload_handler/Makefile: New test.
	* contracts/preload_handler/README: New test.
	* contracts/preload_nocontinue_handler/Makefile: New test.
	* contracts/preload_nocontinue_handler/README: New test.

Co-authored-by: Andrew Sutton  <asutton@lock3software.com>
Co-authored-by: Andrew Marmaduke <amarmaduke@lock3software.com>
Co-authored-by: Michael Lopez <mlopez@lock3software.com>
2021-07-06 05:45:06 -04:00
Jason Merrill
80efc142d3 c++: trivial formatting cleanups
Split out from the C++ contracts patch.

gcc/cp/ChangeLog:

	* cp-tree.h: Fix whitespace.
	* parser.h: Fix whitespace.
	* decl.c: Fix whitespace.
	* parser.c: Fix whitespace.
	* pt.c: Fix whitespace.
2021-07-06 05:45:06 -04:00
159 changed files with 11200 additions and 72 deletions

View File

@@ -1087,6 +1087,12 @@ c_cpp_builtins (cpp_reader *pfile)
else
cpp_define (pfile, "__cpp_concepts=201507L");
}
if (flag_contracts)
{
cpp_define (pfile, "__cpp_contracts=201906L");
cpp_define (pfile, "__cpp_contracts_literal_semantics=201906L");
cpp_define (pfile, "__cpp_contracts_roles=201906L");
}
if (flag_modules)
/* The std-defined value is 201907L, but I don't think we can
claim victory yet. 201810 is the p1103 date. */

View File

@@ -1674,6 +1674,47 @@ fconstexpr-ops-limit=
C++ ObjC++ Joined RejectNegative Host_Wide_Int Var(constexpr_ops_limit) Init(33554432)
-fconstexpr-ops-limit=<number> Specify maximum number of constexpr operations during a single constexpr evaluation.
fcontracts
C++ ObjC++ Var(flag_contracts) Init(0)
Enable certain features present drafts of C++ Contracts.
Enum
Name(on_off) Type(int) UnknownError(argument %qs must be either %<on%> or %<off%>)
EnumValue
Enum(on_off) String(off) Value(0)
EnumValue
Enum(on_off) String(on) Value(1)
fcontract-assumption-mode=
C++ Joined
-fcontract-assumption-mode=[on|off] Enable or disable treating axiom level contracts as assumptions (default on).
fcontract-build-level=
C++ Joined RejectNegative
-fcontract-build-level=[off|default|audit] Specify max contract level to generate runtime checks for
fcontract-strict-declarations=
C++ Var(flag_contract_strict_declarations) Enum(on_off) Joined Init(0) RejectNegative
-fcontract-strict-declarations=[on|off] Enable or disable warnings on generalized redeclaration of functions with contracts (default off).
fcontract-mode=
C++ Var(flag_contract_mode) Enum(on_off) Joined Init(1) RejectNegative
-fcontract-mode=[on|off] Enable or disable all contract facilities (default on).
fcontract-continuation-mode=
C++ Joined
-fcontract-continuation-mode=[on|off] Enable or disable contract continuation mode (default off).
fcontract-role=
C++ Joined
-fcontract-role=<name>:<semantics> Specify the semantics for all levels in a role (default, review), or a custom contract role with given semantics (ex: opt:assume,assume,assume)
fcontract-semantic=
C++ Joined
-fcontract-semantic=<level>:<semantic> Specify the concrete semantics for level
fcoroutines
C++ LTO Var(flag_coroutines)
Enable C++ coroutines (experimental).

View File

@@ -89,7 +89,7 @@ CXX_AND_OBJCXX_OBJS = \
cp/call.o cp/class.o cp/constexpr.o cp/constraint.o \
cp/coroutines.o cp/cp-gimplify.o \
cp/cp-objcp-common.o cp/cp-ubsan.o \
cp/cvt.o cp/cxx-pretty-print.o \
cp/cvt.o cp/contracts.o cp/cxx-pretty-print.o \
cp/decl.o cp/decl2.o cp/dump.o \
cp/error.o cp/except.o cp/expr.o \
cp/friend.o cp/init.o \

View File

@@ -39,6 +39,7 @@ gtfiles="\
\$(srcdir)/c-family/c-common.cc \$(srcdir)/c-family/c-format.cc \
\$(srcdir)/c-family/c-cppbuiltin.cc \$(srcdir)/c-family/c-pragma.cc \
\$(srcdir)/cp/call.cc \$(srcdir)/cp/class.cc \$(srcdir)/cp/constexpr.cc \
\$(srcdir)/cp/contracts.cc \
\$(srcdir)/cp/constraint.cc \$(srcdir)/cp/coroutines.cc \
\$(srcdir)/cp/cp-gimplify.cc \
\$(srcdir)/cp/cp-lang.cc \$(srcdir)/cp/cp-objcp-common.cc \

View File

@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see
#include "stringpool.h"
#include "attribs.h"
#include "fold-const.h"
#include "intl.h"
static bool verify_constant (tree, bool, bool *, bool *);
#define VERIFY_CONSTANT(X) \
@@ -1936,6 +1937,61 @@ diagnose_failing_condition (tree bad, location_t cloc, bool show_expr_p,
inform (cloc, "%qE evaluates to false", bad);
}
/* Process an assert/assume of ORIG_ARG. If it's not supposed to be evaluated,
do it without changing the current evaluation state. If it evaluates to
false, complain and return false; otherwise, return true. */
static bool
cxx_eval_assert (const constexpr_ctx *ctx, tree arg, const char *msg,
location_t loc, bool evaluated,
bool *non_constant_p, bool *overflow_p)
{
if (*non_constant_p)
return true;
tree eval;
if (!evaluated)
{
if (!potential_rvalue_constant_expression (arg))
return true;
constexpr_ctx new_ctx = *ctx;
new_ctx.quiet = true;
bool new_non_constant_p = false, new_overflow_p = false;
/* Avoid modification of existing values. */
modifiable_tracker ms (new_ctx.global);
eval = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue,
&new_non_constant_p,
&new_overflow_p);
}
else
eval = cxx_eval_constant_expression (ctx, arg, vc_prvalue,
non_constant_p,
overflow_p);
if (!*non_constant_p && integer_zerop (eval))
{
if (!ctx->quiet)
{
/* See if we can find which clause was failing
(for logical AND). */
tree bad = find_failing_clause (ctx, arg);
/* If not, or its location is unusable, fall back to the
previous location. */
location_t cloc = cp_expr_loc_or_loc (bad, loc);
/* Report the error. */
auto_diagnostic_group d;
error_at (cloc, msg);
diagnose_failing_condition (bad, cloc, true, ctx);
return bad;
}
*non_constant_p = true;
return false;
}
return true;
}
/* Evaluate a call T to a GCC internal function when possible and return
the evaluated result or, under the control of CTX, give an error, set
NON_CONSTANT_P, and return the unevaluated call T otherwise. */
@@ -1956,41 +2012,11 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
return void_node;
case IFN_ASSUME:
if (potential_rvalue_constant_expression (CALL_EXPR_ARG (t, 0)))
{
constexpr_ctx new_ctx = *ctx;
new_ctx.quiet = true;
tree arg = CALL_EXPR_ARG (t, 0);
bool new_non_constant_p = false, new_overflow_p = false;
/* Avoid modification of existing values. */
modifiable_tracker ms (new_ctx.global);
arg = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue,
&new_non_constant_p,
&new_overflow_p);
if (!new_non_constant_p && !new_overflow_p && integer_zerop (arg))
{
if (!*non_constant_p && !ctx->quiet)
{
/* See if we can find which clause was failing
(for logical AND). */
tree bad = find_failing_clause (&new_ctx,
CALL_EXPR_ARG (t, 0));
/* If not, or its location is unusable, fall back to the
previous location. */
location_t cloc = cp_expr_loc_or_loc (bad, EXPR_LOCATION (t));
auto_diagnostic_group d;
/* Report the error. */
error_at (cloc,
"failed %<assume%> attribute assumption");
diagnose_failing_condition (bad, cloc, false, &new_ctx);
}
*non_constant_p = true;
return t;
}
}
if (!cxx_eval_assert (ctx, CALL_EXPR_ARG (t, 0),
G_("failed %<assume%> attribute assumption"),
EXPR_LOCATION (t), /*eval*/false,
non_constant_p, overflow_p))
return t;
return void_node;
case IFN_ADD_OVERFLOW:
@@ -7845,6 +7871,24 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
r = void_node;
break;
case ASSERTION_STMT:
case PRECONDITION_STMT:
case POSTCONDITION_STMT:
{
contract_semantic semantic = get_contract_semantic (t);
if (semantic == CCS_IGNORE)
break;
if (!cxx_eval_assert (ctx, CONTRACT_CONDITION (t),
G_("contract predicate is false in "
"constant expression"),
EXPR_LOCATION (t), checked_contract_p (semantic),
non_constant_p, overflow_p))
*non_constant_p = true;
r = void_node;
}
break;
case TEMPLATE_ID_EXPR:
{
/* We can evaluate template-id that refers to a concept only if
@@ -9819,6 +9863,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
return false;
}
case ASSERTION_STMT:
case PRECONDITION_STMT:
case POSTCONDITION_STMT:
if (!checked_contract_p (get_contract_semantic (t)))
return true;
return RECUR (CONTRACT_CONDITION (t), rval);
case LABEL_EXPR:
t = LABEL_EXPR_LABEL (t);
if (DECL_ARTIFICIAL (t) || cxx_dialect >= cxx23)

2240
gcc/cp/contracts.cc Normal file

File diff suppressed because it is too large Load Diff

305
gcc/cp/contracts.h Normal file
View File

@@ -0,0 +1,305 @@
/* Definitions for C++ contract levels. Implements functionality described in
the N4820 working draft version of contracts, P1290, P1332, and P1429.
Copyright (C) 2020-2022 Free Software Foundation, Inc.
Contributed by Jeff Chapman II (jchapman@lock3software.com)
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef GCC_CP_CONTRACT_H
#define GCC_CP_CONTRACT_H
/* Contract levels approximate the complexity of the expression. */
enum contract_level
{
CONTRACT_INVALID,
CONTRACT_DEFAULT,
CONTRACT_AUDIT,
CONTRACT_AXIOM
};
/* The concrete semantics determine the behavior of a contract. */
enum contract_semantic
{
CCS_INVALID,
CCS_IGNORE,
CCS_ASSUME,
CCS_NEVER,
CCS_MAYBE
};
/* True if the contract is unchecked. */
inline bool
unchecked_contract_p (contract_semantic cs)
{
return cs == CCS_IGNORE || cs == CCS_ASSUME;
}
/* True if the contract is checked. */
inline bool
checked_contract_p (contract_semantic cs)
{
return cs >= CCS_NEVER;
}
/* Must match std::contract_violation_continuation_mode in <contract>. */
enum contract_continuation
{
NEVER_CONTINUE,
MAYBE_CONTINUE
};
/* Assertion role info. */
struct contract_role
{
const char *name;
contract_semantic default_semantic;
contract_semantic audit_semantic;
contract_semantic axiom_semantic;
};
/* Information for configured contract semantics. */
struct contract_configuration
{
contract_level level;
contract_role* role;
};
/* A contract mode contains information used to derive the checking
and assumption semantics of a contract. This is either a dynamic
configuration, meaning it derives from the build mode, or it is
explicitly specified. */
struct contract_mode
{
contract_mode () : kind(cm_invalid) {}
contract_mode (contract_level level, contract_role *role = NULL)
: kind(cm_dynamic)
{
contract_configuration cc;
cc.level = level;
cc.role = role;
u.config = cc;
}
contract_mode (contract_semantic semantic) : kind(cm_explicit)
{
u.semantic = semantic;
}
contract_level get_level () const
{
gcc_assert (kind == cm_dynamic);
return u.config.level;
}
contract_role *get_role () const
{
gcc_assert (kind == cm_dynamic);
return u.config.role;
}
contract_semantic get_semantic () const
{
gcc_assert (kind == cm_explicit);
return u.semantic;
}
enum { cm_invalid, cm_dynamic, cm_explicit } kind;
union
{
contract_configuration config;
contract_semantic semantic;
} u;
};
extern contract_role *get_contract_role (const char *);
extern contract_role *add_contract_role (const char *,
contract_semantic,
contract_semantic,
contract_semantic,
bool = true);
extern void validate_contract_role (contract_role *);
extern void setup_default_contract_role (bool = true);
extern contract_semantic lookup_concrete_semantic (const char *);
/* Map a source level semantic or level name to its value, or invalid. */
extern contract_semantic map_contract_semantic (const char *);
extern contract_level map_contract_level (const char *);
/* Check if an attribute is a cxx contract attribute. */
extern bool cxx_contract_attribute_p (const_tree);
extern bool cp_contract_assertion_p (const_tree);
/* Returns the default role. */
inline contract_role *
get_default_contract_role ()
{
return get_contract_role ("default");
}
/* Handle various command line arguments related to semantic mapping. */
extern void handle_OPT_fcontract_build_level_ (const char *);
extern void handle_OPT_fcontract_assumption_mode_ (const char *);
extern void handle_OPT_fcontract_continuation_mode_ (const char *);
extern void handle_OPT_fcontract_role_ (const char *);
extern void handle_OPT_fcontract_semantic_ (const char *);
enum contract_matching_context
{
cmc_declaration,
cmc_override
};
/* True if NODE is any kind of contract. */
#define CONTRACT_P(NODE) \
(TREE_CODE (NODE) == ASSERTION_STMT \
|| TREE_CODE (NODE) == PRECONDITION_STMT \
|| TREE_CODE (NODE) == POSTCONDITION_STMT)
/* True if NODE is a contract condition. */
#define CONTRACT_CONDITION_P(NODE) \
(TREE_CODE (NODE) == PRECONDITION_STMT \
|| TREE_CODE (NODE) == POSTCONDITION_STMT)
/* True if NODE is a precondition. */
#define PRECONDITION_P(NODE) \
(TREE_CODE (NODE) == PRECONDITION_STMT)
/* True if NODE is a postcondition. */
#define POSTCONDITION_P(NODE) \
(TREE_CODE (NODE) == POSTCONDITION_STMT)
#define CONTRACT_CHECK(NODE) \
(TREE_CHECK3 (NODE, ASSERTION_STMT, PRECONDITION_STMT, POSTCONDITION_STMT))
/* True iff the FUNCTION_DECL NODE currently has any contracts. */
#define DECL_HAS_CONTRACTS_P(NODE) \
(DECL_CONTRACTS (NODE) != NULL_TREE)
/* For a FUNCTION_DECL of a guarded function, this points to a list of the pre
and post contracts of the first decl of NODE in original order. */
#define DECL_CONTRACTS(NODE) \
(find_contract (DECL_ATTRIBUTES (NODE)))
/* The next contract (if any) after this one in an attribute list. */
#define CONTRACT_CHAIN(NODE) \
(find_contract (TREE_CHAIN (NODE)))
/* The wrapper of the original source location of a list of contracts. */
#define CONTRACT_SOURCE_LOCATION_WRAPPER(NODE) \
(TREE_PURPOSE (TREE_VALUE (NODE)))
/* The original source location of a list of contracts. */
#define CONTRACT_SOURCE_LOCATION(NODE) \
(EXPR_LOCATION (CONTRACT_SOURCE_LOCATION_WRAPPER (NODE)))
/* The actual code _STMT for a contract attribute. */
#define CONTRACT_STATEMENT(NODE) \
(TREE_VALUE (TREE_VALUE (NODE)))
/* True if the contract semantic was specified literally. If true, the
contract mode is an identifier containing the semantic. Otherwise,
it is a TREE_LIST whose TREE_VALUE is the level and whose TREE_PURPOSE
is the role. */
#define CONTRACT_LITERAL_MODE_P(NODE) \
(CONTRACT_MODE (NODE) != NULL_TREE \
&& TREE_CODE (CONTRACT_MODE (NODE)) == IDENTIFIER_NODE)
/* The identifier denoting the literal semantic of the contract. */
#define CONTRACT_LITERAL_SEMANTIC(NODE) \
(TREE_OPERAND (NODE, 0))
/* The written "mode" of the contract. Either an IDENTIFIER with the
literal semantic or a TREE_LIST containing the level and role. */
#define CONTRACT_MODE(NODE) \
(TREE_OPERAND (CONTRACT_CHECK (NODE), 0))
/* The identifier denoting the build level of the contract. */
#define CONTRACT_LEVEL(NODE) \
(TREE_VALUE (CONTRACT_MODE (NODE)))
/* The identifier denoting the role of the contract */
#define CONTRACT_ROLE(NODE) \
(TREE_PURPOSE (CONTRACT_MODE (NODE)))
/* The parsed condition of the contract. */
#define CONTRACT_CONDITION(NODE) \
(TREE_OPERAND (CONTRACT_CHECK (NODE), 1))
/* True iff the condition of the contract NODE is not yet parsed. */
#define CONTRACT_CONDITION_DEFERRED_P(NODE) \
(TREE_CODE (CONTRACT_CONDITION (NODE)) == DEFERRED_PARSE)
/* The raw comment of the contract. */
#define CONTRACT_COMMENT(NODE) \
(TREE_OPERAND (CONTRACT_CHECK (NODE), 2))
/* The VAR_DECL of a postcondition result. For deferred contracts, this
is an IDENTIFIER. */
#define POSTCONDITION_IDENTIFIER(NODE) \
(TREE_OPERAND (POSTCONDITION_STMT_CHECK (NODE), 3))
/* For a FUNCTION_DECL of a guarded function, this holds the function decl
where pre contract checks are emitted. */
#define DECL_PRE_FN(NODE) \
(get_precondition_function ((NODE)))
/* For a FUNCTION_DECL of a guarded function, this holds the function decl
where post contract checks are emitted. */
#define DECL_POST_FN(NODE) \
(get_postcondition_function ((NODE)))
/* True iff the FUNCTION_DECL is the pre function for a guarded function. */
#define DECL_IS_PRE_FN_P(NODE) \
(DECL_ABSTRACT_ORIGIN (NODE) && DECL_PRE_FN (DECL_ABSTRACT_ORIGIN (NODE)) == NODE)
/* True iff the FUNCTION_DECL is the post function for a guarded function. */
#define DECL_IS_POST_FN_P(NODE) \
(DECL_ABSTRACT_ORIGIN (NODE) && DECL_POST_FN (DECL_ABSTRACT_ORIGIN (NODE)) == NODE)
extern void remove_contract_attributes (tree);
extern void copy_contract_attributes (tree, tree);
extern void remap_contracts (tree, tree, tree, bool);
extern void maybe_update_postconditions (tree);
extern void rebuild_postconditions (tree);
extern bool check_postcondition_result (tree, tree, location_t);
extern tree get_precondition_function (tree);
extern tree get_postcondition_function (tree);
extern void duplicate_contracts (tree, tree);
extern void match_deferred_contracts (tree);
extern void defer_guarded_contract_match (tree, tree, tree);
extern bool diagnose_misapplied_contracts (tree);
extern tree finish_contract_attribute (tree, tree);
extern tree invalidate_contract (tree);
extern void update_late_contract (tree, tree, tree);
extern tree splice_out_contracts (tree);
extern bool all_attributes_are_contracts_p (tree);
extern void inherit_base_contracts (tree, tree);
extern tree apply_postcondition_to_return (tree);
extern void start_function_contracts (tree);
extern void finish_function_contracts (tree);
extern void set_contract_functions (tree, tree, tree);
extern tree build_contract_check (tree);
extern void emit_assertion (tree);
#endif /* ! GCC_CP_CONTRACT_H */

View File

@@ -1427,6 +1427,23 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
wtd->bind_expr_stack.pop ();
break;
case ASSERTION_STMT:
case PRECONDITION_STMT:
case POSTCONDITION_STMT:
{
if (tree check = build_contract_check (stmt))
{
*stmt_p = check;
return cp_genericize_r (stmt_p, walk_subtrees, data);
}
/* If we didn't build a check, replace it with void_node so we don't
leak contracts into GENERIC. */
*stmt_p = void_node;
*walk_subtrees = 0;
}
break;
case USING_STMT:
{
tree block = NULL_TREE;

View File

@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see
#include "cp-objcp-common.h"
#include "dwarf2.h"
#include "stringpool.h"
#include "contracts.h"
/* Special routine to get the alias set for C++. */
@@ -86,6 +87,9 @@ cp_tree_size (enum tree_code code)
case CONSTRAINT_INFO: return sizeof (tree_constraint_info);
case USERDEF_LITERAL: return sizeof (tree_userdef_literal);
case TEMPLATE_DECL: return sizeof (tree_template_decl);
case ASSERTION_STMT: return sizeof (tree_exp);
case PRECONDITION_STMT: return sizeof (tree_exp);
case POSTCONDITION_STMT: return sizeof (tree_exp);
default:
switch (TREE_CODE_CLASS (code))
{
@@ -565,6 +569,10 @@ cp_common_init_ts (void)
MARK_TS_EXP (CO_YIELD_EXPR);
MARK_TS_EXP (CO_RETURN_EXPR);
MARK_TS_EXP (ASSERTION_STMT);
MARK_TS_EXP (PRECONDITION_STMT);
MARK_TS_EXP (POSTCONDITION_STMT);
c_common_init_ts ();
}
@@ -577,6 +585,39 @@ cp_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
{
if (handle_module_option (unsigned (scode), arg, value))
return true;
enum opt_code code = (enum opt_code) scode;
bool handled_p = true;
switch (code)
{
case OPT_fcontract_build_level_:
handle_OPT_fcontract_build_level_ (arg);
break;
case OPT_fcontract_assumption_mode_:
handle_OPT_fcontract_assumption_mode_ (arg);
break;
case OPT_fcontract_continuation_mode_:
handle_OPT_fcontract_continuation_mode_ (arg);
break;
case OPT_fcontract_role_:
handle_OPT_fcontract_role_ (arg);
break;
case OPT_fcontract_semantic_:
handle_OPT_fcontract_semantic_ (arg);
break;
default:
handled_p = false;
break;
}
if (handled_p)
return handled_p;
return c_common_handle_option (scode, arg, value, kind, loc, handlers);
}

View File

@@ -587,6 +587,17 @@ DEFTREECODE (CO_YIELD_EXPR, "co_yield", tcc_expression, 2)
DEFTREECODE (CO_RETURN_EXPR, "co_return", tcc_statement, 2)
/* Different flavors of contracts.
Assertions and preconditions have two operands: a node containing
the their mode and condition. Postconditions have an additional
operand to store the optional name for the result value.
CONTRACT_SEMANTIC has the computed behavior of the contract. */
DEFTREECODE (ASSERTION_STMT, "assertion_stmt", tcc_statement, 3)
DEFTREECODE (PRECONDITION_STMT, "precondition_stmt", tcc_statement, 3)
DEFTREECODE (POSTCONDITION_STMT, "postcondition_stmt", tcc_statement, 4)
/*
Local variables:
mode:c

View File

@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see
#include "hard-reg-set.h"
#include "function.h"
#include "tristate.h"
#include "contracts.h"
/* In order for the format checking to accept the C++ front end
diagnostic framework extensions, you must include this file before
@@ -232,6 +233,8 @@ enum cp_tree_index
CPTI_DSO_HANDLE,
CPTI_DCAST,
CPTI_PSEUDO_CONTRACT_VIOLATION,
CPTI_SOURCE_LOCATION_IMPL,
CPTI_FALLBACK_DFLOAT32_TYPE,
@@ -266,6 +269,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
#define current_aggr cp_global_trees[CPTI_AGGR_TAG]
/* std::align_val_t */
#define align_type_node cp_global_trees[CPTI_ALIGN_TYPE]
#define pseudo_contract_violation_type cp_global_trees[CPTI_PSEUDO_CONTRACT_VIOLATION]
/* We cache these tree nodes so as to call get_identifier less frequently.
For identifiers for functions, including special member functions such
@@ -455,6 +459,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
ALIGNOF_EXPR_STD_P (in ALIGNOF_EXPR)
OVL_DEDUP_P (in OVERLOAD)
ATOMIC_CONSTR_MAP_INSTANTIATED_P (in ATOMIC_CONSTR)
contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE)
TI_PENDING_TEMPLATE_FLAG.
TEMPLATE_PARMS_FOR_INLINE.
@@ -493,6 +498,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
LAMBDA_EXPR_CAPTURE_OPTIMIZED (in LAMBDA_EXPR)
IMPLICIT_CONV_EXPR_BRACED_INIT (in IMPLICIT_CONV_EXPR)
PACK_EXPANSION_AUTO_P (in *_PACK_EXPANSION)
contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
3: IMPLICIT_RVALUE_P (in NON_LVALUE_EXPR or STATIC_CAST_EXPR)
ICS_BAD_FLAG (in _CONV)
FN_TRY_BLOCK_P (in TRY_BLOCK)
@@ -505,6 +511,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
PACK_EXPANSION_FORCE_EXTRA_ARGS_P (in *_PACK_EXPANSION)
LAMBDA_EXPR_STATIC_P (in LAMBDA_EXPR)
TARGET_EXPR_ELIDING_P (in TARGET_EXPR)
contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
4: IDENTIFIER_MARKED (IDENTIFIER_NODEs)
TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
CALL_EXPR, or FIELD_DECL).
@@ -1858,6 +1865,7 @@ struct GTY(()) saved_scope {
int x_processing_template_decl;
int x_processing_specialization;
int x_processing_constraint;
int x_processing_contract_condition;
int suppress_location_wrappers;
BOOL_BITFIELD x_processing_explicit_instantiation : 1;
BOOL_BITFIELD need_pop_function_context : 1;
@@ -1932,6 +1940,12 @@ extern GTY(()) struct saved_scope *scope_chain;
#define processing_specialization scope_chain->x_processing_specialization
#define processing_explicit_instantiation scope_chain->x_processing_explicit_instantiation
/* Nonzero if we are parsing the conditional expression of a contract
condition. These expressions appear outside the paramter list (like a
trailing return type), but are potentially evaluated. */
#define processing_contract_condition scope_chain->x_processing_contract_condition
#define in_discarded_stmt scope_chain->discarded_stmt
#define in_consteval_if_p scope_chain->consteval_if_p
@@ -5646,6 +5660,11 @@ extern int comparing_specializations;
FIXME we should always do this except during deduction/ordering. */
extern int comparing_dependent_aliases;
/* Nonzero if we want to consider different member expressions to compare
equal if they designate the same entity. This is set when comparing
contract conditions of overrides. */
extern bool comparing_override_contracts;
/* In parser.cc. */
/* Nonzero if we are parsing an unevaluated operand: an operand to
@@ -7449,8 +7468,10 @@ extern hashval_t iterative_hash_template_arg (tree arg, hashval_t val);
extern tree coerce_template_parms (tree, tree, tree, tsubst_flags_t,
bool = true);
extern tree canonicalize_type_argument (tree, tsubst_flags_t);
extern void register_local_identity (tree);
extern void register_local_specialization (tree, tree);
extern tree retrieve_local_specialization (tree);
extern void register_parameter_specializations (tree, tree);
extern tree extract_fnparm_pack (tree, tree *);
extern tree template_parm_to_arg (tree);
extern tree dguide_name (tree);
@@ -8520,6 +8541,49 @@ extern tree coro_get_actor_function (tree);
extern tree coro_get_destroy_function (tree);
extern tree coro_get_ramp_function (tree);
/* contracts.cc */
extern tree make_postcondition_variable (cp_expr);
extern tree make_postcondition_variable (cp_expr, tree);
extern tree grok_contract (tree, tree, tree, cp_expr, location_t);
extern tree finish_contract_condition (cp_expr);
/* Return the first contract in ATTRS, or NULL_TREE if there are none. */
inline tree
find_contract (tree attrs)
{
while (attrs && !cxx_contract_attribute_p (attrs))
attrs = TREE_CHAIN (attrs);
return attrs;
}
inline void
set_decl_contracts (tree decl, tree contract_attrs)
{
remove_contract_attributes (decl);
DECL_ATTRIBUTES (decl) = chainon (DECL_ATTRIBUTES (decl), contract_attrs);
}
/* Returns the computed semantic of the node. */
inline contract_semantic
get_contract_semantic (const_tree t)
{
return (contract_semantic) (TREE_LANG_FLAG_3 (CONTRACT_CHECK (t))
| (TREE_LANG_FLAG_2 (t) << 1)
| (TREE_LANG_FLAG_0 ((t)) << 2));
}
/* Sets the computed semantic of the node. */
inline void
set_contract_semantic (tree t, contract_semantic semantic)
{
TREE_LANG_FLAG_3 (CONTRACT_CHECK (t)) = semantic & 0x01;
TREE_LANG_FLAG_2 (t) = (semantic & 0x02) >> 1;
TREE_LANG_FLAG_0 (t) = (semantic & 0x04) >> 2;
}
/* Inline bodies. */
inline tree

View File

@@ -2202,6 +2202,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
= DECL_OVERLOADED_OPERATOR_CODE_RAW (olddecl);
new_defines_function = DECL_INITIAL (newdecl) != NULL_TREE;
duplicate_contracts (newdecl, olddecl);
/* Optionally warn about more than one declaration for the same
name, but don't warn about a function declaration followed by a
definition. */
@@ -2275,6 +2277,13 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
specializations. */
gcc_assert (!DECL_TEMPLATE_SPECIALIZATIONS (newdecl));
/* Make sure the contracts are equivalent. */
duplicate_contracts (newdecl, olddecl);
/* Remove contracts from old_result so they aren't appended to
old_result by the merge function. */
remove_contract_attributes (old_result);
DECL_ATTRIBUTES (old_result)
= (*targetm.merge_decl_attributes) (old_result, new_result);
@@ -2797,11 +2806,23 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
}
if (! types_match || new_defines_function)
{
/* These are the final DECL_ARGUMENTS that will be used within the
body; update any references to old DECL_ARGUMENTS in the
contracts, if present. */
if (tree contracts = DECL_CONTRACTS (newdecl))
remap_contracts (olddecl, newdecl, contracts, true);
/* These need to be copied so that the names are available.
Note that if the types do match, we'll preserve inline
info and other bits, but if not, we won't. */
DECL_ARGUMENTS (olddecl) = DECL_ARGUMENTS (newdecl);
DECL_RESULT (olddecl) = DECL_RESULT (newdecl);
/* In some cases, duplicate_contracts will remove contracts from
OLDDECL, to avoid duplications. Sometimes, the contracts end up
shared. If we removed them, re-add them. */
if (!DECL_CONTRACTS (olddecl))
copy_contract_attributes (olddecl, newdecl);
}
/* If redeclaring a builtin function, it stays built in
if newdecl is a gnu_inline definition, or if newdecl is just
@@ -2845,7 +2866,38 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
/* Don't clear out the arguments if we're just redeclaring a
function. */
if (DECL_ARGUMENTS (olddecl))
DECL_ARGUMENTS (newdecl) = DECL_ARGUMENTS (olddecl);
{
/* If we removed contracts from previous definition, re-attach
them. Otherwise, rewrite the contracts so they match the
parameters of the new declaration. */
if (DECL_INITIAL (olddecl)
&& DECL_CONTRACTS (newdecl)
&& !DECL_CONTRACTS (olddecl))
copy_contract_attributes (olddecl, newdecl);
else
{
/* Temporarily undo the re-contexting of parameters so we can
actually remap parameters. The inliner won't replace
parameters if we don't do this. */
tree args = DECL_ARGUMENTS (newdecl);
for (tree p = args; p; p = DECL_CHAIN (p))
DECL_CONTEXT (p) = newdecl;
/* Save new argument names for use in contracts parsing,
unless we've already started parsing the body of olddecl
(particular issues arise when newdecl is from a prior
friend decl with no argument names, see
modules/contracts-tpl-friend-1). */
if (tree contracts = DECL_CONTRACTS (olddecl))
remap_contracts (newdecl, olddecl, contracts, true);
/* And reverse this operation again. */
for (tree p = args; p; p = DECL_CHAIN (p))
DECL_CONTEXT (p) = olddecl;
}
DECL_ARGUMENTS (newdecl) = DECL_ARGUMENTS (olddecl);
}
}
}
else if (TREE_CODE (newdecl) == NAMESPACE_DECL)
@@ -5456,6 +5508,12 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
warn_misplaced_attr_for_class_type (loc, declared_type);
}
/* Diagnose invalid application of contracts, if any. */
if (find_contract (declspecs->attributes))
diagnose_misapplied_contracts (declspecs->attributes);
else
diagnose_misapplied_contracts (declspecs->std_attributes);
return declared_type;
}
@@ -5747,9 +5805,16 @@ start_decl (const cp_declarator *declarator,
if (DECL_EXTERNAL (decl) && ! DECL_TEMPLATE_SPECIALIZATION (decl)
/* Aliases are definitions. */
&& !alias)
permerror (declarator->id_loc,
"declaration of %q#D outside of class is not definition",
decl);
{
if (DECL_VIRTUAL_P (decl) || !flag_contracts)
permerror (declarator->id_loc,
"declaration of %q#D outside of class is not definition",
decl);
else if (flag_contract_strict_declarations)
warning_at (declarator->id_loc, OPT_fcontract_strict_declarations_,
"declaration of %q#D outside of class is not definition",
decl);
}
}
/* Create a DECL_LANG_SPECIFIC so that DECL_DECOMPOSITION_P works. */
@@ -10531,6 +10596,9 @@ grokfndecl (tree ctype,
*attrlist = NULL_TREE;
}
if (DECL_HAS_CONTRACTS_P (decl))
rebuild_postconditions (decl);
/* Check main's type after attributes have been applied. */
if (ctype == NULL_TREE && DECL_MAIN_P (decl))
{
@@ -12731,7 +12799,8 @@ grokdeclarator (const cp_declarator *declarator,
}
}
if (declspecs->std_attributes)
if (declspecs->std_attributes
&& !diagnose_misapplied_contracts (declspecs->std_attributes))
{
location_t attr_loc = declspecs->locations[ds_std_attribute];
if (warning_at (attr_loc, OPT_Wattributes, "attribute ignored"))
@@ -12739,6 +12808,9 @@ grokdeclarator (const cp_declarator *declarator,
"is ignored");
}
if (attrlist)
diagnose_misapplied_contracts (*attrlist);
/* Determine the type of the entity declared by recurring on the
declarator. */
for (; declarator; declarator = declarator->declarator)
@@ -12776,6 +12848,12 @@ grokdeclarator (const cp_declarator *declarator,
inner_declarator = declarator->declarator;
/* Check that contracts aren't misapplied. */
if (tree contract_attr = find_contract (declarator->std_attributes))
if (declarator->kind != cdk_function
|| innermost_code != cdk_function)
diagnose_misapplied_contracts (contract_attr);
/* We don't want to warn in parameter context because we don't
yet know if the parse will succeed, and this might turn out
to be a constructor call. */
@@ -13168,6 +13246,23 @@ grokdeclarator (const cp_declarator *declarator,
else
returned_attrs = attr_chainon (returned_attrs, att);
}
/* Actually apply the contract attributes to the declaration. */
for (tree *p = &attrs; *p;)
{
tree l = *p;
if (cxx_contract_attribute_p (l))
{
*p = TREE_CHAIN (l);
/* Intentionally reverse order of contracts so they're
reversed back into their lexical order. */
TREE_CHAIN (l) = NULL_TREE;
returned_attrs = chainon (l, returned_attrs);
}
else
p = &TREE_CHAIN (l);
}
if (attrs)
/* [dcl.fct]/2:
@@ -14190,7 +14285,10 @@ grokdeclarator (const cp_declarator *declarator,
{
/* Packages tend to use GNU attributes on friends, so we only
warn for standard attributes. */
if (attrlist && !funcdef_flag && cxx11_attribute_p (*attrlist))
if (attrlist
&& !funcdef_flag
&& cxx11_attribute_p (*attrlist)
&& !all_attributes_are_contracts_p (*attrlist))
{
*attrlist = NULL_TREE;
if (warning_at (id_loc, OPT_Wattributes, "attribute ignored"))
@@ -17410,6 +17508,8 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
store_parm_decls (current_function_parms);
start_function_contracts (decl1);
if (!processing_template_decl
&& (flag_lifetime_dse > 1)
&& DECL_CONSTRUCTOR_P (decl1)
@@ -18130,6 +18230,9 @@ finish_function (bool inline_p)
current_function_decl = NULL_TREE;
invoke_plugin_callbacks (PLUGIN_FINISH_PARSE_FUNCTION, fndecl);
finish_function_contracts (fndecl);
return fndecl;
}

View File

@@ -1561,6 +1561,9 @@ cp_check_const_attributes (tree attributes)
tree attr;
for (attr = attributes; attr; attr = TREE_CHAIN (attr))
{
if (cxx_contract_attribute_p (attr))
continue;
tree arg;
/* As we implement alignas using gnu::aligned attribute and
alignas argument is a constant expression, force manifestly
@@ -2106,7 +2109,17 @@ void
comdat_linkage (tree decl)
{
if (flag_weak)
make_decl_one_only (decl, cxx_comdat_group (decl));
{
make_decl_one_only (decl, cxx_comdat_group (decl));
if (HAVE_COMDAT_GROUP && flag_contracts && DECL_CONTRACTS (decl))
{
symtab_node *n = symtab_node::get (decl);
if (tree pre = DECL_PRE_FN (decl))
cgraph_node::get_create (pre)->add_to_same_comdat_group (n);
if (tree post = DECL_POST_FN (decl))
cgraph_node::get_create (post)->add_to_same_comdat_group (n);
}
}
else if (TREE_CODE (decl) == FUNCTION_DECL
|| (VAR_P (decl) && DECL_ARTIFICIAL (decl)))
/* We can just emit function and compiler-generated variables

View File

@@ -567,7 +567,8 @@ dump_type (cxx_pretty_printer *pp, tree t, int flags)
else
{
pp_cxx_cv_qualifier_seq (pp, t);
pp_cxx_tree_identifier (pp, TYPE_IDENTIFIER (t));
if (tree id = TYPE_IDENTIFIER (t))
pp_cxx_tree_identifier (pp, id);
}
break;

View File

@@ -31,6 +31,8 @@ along with GCC; see the file COPYING3. If not see
#define WITHLIBC (1<<3)
/* Skip this option. */
#define SKIPOPT (1<<4)
/* Add -lstdc++exp for experimental features that need library support. */
#define EXPERIMENTAL (1<<5)
#ifndef MATH_LIBRARY
#define MATH_LIBRARY "m"
@@ -158,6 +160,11 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options,
switch (decoded_options[i].opt_index)
{
case OPT_fcontracts:
args[i] |= EXPERIMENTAL;
++added;
break;
case OPT_nostdlib:
case OPT_nostdlib__:
case OPT_nodefaultlibs:
@@ -348,6 +355,11 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options,
&new_decoded_options[j]);
}
if ((args[i] & EXPERIMENTAL)
&& which_library == USE_LIBSTDCXX)
generate_option (OPT_l, "stdc++exp", 1, CL_DRIVER,
&new_decoded_options[++j]);
if ((args[i] & SKIPOPT) != 0)
--j;

View File

@@ -856,6 +856,13 @@ write_encoding (const tree decl)
mangle_return_type_p (decl),
d);
/* If this is the pre/post function for a guarded function, append
.pre/post, like something from create_virtual_clone. */
if (DECL_IS_PRE_FN_P (decl))
write_string (".pre");
else if (DECL_IS_POST_FN_P (decl))
write_string (".post");
/* If this is a coroutine helper, then append an appropriate string to
identify which. */
if (tree ramp = DECL_RAMP_FN (decl))

View File

@@ -10184,6 +10184,20 @@ trees_out::fn_parms_init (tree fn)
base_tag - ix, ix, parm, fn);
tree_node_vals (parm);
}
if (!streaming_p ())
{
/* We must walk contract attrs so the dependency graph is complete. */
for (tree contract = DECL_CONTRACTS (fn);
contract;
contract = CONTRACT_CHAIN (contract))
tree_node (contract);
}
/* Write a reference to contracts pre/post functions, if any, to avoid
regenerating them in importers. */
tree_node (DECL_PRE_FN (fn));
tree_node (DECL_POST_FN (fn));
}
/* Build skeleton parm nodes, read their flags, type & parm indices. */
@@ -10218,6 +10232,11 @@ trees_in::fn_parms_init (tree fn)
return 0;
}
/* Reload references to contract functions, if any. */
tree pre_fn = tree_node ();
tree post_fn = tree_node ();
set_contract_functions (fn, pre_fn, post_fn);
return base_tag;
}
@@ -10805,7 +10824,15 @@ check_mergeable_decl (merge_kind mk, tree decl, tree ovl, merge_key const &key)
Matches decls_match behaviour. */
&& (!DECL_IS_UNDECLARED_BUILTIN (m_inner)
|| !DECL_EXTERN_C_P (m_inner)
|| DECL_EXTERN_C_P (d_inner)))
|| DECL_EXTERN_C_P (d_inner))
/* Reject if one is a different member of a
guarded/pre/post fn set. */
&& (!flag_contracts
|| (DECL_IS_PRE_FN_P (d_inner)
== DECL_IS_PRE_FN_P (m_inner)))
&& (!flag_contracts
|| (DECL_IS_POST_FN_P (d_inner)
== DECL_IS_POST_FN_P (m_inner))))
{
tree m_reqs = get_constraints (m_inner);
if (m_reqs)
@@ -14573,6 +14600,7 @@ module_state_config::get_dialect ()
cxx_dialect < cxx20 && flag_concepts ? "/concepts" : "",
flag_coroutines ? "/coroutines" : "",
flag_module_implicit_inline ? "/implicit-inline" : "",
flag_contracts ? "/contracts" : "",
NULL);
return dialect;

View File

@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3. If not see
#include "cp-name-hint.h"
#include "memmodel.h"
#include "c-family/known-headers.h"
#include "contracts.h"
#include "bitmap.h"
@@ -2192,11 +2193,14 @@ cp_parser_context_new (cp_parser_context* next)
parser->unparsed_queues->last ().nsdmis
#define unparsed_noexcepts \
parser->unparsed_queues->last ().noexcepts
#define unparsed_contracts \
parser->unparsed_queues->last ().contracts
static void
push_unparsed_function_queues (cp_parser *parser)
{
cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL };
cp_unparsed_functions_entry e
= { NULL, make_tree_vector (), NULL, NULL, NULL };
vec_safe_push (parser->unparsed_queues, e);
}
@@ -2695,6 +2699,10 @@ static tree cp_parser_transaction_cancel
static tree cp_parser_yield_expression
(cp_parser *);
/* Contracts */
static void cp_parser_late_contract_condition
(cp_parser *, tree, tree);
enum pragma_context {
pragma_external,
@@ -2907,6 +2915,8 @@ static bool cp_parser_array_designator_p
(cp_parser *);
static bool cp_parser_init_statement_p
(cp_parser *);
static bool cp_parser_skip_up_to_closing_square_bracket
(cp_parser *);
static bool cp_parser_skip_to_closing_square_bracket
(cp_parser *);
static size_t cp_parser_skip_balanced_tokens (cp_parser *, size_t);
@@ -12024,6 +12034,16 @@ cp_parser_handle_statement_omp_attributes (cp_parser *parser, tree attrs)
return attrs;
}
/* True if and only if the name is one of the contract types. */
static bool
contract_attribute_p (const_tree id)
{
return is_attribute_p ("assert", id)
|| is_attribute_p ("pre", id)
|| is_attribute_p ("post", id);
}
/* Handle omp::directive and omp::sequence attributes in *PATTRS
(if any) at the start or after declaration-id of a declaration. */
@@ -12157,7 +12177,10 @@ cp_parser_handle_directive_omp_attributes (cp_parser *parser, tree *pattrs,
is a (possibly labeled) if statement which is not enclosed in braces
and has an else clause. This is used to implement -Wparentheses.
CHAIN is a vector of if-else-if conditions. */
CHAIN is a vector of if-else-if conditions.
Note that this version of parsing restricts assertions to be attached to
empty statements. */
static void
cp_parser_statement (cp_parser* parser, tree in_statement_expr,
@@ -12199,6 +12222,23 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
/* If we have contracts, check that they're valid in this context. */
if (std_attrs != error_mark_node)
{
if (tree pre = lookup_attribute ("pre", std_attrs))
error_at (EXPR_LOCATION (TREE_VALUE (pre)),
"preconditions cannot be statements");
else if (tree post = lookup_attribute ("post", std_attrs))
error_at (EXPR_LOCATION (TREE_VALUE (post)),
"postconditions cannot be statements");
/* Check that assertions are null statements. */
if (cp_contract_assertion_p (std_attrs))
if (token->type != CPP_SEMICOLON)
error_at (token->location, "assertions must be followed by %<;%>");
}
bool omp_attrs_forbidden_p;
omp_attrs_forbidden_p = parser->omp_attrs_forbidden_p;
@@ -12514,6 +12554,15 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
"%<fallthrough%> attribute not followed by %<;%>");
std_attrs = NULL_TREE;
}
/* Handle [[assert: ...]]; */
if (cp_contract_assertion_p (std_attrs))
{
/* Add the assertion as a statement in the current block. */
gcc_assert (!statement || statement == error_mark_node);
emit_assertion (std_attrs);
std_attrs = NULL_TREE;
}
}
/* Set the line number for the statement. */
@@ -15697,7 +15746,12 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
declared. */;
else
{
if (decl_specs->type && CLASS_TYPE_P (decl_specs->type))
if (find_contract (attrs))
{
diagnose_misapplied_contracts (attrs);
attrs = NULL_TREE;
}
else if (decl_specs->type && CLASS_TYPE_P (decl_specs->type))
{
/* This is an attribute following a
class-specifier. */
@@ -25398,11 +25452,11 @@ cp_parser_braced_list (cp_parser* parser, bool* non_constant_p)
return result;
}
/* Consume tokens up to, and including, the next non-nested closing `]'.
/* Consume tokens up to, but not including, the next non-nested closing `]'.
Returns true iff we found a closing `]'. */
static bool
cp_parser_skip_to_closing_square_bracket (cp_parser *parser)
cp_parser_skip_up_to_closing_square_bracket (cp_parser *parser)
{
unsigned square_depth = 0;
@@ -25427,21 +25481,30 @@ cp_parser_skip_to_closing_square_bracket (cp_parser *parser)
case CPP_CLOSE_SQUARE:
if (!square_depth--)
{
cp_lexer_consume_token (parser->lexer);
return true;
}
return true;
break;
default:
break;
}
/* Consume the token. */
/* Consume the current token, skipping it. */
cp_lexer_consume_token (parser->lexer);
}
}
/* Consume tokens up to, and including, the next non-nested closing `]'.
Returns true iff we found a closing `]'. */
static bool
cp_parser_skip_to_closing_square_bracket (cp_parser *parser)
{
bool found = cp_parser_skip_up_to_closing_square_bracket (parser);
if (found)
cp_lexer_consume_token (parser->lexer);
return found;
}
/* Return true if we are looking at an array-designator, false otherwise. */
static bool
@@ -26284,6 +26347,56 @@ cp_parser_class_specifier (cp_parser* parser)
cp_parser_late_parsing_nsdmi (parser, decl);
}
vec_safe_truncate (unparsed_nsdmis, 0);
/* Now contract attributes. */
FOR_EACH_VEC_SAFE_ELT (unparsed_contracts, ix, decl)
{
tree ctx = DECL_CONTEXT (decl);
if (class_type != ctx)
{
if (pushed_scope)
pop_scope (pushed_scope);
class_type = ctx;
pushed_scope = push_scope (class_type);
}
temp_override<tree> cfd(current_function_decl, decl);
/* Make sure that any template parameters are in scope. */
maybe_begin_member_template_processing (decl);
/* Make sure that any member-function parameters are in scope.
This function doesn't expect ccp to be set. */
current_class_ptr = current_class_ref = NULL_TREE;
inject_parm_decls (decl);
/* 'this' is not allowed in static member functions. */
unsigned char local_variables_forbidden_p
= parser->local_variables_forbidden_p;
if (DECL_THIS_STATIC (decl))
parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
/* Now we can parse contract conditions. */
for (tree a = DECL_ATTRIBUTES (decl); a; a = TREE_CHAIN (a))
{
if (cxx_contract_attribute_p (a))
cp_parser_late_contract_condition (parser, decl, a);
}
/* Restore the state of local_variables_forbidden_p. */
parser->local_variables_forbidden_p = local_variables_forbidden_p;
/* Remove any member-function parameters from the symbol table. */
pop_injected_parms ();
/* Remove any template parameters from the symbol table. */
maybe_end_member_template_processing ();
/* Perform any deferred contract matching. */
match_deferred_contracts (decl);
}
vec_safe_truncate (unparsed_contracts, 0);
current_class_ptr = save_ccp;
current_class_ref = save_ccr;
if (pushed_scope)
@@ -26387,6 +26500,8 @@ cp_parser_class_head (cp_parser* parser,
/* Parse the attributes. */
attributes = cp_parser_attributes_opt (parser);
if (find_contract (attributes))
diagnose_misapplied_contracts (attributes);
/* If the next token is `::', that is invalid -- but sometimes
people do try to write:
@@ -29317,10 +29432,361 @@ cp_parser_std_attribute_list (cp_parser *parser, tree attr_ns)
return attributes;
}
/* Optionally parse a C++20 contract role. A NULL return means that no
contract role was specified.
contract-role:
% default
% identifier
If the identifier does not name a known contract role, it will
be assumed to be default. Returns the identifier for the role
token. */
static tree
cp_parser_contract_role (cp_parser *parser)
{
gcc_assert (cp_lexer_next_token_is (parser->lexer, CPP_MOD));
cp_lexer_consume_token (parser->lexer);
cp_token *token = cp_lexer_peek_token (parser->lexer);
tree role_id = NULL_TREE;
if (token->type == CPP_NAME)
role_id = token->u.value;
else if (token->type == CPP_KEYWORD && token->keyword == RID_DEFAULT)
role_id = get_identifier ("default");
else
{
error_at (token->location, "expected contract-role");
return error_mark_node;
}
cp_lexer_consume_token (parser->lexer);
/* FIXME: Warn about invalid/unknown roles? */
return role_id;
}
/* Parse an optional contract mode.
contract-mode:
contract-semantic
[contract-level] [contract-role]
contract-semantic:
check_never_continue
check_maybe_continue
check_always_continue
contract-level:
default
audit
axiom
contract-role:
default
identifier
This grammar is taken from P1332R0. During parsing, this sets options
on the MODE object to determine the configuration of the contract.
Returns a tree containing the identifiers used in the configuration.
This is either an IDENTIFIER with the literal semantic or a TREE_LIST
whose TREE_VALUE is the contract-level and whose TREE_PURPOSE is the
contract-role, if any. NULL_TREE is returned if no information is
given (i.e., all defaults selected). */
static tree
cp_parser_contract_mode_opt (cp_parser *parser,
bool postcondition_p)
{
/* The mode is empty; the level and role are default. */
if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
return NULL_TREE;
/* There is only a role; the level is default. */
if (cp_lexer_next_token_is (parser->lexer, CPP_MOD))
{
tree role_id = cp_parser_contract_role (parser);
return build_tree_list (role_id, get_identifier ("default"));
}
/* Otherwise, match semantic or level. */
cp_token *token = cp_lexer_peek_token (parser->lexer);
contract_level level = CONTRACT_INVALID;
contract_semantic semantic = CCS_INVALID;
tree config_id;
if (token->type == CPP_NAME)
{
config_id = token->u.value;
/* Either a named level, a concrete semantic, or an identifier
for a postcondition. */
const char *ident = IDENTIFIER_POINTER (token->u.value);
level = map_contract_level (ident);
semantic = map_contract_semantic (ident);
/* The identifier is the return value for a postcondition. */
if (level == CONTRACT_INVALID && semantic == CCS_INVALID
&& postcondition_p)
return NULL_TREE;
}
else if (token->type == CPP_KEYWORD && token->keyword == RID_DEFAULT)
{
config_id = get_identifier ("default");
level = CONTRACT_DEFAULT;
}
else
{
/* We got some other token other than a ':'. */
error_at (token->location, "expected contract semantic or level");
return NULL_TREE;
}
/* Consume the literal semantic or level token. */
cp_lexer_consume_token (parser->lexer);
if (semantic == CCS_INVALID && level == CONTRACT_INVALID)
{
error_at (token->location,
"expected contract level: "
"%<default%>, %<audit%>, or %<axiom%>");
return NULL_TREE;
}
/* We matched an explicit semantic. */
if (semantic != CCS_INVALID)
{
if (cp_lexer_next_token_is (parser->lexer, CPP_MOD))
{
error ("invalid use of contract role for explicit semantic");
cp_lexer_consume_token (parser->lexer);
cp_lexer_consume_token (parser->lexer);
}
return config_id;
}
/* We matched a level, there may be a role; otherwise this is default. */
if (cp_lexer_next_token_is (parser->lexer, CPP_MOD))
{
tree role_id = cp_parser_contract_role (parser);
return build_tree_list (role_id, config_id);
}
return build_tree_list (NULL_TREE, config_id);
}
static tree
find_error (tree *tp, int *, void *)
{
if (*tp == error_mark_node)
return *tp;
return NULL_TREE;
}
static bool
contains_error_p (tree t)
{
return walk_tree (&t, find_error, NULL, NULL);
}
/* Parse a standard C++20 contract attribute specifier.
contract-attribute-specifier:
[ [ assert contract-level [opt] : conditional-expression ] ]
[ [ pre contract-level [opt] : conditional-expression ] ]
[ [ post contract-level [opt] identifier [opt] : conditional-expression ] ]
For free functions, we cannot determine the type of the postcondition
identifier because the we haven't called grokdeclarator yet. In those
cases we parse the postcondition as if the identifier was declared as
'auto <identifier>'. We then instantiate the postcondition once the
return type is known.
For member functions, contracts are in the complete-class context, so the
parse is deferred. We also have the return type avaialable (unless it's
deduced), so we don't need to parse the postcondition in terms of a
placeholder. */
static tree
cp_parser_contract_attribute_spec (cp_parser *parser, tree attribute)
{
gcc_assert (contract_attribute_p (attribute));
cp_token *token = cp_lexer_consume_token (parser->lexer);
location_t loc = token->location;
bool assertion_p = is_attribute_p ("assert", attribute);
bool postcondition_p = is_attribute_p ("post", attribute);
/* Parse the optional mode. */
tree mode = cp_parser_contract_mode_opt (parser, postcondition_p);
/* Check for postcondition identifiers. */
cp_expr identifier;
if (postcondition_p && cp_lexer_next_token_is (parser->lexer, CPP_NAME))
identifier = cp_parser_identifier (parser);
if (identifier == error_mark_node)
return error_mark_node;
cp_parser_require (parser, CPP_COLON, RT_COLON);
/* Defer the parsing of pre/post contracts inside class definitions. */
tree contract;
if (!assertion_p &&
current_class_type &&
TYPE_BEING_DEFINED (current_class_type))
{
/* Skip until we reach an unenclose ']'. If we ran into an unnested ']'
that doesn't close the attribute, return an error and let the attribute
handling code emit an error for missing ']]'. */
cp_token *first = cp_lexer_peek_token (parser->lexer);
cp_parser_skip_to_closing_parenthesis_1 (parser,
/*recovering=*/false,
CPP_CLOSE_SQUARE,
/*consume_paren=*/false);
if (cp_lexer_peek_token (parser->lexer)->type != CPP_CLOSE_SQUARE
|| cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_CLOSE_SQUARE)
return error_mark_node;
cp_token *last = cp_lexer_peek_token (parser->lexer);
/* Build a deferred-parse node. */
tree condition = make_node (DEFERRED_PARSE);
DEFPARSE_TOKENS (condition) = cp_token_cache_new (first, last);
DEFPARSE_INSTANTIATIONS (condition) = NULL;
/* And its corresponding contract. */
contract = grok_contract (attribute, mode, identifier, condition, loc);
}
else
{
/* Enable location wrappers when parsing contracts. */
auto suppression = make_temp_override (suppress_location_wrappers, 0);
/* Build a fake variable for the result identifier. */
tree result = NULL_TREE;
if (identifier)
{
begin_scope (sk_block, NULL_TREE);
result = make_postcondition_variable (identifier);
++processing_template_decl;
}
/* Parse the condition, ensuring that parameters or the return variable
aren't flagged for use outside the body of a function. */
++processing_contract_condition;
cp_expr condition = cp_parser_conditional_expression (parser);
--processing_contract_condition;
/* Try to recover from errors by scanning up to the end of the
attribute. Sometimes we get partially parsed expressions, so
we need to search the condition for errors. */
if (contains_error_p (condition))
cp_parser_skip_up_to_closing_square_bracket (parser);
/* Build the contract. */
contract = grok_contract (attribute, mode, result, condition, loc);
/* Leave our temporary scope for the postcondition result. */
if (result)
{
--processing_template_decl;
pop_bindings_and_leave_scope ();
}
}
if (!flag_contracts)
{
error_at (loc, "contracts are only available with %<-fcontracts%>");
return error_mark_node;
}
return finish_contract_attribute (attribute, contract);
}
/* Parse a contract condition for a deferred contract. */
void cp_parser_late_contract_condition (cp_parser *parser,
tree fn,
tree attribute)
{
tree contract = TREE_VALUE (TREE_VALUE (attribute));
/* Make sure we've gotten something that hasn't been parsed yet or that
we're not parsing an invalid contract. */
tree condition = CONTRACT_CONDITION (contract);
if (TREE_CODE (condition) != DEFERRED_PARSE)
return;
tree identifier = NULL_TREE;
if (TREE_CODE (contract) == POSTCONDITION_STMT)
identifier = POSTCONDITION_IDENTIFIER (contract);
/* Build a fake variable for the result identifier. */
tree result = NULL_TREE;
if (identifier)
{
/* TODO: Can we guarantee that the identifier has a location? */
location_t loc = cp_expr_location (contract);
tree type = TREE_TYPE (TREE_TYPE (fn));
if (!check_postcondition_result (fn, type, loc))
{
invalidate_contract (contract);
return;
}
begin_scope (sk_block, NULL_TREE);
result = make_postcondition_variable (identifier, type);
++processing_template_decl;
}
/* 'this' is not allowed in preconditions of constructors or in postconditions
of destructors. Note that the previous value of this variable is
established by the calling function, so we need to save it here. */
tree saved_ccr = current_class_ref;
tree saved_ccp = current_class_ptr;
if ((DECL_CONSTRUCTOR_P (fn) && PRECONDITION_P (contract)) ||
(DECL_DESTRUCTOR_P (fn) && POSTCONDITION_P (contract)))
{
current_class_ref = current_class_ptr = NULL_TREE;
parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
}
push_unparsed_function_queues (parser);
/* Push the saved tokens onto the parser's lexer stack. */
cp_token_cache *tokens = DEFPARSE_TOKENS (condition);
cp_parser_push_lexer_for_tokens (parser, tokens);
/* Parse the condition, ensuring that parameters or the return variable
aren't flagged for use outside the body of a function. */
++processing_contract_condition;
condition = cp_parser_conditional_expression (parser);
--processing_contract_condition;
/* Revert to the main lexer. */
cp_parser_pop_lexer (parser);
/* Restore the queue. */
pop_unparsed_function_queues (parser);
current_class_ref = saved_ccr;
current_class_ptr = saved_ccp;
/* Commit to changes. */
update_late_contract (contract, result, condition);
/* Leave our temporary scope for the postcondition result. */
if (result)
{
--processing_template_decl;
pop_bindings_and_leave_scope ();
}
}
/* Parse a standard C++-11 attribute specifier.
attribute-specifier:
[ [ attribute-using-prefix [opt] attribute-list ] ]
contract-attribute-specifier
alignment-specifier
attribute-using-prefix:
@@ -29328,7 +29794,15 @@ cp_parser_std_attribute_list (cp_parser *parser, tree attr_ns)
alignment-specifier:
alignas ( type-id ... [opt] )
alignas ( alignment-expression ... [opt] ). */
alignas ( alignment-expression ... [opt] ).
Extensions for contracts:
contract-attribute-specifier:
[ [ assert : contract-mode [opt] : conditional-expression ] ]
[ [ pre : contract-mode [opt] : conditional-expression ] ]
[ [ post : contract-mode [opt] identifier [opt] :
conditional-expression ] ] */
static tree
cp_parser_std_attribute_spec (cp_parser *parser)
@@ -29340,10 +29814,27 @@ cp_parser_std_attribute_spec (cp_parser *parser)
&& cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_OPEN_SQUARE)
{
tree attr_ns = NULL_TREE;
tree attr_name = NULL_TREE;
cp_lexer_consume_token (parser->lexer);
cp_lexer_consume_token (parser->lexer);
token = cp_lexer_peek_token (parser->lexer);
if (token->type == CPP_NAME)
{
attr_name = token->u.value;
attr_name = canonicalize_attr_name (attr_name);
}
/* Handle contract-attribute-specs specially. */
if (attr_name && contract_attribute_p (attr_name))
{
tree attrs = cp_parser_contract_attribute_spec (parser, attr_name);
if (attrs != error_mark_node)
attributes = attrs;
goto finish_attrs;
}
if (cp_lexer_next_token_is_keyword (parser->lexer, RID_USING))
{
token = cp_lexer_peek_nth_token (parser->lexer, 2);
@@ -29372,6 +29863,7 @@ cp_parser_std_attribute_spec (cp_parser *parser)
attributes = cp_parser_std_attribute_list (parser, attr_ns);
finish_attrs:
if (!cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE)
|| !cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE))
cp_parser_skip_to_end_of_statement (parser);
@@ -32395,6 +32887,14 @@ cp_parser_save_default_args (cp_parser* parser, tree decl)
tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
if (UNPARSED_NOEXCEPT_SPEC_P (spec))
vec_safe_push (unparsed_noexcepts, decl);
/* Contracts are deferred. */
for (tree attr = DECL_ATTRIBUTES (decl); attr; attr = TREE_CHAIN (attr))
if (cxx_contract_attribute_p (attr))
{
vec_safe_push (unparsed_contracts, decl);
break;
}
}
/* DEFAULT_ARG contains the saved tokens for the initializer of DECL,

View File

@@ -178,6 +178,9 @@ struct GTY(()) cp_unparsed_functions_entry {
/* Functions with noexcept-specifiers that require post-processing. */
vec<tree, va_gc> *noexcepts;
/* Functions with contract attributes that require post-processing. */
vec<tree, va_gc> *contracts;
};

View File

@@ -217,7 +217,6 @@ static tree get_underlying_template (tree);
static tree tsubst_attributes (tree, tree, tsubst_flags_t, tree);
static tree canonicalize_expr_argument (tree, tsubst_flags_t);
static tree make_argument_pack (tree);
static void register_parameter_specializations (tree, tree);
static tree enclosing_instantiation_of (tree tctx);
static void instantiate_body (tree pattern, tree args, tree d, bool nested);
static tree maybe_dependent_member_ref (tree, tree, tsubst_flags_t, tree);
@@ -1968,6 +1967,16 @@ register_local_specialization (tree spec, tree tmpl)
local_specializations->put (tmpl, spec);
}
/* Registers T as a specialization of itself. This is used to preserve
the references to already-parsed parameters when instantiating
postconditions. */
void
register_local_identity (tree t)
{
local_specializations->put (t, t);
}
/* TYPE is a class type. Returns true if TYPE is an explicitly
specialized class. */
@@ -3161,8 +3170,10 @@ check_explicit_specialization (tree declarator,
parm = DECL_CHAIN (parm))
DECL_CONTEXT (parm) = result;
}
return register_specialization (tmpl, gen_tmpl, targs,
decl = register_specialization (tmpl, gen_tmpl, targs,
is_friend, 0);
remove_contract_attributes (result);
return decl;
}
/* Set up the DECL_TEMPLATE_INFO for DECL. */
@@ -3262,6 +3273,10 @@ check_explicit_specialization (tree declarator,
is_friend, 0);
}
/* If this is a specialization, splice any contracts that may have
been inherited from the template, removing them. */
if (decl != error_mark_node && DECL_TEMPLATE_SPECIALIZATION (decl))
remove_contract_attributes (decl);
/* A 'structor should already have clones. */
gcc_assert (decl == error_mark_node
@@ -11576,6 +11591,113 @@ can_complete_type_without_circularity (tree type)
static tree tsubst_omp_clauses (tree, enum c_omp_region_type, tree,
tsubst_flags_t, tree);
/* Instantiate the contract statement. */
static tree
tsubst_contract (tree decl, tree t, tree args, tsubst_flags_t complain,
tree in_decl)
{
tree type = decl ? TREE_TYPE (TREE_TYPE (decl)) : NULL_TREE;
bool auto_p = type_uses_auto (type);
tree r = copy_node (t);
/* Rebuild the result variable. */
if (POSTCONDITION_P (t) && POSTCONDITION_IDENTIFIER (t))
{
tree oldvar = POSTCONDITION_IDENTIFIER (t);
tree newvar = copy_node (oldvar);
TREE_TYPE (newvar) = type;
DECL_CONTEXT (newvar) = decl;
POSTCONDITION_IDENTIFIER (r) = newvar;
/* Make sure the postcondition is valid. */
location_t loc = DECL_SOURCE_LOCATION (oldvar);
if (!auto_p)
if (!check_postcondition_result (decl, type, loc))
return invalidate_contract (r);
/* Make the variable available for lookup. */
register_local_specialization (newvar, oldvar);
}
/* Instantiate the condition. If the return type is undeduced, process
the expression as if inside a template to avoid spurious type errors. */
if (auto_p)
++processing_template_decl;
++processing_contract_condition;
CONTRACT_CONDITION (r)
= tsubst_expr (CONTRACT_CONDITION (t), args, complain, in_decl, false);
--processing_contract_condition;
if (auto_p)
--processing_template_decl;
/* And the comment. */
CONTRACT_COMMENT (r)
= tsubst_expr (CONTRACT_COMMENT (r), args, complain, in_decl, false);
return r;
}
/* Update T by instantiating its contract attribute. */
static void
tsubst_contract_attribute (tree decl, tree t, tree args,
tsubst_flags_t complain, tree in_decl)
{
/* For non-specializations, adjust the current declaration to the most general
version of in_decl. Because we defer the instantiation of contracts as long
as possible, they are still written in terms of the parameters (and return
type) of the most general template. */
tree tmpl = DECL_TI_TEMPLATE (in_decl);
if (!DECL_TEMPLATE_SPECIALIZATION (tmpl))
in_decl = DECL_TEMPLATE_RESULT (most_general_template (in_decl));
local_specialization_stack specs (lss_copy);
register_parameter_specializations (in_decl, decl);
/* Get the contract to be instantiated. */
tree contract = CONTRACT_STATEMENT (t);
/* Use the complete set of template arguments for instantiation. The
contract may not have been instantiated and still refer to outer levels
of template parameters. */
args = DECL_TI_ARGS (decl);
/* For member functions, make this available for semantic analysis. */
tree save_ccp = current_class_ptr;
tree save_ccr = current_class_ref;
if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
{
tree arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl));
tree this_type = TREE_TYPE (TREE_VALUE (arg_types));
inject_this_parameter (this_type, cp_type_quals (this_type));
}
contract = tsubst_contract (decl, contract, args, complain, in_decl);
current_class_ptr = save_ccp;
current_class_ref = save_ccr;
/* Rebuild the attribute. */
TREE_VALUE (t) = build_tree_list (NULL_TREE, contract);
}
/* Rebuild the attribute list for DECL, substituting into contracts
as needed. */
void
tsubst_contract_attributes (tree decl, tree args, tsubst_flags_t complain, tree in_decl)
{
tree list = copy_list (DECL_ATTRIBUTES (decl));
for (tree attr = list; attr; attr = CONTRACT_CHAIN (attr))
{
if (cxx_contract_attribute_p (attr))
tsubst_contract_attribute (decl, attr, args, complain, in_decl);
}
DECL_ATTRIBUTES (decl) = list;
}
/* Instantiate a single dependent attribute T (a TREE_LIST), and return either
T or a new TREE_LIST, possibly a chain in the case of a pack expansion. */
@@ -11585,6 +11707,10 @@ tsubst_attribute (tree t, tree *decl_p, tree args,
{
gcc_assert (ATTR_IS_DEPENDENT (t));
/* Note that contract attributes are never substituted from this function.
Their instantiation is triggered by regenerate_from_template_decl when
we instantiate the body of the function. */
tree val = TREE_VALUE (t);
if (val == NULL_TREE)
/* Nothing to do. */;
@@ -17131,7 +17257,9 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
= do_auto_deduction (TREE_TYPE (r), init, auto_node,
complain, adc_variable_type);
}
gcc_assert (cp_unevaluated_operand || TREE_STATIC (r)
gcc_assert (cp_unevaluated_operand
|| processing_contract_condition
|| TREE_STATIC (r)
|| decl_constant_var_p (r)
|| seen_error ());
if (!processing_template_decl
@@ -18649,6 +18777,19 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
finish_using_directive (USING_STMT_NAMESPACE (t), /*attribs=*/NULL_TREE);
break;
case PRECONDITION_STMT:
case POSTCONDITION_STMT:
gcc_unreachable ();
case ASSERTION_STMT:
{
r = tsubst_contract (NULL_TREE, t, args, complain, in_decl);
if (r != error_mark_node)
add_stmt (r);
RETURN (r);
}
break;
case DECL_EXPR:
{
tree decl, pattern_decl;
@@ -26199,6 +26340,21 @@ regenerate_decl_from_template (tree decl, tree tmpl, tree args)
DECL_CONTEXT (t) = decl;
}
if (DECL_CONTRACTS (decl))
{
/* If we're regenerating a specialization, the contracts will have
been copied from the most general template. Replace those with
the ones from the actual specialization. */
tree tmpl = DECL_TI_TEMPLATE (decl);
if (DECL_TEMPLATE_SPECIALIZATION (tmpl))
{
remove_contract_attributes (decl);
copy_contract_attributes (decl, code_pattern);
}
tsubst_contract_attributes (decl, args, tf_warning_or_error, code_pattern);
}
/* Merge additional specifiers from the CODE_PATTERN. */
if (DECL_DECLARED_INLINE_P (code_pattern)
&& !DECL_DECLARED_INLINE_P (decl))
@@ -26446,7 +26602,7 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain)
/* We're starting to process the function INST, an instantiation of PATTERN;
add their parameters to local_specializations. */
static void
void
register_parameter_specializations (tree pattern, tree inst)
{
tree tmpl_parm = DECL_ARGUMENTS (pattern);

View File

@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see
#include "spellcheck-tree.h"
#include "stringpool.h"
#include "attribs.h"
#include "tree-inline.h"
static int is_subobject_of_p (tree, tree);
static tree dfs_lookup_base (tree, void *);
@@ -2082,6 +2083,33 @@ check_final_overrider (tree overrider, tree basefn)
}
return 0;
}
if (!DECL_HAS_CONTRACTS_P (basefn) && DECL_HAS_CONTRACTS_P (overrider))
{
auto_diagnostic_group d;
error ("function with contracts %q+D overriding contractless function",
overrider);
inform (DECL_SOURCE_LOCATION (basefn),
"overridden function is %qD", basefn);
return 0;
}
else if (DECL_HAS_CONTRACTS_P (basefn) && !DECL_HAS_CONTRACTS_P (overrider))
{
/* We're inheriting basefn's contracts; create a copy of them but
replace references to their parms to our parms. */
inherit_base_contracts (overrider, basefn);
}
else if (DECL_HAS_CONTRACTS_P (basefn) && DECL_HAS_CONTRACTS_P (overrider))
{
/* We're in the process of completing the overrider's class, which means
our conditions definitely are not parsed so simply chain on the
basefn for later checking.
Note that OVERRIDER's contracts will have been fully parsed at the
point the deferred match is run. */
defer_guarded_contract_match (overrider, basefn, DECL_CONTRACTS (basefn));
}
if (DECL_FINAL_P (basefn))
{
auto_diagnostic_group d;

View File

@@ -610,7 +610,8 @@ set_cleanup_locs (tree stmts, location_t loc)
if (TREE_CODE (stmts) == CLEANUP_STMT)
{
tree t = CLEANUP_EXPR (stmts);
protected_set_expr_location (t, loc);
if (t && TREE_CODE (t) != POSTCONDITION_STMT)
protected_set_expr_location (t, loc);
/* Avoid locus differences for C++ cdtor calls depending on whether
cdtor_returns_this: a conversion to void is added to discard the return
value, and this conversion ends up carrying the location, and when it
@@ -2169,7 +2170,8 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope,
/* DR 613/850: Can use non-static data members without an associated
object in sizeof/decltype/alignof. */
if (is_dummy_object (object) && cp_unevaluated_operand == 0
if (is_dummy_object (object)
&& !cp_unevaluated_operand
&& (!processing_template_decl || !current_class_ref))
{
if (complain & tf_error)
@@ -2177,6 +2179,14 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope,
if (current_function_decl
&& DECL_STATIC_FUNCTION_P (current_function_decl))
error ("invalid use of member %qD in static member function", decl);
else if (current_function_decl
&& processing_contract_condition
&& DECL_CONSTRUCTOR_P (current_function_decl))
error ("invalid use of member %qD in constructor %<pre%> contract", decl);
else if (current_function_decl
&& processing_contract_condition
&& DECL_DESTRUCTOR_P (current_function_decl))
error ("invalid use of member %qD in destructor %<post%> contract", decl);
else
error ("invalid use of non-static data member %qD", decl);
inform (DECL_SOURCE_LOCATION (decl), "declared here");
@@ -3010,6 +3020,10 @@ finish_this_expr (void)
tree fn = current_nonlambda_function ();
if (fn && DECL_STATIC_FUNCTION_P (fn))
error ("%<this%> is unavailable for static member functions");
else if (fn && processing_contract_condition && DECL_CONSTRUCTOR_P (fn))
error ("invalid use of %<this%> before it is valid");
else if (fn && processing_contract_condition && DECL_DESTRUCTOR_P (fn))
error ("invalid use of %<this%> after it is valid");
else if (fn)
error ("invalid use of %<this%> in non-member function");
else
@@ -3983,6 +3997,9 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use)
}
return error_mark_node;
}
else if (processing_contract_condition && (TREE_CODE (decl) == PARM_DECL))
/* Use of a parameter in a contract condition is fine. */
return decl;
else
{
if (complain & tf_error)
@@ -4115,7 +4132,8 @@ finish_id_expression_1 (tree id_expression,
body, except inside an unevaluated context (i.e. decltype). */
if (TREE_CODE (decl) == PARM_DECL
&& DECL_CONTEXT (decl) == NULL_TREE
&& !cp_unevaluated_operand)
&& !cp_unevaluated_operand
&& !processing_contract_condition)
{
*error_msg = G_("use of parameter outside function body");
return error_mark_node;
@@ -12286,6 +12304,10 @@ apply_deduced_return_type (tree fco, tree return_type)
TREE_TYPE (fco) = change_return_type (return_type, TREE_TYPE (fco));
maybe_update_postconditions (fco);
/* Apply the type to the result object. */
result = DECL_RESULT (fco);
if (result == NULL_TREE)
return;

View File

@@ -46,6 +46,7 @@ static tree verify_stmt_tree_r (tree *, int *, void *);
static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *);
static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *);
static tree handle_contract_attribute (tree *, tree, tree, int, bool *);
/* If REF is an lvalue, returns the kind of lvalue that REF is.
Otherwise, returns clk_none. */
@@ -3885,6 +3886,50 @@ called_fns_equal (tree t1, tree t2)
return cp_tree_equal (t1, t2);
}
bool comparing_override_contracts;
/* In a component reference, return the innermost object of
the postfix-expression. */
static tree
get_innermost_component (tree t)
{
gcc_assert (TREE_CODE (t) == COMPONENT_REF);
while (TREE_CODE (t) == COMPONENT_REF)
t = TREE_OPERAND (t, 0);
return t;
}
/* Returns true if T is a possibly converted 'this' or '*this' expression. */
static bool
is_this_expression (tree t)
{
t = get_innermost_component (t);
/* See through deferences and no-op conversions. */
if (TREE_CODE (t) == INDIRECT_REF)
t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == NOP_EXPR)
t = TREE_OPERAND (t, 0);
return is_this_parameter (t);
}
static bool
comparing_this_references (tree t1, tree t2)
{
return is_this_expression (t1) && is_this_expression (t2);
}
static bool
equivalent_member_references (tree t1, tree t2)
{
if (!comparing_this_references (t1, t2))
return false;
t1 = TREE_OPERAND (t1, 1);
t2 = TREE_OPERAND (t2, 1);
return t1 == t2;
}
/* Return truthvalue of whether T1 is the same tree structure as T2.
Return 1 if they are the same. Return 0 if they are different. */
@@ -4219,6 +4264,13 @@ cp_tree_equal (tree t1, tree t2)
return false;
return true;
case COMPONENT_REF:
/* If we're comparing contract conditions of overrides, member references
compare equal if they designate the same member. */
if (comparing_override_contracts)
return equivalent_member_references (t1, t2);
break;
default:
break;
}
@@ -5034,6 +5086,8 @@ const struct attribute_spec std_attribute_table[] =
handle_likeliness_attribute, attr_cold_hot_exclusions },
{ "noreturn", 0, 0, true, false, false, false,
handle_noreturn_attribute, attr_noreturn_exclusions },
{ "pre", 0, -1, false, false, false, false, handle_contract_attribute, NULL },
{ "post", 0, -1, false, false, false, false, handle_contract_attribute, NULL },
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
};
@@ -5282,6 +5336,17 @@ handle_abi_tag_attribute (tree* node, tree name, tree args,
return NULL_TREE;
}
/* Perform checking for contract attributes. */
tree
handle_contract_attribute (tree *ARG_UNUSED (node), tree ARG_UNUSED (name),
tree ARG_UNUSED (args), int ARG_UNUSED (flags),
bool *ARG_UNUSED (no_add_attrs))
{
/* TODO: Is there any checking we could do here? */
return NULL_TREE;
}
/* Return a new PTRMEM_CST of the indicated TYPE. The MEMBER is the
thing pointed to by the constant. */

View File

@@ -11260,11 +11260,22 @@ check_return_expr (tree retval, bool *no_warning)
/* Actually copy the value returned into the appropriate location. */
if (retval && retval != result)
retval = cp_build_init_expr (result, retval);
{
/* If there's a postcondition for a scalar return value, wrap
retval in a call to the postcondition function. */
if (tree post = apply_postcondition_to_return (retval))
retval = post;
retval = cp_build_init_expr (result, retval);
}
if (tree set = maybe_set_retval_sentinel ())
retval = build2 (COMPOUND_EXPR, void_type_node, retval, set);
/* If there's a postcondition for an aggregate return value, call the
postcondition function after the return object is initialized. */
if (tree post = apply_postcondition_to_return (result))
retval = build2 (COMPOUND_EXPR, void_type_node, retval, post);
return retval;
}

View File

@@ -3132,6 +3132,85 @@ of a loop too many expressions need to be evaluated, the resulting constexpr
evaluation might take too long.
The default is 33554432 (1<<25).
@item -fcontracts
@opindex fcontracts
Enable experimental support for the C++ Contracts feature, as briefly
added to and then removed from the C++20 working paper (N4820). The
implementation also includes proposed enhancements from papers P1290,
P1332, and P1429. This functionality is intended mostly for those
interested in experimentation towards refining the feature to get it
into shape for a future C++ standard.
On violation of a checked contract, the violation handler is called.
Users can replace the violation handler by defining
@smallexample
void handle_contract_violation (const std::experimental::contract_violation&);
@end smallexample
There are different sets of additional flags that can be used together
to specify which contracts will be checked and how, for N4820
contracts, P1332 contracts, or P1429 contracts; these sets cannot be
used together.
@table @gcctabopt
@item -fcontract-mode=[on|off]
@opindex fcontract-mode
Control whether any contracts have any semantics at all. Defaults to on.
@item -fcontract-assumption-mode=[on|off]
@opindex fcontract-assumption-mode
[N4820] Control whether contracts with level @samp{axiom}
should have the assume semantic. Defaults to on.
@item -fcontract-build-level=[off|default|audit]
@opindex fcontract-build-level
[N4820] Specify which level of contracts to generate checks
for. Defaults to @samp{default}.
@item -fcontract-continuation-mode=[on|off]
@opindex fcontract-continuation-mode
[N4820] Control whether to allow the program to continue executing
after a contract violation. That is, do checked contracts have the
@samp{maybe} semantic described below rather than the @samp{never}
semantic. Defaults to off.
@item -fcontract-role=<name>:<default>,<audit>,<axiom>
@opindex fcontract-role
[P1332] Specify the concrete semantics for each contract level
of a particular contract role.
@item -fcontract-semantic=[default|audit|axiom]:<semantic>
[P1429] Specify the concrete semantic for a particular
contract level.
@item -fcontract-strict-declarations=[on|off]
@opindex fcontract-strict-declarations
Control whether to reject adding contracts to a function after its
first declaration. Defaults to off.
@end table
The possible concrete semantics for that can be specified with
@samp{-fcontract-role} or @samp{-fcontract-semantic} are:
@table @code
@item ignore
This contract has no effect.
@item assume
This contract is treated like C++23 @code{[[assume]]}.
@item check_never_continue
@itemx never
@itemx abort
This contract is checked. If it fails, the violation handler is
called. If the handler returns, @code{std::terminate} is called.
@item check_maybe_continue
@itemx maybe
This contract is checked. If it fails, the violation handler is
called. If the handler returns, execution continues normally.
@end table
@item -fcoroutines
@opindex fcoroutines
Enable support for the C++ coroutines extension (experimental).

View File

@@ -949,6 +949,98 @@ location_get_source_line (const char *file_path, int line)
return char_span (buffer, len);
}
/* Return a NUL-terminated copy of the source text between two locations, or
NULL if the arguments are invalid. The caller is responsible for freeing
the return value. */
char *
get_source_text_between (location_t start, location_t end)
{
expanded_location expstart =
expand_location_to_spelling_point (start, LOCATION_ASPECT_START);
expanded_location expend =
expand_location_to_spelling_point (end, LOCATION_ASPECT_FINISH);
/* If the locations are in different files or the end comes before the
start, give up and return nothing. */
if (!expstart.file || !expend.file)
return NULL;
if (strcmp (expstart.file, expend.file) != 0)
return NULL;
if (expstart.line > expend.line)
return NULL;
if (expstart.line == expend.line
&& expstart.column > expend.column)
return NULL;
/* These aren't real column numbers, give up. */
if (expstart.column == 0 || expend.column == 0)
return NULL;
/* For a single line we need to trim both edges. */
if (expstart.line == expend.line)
{
char_span line = location_get_source_line (expstart.file, expstart.line);
if (line.length () < 1)
return NULL;
int s = expstart.column - 1;
int len = expend.column - s;
if (line.length () < (size_t)expend.column)
return NULL;
return line.subspan (s, len).xstrdup ();
}
struct obstack buf_obstack;
obstack_init (&buf_obstack);
/* Loop through all lines in the range and append each to buf; may trim
parts of the start and end lines off depending on column values. */
for (int lnum = expstart.line; lnum <= expend.line; ++lnum)
{
char_span line = location_get_source_line (expstart.file, lnum);
if (line.length () < 1 && (lnum != expstart.line && lnum != expend.line))
continue;
/* For the first line in the range, only start at expstart.column */
if (lnum == expstart.line)
{
unsigned off = expstart.column - 1;
if (line.length () < off)
return NULL;
line = line.subspan (off, line.length() - off);
}
/* For the last line, don't go past expend.column */
else if (lnum == expend.line)
{
if (line.length () < (size_t)expend.column)
return NULL;
line = line.subspan (0, expend.column);
}
/* Combine spaces at the beginning of later lines. */
if (lnum > expstart.line)
{
unsigned off;
for (off = 0; off < line.length(); ++off)
if (line[off] != ' ' && line[off] != '\t')
break;
if (off > 0)
{
obstack_1grow (&buf_obstack, ' ');
line = line.subspan (off, line.length() - off);
}
}
/* This does not include any trailing newlines. */
obstack_grow (&buf_obstack, line.get_buffer (), line.length ());
}
/* NUL-terminate and finish the buf obstack. */
obstack_1grow (&buf_obstack, 0);
const char *buf = (const char *) obstack_finish (&buf_obstack);
return xstrdup (buf);
}
/* Determine if FILE_PATH missing a trailing newline on its final line.
Only valid to call once all of the file has been loaded, by
requesting a line number beyond the end of the file. */

View File

@@ -111,6 +111,7 @@ class char_span
};
extern char_span location_get_source_line (const char *file_path, int line);
extern char *get_source_text_between (location_t, location_t);
extern bool location_missing_trailing_newline (const char *file_path);

View File

@@ -0,0 +1,13 @@
CXXFLAGS=--std=c++17 -g
default: assert_fail libhandle_contract_violation.so
run: default
LD_PRELOAD=./libhandle_contract_violation.so ./assert_fail
./libhandle_contract_violation.so: ./handle_contract_violation.cpp
${CXX} ${CXXFLAGS} -shared -fPIC -o $@ $<
clean:
rm -fr ./libhandle_contract_violation.so ./assert_fail

View File

@@ -0,0 +1,12 @@
build and install gcc to $prefix, then to see the raw backtrace info run:
LD_LIBRARY_PATH=$prefix/lib64 CXX=$prefix/bin/g++ make run
for a filtered view using addr2line, see ./prettytrace.sh:
LD_LIBRARY_PATH=$prefix/lib64 CXX=$prefix/bin/g++ make run |& ./prettytrace.sh
prettytrace.sh relies on addr2line and c++filt to lookup and demangle names,
and misc coreutils
example_out.txt has an example of the raw output while example_pretty.txt
shows the corresponding prettified output

View File

@@ -0,0 +1,23 @@
void fun1() {
int x = 0;
[[ assert: x < 0 ]];
}
namespace tns {
void fun2() {
fun1();
}
}
template<typename T>
void fun3(T a) {
tns::fun2();
}
void fun4() {
fun3(5);
}
int main(int, char**) {
void (*fp)() = nullptr;
fp = fun4;
fp();
return 0;
}

View File

@@ -0,0 +1,12 @@
LD_PRELOAD=./libhandle_contract_violation.so ./assert_fail
contract violation: assert_fail.cpp:3: fun1::x < 0 is false [with contract level=default]
violation occurs here:
./assert_fail[0x4011ad]
./assert_fail[0x4011e0]
./assert_fail[0x401230]
./assert_fail[0x4011f1]
./assert_fail[0x401219]
/usr/lib/libc.so.6(__libc_start_main+0xf3)[0x7f26e4fa9223]
./assert_fail[0x4010be]
[0x0]
end of violation

View File

@@ -0,0 +1,8 @@
LD_PRELOAD=./libhandle_contract_violation.so ./assert_fail
contract violation: assert_fail.cpp:3: fun1::x < 0 is false [with contract level=default]
violation occurs here:
assert_fail.cpp:fun1():4
assert_fail.cpp:tns::fun2():8
assert_fail.cpp:void fun3<int>(int):13
assert_fail.cpp:fun4():16
assert_fail.cpp:main:21

View File

@@ -0,0 +1,26 @@
#include <iostream>
#include <contract>
#include <execinfo.h>
#include <unistd.h>
static constexpr int MAX_BACKTRACE_DEPTH = 128;
void handle_contract_violation(const std::contract_violation &violation) {
size_t _backtraceSize{0};
void *_backtrace[MAX_BACKTRACE_DEPTH]{};
_backtraceSize = backtrace(_backtrace, MAX_BACKTRACE_DEPTH);
if(_backtraceSize == MAX_BACKTRACE_DEPTH)
std::cerr << "warning: backtrace may have been truncated" << std::endl;
std::cerr << "contract violation: " << violation.file_name()
<< ":" << violation.line_number()
<< ": " << violation.comment() << " is false"
<< " [with contract level=" << violation.assertion_level() << "]" << std::endl
<< "violation occurs here:" << std::endl;
// skip the stack frame of handle_contract_violation and
// on_contract_violation to get to wherever the assert was
backtrace_symbols_fd(_backtrace + 2, _backtraceSize - 1, STDERR_FILENO);
std::cerr << "end of violation" << std::endl;
}

View File

@@ -0,0 +1,30 @@
#!/bin/bash
inViolation="false"
sed 's/^/:/' /dev/stdin | while read -r line; do
line="$(echo "$line" | sed 's/^://')"
if [[ "${inViolation}" == "false" ]]; then
echo "$line"
if [[ -n "$(echo "$line" | grep 'violation occurs here:')" ]]; then
inViolation="true"
fi
continue
fi
if [[ -n "$(echo "$line" | grep 'end of violation')" ]]; then
inViolation="false"
continue
fi
addr="$(echo "$line" | sed -r 's/.*\[0x([a-f0-9]+)\]$/\1/')"
bin="$(echo "$line" | sed -r 's/^([^([]*).*/\1/')"
[[ -n "${bin}" ]] || continue
t="$(addr2line -e "$bin" "$addr" -f)"
file="$(echo "$t" | tail -1 | tr ':' '\n' | head -1)"
file="$(echo "$file" | sed -r "s:^$(pwd)/?::")"
line="$(echo "$t" | tail -1 | tr ':' '\n' | tail -1 | cut -d' ' -f1)"
func="$(echo "$t" | head -1 | c++filt)"
[[ $file != "??" ]] && echo " $file:$func:$line"
done

View File

@@ -0,0 +1,128 @@
// ensure that that preconditions can access public, protected, and private
// members of the current and base classes
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
struct Base
{
int pub{-1};
virtual int b()
[[ pre: pub > 0 ]]
[[ pre: pro > 0 ]]
[[ pre: pri > 0 ]]
{
return pub * pro * pri;
}
protected:
int pro{-1};
int pri{-1};
};
struct Child : Base
{
int fun()
[[ pre: pub > 0 ]]
[[ pre: pro > 0 ]]
[[ pre: pri > 0 ]]
{
return pub * pro;
}
};
struct VChild : Base
{
int b()
[[ pre: pub > 0 ]]
[[ pre: pro > 0 ]]
[[ pre: pri > 0 ]]
{
return pub * pro;
}
};
template<typename B>
struct TChild : B
{
int fun()
[[ pre: B::pub > 0 ]]
[[ pre: B::pro > 0 ]]
[[ pre: B::pri > 0 ]]
{
return B::pub * B::pro;
}
};
struct PubBase
{
int pub{-1};
int pro{-1};
int pri{-1};
};
struct PubChild : PubBase
{
int fun()
[[ pre: pub > 0 ]]
[[ pre: pro > 0 ]]
[[ pre: pri > 0 ]]
{
return pub * pro;
}
};
template<typename B>
struct TPubChild : B
{
int fun()
[[ pre: B::pub > 0 ]]
[[ pre: B::pro > 0 ]]
[[ pre: B::pri > 0 ]]
{
return B::pub * B::pro;
}
};
int main()
{
Base base{};
base.b();
Child child{};
child.fun();
VChild vchild{};
vchild.b();
TChild<Base> tchild{};
tchild.fun();
PubChild pubchild{};
pubchild.fun();
TPubChild<PubBase> tpubchild;
tpubchild.fun();
return 0;
}
// { dg-output "default std::handle_contract_violation called: .*.C 11 Base::b .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 12 Base::b .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 13 Base::b .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 26 Child::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 27 Child::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 28 Child::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 37 VChild::b .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 38 VChild::b .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 39 VChild::b .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 49 TChild<Base>::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 50 TChild<Base>::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 51 TChild<Base>::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 67 PubChild::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 68 PubChild::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 69 PubChild::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 79 TPubChild<PubBase>::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 80 TPubChild<PubBase>::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 81 TPubChild<PubBase>::fun .*(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,30 @@
// test that assumed contracts do instatiate templates
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
template<typename T>
int f(T t)
{
return -t;
}
int dummy()
{
[[ assert assume: f(1.0) > 0 ]];
return -1;
}
template<>
int f(double t) // { dg-error "specialization of.*after instantiation" }
{
return -1.0;
}
int main()
{
dummy();
f(1);
f(1.0);
return 0;
}

View File

@@ -0,0 +1,34 @@
// ensure that assert contracts can be turned into compile time assumptions
// and that they can be used for optimization.
//
// Even though x == -1, the assert contract tells the compiler that it is
// safe to assume the x <= 0 branch is never taken fun can be transformed into
// just
// printf("%d: test x>0\n", x);
// return 0;
// we ensure this by matching on the output and expecting a 0 return code from
// main -- unlike contracts-ignore2 which expects a failing return code
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-role=default:never,assume,ignore -O1" }
#include <cstdio>
int fun(int x) {
[[assert audit: x > 0]];
if(x <= 0)
{
printf("%d: test x<=0 opt out\n", x);
return -1;
}
else
{
printf("%d: test x>0\n", x);
return 0;
}
}
int main(int, char**) {
volatile int x = -1;
return fun(x);
}
// { dg-output "-1: test x>0(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,19 @@
// test that assumed contracts that reference undefined entities do not cause
// a link failure
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts" }
int f(int t);
int dummy()
{
[[ assert assume: f(1) > 0 ]];
return -1;
}
int main()
{
dummy();
return 0;
}

View File

@@ -0,0 +1,19 @@
// test that assumed constexpr contracts that reference undefined entities do
// not cause constexpr eval failure
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts" }
constexpr int f(int t); // { dg-warning "used but never defined" }
constexpr int dummy()
{
[[ assert assume: f(1) > 0 ]];
return -1;
}
int main()
{
constexpr int n = dummy();
return 0;
}

View File

@@ -0,0 +1,34 @@
// test that assumed constexpr contracts that reference defined entities, or
// undefined entities in unevaluated context, cause constexpr eval failure when
// the predicate is constexpr false
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
constexpr int f(int t)
{
return -1;
}
constexpr int dummy()
{
[[ assert assume: f(1) > 0 ]];
return -1;
}
constexpr int undef(int t);
constexpr int dummy2()
{
[[ assert assume: sizeof(decltype(undef(1))) < 0 ]];
return -1;
}
int main()
{
constexpr int n = dummy(); // { dg-message "in .constexpr. expansion" }
// { dg-error "contract predicate" "" { target *-*-* } 14 }
constexpr int m = dummy2(); // { dg-message "in .constexpr. expansion" }
// { dg-error "contract predicate" "" { target *-*-* } 22 }
return 0;
}

View File

@@ -0,0 +1,61 @@
// ensure that non-defined entities in assume contracts do not error
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
template<typename T>
T id2(T n);
int fun(int n)
[[ pre assume: id2(n) > 0 ]]
[[ pre: n > 0 ]]
{
return -n;
}
template<typename T>
T tfun(T n)
[[ pre assume: id2(n) > 0 ]]
[[ pre: n > 0 ]]
{
return -n;
}
template<typename T>
constexpr T id(T n); // { dg-warning "used but never defined" }
template<typename T>
constexpr T cfun(T n)
[[ pre assume: id(n) > 0 ]]
[[ pre: id(n) > 0 ]] // { dg-error "used before its definition" }
{
return -n;
}
template<typename T>
constexpr T id3(T n)
{
return n;
}
template<typename T>
constexpr T cfun2(T n)
[[ pre assume: id3(n) > 0 ]] // { dg-error "contract predicate" }
{
return -n;
}
template<typename T>
constexpr T cfun3(T n)
[[ pre: id3(n) > 0 ]] // { dg-error "contract predicate" }
{
return -n;
}
int main() {
constexpr int n = cfun(-5);
constexpr int n2 = cfun2(-5);
constexpr int n3 = cfun3(-5);
fun(-5);
tfun(-5);
}

View File

@@ -0,0 +1,19 @@
// Contract condition functions should be local symbols in a comdat group with
// the guarded function.
// { dg-do compile { target { c++20 && comdat_group } } }
// { dg-additional-options -fcontracts }
// { dg-final { scan-assembler-not "_Z1fi.pre,comdat" } }
// { dg-final { scan-assembler-not {(weak|globl)[^\n]*_Z1fi.pre} } }
inline int f(int i)
[[ pre: i > 0 ]]
{
return i;
}
int main()
{
if (f(42) != 42)
__builtin_abort ();
}

View File

@@ -0,0 +1,36 @@
// Small test to ensure that the level and role information printed by various
// contract configurations is correct.
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-role=default:maybe,maybe,ignore" }
int fun(int n)
[[ post default r: r > 0 ]]
{
return -n;
}
int main(int, char **)
{
[[ assert default: false ]];
[[ assert: false ]];
[[ assert audit: false ]];
[[ assert default %new_role: false ]];
[[ assert %new_role: false ]];
[[ assert audit %new_role: false ]];
[[ assert check_maybe_continue: false ]];
[[ assert %default: false ]];
[[ assert audit %default: false ]];
fun(5);
return 0;
}
// { dg-output "default std::handle_contract_violation called: .*main false default default 1.*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*main false default default 1.*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*main false audit default 1.*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*main false default new_role 1.*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*main false default new_role 1.*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*main false audit new_role 1.*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*main false 1.*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*main false default default 1.*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*main false audit default 1.*(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,74 @@
// ensure that passing pre/post do not affect constexpr functions
// ensure that failing pre/post generate an error at runtime in constexpr funcs
// { dg-do run }
// { dg-options "-std=c++20 -fcontracts -fcontract-continuation-mode=on" }
constexpr int wfun(int a)
[[ pre: a > 0 ]]
[[ post r: r > 0 ]]
{
return a;
}
constexpr int ffun(int a)
[[ pre: a > 0 ]]
[[ post r: r > 0 ]]
{
return a;
}
template<typename T>
constexpr int tfun(T a)
[[ pre: a > 0 ]]
[[ post r: r > 0 ]]
{
return a;
}
template<typename T>
constexpr int wtfun(T a)
[[ pre: a > 0 ]]
[[ post r: r > 0 ]]
{
return a;
}
template<typename T>
constexpr int ftfun(T a)
[[ pre: a > 0 ]]
[[ post r: r > 0 ]]
{
return a;
}
constexpr int explicitfn(int a)
[[ pre ignore: a > 0 ]]
[[ pre check_maybe_continue: a > 0 ]]
[[ post ignore r: r > 0 ]]
[[ post check_maybe_continue r: r > 0 ]]
{
return a;
}
int main(int, char **) {
constexpr int a = wfun(10);
int b = ffun(-10);
constexpr int c = wtfun(10);
int d = ftfun(-10);
int e = explicitfn(-10);
int z = ftfun(-10.0);
return 0;
}
// { dg-output "default std::handle_contract_violation called: .*.C 14 ffun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 15 ffun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 38 ftfun<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 39 ftfun<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 46 explicitfn .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 48 explicitfn .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 38 ftfun<double> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 39 ftfun<double> .*(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,58 @@
// ensure that failing pre/post can fail at constexpr time
// { dg-do compile }
// { dg-options "-std=c++20 -fcontracts -fcontract-continuation-mode=on" }
constexpr int ffun(int a)
[[ pre: a > 0 ]]
[[ post r: r > 10 ]]
{
return a;
}
template<typename T>
constexpr int ftfun(T a)
[[ pre: a > 0 ]]
[[ post r: r > 10 ]]
{
return a;
}
constexpr int explicitfn(int a)
[[ pre ignore: a > 0 ]]
[[ pre check_maybe_continue: a > 0 ]]
[[ post ignore r: r > 10 ]]
[[ post check_maybe_continue r: r > 10 ]]
{
return a;
}
template<typename T>
constexpr int ftfun2(T a)
[[ pre: a > 0 ]]
[[ post r: r > 10 ]]
{
return a;
}
int main(int, char **) {
constexpr int a = ffun(-10);
// { dg-error "contract predicate" "" { target *-*-* } 6 }
constexpr int b = ftfun(-10);
// { dg-error "contract predicate" "" { target *-*-* } 14 }
constexpr int c = explicitfn(-10);
// { dg-error "contract predicate" "" { target *-*-* } 22 }
constexpr int d = ftfun2(-10.0);
// { dg-error "contract predicate" "" { target *-*-* } 31 }
constexpr int e = ffun(5);
// { dg-error "contract predicate" "" { target *-*-* } 7 }
constexpr int f = ftfun(5);
// { dg-error "contract predicate" "" { target *-*-* } 15 }
constexpr int g = explicitfn(5);
// { dg-error "contract predicate" "" { target *-*-* } 24 }
constexpr int h = ftfun2(5.5);
// { dg-error "contract predicate" "" { target *-*-* } 32 }
return 0;
}

View File

@@ -0,0 +1,10 @@
// An assumed contract shouldn't break constant evaluation.
// { dg-do compile { target c++20 } }
// { dg-additional-options -fcontracts }
bool b;
constexpr int f() [[ pre assume: b ]] { return 42; }
static_assert (f() > 0);

View File

@@ -0,0 +1,19 @@
// Test to ensure that diagnostic location for condition conversion is in the
// right place.
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
template<typename T>
void fn()
[[ pre: T{} ]] // { dg-error "no match" }
{
}
struct Z { };
int main(int, char**) {
fn<int>();
fn<Z>();
return 0;
}

View File

@@ -0,0 +1,177 @@
// Tests to ensure that contracts are properly emitted for constructors,
// destructors, and their intersection with templates.
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
bool pre_{false}, post_{false};
bool delegate_{false};
struct S
{
S() [[ pre: pre_ ]] [[ post: post_ ]];
~S() [[ pre: pre_ ]] [[ post: post_ ]];
};
S::S() { return; }
S::~S() { return; }
struct SInline
{
SInline() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
~SInline() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
};
struct SDelegate0
{
SDelegate0(int) [[ pre: pre_ ]] [[ post: post_ ]] { return; }
SDelegate0() : SDelegate0(0) { return; }
~SDelegate0() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
};
struct SDelegate1
{
SDelegate1(int) { return; }
SDelegate1() [[ pre: pre_ ]] [[ post: post_ ]] : SDelegate1(0) { return; }
~SDelegate1() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
};
struct SDelegate2
{
SDelegate2(int) [[ pre: pre_ ]] [[ post: post_ ]] { return; }
SDelegate2() [[ pre: pre_ && delegate_ ]] [[ post: post_ && delegate_ ]] : SDelegate2(0) { return; }
~SDelegate2() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
};
struct SDelegate3
{
SDelegate3(int) [[ pre: pre_ ]] [[ post: post_ ]] { return; }
SDelegate3() [[ pre: pre_ && delegate_ ]] [[ post: post_ && delegate_ ]] : SDelegate3(0) { return; }
~SDelegate3() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
};
template<typename T>
struct S1
{
S1() [[ pre: pre_ ]] [[ post: post_ ]] { }
};
struct S2
{
template<typename T>
S2(T) [[ pre: pre_ ]] [[ post: post_ ]] { }
};
template<typename T>
struct S3
{
template<typename S>
S3(S) [[ pre: pre_ ]] [[ post: post_ ]] { }
};
struct G0
{
G0() [[ post: x > 0 ]] {}
~G0() [[ pre: x > 0 ]] {}
int x{-1};
};
struct G1
{
G1() [[ post: this->x > 0 ]] {}
~G1() [[ pre: this->x > 0 ]] {}
int x{-1};
};
int x{-1};
struct G2
{
G2() [[ pre: ::x > 0 ]] {}
~G2() [[ post: ::x > 0 ]] {}
int x{1};
};
void test0()
{
S s;
SInline si;
SDelegate0 sd0;
SDelegate1 sd1;
SDelegate2 sd2;
SDelegate3 sd3;
S1<int> s1_i;
S1<double> s1_d;
S2 s2_i{1};
S2 s2_d{.1};
S3<int> s3_i_i{1};
S3<int> s3_i_d{.1};
S3<double> s3_d_i{1};
S3<double> s3_d_d{.1};
}
void test1()
{
G0 g0;
G1 g1;
G2 g2;
}
int main(int, char**)
{
test0();
test1();
return 0;
};
// test0
// { dg-output "default std::handle_contract_violation called: .*.C 11 S::S .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 11 S::S .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 20 SInline::SInline .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 20 SInline::SInline .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 26 SDelegate0::SDelegate0 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 26 SDelegate0::SDelegate0 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 34 SDelegate1::SDelegate1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 34 SDelegate1::SDelegate1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 41 SDelegate2::SDelegate2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 40 SDelegate2::SDelegate2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 40 SDelegate2::SDelegate2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 41 SDelegate2::SDelegate2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 48 SDelegate3::SDelegate3 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 47 SDelegate3::SDelegate3 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 47 SDelegate3::SDelegate3 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 48 SDelegate3::SDelegate3 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 55 S1<int>::S1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 55 S1<int>::S1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 55 S1<double>::S1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 55 S1<double>::S1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 61 S2::S2<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 61 S2::S2<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 61 S2::S2<double> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 61 S2::S2<double> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<int>::S3<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<int>::S3<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<int>::S3<double> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<int>::S3<double> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<double>::S3<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<double>::S3<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<double>::S3<double> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<double>::S3<double> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 49 SDelegate3::~SDelegate3 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 49 SDelegate3::~SDelegate3 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 42 SDelegate2::~SDelegate2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 42 SDelegate2::~SDelegate2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 35 SDelegate1::~SDelegate1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 35 SDelegate1::~SDelegate1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 28 SDelegate0::~SDelegate0 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 28 SDelegate0::~SDelegate0 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 21 SInline::~SInline .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 21 SInline::~SInline .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 12 S::~S .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 12 S::~S .*(\n|\r\n|\r)*" }
// test1
// { dg-output "default std::handle_contract_violation called: .*.C 73 G0::G0 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 80 G1::G1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 81 G1::~G1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 74 G0::~G0 .*(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,35 @@
// Tests to ensure that an invalid this parm cannot be used in pre on ctors or
// in post on dtors.
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
struct F0
{
F0() [[ pre: x > 0 ]]; // { dg-error "invalid use of member" }
~F0() [[ post: x > 0 ]]; // { dg-error "invalid use of member" }
int x{-1};
};
struct F1
{
F1() [[ pre: this->x > 0 ]]; // { dg-error "may not be used" }
~F1() [[ post: this->x > 0 ]]; // { dg-error "may not be used" }
int x{-1};
};
struct F2
{
F2()
[[ post ret: false ]] // { dg-error "does not return a value" }
{
}
~F2()
[[ post r: false ]] // { dg-error "does not return a value" }
{
}
void f()
[[ post r: false ]] // { dg-error "does not return a value" }
{
}
};

View File

@@ -0,0 +1,37 @@
// Tests to ensure that contracts have a properly cv qualified this
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
struct S
{
int g() const { return x_; }
int f() { return x_; }
void mem_c() const
[[ pre: f() ]] // { dg-error "discards qualifiers" }
{
}
void mem_nc()
[[ pre: f() ]]
{
}
void memc_c() const
[[ pre: g() ]]
{
}
void memc_nc()
[[ pre: g() ]]
{
}
private:
int x_{-10};
};
int main(int, char**)
{
S s;
return 0;
};

View File

@@ -0,0 +1,108 @@
// Tests to ensure that deduced return types work with postconditions using
// the return value on defining declarations.
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts --param ggc-min-heapsize=0 --param ggc-min-expand=0" }
auto undeduced(int z)
{
if (!(sizeof(decltype(undeduced(5))) > 4)) // { dg-error "before deduction" }
return 5;
return 4;
}
// defining declaration, fine
auto g0(int a) [[ pre: a < 0 ]] [[ post r: r > 0 ]]
{
return -a * 1.2;
}
// non defining post using nondeduced identifier, fine
int g1(int m) [[ post r: r == m ]];
int g1(int n) [[ post s: s == n ]]
{
return -n;
}
int g2(int z)
[[ pre: sizeof(decltype(g2(5))) > 4 ]]; // { dg-error "not declared" }
int g3(int z)
[[ pre: sizeof(decltype(g2(5))) > 4 ]]
{
return -z;
}
// deduced that doesn't use return, good
auto g4(int m) [[ post: m ]];
auto g4(int m) [[ post: m ]]
{
return -m;
}
auto g5(int m) [[ pre: m ]];
auto g5(int m) [[ pre: m ]]
{
return -m;
}
template<typename T>
auto g6(T t) [[ post r: r == t ]];
template<typename S>
auto g6(S s) [[ post q: q == s ]]
{
return s;
}
template<typename T>
T g7(T t) [[ post r: r == t ]];
template<typename S>
S g7(S s) [[ post q: q == s ]]
{
return s;
}
template<typename T>
auto g8(T t) [[ post r: r == t && sizeof(decltype(::g8(t))) > 2 ]]; // { dg-error "not been declared" }
// This failure is related to the fact that we've invalidated the previous
// contract.
template<typename S>
auto g8(S s) [[ post q: q == s && sizeof(decltype(::g8(s))) > 2 ]] // { dg-error "mismatched" }
{
return s;
}
// non defining pre, bad
auto f0(int z)
[[ pre: sizeof(decltype(f0(5))) > 4 ]]; // { dg-error "not declared" }
// defining pre, still ill formed
auto f1(int z)
[[ pre: sizeof(decltype(f1(5))) > 4 ]] // { dg-error "not declared" }
{
return '5';
}
// undeduced using postcon, OK
auto f2(int m) [[ post r: r == m ]];
auto f2(int n) [[ post s: s == n ]]
{
return n;
}
template<typename T>
void f3(T t) [[ post r: false ]] // { dg-error "function does not return a value" }
{
}
int main(int, char**)
{
return 0;
}

View File

@@ -0,0 +1,84 @@
// check that contracts work around deduced return types
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
auto g0(int a) [[ pre: a < 0 ]] [[ post r: r > 0 ]]
{
return -a * 1.2;
}
int g1(int m) [[ post r: r == m ]];
int g1(int n) [[ post s: s == n ]]
{
return -n;
}
int g2(int z)
{
return -z;
}
int g3(int z)
[[ pre: sizeof(decltype(g2(5))) > 4 ]]
{
return -z;
}
auto g4(int m) [[ post: m ]];
auto g4(int m) [[ post: m ]]
{
return -m;
}
auto g5(int m) [[ pre: m ]];
auto g5(int m) [[ pre: m ]]
{
return -m;
}
template<typename T>
auto g6(T t) [[ post r: r == t ]];
template<typename S>
auto g6(S s) [[ post q: q == s ]]
{
return -s;
}
// template<typename T>
// T g7(T t) [[ post r: r == t ]];
template<typename S>
S g7(S s) [[ post q: q == s ]]
{
return -s;
}
int main(int, char**) {
g0(5);
g1(6);
g2(1);
g3(1);
g4(0);
g5(0);
g6(5);
g6(5.5);
g7(5);
g7(6.6);
return 0;
}
// { dg-output "default std::handle_contract_violation called: .*.C 5 g0 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 5 g0 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 12 g1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 23 g3 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 30 g4 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 37 g5 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 46 g6<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 46 g6<double> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 55 g7<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 55 g7<double> .*(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,40 @@
// ensure contracts on friend declarations are a complete class context
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
struct X {
friend void fn0(X x) [[ pre: x.a > 0 ]] { }
friend void fn2(X x);
static void fns0(X x) [[ pre: x.a > 0 ]] { }
static void fns1(X x) [[ pre: x.a > 0 ]];
static void fns2(X x);
friend void fn(X &x) { x.a = -5; }
private:
int a{10};
};
void fn2(X x) [[ pre: x.a > 0 ]] { }
void X::fns1(X x) { }
void X::fns2(X x) [[ pre: x.a > 0 ]] { }
int main(int, char**) {
X x;
fn(x); // no contract
fn0(x);
fn2(x);
X::fns0(x);
X::fns1(x);
X::fns2(x);
return 0;
}
// { dg-output "default std::handle_contract_violation called: .*.C 6 fn0 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 19 fn2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 9 X::fns0 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 X::fns1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 21 X::fns2 .*(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,14 @@
// { dg-do compile }
#ifdef __cpp_contracts
static_assert (false);
#endif
#ifdef __cpp_contracts_literal_semantics
static_assert (false);
#endif
#ifdef __cpp_contracts_roles
static_assert (false);
#endif

View File

@@ -0,0 +1,30 @@
// test that ignored contracts do instatiate templates
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
template<typename T>
int f(T t)
{
return -t;
}
int dummy()
{
[[ assert ignore: f(1.0) > 0 ]];
return -1;
}
template<>
int f(double t) // { dg-error "specialization of.*after instantiation" }
{
return -1.0;
}
int main()
{
dummy();
f(1);
f(1.0);
return 0;
}

View File

@@ -0,0 +1,26 @@
// baseline for testing assert contracts being turned into compile time
// assumptions; see contracts-assume2 for the assumed case
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts" }
#include <cstdio>
int fun(int x) {
[[assert audit: x > 0]];
if(x <= 0)
{
printf("%d: test x<=0 opt out\n", x);
return -1;
}
else
{
printf("%d: test x>0\n", x);
return 0;
}
}
int main(int, char**) {
volatile int x = -1;
return fun(x);
}
// { dg-shouldfail "" }

View File

@@ -0,0 +1,15 @@
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
struct Foo
{
int x;
bool y;
long z[4];
};
Foo foo() [[ pre: true ]]
{
return {};
}

View File

@@ -0,0 +1,19 @@
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
int main(int, char **)
{
int x = 5;
int y = 10;
[[ assert:
x
<
10
&&
y
>
123
]];
}
// { dg-output "default std::handle_contract_violation called: .*.C 8 main x < 10 && y > 123.*(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,15 @@
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
struct BaseA {
virtual int fun(int n) [[ pre: n > 0 ]] { return -n; }
};
struct BaseB {
virtual int fun(int n) [[ pre: n > 0 ]] { return -n; }
};
struct Child : public BaseA, BaseB {
int fun(int n) [[ pre: n > 0 ]] { return -n; }
};

View File

@@ -0,0 +1,33 @@
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
struct BaseA {
virtual int fun(int n) [[ pre: n > 0 ]] { return -n; }
};
struct BaseB {
virtual int fun(int n) [[ pre: n < 0 ]] { return -n; }
};
struct Child1 : public BaseA, BaseB {
int fun(int n) [[ pre: n > 0 ]] { return -n; } // { dg-error "mismatched" }
};
struct Child2 : public BaseA, BaseB {
int fun(int n) [[ pre: n < 0 ]] { return -n; } // { dg-error "mismatched" }
};
struct Child3 : public BaseA, BaseB {
int fun(int n) { return -n; }
};
struct Child4 : public BaseA {
int fun(int n);
};
int Child4::fun(int n)
[[ pre: n != 0 ]] // { dg-error "mismatched" }
{
return -n;
}

View File

@@ -0,0 +1,24 @@
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
void gfn3(int n) [[ pre: n > 0 ]];
struct Outer {
struct Inner {
void fn(int n) [[ pre: n > 0 && bob > 1 ]];
void fn2(int n) [[ pre: n > 0 && bob > 1 ]];
};
void fn(int m) [[ pre: m > 1 ]];
friend void Inner::fn(int n) [[ pre: n > 0 && bob > 1 ]]; // { dg-error "not declared" }
friend void gfn(int p) [[ pre: p > 0 ]];
friend void gfn(int q) [[ pre: q > 1 ]]; // { dg-error "'q' was not declared" }
// This should be okay.
friend void gfn2(int q);
friend void gfn2(int p) [[ pre: p > 0 ]] { }
static int bob;
};
int Outer::bob{-1};

View File

@@ -0,0 +1,40 @@
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
void gfn3(int n) [[ pre: n > 0 ]];
struct Outer {
struct Inner {
void fn(int n) [[ pre: n > 0 && bob > 1 ]];
};
void fn(int m) [[ pre: m > 1 ]];
friend void gfn1(int q);
friend void gfn1(int p) [[ pre: p > 0 ]] { }
friend void gfn2(int q, Outer *);
friend void gfn2(int p, Outer *) [[ pre: p > 0 ]] { }
friend void gfn3(int n);
static int bob;
};
int Outer::bob{-1};
void Outer::Inner::fn(int x) { }
void Outer::fn(int y) { }
void gfn3(int n) { }
void gfn1(int q);
int main(int, char **) {
Outer::Inner in;
in.fn(-5);
Outer out;
out.fn(-6);
gfn1(-7);
gfn2(-8, &out);
gfn3(-9);
}

View File

@@ -0,0 +1,24 @@
// Contracts shouldn't introduce more copies.
// { dg-do compile { target c++20 } }
// { dg-additional-options -fcontracts }
struct A
{
int i;
A(int i): i(i) { }
A(const A&) = delete;
};
A f(A a)
[[ pre: a.i > 0 ]]
[[ post r: r.i > 0 ]]
{
return {a.i};
}
int main()
{
if (f({42}).i != 42)
__builtin_abort ();
}

View File

@@ -0,0 +1,43 @@
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
struct Foo {
virtual int f0(int n) [[ pre: false ]] { return n; }
virtual int f1(int n) [[ pre: false ]] { return n; }
virtual int f2(int n) [[ pre: false ]] { return n; }
virtual int f3(int n) [[ pre: false ]] { return n; }
virtual int f4(int n) [[ pre: false ]] { return n; }
virtual int f5(int n) [[ pre: false ]] { return n; }
virtual int f6(int n) [[ pre: false ]] { return n; }
virtual int f7(int n) [[ pre: false ]] { return n; }
virtual int f8(int n) [[ pre: false ]] { return n; }
virtual int f9(int n) [[ pre: false ]] { return n; }
virtual int f10(int n) [[ pre: false ]] { return n; }
virtual int f11(int n) [[ pre: n > 0 ]] [[ pre: n > 1 ]] { return n; }
virtual int f12(int n) [[ pre: n > 0 ]] [[ pre: n > 1 ]] { return n; }
};
struct Bar : Foo {
[[ pre: n > -1 ]] int f0(int n = 0) override { return -n; } // { dg-error "contracts must appertain" }
int [[ pre: n > -2 ]] f1(int n = 0) override { return -n; } // { dg-error "contracts must appertain" }
int f2 [[ pre: n > -3 ]] (int n = 0) override { return -n; } // { dg-error "contracts must appertain" }
int f4([[ pre: n > -4 ]] int n = 0) override { return -n; } // { dg-error "contracts must appertain" }
int f5(int [[ pre: n > -5 ]] n = 0) override { return -n; } // { dg-error "contracts must appertain" }
int f6(int n [[ pre: n > -6 ]] = 0) override { return -n; } // { dg-error "contracts must appertain" }
int f7(int n = [[ pre: n > -7 ]] 0) override { return -n; }
// { dg-error "expected identifier" "" { target *-*-* } .-1 }
// { dg-error "expected .\{. before numeric" "" { target *-*-* } .-2 }
// { dg-error "invalid user-defined conversion" "" { target *-*-* } .-3 }
// { dg-error "expected .,." "" { target *-*-* } .-4 }
int f8(int n = 0 [[ pre: n > -8 ]]) override { return -n; }
// { dg-error "shall only introduce an attribute" "" { target *-*-* } .-1 }
int f9(int n = 0) [[ pre: n > -9 ]] override { return -n; } // { dg-error "mismatched contract" }
// The grammar doesn't appear to permit contracts after the virt-specifiers
// but the parser will happily add these to an attribute list that is not
// owned by the function declarator.
int f10(int n = 0) override [[ pre: n > -10 ]] { return -n; } // { dg-error "contracts must appertain" }
int f11(int n) [[ pre: n > 1 ]] override [[ pre: n > 0 ]] { return -n; } // { dg-error "contracts must appertain" }
int f12(int n) [[ pre: n > 0 ]] override [[ pre: n > 1 ]] { return -n; } // { dg-error "contracts must appertain" }
};

View File

@@ -0,0 +1,74 @@
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
int f1(int n)
[[pre: n >= 0]]
[[post r: r >= 0]]
[[post r: !(r < 0)]]
{
return n;
}
int f2(int n)
[[post: true]]
{
return 0;
}
int f3(int n)
[[post r: r >= n]]
{
return n + 1;
}
int f4(int n)
[[post: x > 0]] // { dg-error "not declared" }
{
return 0;
}
void f5()
[[post: true]]
{ }
void f6()
[[post r: true]] // { dg-error "function does not return a value" }
{ }
int f7(int n)
[[post: n > 0]]
{
return x; // { dg-error "not declared" }
}
void f8(int n)
[[post: n > 0]]
{
return;
}
void f9(int n)
[[post: n > 0]]
{
return n; // { dg-error "return-statement with a value" }
}
int f10(int n)
[[post: n > 0]]
{
return; // { dg-error "return-statement with no value" }
}
void f11()
[[post: true]]
{
constexpr int n = 0;
return n; // { dg-error "return-statement with a value" }
}
int f12()
[[post: true]]
{
return; // { dg-error "return-statement with no value" }
}

View File

@@ -0,0 +1,13 @@
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts" }
int f1(int n)
[[post r: r == n]]
{
return n;
}
int main()
{
f1(0);
}

View File

@@ -0,0 +1,15 @@
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts" }
// { dg-shouldfail "assert violation" }
// { dg-output "default std::handle_contract_violation called" }
int f1(int n)
[[post r: r > n]]
{
return n;
}
int main()
{
f1(0);
}

View File

@@ -0,0 +1,36 @@
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
struct S
{
S() [[post: n == 0]]
: n(0)
{ }
~S() [[post: true]]
{ }
int f1()
[[post r: n == r]]
{
return n;
}
int f2()
[[post r: r == x]] // { dg-error "not declared" }
{
return n;
}
void f3()
[[post r: n]] // { dg-error "does not return a value" }
{
}
int n = 0;
};
int main()
{
// f1(0);
}

View File

@@ -0,0 +1,19 @@
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
// Tests for function templates
template<typename T>
T f1(T n)
[[pre: n >= 0]]
[[post r: r >= 0]]
[[post r: !(r < 0)]]
{
return n;
}
void driver()
{
f1(0);
}

View File

@@ -0,0 +1,30 @@
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
// Test for class members
template<typename T>
struct S
{
S(T n)
[[post: true]]
: n(n)
{ }
T f1(T n)
[[pre: n >= 0]]
[[post r: r >= 0]]
[[post r: !(r < 0)]]
{
return n;
}
T n;
};
void driver()
{
S<int> s1(0);
s1.f1(2);
}

View File

@@ -0,0 +1,36 @@
// generic pre contract parsing checks
// check omitted, 'default', 'audit', and 'axiom' contract levels parse
// ensure that an invalid contrcat level 'off' errors
// ensure that a predicate referencing an undefined variable errors
// ensure that a missing colon after contract level errors
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
void f1(int x) [[ pre: x >= 0 ]] { }
void f2(int x) [[ pre default: x >= 0 ]] { }
void f3(int x) [[ pre audit: x >= 0 ]] { }
void f4(int x) [[ pre axiom: x >= 0 ]] { }
void finvalid(int x) [[ pre invalid: x >= 0 ]] { } // { dg-error "expected contract level" }
void fundeclared() [[ pre: x >= 0 ]] { } // { dg-error ".x. was not declared in this scope" }
void fmissingcolon(int x) [[ pre default x == 0]] { } // { dg-error "expected .:. before .x." }
int Z;
void (*fp1)(int x) [[ pre: Z > 0 ]]; // { dg-error "contracts must appertain" }
void (*fp2 [[ pre: Z > 0 ]])(int x); // { dg-error "contracts must appertain" }
typedef void (*fp3)(int x) [[ pre: Z > 0 ]]; // { dg-error "contracts must appertain" }
typedef void (*fp4 [[ pre: Z > 0 ]])(int x); // { dg-error "contracts must appertain" }
fp3 fn5(int a) [[ pre: a > 0 ]]; // { dg-bogus "contracts must appertain" }
int xyz;
[[ pre: xyz ]] struct Bar; // { dg-error "contracts must appertain" }
// { dg-warning "attribute ignored" "" { target *-*-* } .-1 }
struct [[ pre: xyz ]] Bar; // { dg-error "contracts must appertain" }
struct Bar [[ pre: xyz ]]; // { dg-error "contracts must appertain" }
struct Zoo {} x [[ pre: xyz ]]; // { dg-error "contracts must appertain" }
void f6(int x) [[ pre: x > 0 ; // { dg-error "expected .]." }
void f7(int x) [[ pre: x > 0 ]; // { dg-error "expected .]." }
void f8(int x) [[ pre: x > 0 { }; // { dg-error "expected .]." }
void f9(int x) [[ pre: x > 0 ] { }; // { dg-error "expected .]." }

View File

@@ -0,0 +1,190 @@
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
struct M
{
template<typename T>
int f(int a) [[ pre: a > 0 ]];
template<typename T>
int g(int a) [[ pre: a > 0 ]]
{
return -a;
}
template<typename T>
int f_arg(T a) [[ pre: a > 0 ]];
template<typename T>
int g_arg(T a) [[ pre: a > 0 ]]
{
return (int)-a;
}
template<typename T>
T f_ret(int a) [[ pre: a > 0 ]];
template<typename T>
T g_ret(int a) [[ pre: a > 0 ]]
{
return -a * 1.5;
}
};
template<typename T>
int M::f(int a)
{
return -a;
}
template<typename T>
int M::f_arg(T a)
{
return (int)-a;
}
template<typename T>
T M::f_ret(int a)
{
return -a * (T)1.5;
}
template<typename T>
struct S
{
template<typename Q>
int f(int a) [[ pre: a > 0 ]];
template<typename Q>
int g(int a) [[ pre: a > 0 ]]
{
return -a;
}
};
template<typename T>
template<typename Q>
int S<T>::f(int a)
{
return -a;
}
#include <cstdio>
int main(int, char**)
{
{
M m;
printf ("=================================\n");
printf ("m.f<int>(-10): %d\n", m.f<int>(-10));
printf ("m.f<double>(-11.5): %d\n", m.f<double>(-11.5));
printf ("m.f<int>(10): %d\n", m.f<int>(10));
printf ("m.f<double>(11.5): %d\n", m.f<double>(11.5));
printf ("=================================\n");
printf ("m.g<int>(-10): %d\n", m.g<int>(-10));
printf ("m.g<double>(-11.5): %d\n", m.g<double>(-11.5));
printf ("m.g<int>(10): %d\n", m.g<int>(10));
printf ("m.g<double>(11.5): %d\n", m.g<double>(11.5));
printf ("=================================\n");
printf ("m.f_arg(-10): %d\n", m.f_arg(-10));
printf ("m.f_arg(-11.5): %d\n", m.f_arg(-11.5));
printf ("m.f_arg(10): %d\n", m.f_arg(10));
printf ("m.f_arg(11.5): %d\n", m.f_arg(11.5));
printf ("=================================\n");
printf ("m.g_arg(-10): %d\n", m.g_arg(-10));
printf ("m.g_arg(-11.5): %d\n", m.g_arg(-11.5));
printf ("m.g_arg(10): %d\n", m.g_arg(10));
printf ("m.g_arg(11.5): %d\n", m.g_arg(11.5));
printf ("=================================\n");
printf ("m.f_ret<int>(-10): %d\n", m.f_ret<int>(-10));
printf ("m.f_ret<double>(-11.5): %f\n", m.f_ret<double>(-11.5));
printf ("m.f_ret<int>(10): %d\n", m.f_ret<int>(10));
printf ("m.f_ret<double>(11.5): %f\n", m.f_ret<double>(11.5));
printf ("=================================\n");
printf ("m.g_ret<int>(-10): %d\n", m.g_ret<int>(-10));
printf ("m.g_ret<double>(-11.5): %f\n", m.g_ret<double>(-11.5));
printf ("m.g_ret<int>(10): %d\n", m.g_ret<int>(10));
printf ("m.g_ret<double>(11.5): %f\n", m.g_ret<double>(11.5));
}
{
S<int> s;
printf ("=================================\n");
s.f<int>(-10);
s.f<double>(-10);
printf ("=================================\n");
s.g<int>(-10);
s.g<double>(-10);
}
{
S<double> s;
printf ("=================================\n");
s.f<int>(-10);
s.f<double>(-10);
printf ("=================================\n");
s.g<int>(-10);
s.g<double>(-10);
}
return 0;
}
// { dg-output "=================================(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 7 M::f<int> .*(\n|\r\n|\r)*" }
// { dg-output "m.f<int>.-10.: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 7 M::f<double> .*(\n|\r\n|\r)*" }
// { dg-output "m.f<double>.-11.5.: 11(\n|\r\n|\r)*" }
// { dg-output "m.f<int>.10.: -10(\n|\r\n|\r)*" }
// { dg-output "m.f<double>.11.5.: -11(\n|\r\n|\r)*" }
// { dg-output "=================================(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 M::g<int> .*(\n|\r\n|\r)*" }
// { dg-output "m.g<int>.-10.: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 M::g<double> .*(\n|\r\n|\r)*" }
// { dg-output "m.g<double>.-11.5.: 11(\n|\r\n|\r)*" }
// { dg-output "m.g<int>.10.: -10(\n|\r\n|\r)*" }
// { dg-output "m.g<double>.11.5.: -11(\n|\r\n|\r)*" }
// { dg-output "=================================(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 16 M::f_arg<int> .*(\n|\r\n|\r)*" }
// { dg-output "m.f_arg.-10.: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 16 M::f_arg<double> .*(\n|\r\n|\r)*" }
// { dg-output "m.f_arg.-11.5.: 11(\n|\r\n|\r)*" }
// { dg-output "m.f_arg.10.: -10(\n|\r\n|\r)*" }
// { dg-output "m.f_arg.11.5.: -11(\n|\r\n|\r)*" }
// { dg-output "=================================(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 19 M::g_arg<int> .*(\n|\r\n|\r)*" }
// { dg-output "m.g_arg.-10.: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 19 M::g_arg<double> .*(\n|\r\n|\r)*" }
// { dg-output "m.g_arg.-11.5.: 11(\n|\r\n|\r)*" }
// { dg-output "m.g_arg.10.: -10(\n|\r\n|\r)*" }
// { dg-output "m.g_arg.11.5.: -11(\n|\r\n|\r)*" }
// { dg-output "=================================(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 25 M::f_ret<int> .*(\n|\r\n|\r)*" }
// { dg-output "m.f_ret<int>.-10.: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 25 M::f_ret<double> .*(\n|\r\n|\r)*" }
// { dg-output "m.f_ret<double>.-11.5.: 16.500000(\n|\r\n|\r)*" }
// { dg-output "m.f_ret<int>.10.: -10(\n|\r\n|\r)*" }
// { dg-output "m.f_ret<double>.11.5.: -16.500000(\n|\r\n|\r)*" }
// { dg-output "=================================(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 28 M::g_ret<int> .*(\n|\r\n|\r)*" }
// { dg-output "m.g_ret<int>.-10.: 15(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 28 M::g_ret<double> .*(\n|\r\n|\r)*" }
// { dg-output "m.g_ret<double>.-11.5.: 16.500000(\n|\r\n|\r)*" }
// { dg-output "m.g_ret<int>.10.: -15(\n|\r\n|\r)*" }
// { dg-output "m.g_ret<double>.11.5.: -16.500000(\n|\r\n|\r)*" }
// { dg-output "=================================(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 59 S<int>::g<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 59 S<int>::g<double> .*(\n|\r\n|\r)*" }
// { dg-output "=================================(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 59 S<double>::g<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 59 S<double>::g<double> .*(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,212 @@
// basic test to ensure pre contracts work for free functions
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
#include <cstdio>
namespace nullary
{
int x = 10;
int y = 10;
void fun()
[[ pre: x < 0 ]]
{
printf("fun::x: %d\n", x);
}
void fun2()
[[ pre: x < 0 ]]
[[ pre: y < 0 ]]
{
printf("fun2::x: %d fun2::y: %d\n", x, y);
}
void funend()
[[ pre: x < 0 ]];
}
namespace nonvoid
{
int x = 10;
double y = 10.5;
struct S
{
bool z;
};
void vfun()
[[ pre: x < 0 ]]
{
printf("vfun::x: %d\n", x);
}
int fun()
[[ pre: x < 0 ]]
{
printf("fun::x: %d\n", x);
return x;
}
double fun2()
[[ pre: x < 0 ]]
[[ pre: y < 0 ]]
{
printf("fun2::x: %d fun2::y: %f\n", x, y);
return y;
}
S funend()
[[ pre: x < 0 ]];
}
namespace nonnullary
{
int x = 10;
double y = 10.5;
struct S
{
bool z;
};
void vfun(int m, double n)
[[ pre: x < 0 ]]
[[ pre: m < 0 ]]
{
printf("vfun::x: %d\n", x);
}
int fun(int m, double n)
[[ pre: x < 0 ]]
{
printf("fun::x: %d\n", x);
return x;
}
double fun2(int m, double n)
[[ pre: x < 0 ]]
[[ pre: y < 0 ]]
[[ pre: m < 0 ]]
[[ pre: n < 0 ]]
{
printf("fun2::x: %d fun2::y: %f\n", x, y);
return y;
}
S funend(int m, double n)
[[ pre: x < 0 ]]
[[ pre: m < 0 ]];
}
int main(int, char**) {
// nullary void
{
nullary::fun();
nullary::fun2();
nullary::funend();
}
// nullary non void
{
nonvoid::vfun();
int f = 13;
f = nonvoid::fun();
printf("main::f: %d\n", f);
double d = 13.37;
d = nonvoid::fun2();
printf("main::d: %f\n", d);
nonvoid::S s = nonvoid::funend();
printf("main::s.z: %d\n", s.z ? 1 : 0);
}
// non-nullary non-void
{
int x = 11;
double y = 11.5;
nonnullary::vfun(x, y);
int f = 13;
f = nonnullary::fun(x, y);
printf("main::f: %d\n", f);
double d = 13.37;
d = nonnullary::fun2(x, y);
printf("main::d: %f\n", d);
nonnullary::S s = nonnullary::funend(x, y);
printf("main::s.z: %d\n", s.z ? 1 : 0);
}
return 0;
}
namespace nullary
{
void funend()
[[ pre: x < 0 ]]
{
printf("funend::x: %d\n", x);
}
}
namespace nonvoid
{
S funend()
[[ pre: x < 0 ]]
{
printf("funend::x: %d\n", x);
S s;
s.z = true;
return s;
}
}
namespace nonnullary
{
S funend(int m, double n)
[[ pre: x < 0 ]]
[[ pre: m < 0 ]]
{
printf("funend::x: %d\n", x);
S s;
s.z = true;
return s;
}
}
// { dg-output "default std::handle_contract_violation called: .*.C 12 nullary::fun .*(\n|\r\n|\r)*" }
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 18 nullary::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 19 nullary::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "fun2::x: 10 fun2::y: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 146 nullary::funend .*(\n|\r\n|\r)*" }
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 39 nonvoid::vfun .*(\n|\r\n|\r)*" }
// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 45 nonvoid::fun .*(\n|\r\n|\r)*" }
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
// { dg-output "main::f: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 52 nonvoid::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 53 nonvoid::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 155 nonvoid::funend .*(\n|\r\n|\r)*" }
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 74 nonnullary::vfun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 75 nonnullary::vfun .*(\n|\r\n|\r)*" }
// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 81 nonnullary::fun .*(\n|\r\n|\r)*" }
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
// { dg-output "main::f: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 88 nonnullary::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 89 nonnullary::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 90 nonnullary::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 91 nonnullary::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 167 nonnullary::funend .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 168 nonnullary::funend .*(\n|\r\n|\r)*" }
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,33 @@
// ensure the feature test macros are defined pre c++20 while we still support
// -fcontracts independent of std version
// { dg-do compile { target c++11 } }
// { dg-additional-options "-fcontracts" }
static_assert (__cpp_contracts >= 201906, "__cpp_contracts");
static_assert (__cpp_contracts_literal_semantics >= 201906, "__cpp_contracts_literal_semantics");
static_assert (__cpp_contracts_roles >= 201906, "__cpp_contracts_roles");
int main()
{
int x;
[[assert: x >= 0]];
[[assert default: x < 0]];
[[assert audit: x == 0]];
[[assert axiom: x == 1]];
[[assert: x > 0 ? true : false]];
[[assert: x < 0 ? true : false]];
[[assert ignore: x >= 0]];
[[assert assume: x >= 0]];
[[assert check_never_continue: x >= 0]];
[[assert check_maybe_continue: x >= 0]];
[[assert %default: x >= 0]];
[[assert default %default: x < 0]];
[[assert audit %default: x == 0]];
[[assert axiom %default: x == 1]];
return 0;
}

View File

@@ -0,0 +1,22 @@
// basic test to ensure contracts work pre-c++2a
// { dg-do run { target c++11 } }
// { dg-additional-options "-fcontracts -fcontract-continuation-mode=on" }
int f(int n)
[[ pre: n > 0 ]]
[[ post r: r < 0 ]]
{
[[ assert: n > 0 ]];
return -n;
}
int main()
{
f(-5);
return 0;
}
// { dg-output "default std::handle_contract_violation called: .*.C 6 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 9 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 7 .*(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,525 @@
// tests to ensure pre contracts work on member functions
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
#include <cstdio>
namespace member
{
int x = 10;
double y = 10.5;
struct S
{
bool z;
};
struct T1
{
void vfun(int m, double n)
[[ pre: x < 0 ]]
[[ pre: m < 0 ]];
int fun(int m, double n)
[[ pre: x < 0 ]];
double fun2(int m, double n)
[[ pre: x < 0 ]]
[[ pre: y < 0 ]]
[[ pre: m < 0 ]]
[[ pre: n < 0 ]];
S funend(int m, double n)
[[ pre: x < 0 ]]
[[ pre: m < 0 ]];
};
void T1::vfun(int m, double n)
[[ pre: x < 0 ]]
[[ pre: m < 0 ]]
{
printf("vfun::x: %d\n", x);
}
int T1::fun(int m, double n)
[[ pre: x < 0 ]]
{
printf("fun::x: %d\n", x);
return x;
}
double T1::fun2(int m, double n)
[[ pre: x < 0 ]]
[[ pre: y < 0 ]]
[[ pre: m < 0 ]]
[[ pre: n < 0 ]]
{
printf("fun2::x: %d fun2::y: %f\n", x, y);
return y;
}
}
namespace special
{
struct T1
{
T1(int m, int n)
[[ pre: m < 0 ]]
[[ pre: n < 0 ]];
int operator+(int m)
[[ pre: m > 0 ]]
[[ pre: v > 0 ]];
int operator-(int m)
[[ pre: m > 0 ]]
[[ pre: v > 0 ]];
~T1()
[[ pre: v > 0 ]];
int v{-10};
};
T1::T1(int m, int n)
[[ pre: m < 0 ]]
[[ pre: n < 0 ]]
: v{-m * n}
{
}
int T1::operator+(int m)
[[ pre: m > 0 ]]
[[ pre: v > 0 ]]
{
return v + m;
}
int T1::operator-(int m)
[[ pre: m > 0 ]]
[[ pre: v > 0 ]]
{
return v - m;
}
T1::~T1()
[[ pre: v > 0 ]]
{
}
struct T2
{
T2(int m, int n)
[[ pre: m < 0 ]]
[[ pre: n < 0 ]]
: v{-m * n}
{
}
int operator+(int m)
[[ pre: m > 0 ]]
[[ pre: v > 0 ]]
{
return v + m;
}
int operator-(int m)
[[ pre: m > 0 ]]
[[ pre: v > 0 ]]
{
return v - m;
}
~T2()
[[ pre: v > 0 ]]
{
}
int v{-10};
};
struct TC : T1
{
TC(int m, int n)
[[ pre: m < -1 ]]
[[ pre: n < -1 ]]
: T1{m, n}
{
}
~TC()
[[ pre: vc < 0 ]]
{
}
TC(int a)
[[ pre: a < 0 ]]
: TC{a, a}
{
}
int vc{10};
};
void test()
{
T1 t1{10, 20};
int a = t1 - -5;
int b = t1 + -5;
printf("==========\n");
T2 t2{10, 20};
int k = t2 - -5;
int j = t2 + -5;
printf("==========\n");
TC tc{10, 20};
printf("==========\n");
TC tc2{10};
printf("==========\n");
}
}
namespace virt
{
struct T1
{
virtual int fun(int m, int n)
[[ pre: m > 0 ]]
[[ pre: n > 0 ]]
[[ pre: v > 0 ]];
int v{-10};
};
int T1::fun(int m, int n)
[[ pre: m > 0 ]]
[[ pre: n > 0 ]]
[[ pre: v > 0 ]]
{
printf("T1::fun::m: %d, T1::fun::n: %d, T1::v: %d\n", m, n, v);
return m * n * v;
}
struct T2 : T1
{
};
struct T3 : T2
{
virtual int fun(int m, int n)
[[ pre: m > 0 ]]
[[ pre: n > 0 ]]
[[ pre: v > 0 ]]
override
{
printf("T3::fun::m: %d, T3::fun::n: %d, T3::v: %d\n", m, n, v);
return m * n * v;
}
};
struct T3b : T2
{
virtual int fun(int m, int n)
[[ pre: m > 0 ]]
[[ pre: n > 0 ]]
[[ pre: v > 0 ]]
override;
int p(int a)
[[ pre: a > 0 ]]
[[ pre: v > 0 ]];
int u(int a)
[[ pre: a > 0 ]]
[[ pre: z > 0 ]];
int n(int a)
[[ pre: a > 0 ]];
static int Sn(int a)
[[ pre: a > 0 ]];
int z{-10};
};
int T3b::fun(int m, int n)
[[ pre: m > 0 ]]
[[ pre: n > 0 ]]
[[ pre: v > 0 ]]
{
printf("T3b::fun::m: %d, T3b::fun::n: %d, T3b::v: %d\n", m, n, v);
return m * n * v;
}
int T3b::p(int a)
[[ pre: a > 0 ]]
[[ pre: v > 0 ]]
{
printf("T3b::p: a: %d, v: %d\n", a, v);
return a * v;
}
int T3b::u(int a)
[[ pre: a > 0 ]]
[[ pre: z > 0 ]]
{
printf("T3b::u: a: %d, z: %d\n", a, z);
return a * z;
}
int T3b::n(int a)
[[ pre: a > 0 ]]
{
printf("T3b::n: a: %d\n", a);
return a;
}
int T3b::Sn(int a)
[[ pre: a > 0 ]]
{
printf("T3b::Sn: a: %d\n", a);
return a;
}
struct T3c : T2
{
int fun(int m, int n)
[[ pre: m > 0 ]]
[[ pre: n > 0 ]]
[[ pre: v > 0 ]]
{
printf("T3c::fun::m: %d, T3c::fun::n: %d, T3c::v: %d\n", m, n, v);
return m * n * v;
}
int p(int a)
[[ pre: a > 0 ]]
[[ pre: v > 0 ]]
{
printf("T3c::p: a: %d, v: %d\n", a, v);
return a * v;
}
int u(int a)
[[ pre: a > 0 ]]
[[ pre: z > 0 ]]
{
printf("T3c::u: a: %d, z: %d\n", a, z);
return a * z;
}
int n(int a)
[[ pre: a > 0 ]]
{
printf("T3c::n: a: %d\n", a);
return a;
}
static int Sn(int a)
[[ pre: a > 0 ]]
{
printf("T3c::Sn: a: %d\n", a);
return a;
}
int z{-10};
};
void t(const char *kind, T1 *t)
{
printf("=================\n%s:\n", kind);
t->fun(-1, -2);
}
void test()
{
T1 t1;
t1.fun(-10, -20);
T2 t2;
t2.fun(-10, -20);
T3 t3;
t3.fun(-10, -20);
T3b t3b;
t3b.fun(-10, -20);
T3c t3c;
t3c.fun(-10, -20);
t("T1", &t1);
t("T2", &t2);
t("T3", &t3);
t("T3b", &t3b);
t("T3c", &t3c);
printf("=============\n");
t3b.p(-3);
t3b.u(-3);
t3b.n(-3);
T3b::Sn(-3);
printf("=============\n");
t3c.p(-3);
t3c.u(-3);
t3c.n(-3);
T3c::Sn(-3);
}
}
int main(int, char**)
{
// ordinary member functions
{
int x = 11;
double y = 11.5;
member::T1 t1;
t1.vfun(x, y);
int f = 13;
f = t1.fun(x, y);
printf("main::f: %d\n", f);
double d = 13.37;
d = t1.fun2(x, y);
printf("main::d: %f\n", d);
member::S s = t1.funend(x, y);
printf("main::s.z: %d\n", s.z ? 1 : 0);
}
special::test();
virt::test();
return 0;
}
member::S member::T1::funend(int m, double n)
[[ pre: x < 0 ]]
[[ pre: m < 0 ]]
{
printf("funend::x: %d\n", x);
S s;
s.z = true;
return s;
}
// { dg-output "default std::handle_contract_violation called: .*.C 37 member::T1::vfun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 38 member::T1::vfun .*(\n|\r\n|\r)*" }
// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 45 member::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
// { dg-output "main::f: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 52 member::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 53 member::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 54 member::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 55 member::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 397 member::T1::funend .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 398 member::T1::funend .*(\n|\r\n|\r)*" }
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 85 special::T1::T1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 86 special::T1::T1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 99 special::T1::operator- .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 100 special::T1::operator- .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 92 special::T1::operator. .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 93 special::T1::operator. .*(\n|\r\n|\r)*" }
// { dg-output "==========(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 113 special::T2::T2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 114 special::T2::T2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 127 special::T2::operator- .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 128 special::T2::operator- .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 120 special::T2::operator. .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 121 special::T2::operator. .*(\n|\r\n|\r)*" }
// { dg-output "==========(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 144 special::TC::TC .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 145 special::TC::TC .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 85 special::T1::T1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 86 special::T1::T1 .*(\n|\r\n|\r)*" }
// { dg-output "==========(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 156 special::TC::TC .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 144 special::TC::TC .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 145 special::TC::TC .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 85 special::T1::T1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 86 special::T1::T1 .*(\n|\r\n|\r)*" }
// { dg-output "==========(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 151 special::TC::~TC .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 106 special::T1::~T1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 151 special::TC::~TC .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 106 special::T1::~T1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 134 special::T2::~T2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 106 special::T1::~T1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 196 virt::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 197 virt::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 198 virt::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "T1::fun::m: -10, T1::fun::n: -20, T1::v: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 196 virt::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 197 virt::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 198 virt::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "T1::fun::m: -10, T1::fun::n: -20, T1::v: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 211 virt::T3::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 212 virt::T3::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 213 virt::T3::fun .*(\n|\r\n|\r)*" }
// { dg-output "T3::fun::m: -10, T3::fun::n: -20, T3::v: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 247 virt::T3b::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 248 virt::T3b::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 249 virt::T3b::fun .*(\n|\r\n|\r)*" }
// { dg-output "T3b::fun::m: -10, T3b::fun::n: -20, T3b::v: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 288 virt::T3c::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 289 virt::T3c::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 290 virt::T3c::fun .*(\n|\r\n|\r)*" }
// { dg-output "T3c::fun::m: -10, T3c::fun::n: -20, T3c::v: -10(\n|\r\n|\r)*" }
// { dg-output "=================(\n|\r\n|\r)*" }
// { dg-output "T1:(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 196 virt::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 197 virt::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 198 virt::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "T1::fun::m: -1, T1::fun::n: -2, T1::v: -10(\n|\r\n|\r)*" }
// { dg-output "=================(\n|\r\n|\r)*" }
// { dg-output "T2:(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 196 virt::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 197 virt::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 198 virt::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "T1::fun::m: -1, T1::fun::n: -2, T1::v: -10(\n|\r\n|\r)*" }
// { dg-output "=================(\n|\r\n|\r)*" }
// { dg-output "T3:(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 211 virt::T3::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 212 virt::T3::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 213 virt::T3::fun .*(\n|\r\n|\r)*" }
// { dg-output "T3::fun::m: -1, T3::fun::n: -2, T3::v: -10(\n|\r\n|\r)*" }
// { dg-output "=================(\n|\r\n|\r)*" }
// { dg-output "T3b:(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 247 virt::T3b::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 248 virt::T3b::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 249 virt::T3b::fun .*(\n|\r\n|\r)*" }
// { dg-output "T3b::fun::m: -1, T3b::fun::n: -2, T3b::v: -10(\n|\r\n|\r)*" }
// { dg-output "=================(\n|\r\n|\r)*" }
// { dg-output "T3c:(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 288 virt::T3c::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 289 virt::T3c::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 290 virt::T3c::fun .*(\n|\r\n|\r)*" }
// { dg-output "T3c::fun::m: -1, T3c::fun::n: -2, T3c::v: -10(\n|\r\n|\r)*" }
// { dg-output "=============(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 256 virt::T3b::p .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 257 virt::T3b::p .*(\n|\r\n|\r)*" }
// { dg-output "T3b::p: a: -3, v: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 264 virt::T3b::u .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 265 virt::T3b::u .*(\n|\r\n|\r)*" }
// { dg-output "T3b::u: a: -3, z: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 272 virt::T3b::n .*(\n|\r\n|\r)*" }
// { dg-output "T3b::n: a: -3(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 279 virt::T3b::Sn .*(\n|\r\n|\r)*" }
// { dg-output "T3b::Sn: a: -3(\n|\r\n|\r)*" }
// { dg-output "=============(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 297 virt::T3c::p .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 298 virt::T3c::p .*(\n|\r\n|\r)*" }
// { dg-output "T3c::p: a: -3, v: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 305 virt::T3c::u .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 306 virt::T3c::u .*(\n|\r\n|\r)*" }
// { dg-output "T3c::u: a: -3, z: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 313 virt::T3c::n .*(\n|\r\n|\r)*" }
// { dg-output "T3c::n: a: -3(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 320 virt::T3c::Sn .*(\n|\r\n|\r)*" }
// { dg-output "T3c::Sn: a: -3(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,92 @@
// test that contracts on overriding functions are found correctly
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
struct Base
{
virtual int f(int a) [[ pre: a > 0 ]];
};
int Base::f(int a)
{
return a + 10;
}
// inherits original
struct Child0 : Base
{
};
// defined out of line, explicit override
struct Child1 : Base
{
virtual int f(int a) override;
};
int Child1::f(int a)
{
return a + 20;
}
// defined out of line
struct Child2 : Base
{
int f(int a);
};
int Child2::f(int a)
{
return a + 30;
}
// defined inline, explicitly override
struct Child3 : Base
{
virtual int f(int a) override
{
return a + 40;
}
};
// defined inline
struct Child4 : Base
{
int f(int a)
{
return a + 50;
}
};
#include <cstdio>
int main(int, char**)
{
Base b;
Child0 c0;
Child1 c1;
Child2 c2;
Child3 c3;
Child4 c4;
printf("Base: %d\n", b.f(-10));
printf("Child0: %d\n", c0.f(-10));
printf("Child1: %d\n", c1.f(-10));
printf("Child2: %d\n", c2.f(-10));
printf("Child3: %d\n", c3.f(-10));
printf("Child4: %d\n", c4.f(-10));
return 0;
}
// { dg-output "default std::handle_contract_violation called: .*.C 7 Base::f .*(\n|\r\n|\r)*" }
// { dg-output "Base: 0(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 7 Base::f .*(\n|\r\n|\r)*" }
// { dg-output "Child0: 0(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 7 Child1::f .*(\n|\r\n|\r)*" }
// { dg-output "Child1: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 7 Child2::f .*(\n|\r\n|\r)*" }
// { dg-output "Child2: 20(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 7 Child3::f .*(\n|\r\n|\r)*" }
// { dg-output "Child3: 30(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 7 Child4::f .*(\n|\r\n|\r)*" }
// { dg-output "Child4: 40(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,81 @@
// basic test to ensure pre contracts work for free templates
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
#include <cstdio>
template<typename T>
int body(int a)
[[ pre: a > 0 ]]
{
T t = a * 2.5;
return t;
}
template<typename T>
int none(int a)
[[ pre: a > 0 ]]
{
return -a;
}
template<typename T>
int arg0(T t)
[[ pre: t > 0 ]]
{
return -t - 10;
}
template<typename T>
int arg1(int a, T t)
[[ pre: a > 0 ]]
[[ pre: t > 0 ]]
{
return -t * a;
}
template<typename T>
T ret(int a)
[[ pre: a > 0 ]]
{
return -a;
}
int main(int, char**)
{
printf("%d\n", body<int>(-1));
printf("%d\n", body<double>(-2));
printf("%d\n", none<int>(-1));
printf("%d\n", none<double>(-2));
printf("%d\n", arg0(-1));
printf("%d\n", arg0(-2.9));
printf("%d\n", arg1(-3, -1));
printf("%d\n", arg1(-4, -2.9));
printf("%d\n", (int)ret<int>(-3));
printf("%d\n", (int)ret<double>(-4.9));
return 0;
}
// { dg-output "default std::handle_contract_violation called: .*.C 8 body<int> .*(\n|\r\n|\r)*" }
// { dg-output "-2(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 8 body<double> .*(\n|\r\n|\r)*" }
// { dg-output "-5(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 16 none<int> .*(\n|\r\n|\r)*" }
// { dg-output "1(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 16 none<double> .*(\n|\r\n|\r)*" }
// { dg-output "2(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 23 arg0<int> .*(\n|\r\n|\r)*" }
// { dg-output "-9(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 23 arg0<double> .*(\n|\r\n|\r)*" }
// { dg-output "-7(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 30 arg1<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 31 arg1<int> .*(\n|\r\n|\r)*" }
// { dg-output "-3(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 30 arg1<double> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 31 arg1<double> .*(\n|\r\n|\r)*" }
// { dg-output "-11(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 38 ret<int> .*(\n|\r\n|\r)*" }
// { dg-output "3(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 38 ret<double> .*(\n|\r\n|\r)*" }
// { dg-output "4(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,74 @@
// ensure no errors are thrown when we have to insert a decl for the internal
// unchecked function after leaving a (possibly nested) namespace
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
namespace ns0
{
int f(int a) [[ pre: a > 0 ]];
}
int ns0::f(int a) [[ pre: a > 0 ]]
{
return -a;
}
namespace ns0
{
namespace ns1
{
int f(int a) [[ pre: a > 0 ]];
}
}
int ns0::ns1::f(int a) [[ pre: a > 0 ]]
{
return -a;
}
namespace ns0
{
namespace ns1
{
int f2(int a) [[ pre: a > 0 ]];
namespace ns2
{
int f(int a) [[ pre: a > 0 ]];
}
}
int ns1::f2(int a) [[ pre: a > 0 ]]
{
return -a;
}
}
int ns0::ns1::ns2::f(int a) [[ pre: a > 0 ]]
{
return -a;
}
namespace ns0
{
struct S
{
int f(int a) [[ pre: a > 0 ]];
};
namespace ns1
{
struct S2
{
int f(int a) [[ pre: a > 0 ]];
};
}
}
int ns0::S::f(int a) [[ pre: a > 0 ]]
{
return -a;
}
int ns0::ns1::S2::f(int a) [[ pre: a > 0 ]]
{
return -a;
}

View File

@@ -0,0 +1,134 @@
// ensure no errors are thrown when we have to insert a decl for the internal
// unchecked function after leaving a (possibly nested) namespace
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
namespace ns0
{
template<typename T>
int f(T a) [[ pre: a > 0 ]];
}
template<typename T>
int ns0::f(T a) [[ pre: a > 0 ]]
{
return (int)-a;
}
namespace ns0
{
namespace ns1
{
template<typename T>
int f(T a) [[ pre: a > 0 ]];
}
}
template<typename T>
int ns0::ns1::f(T a) [[ pre: a > 0 ]]
{
return -a;
}
namespace ns0
{
namespace ns1
{
template<typename T>
int f2(T a) [[ pre: a > 0 ]];
namespace ns2
{
template<typename T>
int f(T a) [[ pre: a > 0 ]];
}
}
template<typename T>
int ns1::f2(T a) [[ pre: a > 0 ]]
{
return -a;
}
}
template<typename T>
int ns0::ns1::ns2::f(T a) [[ pre: a > 0 ]]
{
return -a;
}
namespace ns0
{
template<typename T>
struct S
{
int f(T a) [[ pre: a > 0 ]];
};
namespace ns1
{
template<typename T>
struct S2
{
int f(T a) [[ pre: a > 0 ]];
};
}
}
template<typename T>
int ns0::S<T>::f(T a) [[ pre: a > 0 ]]
{
return -a;
}
template<typename T>
int ns0::ns1::S2<T>::f(T a) [[ pre: a > 0 ]]
{
return -a;
}
#include <cstdio>
int main(int, char**)
{
printf ("%d\n", ns0::f(-1));
printf ("%d\n", ns0::ns1::f(-2));
printf ("%d\n", ns0::ns1::f2(-3));
printf ("%d\n", ns0::ns1::ns2::f(-4));
ns0::S<int> ns0_s;
printf ("%d\n", ns0_s.f(-5));
ns0::ns1::S2<int> ns0_ns1_s2;
printf ("%d\n", ns0_ns1_s2.f(-6));
printf ("%d\n", ns0::f(-7.5));
printf ("%d\n", ns0::ns1::f(-8.5));
printf ("%d\n", ns0::ns1::f2(-9.5));
printf ("%d\n", ns0::ns1::ns2::f(-10.5));
ns0::S<double> ns0_sd;
printf ("%d\n", ns0_sd.f(-11.5));
ns0::ns1::S2<double> ns0_ns1_s2d;
printf ("%d\n", ns0_ns1_s2d.f(-12.5));
return 0;
}
// { dg-output "default std::handle_contract_violation called: .*.C 13 ns0::f<int> .*(\n|\r\n|\r)*" }
// { dg-output "1(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 28 ns0::ns1::f<int> .*(\n|\r\n|\r)*" }
// { dg-output "2(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 46 ns0::ns1::f2<int> .*(\n|\r\n|\r)*" }
// { dg-output "3(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 53 ns0::ns1::ns2::f<int> .*(\n|\r\n|\r)*" }
// { dg-output "4(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 76 ns0::S<int>::f .*(\n|\r\n|\r)*" }
// { dg-output "5(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 82 ns0::ns1::S2<int>::f .*(\n|\r\n|\r)*" }
// { dg-output "6(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 13 ns0::f<double> .*(\n|\r\n|\r)*" }
// { dg-output "7(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 28 ns0::ns1::f<double> .*(\n|\r\n|\r)*" }
// { dg-output "8(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 46 ns0::ns1::f2<double> .*(\n|\r\n|\r)*" }
// { dg-output "9(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 53 ns0::ns1::ns2::f<double> .*(\n|\r\n|\r)*" }
// { dg-output "10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 76 ns0::S<double>::f .*(\n|\r\n|\r)*" }
// { dg-output "11(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 82 ns0::ns1::S2<double>::f .*(\n|\r\n|\r)*" }
// { dg-output "12(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,146 @@
// ensure no errors are thrown for various combinations of class templates
// with guarded members
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
template<typename T>
struct S
{
int f(int a) [[ pre: a > 0 ]];
int g(int a) [[ pre: a > 0 ]];
};
template<typename T>
int S<T>::f(int a) [[ pre: a > 0 ]]
{
return -a;
}
template<typename T>
int S<T>::g(int a) // Contract is inherited (error from line 10).
{
return -a;
}
template<typename T>
struct S_arg
{
int f(T a) [[ pre: a > 0 ]];
int g(T a) [[ pre: a > 0 ]];
};
template<typename T>
int S_arg<T>::f(T a) [[ pre: a > 0 ]]
{
return -a;
}
template<typename T>
int S_arg<T>::g(T a) // Contract is inherited (error from line 29).
{
return -a;
}
template<typename T>
struct S_ret
{
T f(int a) [[ pre: a > 0 ]];
T g(int a) [[ pre: a > 0 ]];
};
template<typename T>
T S_ret<T>::f(int a) [[ pre: a > 0 ]]
{
return -a;
}
template<typename T>
T S_ret<T>::g(int a) // Contract is inherited (error from line 48).
{
return -a;
}
#include <cstdio>
int main(int, char**)
{
{
S<int> s_int;
printf ("s_int.f(-10): %d\n", s_int.f(-10));
printf ("s_int.g(-10): %d\n", s_int.g(-10));
printf ("s_int.f(10): %d\n", s_int.f(10));
printf ("s_int.g(10): %d\n", s_int.g(10));
S<double> s_double;
printf ("s_double.f(-10.5): %d\n", s_double.f(-10.5));
printf ("s_double.g(-10.5): %d\n", s_double.g(-10.5));
printf ("s_double.f(10.5): %d\n", s_double.f(10.5));
printf ("s_double.g(10.5): %d\n", s_double.g(10.5));
}
{
S_arg<int> s_arg_int;
printf ("s_arg_int.f(-10): %d\n", s_arg_int.f(-10));
printf ("s_arg_int.g(-10): %d\n", s_arg_int.g(-10));
printf ("s_arg_int.f(10): %d\n", s_arg_int.f(10));
printf ("s_arg_int.g(10): %d\n", s_arg_int.g(10));
S_arg<double> s_arg_double;
printf ("s_arg_double.f(-10): %d\n", s_arg_double.f(-10));
printf ("s_arg_double.g(-10): %d\n", s_arg_double.g(-10));
printf ("s_arg_double.f(10): %d\n", s_arg_double.f(10));
printf ("s_arg_double.g(10): %d\n", s_arg_double.g(10));
}
{
S_ret<int> s_ret_int;
printf ("s_ret_int.f(-10): %d\n", s_ret_int.f(-10));
printf ("s_ret_int.g(-10): %d\n", s_ret_int.g(-10));
printf ("s_ret_int.f(10): %d\n", s_ret_int.f(10));
printf ("s_ret_int.g(10): %d\n", s_ret_int.g(10));
S_ret<double> s_ret_double;
printf ("s_ret_double.f(-10): %f\n", s_ret_double.f(-10));
printf ("s_ret_double.g(-10): %f\n", s_ret_double.g(-10));
printf ("s_ret_double.f(10): %f\n", s_ret_double.f(10));
printf ("s_ret_double.g(10): %f\n", s_ret_double.g(10));
}
return 0;
}
// { dg-output "default std::handle_contract_violation called: .*.C 14 S<int>::f .*(\n|\r\n|\r)*" }
// { dg-output "s_int.f.-10.: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 S<int>::g .*(\n|\r\n|\r)*" }
// { dg-output "s_int.g.-10.: 10(\n|\r\n|\r)*" }
// { dg-output "s_int.f.10.: -10(\n|\r\n|\r)*" }
// { dg-output "s_int.g.10.: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 14 S<double>::f .*(\n|\r\n|\r)*" }
// { dg-output "s_double.f.-10.5.: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 S<double>::g .*(\n|\r\n|\r)*" }
// { dg-output "s_double.g.-10.5.: 10(\n|\r\n|\r)*" }
// { dg-output "s_double.f.10.5.: -10(\n|\r\n|\r)*" }
// { dg-output "s_double.g.10.5.: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 33 S_arg<int>::f .*(\n|\r\n|\r)*" }
// { dg-output "s_arg_int.f.-10.: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 29 S_arg<int>::g .*(\n|\r\n|\r)*" }
// { dg-output "s_arg_int.g.-10.: 10(\n|\r\n|\r)*" }
// { dg-output "s_arg_int.f.10.: -10(\n|\r\n|\r)*" }
// { dg-output "s_arg_int.g.10.: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 33 S_arg<double>::f .*(\n|\r\n|\r)*" }
// { dg-output "s_arg_double.f.-10.: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 29 S_arg<double>::g .*(\n|\r\n|\r)*" }
// { dg-output "s_arg_double.g.-10.: 10(\n|\r\n|\r)*" }
// { dg-output "s_arg_double.f.10.: -10(\n|\r\n|\r)*" }
// { dg-output "s_arg_double.g.10.: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 52 S_ret<int>::f .*(\n|\r\n|\r)*" }
// { dg-output "s_ret_int.f.-10.: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 48 S_ret<int>::g .*(\n|\r\n|\r)*" }
// { dg-output "s_ret_int.g.-10.: 10(\n|\r\n|\r)*" }
// { dg-output "s_ret_int.f.10.: -10(\n|\r\n|\r)*" }
// { dg-output "s_ret_int.g.10.: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 52 S_ret<double>::f .*(\n|\r\n|\r)*" }
// { dg-output "s_ret_double.f.-10.: 10.000000(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 48 S_ret<double>::g .*(\n|\r\n|\r)*" }
// { dg-output "s_ret_double.g.-10.: 10.000000(\n|\r\n|\r)*" }
// { dg-output "s_ret_double.f.10.: -10.000000(\n|\r\n|\r)*" }
// { dg-output "s_ret_double.g.10.: -10.000000(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,149 @@
// generic error tests for contract redecls with generalized redecl
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
// OK if equivalent -- even through renames.
int g0(int a) [[ pre: a > 0 ]];
int g0(int a) [[ pre: a > 0 ]];
int g0b(int a) [[ pre: a > 0 ]];
int g0b(int b) [[ pre: b > 0 ]];
int g0b(int c) [[ pre: c > 0 ]]
{
return 0;
}
// OK if specified before.
int g1(int a) [[ pre: a > 0 ]];
int g1(int a);
// OK if specified after.
int g2(int a);
int g2(int a) [[ pre: a > 0 ]];
int g2b(int a);
int g2b(int b) [[ pre: b > 0 ]];
// can add to non-virtual methods
struct G0
{
int f(int a);
};
// OK to add contracts at the point of definition.
int G0::f(int a) [[ pre: a > 0 ]]
{
return -a;
}
struct G1
{
int f1(int a);
};
// OK to redeclare functions and add constraints...
int G1::f1(int a) [[ pre: a > 0 ]];
// ...and leave them off later.
int G1::f1(int a)
{
return -a;
}
int f0(int a) [[ pre: a > 0 ]];
int f0(int a) [[ pre: a > 0 ]] [[ pre: a > 10 ]]; // { dg-error "different number of contracts" }
int f1(int a) [[ pre: a > 0 ]] [[ pre: a > 10 ]];
int f1(int a) [[ pre: a > 0 ]]; // { dg-error "different number of contracts" }
int f2(int a) [[ pre: a > 0 ]];
int f2(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
int f3(int a) { return a; }
int f3(int a) [[ pre: a < 0 ]]; // { dg-error "cannot add contracts" }
struct Base
{
virtual int f(int a) [[ pre: a > 0 ]];
};
struct Child : Base
{
int f(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
};
struct S1
{
virtual int f(int a); // contracts are inherited at the point of declarations
};
int S1::f(int a) [[ pre: a > 0 ]] // { dg-error "cannot add" }
{
return -a;
}
struct S2
{
int f() { return 0; }
};
int S2::f(); // OK?
struct S3
{
int f() { return 0; }
};
int S3::f() [[pre: true]]; // { dg-error "cannot add contracts" }
// The initial decl of a guarded member must appear inside the class.
struct S4
{
int f(int a);
};
int S4::g(int a) [[ pre: a > 0 ]]; // { dg-error "no declaration matches" }
struct S5
{
template<typename T>
S5(T a);
};
template<typename T>
S5::S5(T a) [[ pre: a > 0 ]]
{
}
struct S6
{
template<typename T>
S6(T a);
};
template<typename T>
S6::S6(T a) [[ pre: a > 0 ]];
template<typename T>
S6::S6(T a)
{
}
int p0(int n)
[[ post r: r > 0 && r == n ]]
[[ post r: r > 1 && r == n ]]
[[ post r: r > 2 && r == n ]]
[[ post r: r > 3 && r == n ]];
int p0(int z)
[[ post r: r > 0 && r == z ]]
[[ post r1: r1 > 1 && r1 == z ]]
[[ post r2: r2 > 2 && r2 == z ]]
[[ post r3: r3 > 3 && r3 == z ]]
{
return z;
}

View File

@@ -0,0 +1,149 @@
// generic error tests for generalized contract redecls
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
// allowed to repeat contracts or omit them
int g0(int a) [[ pre: a > 0 ]];
int g0(int a) [[ pre: a > 0 ]];
int g1(int a) [[ pre: a > 0 ]];
int g1(int a);
// allowed to add from none if generalized redecl is on (by default)
int g2(int a);
int g2(int a) [[ pre: a > 0 ]];
// can add to non-virtual methods
struct G0
{
int f(int a);
};
int G0::f(int a) [[ pre: a > 0 ]]
{
return -a;
}
struct G1
{
int f(int a);
};
int G1::f(int a) [[ pre: a > 0 ]];
int G1::f(int a)
{
return -a;
}
// allowed to redeclare even without contracts
struct G2
{
int f(int a);
};
int G2::f(int a);
int f0(int a) [[ pre: a > 0 ]];
int f0(int a) [[ pre: a > 0 ]] [[ pre: a > 10 ]]; // { dg-error "different number of contracts" }
int f1(int a) [[ pre: a > 0 ]];
int f1(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
int f2(int a) { return a; }
int f2(int a) [[ pre: a < 0 ]]; // { dg-error "cannot add contracts after definition" }
struct Base
{
virtual int f(int a) [[ pre: a > 0 ]];
};
struct Child : Base
{
int f(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
};
// the initial decl of a guarded member must appear inside the class
struct F2
{
int f(int a);
};
int F2::g(int a) [[ pre: a > 0 ]]; // { dg-error "no declaration matches" }
// FIXME if we move F2 down then a different error makes F2 undeclared
struct F0
{
virtual int f(int a);
};
int F0::f(int a); // { dg-error "declaration.*is not definition" }
struct F1
{
virtual int f(int a);
};
int F1::f(int a) [[ pre: a > 0 ]] // { dg-error "cannot add" }
{
return -a;
}
// cannot "re"declare members of a forward declared class
struct F2;
int F2::test(); // { dg-error "no declaration matches" }
int F2::test2() [[ pre: true ]]; // { dg-error "no declaration matches" }
// can only redeclare member functions
struct F3
{
int x;
typedef int my_int;
struct Inner0;
struct Inner1;
enum my_enum0; // { dg-error "use of enum.*without previous decl" }
enum my_enum1 { E1, E2 };
int test0();
int test1();
int test2();
};
int F3::x{-1}; // { dg-error "is not a static data member" }
typedef double F3::my_int; // { dg-error "typedef name may not be a nested-name-specifier" }
struct F3::Inner0; // { dg-warning "declaration.*does not declare anything" }
struct F3::Inner1 { };
enum F3::my_enum1 { E0, E1, END }; // { dg-error "multiple definition" }
struct F4
{
int test0();
int F3::test0() [[ pre: true ]]; // { dg-error "cannot declare member function" }
friend int F3::test1();
friend int F3::test2();
};
int F3::test2() [[ pre: true ]] { return -1; }
void dummy0()
{
int F4::test0() [[ pre: true ]]; // { dg-error "qualified-id in declaration" }
}
namespace ns0
{
typedef int value;
struct X
{
int test1(value);
typedef double value;
int test2(value);
};
int X::test1(value); // { dg-error "no declaration matches" }
int X::test2(value);
}

View File

@@ -0,0 +1,195 @@
// basic test to ensure contracts generalized redecl works
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
#include <cstdio>
namespace defining
{
int x = 10;
double y = 10.5;
struct S
{
bool z;
};
struct T1
{
void vfun(int m, double n);
int fun(int m, double n);
double fun2(int m, double n);
S funend(int m, double n);
};
void T1::vfun(int m, double n)
[[ pre: x < 0 ]]
[[ pre: m < 0 ]]
{
printf("vfun::x: %d\n", x);
}
int T1::fun(int m, double n)
[[ pre: x < 0 ]]
{
printf("fun::x: %d\n", x);
return x;
}
double T1::fun2(int m, double n)
[[ pre: x < 0 ]]
[[ pre: y < 0 ]]
[[ pre: m < 0 ]]
[[ pre: n < 0 ]]
{
printf("fun2::x: %d fun2::y: %f\n", x, y);
return y;
}
}
namespace nondefining
{
int x = 10;
double y = 10.5;
struct S
{
bool z;
};
struct T1
{
void vfun(int m, double n);
int fun(int m, double n);
double fun2(int m, double n);
S funend(int m, double n);
};
void T1::vfun(int m, double n)
[[ pre: x < 0 ]]
[[ pre: m < 0 ]];
int T1::fun(int m, double n)
[[ pre: x < 0 ]];
double T1::fun2(int m, double n)
[[ pre: x < 0 ]]
[[ pre: y < 0 ]]
[[ pre: m < 0 ]]
[[ pre: n < 0 ]];
void T1::vfun(int m, double n)
{
printf("vfun::x: %d\n", x);
}
int T1::fun(int m, double n)
{
printf("fun::x: %d\n", x);
return x;
}
double T1::fun2(int m, double n)
{
printf("fun2::x: %d fun2::y: %f\n", x, y);
return y;
}
}
int main(int, char**) {
// defining redecl
{
int x = 11;
double y = 11.5;
defining::T1 t1;
t1.vfun(x, y);
int f = 13;
f = t1.fun(x, y);
printf("main::f: %d\n", f);
double d = 13.37;
d = t1.fun2(x, y);
printf("main::d: %f\n", d);
defining::S s = t1.funend(x, y);
printf("main::s.z: %d\n", s.z ? 1 : 0);
}
// nondefining redecl
{
int x = 12;
double y = 12.5;
nondefining::T1 t1;
t1.vfun(x, y);
int f = 13;
f = t1.fun(x, y);
printf("main::f: %d\n", f);
double d = 13.37;
d = t1.fun2(x, y);
printf("main::d: %f\n", d);
nondefining::S s = t1.funend(x, y);
printf("main::s.z: %d\n", s.z ? 1 : 0);
}
return 0;
}
namespace defining
{
S T1::funend(int m, double n)
[[ pre: x < 0 ]]
[[ pre: m < 0 ]]
{
printf("funend::x: %d\n", x);
S s;
s.z = true;
return s;
}
}
namespace nondefining
{
S T1::funend(int m, double n)
[[ pre: x < 0 ]]
[[ pre: m < 0 ]];
S T1::funend(int m, double n)
{
printf("funend::x: %d\n", x);
S s;
s.z = true;
return s;
}
}
// { dg-output "default std::handle_contract_violation called: .*.C 25 defining::T1::vfun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 26 defining::T1::vfun .*(\n|\r\n|\r)*" }
// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 32 defining::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
// { dg-output "main::f: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 39 defining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 40 defining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 41 defining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 42 defining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 138 defining::T1::funend .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 139 defining::T1::funend .*(\n|\r\n|\r)*" }
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 68 nondefining::T1::vfun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 69 nondefining::T1::vfun .*(\n|\r\n|\r)*" }
// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 71 nondefining::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
// { dg-output "main::f: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 73 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 74 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 75 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 76 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 151 nondefining::T1::funend .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 152 nondefining::T1::funend .*(\n|\r\n|\r)*" }
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,56 @@
// test that free functions can be redeclared with contracts without affecting
// normal default parm handling
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
#include <cstdio>
int f(int a, int, int c = 10);
int f(int a, int b = 11, int);
int f(int a, int b, int c)
[[ pre: a < 0 ]]
[[ pre: b < 0 ]]
[[ pre: c < 0 ]];
int f(int, int, int);
int f(int a, int b, int c)
{
printf("f: a: %d, b: %d, c: %d\n", a, b, c);
return a * b - c;
}
int f(int a = 12, int, int);
int main(int, char **)
{
f(1,1,1);
printf("=====\n");
f(1,1);
printf("=====\n");
f(1);
printf("=====\n");
f();
printf("=====\n");
}
// { dg-output "default std::handle_contract_violation called: .*.C 10 f .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 11 f .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 12 f .*(\n|\r\n|\r)*" }
// { dg-output "f: a: 1, b: 1, c: 1(\n|\r\n|\r)*" }
// { dg-output "=====(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 f .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 11 f .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 12 f .*(\n|\r\n|\r)*" }
// { dg-output "f: a: 1, b: 1, c: 10(\n|\r\n|\r)*" }
// { dg-output "=====(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 f .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 11 f .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 12 f .*(\n|\r\n|\r)*" }
// { dg-output "f: a: 1, b: 11, c: 10(\n|\r\n|\r)*" }
// { dg-output "=====(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 f .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 11 f .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 12 f .*(\n|\r\n|\r)*" }
// { dg-output "f: a: 12, b: 11, c: 10(\n|\r\n|\r)*" }
// { dg-output "=====(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,101 @@
// generic error tests for generalized contract redecls
// we also test for the warning diagnostic for strict redecl
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts -fcontract-strict-declarations=on" }
// allowed to repeat contracts or omit them
int g0(int a) [[ pre: a > 0 ]];
int g0(int a) [[ pre: a > 0 ]];
int g1(int a) [[ pre: a > 0 ]];
int g1(int a);
// allowed to add from none if generalized redecl is on (by default)
int g2(int a);
int g2(int a) [[ pre: a > 0 ]]; // { dg-warning "adds contracts" }
int g2(int a) [[ pre: a > 0 ]]; // { dg-bogus "adds contracts" }
// can add to non-virtual methods
struct G0
{
int f(int a);
};
int G0::f(int a) [[ pre: a > 0 ]] // { dg-warning "adds contracts" }
{
return -a;
}
struct G1
{
int f(int a);
};
int G1::f(int a) [[ pre: a > 0 ]]; // { dg-warning "adds contracts" }
// { dg-warning "outside of class is not definition" "" { target *-*-* } .-1 }
int G1::f(int a);
// { dg-warning "outside of class is not definition" "" { target *-*-* } .-1 }
int G1::f(int a) [[ pre: a > 0 ]];
// { dg-warning "outside of class is not definition" "" { target *-*-* } .-1 }
int G1::f(int a)
{
return -a;
}
// allowed to redeclare even without contracts
struct G2
{
int f(int a);
};
int G2::f(int a); // { dg-warning "outside of class is not definition" }
int f0(int a) [[ pre: a > 0 ]];
int f0(int a) [[ pre: a > 0 ]] [[ pre: a > 10 ]]; // { dg-error "different number of contracts" }
int f1(int a) [[ pre: a > 0 ]];
int f1(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
int f2(int a) { return a; }
int f2(int a) [[ pre: a < 0 ]]; // { dg-error "cannot add contracts after definition" }
struct Base
{
virtual int f(int a) [[ pre: a > 0 ]];
};
struct Child : Base
{
int f(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
};
// the initial decl of a guarded member must appear inside the class
struct F2
{
int f(int a);
};
int F2::g(int a) [[ pre: a > 0 ]]; // { dg-error "no declaration matches" }
// FIXME if we move F2 down then a different error makes F2 undeclared
struct F0
{
virtual int f(int a);
};
int F0::f(int a); // { dg-error "declaration.*is not definition" }
struct F1
{
virtual int f(int a);
};
int F1::f(int a) [[ pre: a > 0 ]] // { dg-error "cannot add" }
{
return -a;
}

View File

@@ -0,0 +1,195 @@
// basic test to ensure contracts generalized redecl works
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
#include <cstdio>
namespace defining
{
int x = 10;
double y = 10.5;
struct S
{
bool z;
};
struct T1
{
void vfun(int m, double n) const;
int fun(int m, double n) volatile;
double fun2(int m, double n) const volatile;
static S funend(int m, double n);
};
void T1::vfun(int m, double n) const
[[ pre: x < 0 ]]
[[ pre: m < 0 ]]
{
printf("vfun::x: %d\n", x);
}
int T1::fun(int m, double n) volatile
[[ pre: x < 0 ]]
{
printf("fun::x: %d\n", x);
return x;
}
double T1::fun2(int m, double n) const volatile
[[ pre: x < 0 ]]
[[ pre: y < 0 ]]
[[ pre: m < 0 ]]
[[ pre: n < 0 ]]
{
printf("fun2::x: %d fun2::y: %f\n", x, y);
return y;
}
}
namespace nondefining
{
int x = 10;
double y = 10.5;
struct S
{
bool z;
};
struct T1
{
void vfun(int m, double n) const;
int fun(int m, double n) volatile;
double fun2(int m, double n) const volatile;
static S funend(int m, double n);
};
void T1::vfun(int m, double n) const
[[ pre: x < 0 ]]
[[ pre: m < 0 ]];
int T1::fun(int m, double n) volatile
[[ pre: x < 0 ]];
double T1::fun2(int m, double n) const volatile
[[ pre: x < 0 ]]
[[ pre: y < 0 ]]
[[ pre: m < 0 ]]
[[ pre: n < 0 ]];
void T1::vfun(int m, double n) const
{
printf("vfun::x: %d\n", x);
}
int T1::fun(int m, double n) volatile
{
printf("fun::x: %d\n", x);
return x;
}
double T1::fun2(int m, double n) const volatile
{
printf("fun2::x: %d fun2::y: %f\n", x, y);
return y;
}
}
int main(int, char**) {
// defining redecl
{
int x = 11;
double y = 11.5;
defining::T1 t1;
t1.vfun(x, y);
int f = 13;
f = t1.fun(x, y);
printf("main::f: %d\n", f);
double d = 13.37;
d = t1.fun2(x, y);
printf("main::d: %f\n", d);
defining::S s = defining::T1::funend(x, y);
printf("main::s.z: %d\n", s.z ? 1 : 0);
}
// nondefining redecl
{
int x = 12;
double y = 12.5;
nondefining::T1 t1;
t1.vfun(x, y);
int f = 13;
f = t1.fun(x, y);
printf("main::f: %d\n", f);
double d = 13.37;
d = t1.fun2(x, y);
printf("main::d: %f\n", d);
nondefining::S s = nondefining::T1::funend(x, y);
printf("main::s.z: %d\n", s.z ? 1 : 0);
}
return 0;
}
namespace defining
{
S T1::funend(int m, double n)
[[ pre: x < 0 ]]
[[ pre: m < 0 ]]
{
printf("funend::x: %d\n", x);
S s;
s.z = true;
return s;
}
}
namespace nondefining
{
S T1::funend(int m, double n)
[[ pre: x < 0 ]]
[[ pre: m < 0 ]];
S T1::funend(int m, double n)
{
printf("funend::x: %d\n", x);
S s;
s.z = true;
return s;
}
}
// { dg-output "default std::handle_contract_violation called: .*.C 25 defining::T1::vfun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 26 defining::T1::vfun .*(\n|\r\n|\r)*" }
// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 32 defining::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
// { dg-output "main::f: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 39 defining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 40 defining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 41 defining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 42 defining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 138 defining::T1::funend .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 139 defining::T1::funend .*(\n|\r\n|\r)*" }
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 68 nondefining::T1::vfun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 69 nondefining::T1::vfun .*(\n|\r\n|\r)*" }
// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 71 nondefining::T1::fun .*(\n|\r\n|\r)*" }
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
// { dg-output "main::f: 10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 73 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 74 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 75 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 76 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 151 nondefining::T1::funend .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 152 nondefining::T1::funend .*(\n|\r\n|\r)*" }
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,95 @@
// test that contracts can be added during (defining) friend declarations
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
#include <cstdio>
struct T;
struct S
{
int now(int a, T *t) [[ pre: a > 0 ]] [[ pre: x < 0 ]];
int x{-1};
};
int now(int a, T *t) [[ pre: a > 0 ]];
int later(int a, T *t);
int both(int a, T *t) [[ pre: a > 0 ]];
struct T
{
friend int now(int a, T *t);
friend int later(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]]
{
printf("later: a: %d, t->pri: %d\n", a, t->pri);
return -a * t->pri;
}
friend int both(int a, T *t) [[ pre: a > 0 ]]
{
printf("both: a: %d, t->pri: %d\n", a, t->pri);
return -a * t->pri;
}
friend int S::now(int a, T *t);
friend int hidden(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]]
{
printf("hidden: a: %d, t->pri: %d\n", a, t->pri);
return -a * t->pri;
}
friend int hidden2(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]]
{
printf("hidden2: a: %d, t->pri: %d\n", a, t->pri);
return -a * t->pri;
}
int x{1};
private:
int pri{-10};
};
int hidden2(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]];
int S::now(int a, T *t)
{
printf("S::now: a: %d, t->pri: %d\n", a, t->pri);
return -a * t->pri;
}
int now(int a, T *t)
{
printf("now: a: %d, t->pri: %d\n", a, t->pri);
return -a * t->pri;
}
int main(int, char**)
{
T t;
S s;
s.now(-10, &t);
now(-20, &t);
later(-21, &t);
both(-22, &t);
hidden(-23, &t);
hidden2(-24, &t);
return 0;
}
// { dg-output "default std::handle_contract_violation called: .*.C 9 S::now .*(\n|\r\n|\r)*" }
// { dg-output "S::now: a: -10, t->pri: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 15 now .*(\n|\r\n|\r)*" }
// { dg-output "now: a: -20, t->pri: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 22 later .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 22 later .*(\n|\r\n|\r)*" }
// { dg-output "later: a: -21, t->pri: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 27 both .*(\n|\r\n|\r)*" }
// { dg-output "both: a: -22, t->pri: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 36 hidden .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 36 hidden .*(\n|\r\n|\r)*" }
// { dg-output "hidden: a: -23, t->pri: -10(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 41 hidden2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 41 hidden2 .*(\n|\r\n|\r)*" }
// { dg-output "hidden2: a: -24, t->pri: -10(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,64 @@
// test that contracts are matched on friend decls when the type is complete
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
struct T;
int both(int x, T *t) [[ pre: x > 0 ]] { return 0; }
int both2(int x, T *t) [[ pre: x > 0 ]];
template<typename Z>
int fn(int x, Z *z) [[ pre: x > 0 ]];
template<typename Z>
int fn2(int x, Z *z);
template<typename Z>
int fn3(int x, Z *z) [[ pre: x > 0 ]];
template<>
int fn3<T>(int x, T *z) [[ pre: x > 1 ]];
struct T
{
friend int both2(int x, T *t) [[ pre: x > 1 ]] // { dg-error "mismatched" }
{
return 0;
}
friend int hidden(int x, T *t)
[[ pre: x > 1 ]] [[ pre: t->pri > 0 ]]
{
return x;
}
/* cannot define friend spec, so we never get to matching contracts
friend int fn<T>(int x, T *t)
[[ pre: t->pri > 0 ]] { return 0; } // error defining explicit spec friend
*/
// bad, general contracts must match general
template<typename Z>
friend int fn(int x, Z *z)
[[ pre: z->pri > 1 ]] { return 0; } // { dg-error "mismatched" }
// fine, can add contracts
template<typename Z>
friend int fn2(int x, Z *z)
[[ pre: z->pri > 1 ]] { return 0; } // { dg-bogus "mismatched" }
/* cannot declare without definition, so dup friend can't occur:
friend int dup(int x, T *t)
[[ pre: t->pri > 0 ]]; // error non-defining friend with contracts
friend int dup(int x, T *t)
[[ pre: t->pri > 1 ]]; // error non-defining friend with contracts
*/
int x{1};
private:
int pri{-10};
};
int hidden(int x, T *t)
[[ pre: x > 0 ]] [[ pre: t->pri > 1 ]]; // { dg-error "mismatched" }

View File

@@ -0,0 +1,19 @@
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
template<typename T>
[[z]]
[[nodiscard]]
T fun(T n)
[[ pre: n > 0 ]]
[[ post r: r > 0 ]] // { dg-warning ".z. attribute.*ignored" }
{
return n;
}
int main(int, char**) {
fun(-5); // { dg-warning "ignoring return value" }
fun(-5.3); // { dg-warning "ignoring return value" }
return 0;
}

View File

@@ -0,0 +1,121 @@
// basic test to ensure pre contracts work for free template specializations
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
#include <cstdio>
template<typename T>
int body(int a)
[[ pre: a > 0 ]]
{
T t = a * 2.5;
return t;
}
template<>
int body<double>(int a)
[[ pre: a > 0 ]]
{
double t = a * 3.3;
return t;
}
template<typename T>
int none(int a)
[[ pre: a > 0 ]]
{
return -a;
}
template<>
int none<double>(int a)
[[ pre: a > 0 ]]
{
return a - 100;
}
template<typename T>
int arg0(T t)
[[ pre: t > 0 ]]
{
return -t - 10;
}
template<>
int arg0<double>(double t)
[[ pre: t > 0 ]]
{
return -t + 10;
}
template<typename T>
int arg1(int a, T t)
[[ pre: a > 0 ]]
[[ pre: t > 0 ]]
{
return -t * a;
}
template<>
int arg1<double>(int a, double t)
[[ pre: a > 0 ]]
[[ pre: t > 0 ]]
{
return -t * a + 17;
}
template<typename T>
T ret(int a)
[[ pre: a > 0 ]]
{
return -a;
}
template<>
double ret<double>(int a)
[[ pre: a > 0 ]]
{
return -a * 3.3;
}
int main(int, char**)
{
printf("%d\n", body<int>(-1));
printf("%d\n", body<double>(-1));
printf("%d\n", none<int>(-1));
printf("%d\n", none<double>(-1));
printf("%d\n", arg0(-1));
printf("%d\n", arg0(-1.0));
printf("%d\n", arg1(-3, -1));
printf("%d\n", arg1(-3, -1.0));
printf("%d\n", (int)ret<int>(-1));
printf("%d\n", (int)ret<double>(-1));
printf("%f\n", ret<double>(-1));
return 0;
}
// { dg-output "default std::handle_contract_violation called: .*.C 8 body<int> .*(\n|\r\n|\r)*" }
// { dg-output "-2(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 16 body<double> .*(\n|\r\n|\r)*" }
// { dg-output "-3(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 24 none<int> .*(\n|\r\n|\r)*" }
// { dg-output "1(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 31 none<double> .*(\n|\r\n|\r)*" }
// { dg-output "-101(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 38 arg0<int> .*(\n|\r\n|\r)*" }
// { dg-output "-9(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 45 arg0<double> .*(\n|\r\n|\r)*" }
// { dg-output "11(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 52 arg1<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 53 arg1<int> .*(\n|\r\n|\r)*" }
// { dg-output "-3(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 60 arg1<double> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 61 arg1<double> .*(\n|\r\n|\r)*" }
// { dg-output "14(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 68 ret<int> .*(\n|\r\n|\r)*" }
// { dg-output "1(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 75 ret<double> .*(\n|\r\n|\r)*" }
// { dg-output "3(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 75 ret<double> .*(\n|\r\n|\r)*" }
// { dg-output "3.300000(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,395 @@
// basic test to ensure contracts work for class and member specializations
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
#include <cstdio>
// template specializations can have differing contracts
template<typename T>
int body(int a)
[[ pre: a > 0 ]]
{
T t = a * 2.5;
return t;
}
template<>
int body<double>(int a)
[[ pre: a > 1 ]]
{
double t = a * 3.3;
return t;
}
template<typename T>
int none(int a)
[[ pre: a > 0 ]]
{
return -a;
}
template<>
int none<double>(int a)
[[ pre: a > 1 ]]
{
return a - 100;
}
template<typename T>
int arg0(T t)
[[ pre: t > 0 ]]
{
return -t - 10;
}
template<>
int arg0<double>(double t)
[[ pre: t > 1 ]]
{
return -t + 10;
}
template<typename T>
int arg1(int a, T t)
[[ pre: a > 0 ]]
[[ pre: t > 0 ]]
{
return -t * a;
}
template<>
int arg1<double>(int a, double t)
[[ pre: a > 1 ]]
[[ pre: t > 1 ]]
{
return -t * a + 17;
}
template<typename T>
T ret(int a)
[[ pre: a > 0 ]]
{
return -a;
}
template<>
double ret<double>(int a)
[[ pre: a > 1 ]]
{
return -a * 3.3;
}
// template specializations can have no contracts
template<typename T>
int g1(T t) [[ pre: t > 0 ]]
{
return (int)t;
}
template<>
int g1<double>(double t)
{
return (int)t;
}
// template specializations can have no contracts in the first decl but add
// them later
template<typename T>
int g2(T t) [[ pre: t > 0 ]]
{
return (int)t;
}
template<>
int g2<double>(double t);
template<>
int g2<double>(double t)
[[ pre: t < 0 ]]
{
return (int)t;
}
template<>
int g2<char>(char t)
[[ pre: t < 'c' ]]
{
return (int)t;
}
// contracts can be different on the general template, partial and full specs
template<typename T, typename S>
struct G3
{
void f(T t, S s)
[[ pre: t > 0 ]]
[[ pre: s > 0 ]]
{
printf ("G3 general T S\n");
}
};
template<typename S>
struct G3<int, S>
{
void f(int t, S s);
};
template<typename S>
void G3<int, S>::f(int t, S s)
[[ pre: t > 1 ]]
[[ pre: s > 1 ]]
{
printf ("G3 partial int S\n");
}
template<>
void G3<int, double>::f(int t, double s)
[[ pre: t > 2 ]]
[[ pre: s > 2 ]]
{
printf ("G3 full int double\n");
}
struct C
{
bool operator>(int rhs) { return false; }
};
// deletes contracts
template<>
void G3<int, C>::f(int t, C s);
template<>
void G3<int, C>::f(int t, C s)
{
printf ("G3 full int C\n");
};
// specialized ctors
template<typename T, typename S>
struct G4
{
G4(T t, S s)
[[ pre: t > 0 ]]
[[ pre: s > 0 ]]
[[ post: x > 0 ]]
{
printf ("G4 general T S\n");
return;
}
int x{-1};
};
template<typename S>
struct G4<char, S>
{
G4(char t, S s)
[[ pre: t > 'c' ]]
[[ pre: s > 3 ]]
[[ post: x2 > 3 ]]
{
printf ("G4 partial char S\n");
return;
}
int x2{-1};
};
template<>
G4<double, double>::G4(double, double)
{
printf ("G4 full double double\n");
return;
}
template<>
G4<double, char>::G4(double a, char b)
[[ pre: a > 0 ]]
[[ pre: b > 'b' ]]
[[ post: x > 1 ]]
{
printf ("G4 full double char\n");
return;
}
// crossover of template classes and template members ok
template<typename T, typename S>
struct G5
{
template<typename P>
void f(T t, S s, P r)
[[ pre: t > 0 ]]
[[ pre: s > 0 ]]
[[ pre: r > 0 ]]
{
printf ("G5 gen T S, f gen R\n");
}
};
template<typename S>
struct G5<char, S>
{
template<typename R>
void f(char x, S y, R z)
[[ pre: x > 'z' ]]
[[ pre: y > 1 ]]
[[ pre: z > 1 ]]
{
printf ("G5 partial char S, f gen R\n");
}
};
template<>
template<typename Q>
void G5<double, double>::f(double a, double b, Q c)
[[ pre: a > 2 ]]
[[ pre: b > 2 ]]
[[ pre: c > 2 ]]
{
printf ("G5 full double double, f gen R\n");
}
int main(int, char**)
{
printf("%d\n", body<int>(-1));
printf("%d\n", body<double>(-1));
printf("%d\n", none<int>(-1));
printf("%d\n", none<double>(-1));
printf("%d\n", arg0(-1));
printf("%d\n", arg0(-1.0));
printf("%d\n", arg1(-3, -1));
printf("%d\n", arg1(-3, -1.0));
printf("%d\n", (int)ret<int>(-1));
printf("%d\n", (int)ret<double>(-1));
printf("%f\n", ret<double>(-1));
printf("%d\n", g1(-1));
printf("%d\n", g1(-1.0));
printf("%d\n", g2(-1));
printf("%d\n", g2(1.0));
printf("%d\n", g2('d'));
G3<double, double> g3_gen;
G3<int, int> g3_partial;
G3<int, double> g3_full;
g3_gen.f(-1.0, -1.0); // general
g3_partial.f(-2, -2); // partial spec
g3_full.f(-3, -3.0); // full spec
G3<char, char> g3_gen2;
G3<int, char> g3_partial2;
g3_gen2.f((char)-1, (char)-1);
g3_partial2.f(-1, (char)-1);
G3<int, C> g3_full2;
g3_full2.f(5, C{});
g3_full2.f(-5, C{});
G4 g4_gen{-1, -1};
G4 g4_full1{-1.0, -1.0};
G4 g4_full2{-1.0, (char)'b'};
G4 g4_partial{(char)'c', -5};
G5<int, int> g5_gen;
g5_gen.f(-1, -1, -2);
g5_gen.f(-1, -1, -2.0);
G5<char, int> g5_part;
g5_part.f('a', -1, -2);
g5_part.f('a', -1, -2.1);
G5<double, double> g5_full;
g5_full.f(-1.0, -1.0, -2);
g5_full.f(-1.0, -1.0, -2.1);
return 0;
}
// { dg-output "default std::handle_contract_violation called: .*.C 9 body<int> .*(\n|\r\n|\r)*" }
// { dg-output "-2(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 17 body<double> .*(\n|\r\n|\r)*" }
// { dg-output "-3(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 25 none<int> .*(\n|\r\n|\r)*" }
// { dg-output "1(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 32 none<double> .*(\n|\r\n|\r)*" }
// { dg-output "-101(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 39 arg0<int> .*(\n|\r\n|\r)*" }
// { dg-output "-9(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 46 arg0<double> .*(\n|\r\n|\r)*" }
// { dg-output "11(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 53 arg1<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 54 arg1<int> .*(\n|\r\n|\r)*" }
// { dg-output "-3(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 61 arg1<double> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 62 arg1<double> .*(\n|\r\n|\r)*" }
// { dg-output "14(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 69 ret<int> .*(\n|\r\n|\r)*" }
// { dg-output "1(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 76 ret<double> .*(\n|\r\n|\r)*" }
// { dg-output "3(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 76 ret<double> .*(\n|\r\n|\r)*" }
// { dg-output "3.300000(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 83 g1<int> .*(\n|\r\n|\r)*" }
// { dg-output "-1(\n|\r\n|\r)*" }
// { dg-output "-1(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 97 g2<int> .*(\n|\r\n|\r)*" }
// { dg-output "-1(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 107 g2<double> .*(\n|\r\n|\r)*" }
// { dg-output "1(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 114 g2<char> .*(\n|\r\n|\r)*" }
// { dg-output "100(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 124 G3<double, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 125 G3<double, .*(\n|\r\n|\r)*" }
// { dg-output "G3 general T S(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 139 G3<int, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 140 G3<int, .*(\n|\r\n|\r)*" }
// { dg-output "G3 partial int S(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 147 G3<int, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 148 G3<int, .*(\n|\r\n|\r)*" }
// { dg-output "G3 full int double(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 124 G3<char, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 125 G3<char, .*(\n|\r\n|\r)*" }
// { dg-output "G3 general T S(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 139 G3<int, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 140 G3<int, .*(\n|\r\n|\r)*" }
// { dg-output "G3 partial int S(\n|\r\n|\r)*" }
// { dg-output "G3 full int C(\n|\r\n|\r)*" }
// { dg-output "G3 full int C(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 173 G4<int, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 174 G4<int, .*(\n|\r\n|\r)*" }
// { dg-output "G4 general T S(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 175 G4<int, .*(\n|\r\n|\r)*" }
// { dg-output "G4 full double double(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 206 G4<double, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 207 G4<double, .*(\n|\r\n|\r)*" }
// { dg-output "G4 full double char(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 208 G4<double, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 187 G4<char, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 188 G4<char, .*(\n|\r\n|\r)*" }
// { dg-output "G4 partial char S(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 189 G4<char, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 220 G5<int, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 221 G5<int, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 222 G5<int, .*(\n|\r\n|\r)*" }
// { dg-output "G5 gen T S, f gen R(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 220 G5<int, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 221 G5<int, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 222 G5<int, .*(\n|\r\n|\r)*" }
// { dg-output "G5 gen T S, f gen R(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 233 G5<char, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 234 G5<char, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 235 G5<char, .*(\n|\r\n|\r)*" }
// { dg-output "G5 partial char S, f gen R(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 233 G5<char, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 234 G5<char, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 235 G5<char, .*(\n|\r\n|\r)*" }
// { dg-output "G5 partial char S, f gen R(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 244 G5<double, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 245 G5<double, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 246 G5<double, .*(\n|\r\n|\r)*" }
// { dg-output "G5 full double double, f gen R(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 244 G5<double, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 245 G5<double, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 246 G5<double, .*(\n|\r\n|\r)*" }
// { dg-output "G5 full double double, f gen R(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,45 @@
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
#include <cstdio>
template<typename T, typename S>
struct G5
{
template<typename R>
void f(T t, S s, R r)
[[ pre: t > 0 ]] [[ pre: s > 0 ]] [[ pre: r > 0 ]]
{
printf ("G5 gen T S, f gen R\n");
}
};
// specializations can remove contracts
template<>
template<typename R>
void G5<double, double>::f(double a, double b, R c)
{
printf ("G5 full double double, f gen R\n");
}
int main(int, char**) {
G5<double, double> g5_full;
g5_full.f(-1.0, -1.0, -2);
g5_full.f(-1.0, -1.0, -2.1);
G5<int, double> g5_gen;
g5_gen.f(-1, -1.0, -2);
g5_gen.f(-1, -1.0, -2.1);
return 0;
}
// { dg-output "G5 full double double, f gen R(\n|\r\n|\r)*" }
// { dg-output "G5 full double double, f gen R(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* t > 0 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* s > 0 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* r > 0 .*(\n|\r\n|\r)*" }
// { dg-output "G5 gen T S, f gen R(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* t > 0 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* s > 0 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* r > 0 .*(\n|\r\n|\r)*" }
// { dg-output "G5 gen T S, f gen R(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,49 @@
// generic assert contract parsing checks
// check omitted, 'default', 'audit', and 'axiom' contract levels parse
// check that all concrete semantics parse
// check omitted, '%default' contract roles parse
// ensure that an invalid contract level 'invalid' errors
// ensure that a predicate referencing an undefined variable errors
// ensure that a missing colon after contract level errors
// ensure that an invalid contract role 'invalid' errors
// ensure that a missing colon after contract role errors
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
static_assert (__cpp_contracts >= 201906);
static_assert (__cpp_contracts_literal_semantics >= 201906);
static_assert (__cpp_contracts_roles >= 201906);
int main()
{
int x;
[[assert: x >= 0]];
[[assert default: x < 0]];
[[assert audit: x == 0]];
[[assert axiom: x == 1]];
[[assert: x > 0 ? true : false]];
[[assert: x < 0 ? true : false]];
[[assert: x = 0]]; // { dg-error "expected .]. before .=. token" }
[[assert ignore: x >= 0]];
[[assert assume: x >= 0]];
[[assert check_never_continue: x >= 0]];
[[assert check_maybe_continue: x >= 0]];
[[assert %default: x >= 0]];
[[assert default %default: x < 0]];
[[assert audit %default: x == 0]];
[[assert axiom %default: x == 1]];
[[assert check_always_continue: x >= 0]]; // { dg-error "expected contract level" }
[[assert invalid: x == 0]]; // { dg-error "expected contract level" }
[[assert: y == 0]]; // { dg-error ".y. was not declared in this scope" }
[[assert default x == 0]]; // { dg-error "expected .:. before .x." }
[[assert %default x >= 0]]; // { dg-error "expected .:. before .x." }
[[assert %invalid: x >= 0]]; // TODO: optional warning?
return 0;
}

View File

@@ -0,0 +1,73 @@
// general checks to ensure that contract violations are generated during
// runtime when appropriate
// each check also validates the expected file name, line number, function,
// predicate, and contract level are included in the violation_info object
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-build-level=audit -fcontract-continuation-mode=on" }
namespace tns
{
int fun()
{
int x = 1;
[[ assert: x < 0 ]];
return 0;
}
int fun2();
struct TestType
{
static int fun();
static int fun2()
{
int x = 1;
[[ assert: x < 0 ]];
return 0;
}
};
}
int tns::fun2()
{
int x = 1;
[[ assert: x < 0 ]];
return 0;
}
int tns::TestType::fun()
{
int x = 1;
[[ assert: x < 0 ]];
return 0;
}
int main()
{
int x = 100;
[[assert: x < 0]];
[[assert default: x < 1]];
[[assert audit: x < 2]];
// contract_violation.line_number() may eventually come from
// std::source_location which *is* affected by the #line macro; our current
// implementation conforms to this so we've included it as a check
#line 100
[[assert: x < 3]];
[[assert axiom: x < 4]];
tns::fun();
tns::fun2();
tns::TestType::fun();
tns::TestType::fun2();
return 0;
}
// { dg-output "default std::handle_contract_violation called: .*.C 47 main .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 48 main .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 49 main .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 100 main .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 13 tns::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 33 tns::fun2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 40 tns::TestType::fun .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 24 tns::TestType::fun2 .*(\n|\r\n|\r)*" }

View File

@@ -0,0 +1,103 @@
// ensure that assert contract predicates that are not convertible to bool
// generate an error
// ensure the same for instatiated template functions
// ensure the same for non-instatiated template functions when the predicate
// is not dependent on the template parameters
// ensure template parameter dependent, potentially non-boolean, contract
// predicates do not generate an error if never instatiated
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
void fun()
{
return;
}
template<typename T>
void fun2(T a)
{
[[assert: fun()]]; // { dg-error "could not convert|in argument" }
}
template<typename T>
void fun3(T a)
{
[[assert: fun()]]; // { dg-error "could not convert|in argument" }
}
template<typename T>
void fun4(T a)
{
[[assert: a.fun()]];
}
struct test
{
void fun() { }
void fun2() { }
};
template<typename T>
void fun5(T a)
{
[[assert: a.fun2()]]; // { dg-error "could not convert" }
}
struct VoidFun
{
void fun() { }
};
struct BoolFun
{
bool fun() { return true; }
};
template<typename T>
void fun6(T a)
{
[[ assert: a.fun() ]]; // { dg-error "could not convert" }
}
template void fun6(VoidFun);
template<typename T>
void fun7(T a)
{
[[ assert: a.fun() ]];
}
template void fun7(BoolFun);
struct ImplicitBool
{
operator bool() { return true; }
};
struct ExplicitBool
{
explicit operator bool() { return true; }
};
template<typename T>
void fun8(T a)
{
[[ assert: T() ]];
}
template void fun8(ImplicitBool);
template void fun8(ExplicitBool);
void fun9()
{
[[ assert: ImplicitBool() ]];
[[ assert: ExplicitBool() ]];
}
int main()
{
[[assert: fun()]]; // { dg-error "could not convert" }
fun2(1);
test t;
fun5(t);
return 0;
}

View File

@@ -0,0 +1,15 @@
// ensure that constants for contract levels are inserted into the binary when
// used and omitted when the runtime check is not generated
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts -fcontract-build-level=default" }
// { dg-final { scan-assembler-not "audit" } }
// { dg-final { scan-assembler "default" } }
int main()
{
int x = 1;
[[assert: x < 0]];
[[assert default: x < 0]];
[[assert audit: x < 0]];
return 0;
}

View File

@@ -0,0 +1,51 @@
// ensure that passing asserts do not affect constexpr functions
// ensure that failing asserts generate an error in a constexpr function
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
constexpr int wfun(int a) {
[[assert: a > 0]];
return a;
}
constexpr int ffun(int a) {
[[assert: a > 0]];
return a;
}
template<typename T>
constexpr int tfun(T a) {
[[assert: a > 0]];
return a;
}
template<typename T>
constexpr int wtfun(T a) {
[[assert: a > 0]];
return a;
}
template<typename T>
constexpr int ftfun(T a) {
[[assert: a > 0]];
return a;
}
constexpr int explicitfn(int a) {
[[assert ignore: a > 0]];
[[assert check_never_continue: a > 0]];
return a;
}
int main(int, char **) {
constexpr int a = wfun(10);
constexpr int b = ffun(-10); // { dg-message "in .constexpr. expansion" }
// { dg-error "contract predicate" "" { target *-*-* } 12 }
constexpr int c = wtfun(10);
constexpr int d = ftfun(-10); // { dg-message "in .constexpr. expansion" }
// { dg-error "contract predicate" "" { target *-*-* } 30 }
constexpr int e = explicitfn(-10); // { dg-message "in .constexpr. expansion" }
// { dg-error "contract predicate" "" { target *-*-* } 36 }
return 0;
}

View File

@@ -0,0 +1,58 @@
// ensure that exceptions thrown inside a custom contract violation handler
// are catchable up the call stack
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
#include <iostream>
#include <experimental/contract>
void handle_contract_violation(const std::experimental::contract_violation &violation) {
std::cerr << "custom std::handle_contract_violation called:"
<< " " << violation.line_number()
<< " " << violation.file_name()
<< std::endl;
throw -(int)violation.line_number();
}
int fun() {
int x = 0;
[[ assert: x < 0 ]];
return 0;
}
int fun3() {
fun();
return 2;
}
int main(int, char**) {
try {
int x = 0;
[[ assert: x < 0 ]];
} catch(int &ex) {
std::cerr << "synth caught direct: " << ex << std::endl;
}
try {
fun();
} catch(int &ex) {
std::cerr << "synth caught indirect: " << ex << std::endl;
}
try {
fun3();
} catch(int &ex) {
std::cerr << "synth caught double indirect: " << ex << std::endl;
}
std::cerr << "end main" << std::endl;
return 0;
}
// { dg-output "custom std::handle_contract_violation called: 30 .*/contracts14.C(\n|\r\n|\r)*" }
// { dg-output "synth caught direct: -30(\n|\r\n|\r)*" }
// { dg-output "custom std::handle_contract_violation called: 18 .*/contracts14.C(\n|\r\n|\r)*" }
// { dg-output "synth caught indirect: -18(\n|\r\n|\r)*" }
// { dg-output "custom std::handle_contract_violation called: 18 .*/contracts14.C(\n|\r\n|\r)*" }
// { dg-output "synth caught double indirect: -18(\n|\r\n|\r)*" }
// { dg-output "end main" }

View File

@@ -0,0 +1,56 @@
// ensure that exceptions thrown inside a custom contract violation handler
// are not catchable up the call stack when failing in a noexcept function
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
#include <iostream>
#include <experimental/contract>
void handle_contract_violation(const std::experimental::contract_violation &violation) {
std::cerr << "custom std::handle_contract_violation called:"
<< " " << violation.line_number()
<< " " << violation.file_name()
<< std::endl;
throw -(int)violation.line_number();
}
int fun() noexcept {
int x = 0;
[[ assert: x < 0 ]];
return 0;
}
int fun3() {
fun();
return 2;
}
int main(int, char**) {
try {
int x = 0;
[[ assert: x < 0 ]];
} catch(int &ex) {
std::cerr << "synth caught direct: " << ex << std::endl;
}
try {
fun();
} catch(int &ex) {
std::cerr << "synth caught indirect: " << ex << std::endl;
}
try {
fun3();
} catch(int &ex) {
std::cerr << "synth caught double indirect: " << ex << std::endl;
}
std::cerr << "end main" << std::endl;
return 0;
}
// { dg-output "custom std::handle_contract_violation called: 30 .*/contracts15.C(\n|\r\n|\r)*" }
// { dg-output "synth caught direct: -30(\n|\r\n|\r)*" }
// { dg-output "custom std::handle_contract_violation called: 18 .*/contracts15.C(\n|\r\n|\r)*" }
// { dg-output "terminate called after throwing an instance of .int.(\n|\r\n|\r)*" }
// { dg-shouldfail "throwing in noexcept" }

Some files were not shown because too many files have changed in this diff Show More