public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/c++-contracts] c++: build contract_violation object directly
@ 2022-11-01 11:43 Jason Merrill
0 siblings, 0 replies; only message in thread
From: Jason Merrill @ 2022-11-01 11:43 UTC (permalink / raw)
To: gcc-cvs, libstdc++-cvs
https://gcc.gnu.org/g:6f047a4540277fe70d0fe73b2e2db24659093ec5
commit 6f047a4540277fe70d0fe73b2e2db24659093ec5
Author: Jason Merrill <jason@redhat.com>
Date: Thu Oct 27 10:31:28 2022 -0400
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.
Diff:
---
gcc/cp/cp-tree.h | 7 +-
gcc/cp/contracts.cc | 214 ++++++++++++++++-------
gcc/cp/decl.cc | 3 -
gcc/testsuite/g++.dg/contracts/contracts-post6.C | 3 +-
gcc/testsuite/g++.dg/contracts/contracts14.C | 35 +---
gcc/testsuite/g++.dg/contracts/contracts15.C | 17 +-
libstdc++-v3/src/experimental/contract.cc | 30 +---
libstdc++-v3/include/experimental/contract | 15 +-
8 files changed, 171 insertions(+), 153 deletions(-)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 48d2fd15f5d..11ac082c89f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -233,8 +233,7 @@ enum cp_tree_index
CPTI_DSO_HANDLE,
CPTI_DCAST,
- CPTI_ON_CONTRACT_VIOLATION,
- CPTI_ON_CONTRACT_VIOLATION_NEVER,
+ CPTI_PSEUDO_CONTRACT_VIOLATION,
CPTI_SOURCE_LOCATION_IMPL,
@@ -270,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
@@ -372,9 +372,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
#define throw_fn cp_global_trees[CPTI_THROW_FN]
#define rethrow_fn cp_global_trees[CPTI_RETHROW_FN]
-#define on_contract_violation_fn cp_global_trees[CPTI_ON_CONTRACT_VIOLATION]
-#define on_contract_violation_never_fn cp_global_trees[CPTI_ON_CONTRACT_VIOLATION_NEVER]
-
/* The type of the function-pointer argument to "__cxa_atexit" (or
"std::atexit", if "__cxa_atexit" is not being used). */
#define atexit_fn_ptr_type_node cp_global_trees[CPTI_ATEXIT_FN_PTR_TYPE]
diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc
index 2fa7023f76b..214be7c671f 100644
--- a/gcc/cp/contracts.cc
+++ b/gcc/cp/contracts.cc
@@ -34,7 +34,7 @@ along with GCC; see the file COPYING3. If not see
is transformed into:
if (!(v > 0)) {
- __on_contract_violation (true, // continue_
+ handle_contract_violation(__pseudo_contract_violation{
5, // line_number,
"main.cpp", // file_name,
"fun", // function_name,
@@ -42,21 +42,18 @@ along with GCC; see the file COPYING3. If not see
"default", // assertion_level,
"default", // assertion_role,
MAYBE_CONTINUE, // continuation_mode
- );
+ });
+ terminate (); // if NEVER_CONTINUE
}
- Here, __on_contract_violation is a shim used to actually construct the
- std::contract_violation and call the installed handler, finally terminating
- if the contract should not continue on violation. This prevents requiring
- including <contract> and simplifies building the call.
+ We use an internal type with the same layout as contract_violation rather
+ than try to define the latter internally and somehow deal with its actual
+ definition in a TU that includes <contract>.
- FIXME the overhead would be lower if we write out the contract_violation
- object statically and pass it directly to the handler. Though the current
- way is more tolerant of layout changes, so maybe leave it alone until the
- feature is more mature.
+ ??? is it worth factoring out the calls to handle_contract_violation and
+ terminate into a local function?
- Assumed contracts have a similar transformation that results the body of the
- if being __builtin_unreachable ();
+ Assumed contracts use the same implementation as C++23 [[assume]].
Parsing of pre and post contract conditions need to be deferred when the
contracts are attached to a member function. The postcondition identifier
@@ -163,6 +160,7 @@ along with GCC; see the file COPYING3. If not see
#include "attribs.h"
#include "tree-iterator.h"
#include "print-tree.h"
+#include "stor-layout.h"
const int max_custom_roles = 32;
static contract_role contract_build_roles[max_custom_roles] = {
@@ -1581,43 +1579,160 @@ get_contract_role_name (tree contract)
return "default";
}
-static void
-build_contract_handler_call (tree contract,
- contract_continuation cmode)
+/* Build a layout-compatible internal version of std::contract_violation. */
+
+static tree
+get_pseudo_contract_violation_type ()
{
- const char *level = get_contract_level_name (contract);
- const char *role = get_contract_role_name (contract);
- tree comment = CONTRACT_COMMENT (contract);
+ if (!pseudo_contract_violation_type)
+ {
+ /* Must match <contract>:
+ class contract_violation {
+ uint_least32_t _M_line;
+ const char* _M_file;
+ const char* _M_function;
+ const char* _M_comment;
+ const char* _M_level;
+ const char* _M_role;
+ signed char _M_continue;
+ If this changes, also update the initializer in
+ build_contract_violation. */
+ const tree types[] = { uint_least32_type_node,
+ const_string_type_node,
+ const_string_type_node,
+ const_string_type_node,
+ const_string_type_node,
+ const_string_type_node,
+ signed_char_type_node };
+ tree fields = NULL_TREE;
+ for (tree type : types)
+ {
+ /* finish_builtin_struct wants fieldss chained in reverse. */
+ tree next = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, type);
+ DECL_CHAIN (next) = fields;
+ fields = next;
+ }
+ iloc_sentinel ils (input_location);
+ input_location = BUILTINS_LOCATION;
+ pseudo_contract_violation_type = make_class_type (RECORD_TYPE);
+ finish_builtin_struct (pseudo_contract_violation_type,
+ "__pseudo_contract_violation",
+ fields, NULL_TREE);
+ CLASSTYPE_AS_BASE (pseudo_contract_violation_type)
+ = pseudo_contract_violation_type;
+ DECL_CONTEXT (TYPE_NAME (pseudo_contract_violation_type))
+ = FROB_CONTEXT (global_namespace);
+ TREE_PUBLIC (TYPE_NAME (pseudo_contract_violation_type)) = true;
+ CLASSTYPE_LITERAL_P (pseudo_contract_violation_type) = true;
+ CLASSTYPE_LAZY_COPY_CTOR (pseudo_contract_violation_type) = true;
+ xref_basetypes (pseudo_contract_violation_type, /*bases=*/NULL_TREE);
+ pseudo_contract_violation_type
+ = cp_build_qualified_type (pseudo_contract_violation_type,
+ TYPE_QUAL_CONST);
+ }
+ return pseudo_contract_violation_type;
+}
+/* Return a VAR_DECL to pass to handle_contract_violation. */
+
+static tree
+build_contract_violation (tree contract, contract_continuation cmode)
+{
expanded_location loc = expand_location (EXPR_LOCATION (contract));
-
- tree continue_mode = build_int_cst (boolean_type_node, cmode != NEVER_CONTINUE);
- tree line_number = build_int_cst (integer_type_node, loc.line);
- tree file_name = build_string_literal (strlen (loc.file) + 1, loc.file);
- const char *function_name_str =
+ const char *function =
TREE_CODE (contract) == ASSERTION_STMT
|| DECL_CONSTRUCTOR_P (current_function_decl)
|| DECL_DESTRUCTOR_P (current_function_decl)
? current_function_name ()
: fndecl_name (DECL_ORIGINAL_FN (current_function_decl));
- tree function_name = build_string_literal (strlen (function_name_str) + 1,
- function_name_str);
- tree level_str = build_string_literal (strlen (level) + 1, level);
- tree role_str = build_string_literal (strlen (role) + 1, role);
+ const char *level = get_contract_level_name (contract);
+ const char *role = get_contract_role_name (contract);
- /* FIXME: Do we want a string for this?. */
- tree continuation = build_int_cst (integer_type_node, cmode);
+ /* Must match the type layout in get_pseudo_contract_violation_type. */
+ tree ctor = build_constructor_va
+ (init_list_type_node, 7,
+ NULL_TREE, build_int_cst (uint_least32_type_node, loc.line),
+ NULL_TREE, build_string_literal (loc.file),
+ NULL_TREE, build_string_literal (function),
+ NULL_TREE, CONTRACT_COMMENT (contract),
+ NULL_TREE, build_string_literal (level),
+ NULL_TREE, build_string_literal (role),
+ NULL_TREE, build_int_cst (signed_char_type_node, cmode));
- tree violation_fn;
- if (cmode == MAYBE_CONTINUE)
- violation_fn = on_contract_violation_fn;
+ ctor = finish_compound_literal (get_pseudo_contract_violation_type (),
+ ctor, tf_none);
+ protected_set_expr_location (ctor, EXPR_LOCATION (contract));
+ return ctor;
+}
+
+/* Return handle_contract_violation(), declaring it if needed. */
+
+static tree
+declare_handle_contract_violation ()
+{
+ tree fnname = get_identifier ("handle_contract_violation");
+ tree viol_name = get_identifier ("contract_violation");
+ tree l = lookup_qualified_name (global_namespace, fnname,
+ LOOK_want::HIDDEN_FRIEND);
+ for (tree f: lkp_range (l))
+ if (TREE_CODE (f) == FUNCTION_DECL)
+ {
+ tree parms = TYPE_ARG_TYPES (TREE_TYPE (f));
+ if (remaining_arguments (parms) != 1)
+ continue;
+ tree parmtype = non_reference (TREE_VALUE (parms));
+ if (CLASS_TYPE_P (parmtype)
+ && TYPE_IDENTIFIER (parmtype) == viol_name)
+ return f;
+ }
+
+ tree id_exp = get_identifier ("experimental");
+ tree ns_exp = lookup_qualified_name (std_node, id_exp);
+
+ tree violation = error_mark_node;
+ if (TREE_CODE (ns_exp) == NAMESPACE_DECL)
+ violation = lookup_qualified_name (ns_exp, viol_name,
+ LOOK_want::TYPE
+ |LOOK_want::HIDDEN_FRIEND);
+
+ if (TREE_CODE (violation) == TYPE_DECL)
+ violation = TREE_TYPE (violation);
else
- violation_fn = on_contract_violation_never_fn;
- tree call = build_call_n (violation_fn, 8, continue_mode, line_number,
- file_name, function_name, comment,
- level_str, role_str,
- continuation);
+ {
+ push_nested_namespace (std_node);
+ push_namespace (id_exp, /*inline*/false);
+ violation = make_class_type (RECORD_TYPE);
+ create_implicit_typedef (viol_name, violation);
+ DECL_SOURCE_LOCATION (TYPE_NAME (violation)) = BUILTINS_LOCATION;
+ DECL_CONTEXT (TYPE_NAME (violation)) = current_namespace;
+ pushdecl_namespace_level (TYPE_NAME (violation), /*hidden*/true);
+ pop_namespace ();
+ pop_nested_namespace (std_node);
+ }
+ tree argtype = cp_build_qualified_type (violation, TYPE_QUAL_CONST);
+ argtype = cp_build_reference_type (argtype, /*rval*/false);
+ tree fntype = build_function_type_list (void_type_node, argtype, NULL_TREE);
+
+ push_nested_namespace (global_namespace);
+ tree fn = build_cp_library_fn_ptr ("handle_contract_violation", fntype,
+ ECF_COLD);
+ pushdecl_namespace_level (fn, /*hiding*/true);
+ pop_nested_namespace (global_namespace);
+
+ return fn;
+}
+
+/* Build the call to handle_contract_violation for CONTRACT. */
+
+static void
+build_contract_handler_call (tree contract,
+ contract_continuation cmode)
+{
+ tree violation = build_contract_violation (contract, cmode);
+ tree violation_fn = declare_handle_contract_violation ();
+ tree call = build_call_n (violation_fn, 1, build_address (violation));
finish_expr_stmt (call);
}
@@ -1683,6 +1798,8 @@ build_contract_check (tree contract)
}
build_contract_handler_call (contract, cmode);
+ if (cmode == NEVER_CONTINUE)
+ finish_expr_stmt (build_call_a (terminate_fn, 0, nullptr));
finish_then_clause (if_stmt);
tree scope = IF_SCOPE (if_stmt);
@@ -1970,27 +2087,4 @@ apply_postcondition_to_return (tree expr)
return call;
}
-/* Set up built-ins for contracts. */
-
-void
-init_contract_processing (void)
-{
- /* std::contract_violation */
- tree tmp = build_function_type_list (integer_type_node,
- boolean_type_node,
- integer_type_node,
- const_string_type_node,
- const_string_type_node,
- const_string_type_node,
- const_string_type_node,
- const_string_type_node,
- integer_type_node,
- NULL_TREE);
- on_contract_violation_fn =
- build_cp_library_fn_ptr ("__on_contract_violation", tmp, ECF_COLD);
- on_contract_violation_never_fn =
- build_cp_library_fn_ptr ("__on_contract_violation", tmp,
- ECF_COLD | ECF_NORETURN);
-}
-
#include "gt-cp-contracts.h"
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 4423049cd6c..b5e132c09d5 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -5025,9 +5025,6 @@ cxx_init_decl_processing (void)
if (flag_exceptions)
init_exception_processing ();
- if (flag_contracts)
- init_contract_processing ();
-
if (modules_p ())
init_modules (parse_in);
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-post6.C b/gcc/testsuite/g++.dg/contracts/contracts-post6.C
index dacb97b9ebf..f8246fbc15f 100644
--- a/gcc/testsuite/g++.dg/contracts/contracts-post6.C
+++ b/gcc/testsuite/g++.dg/contracts/contracts-post6.C
@@ -26,4 +26,5 @@ struct S
void driver()
{
S<int> s1(0);
-}
\ No newline at end of file
+ s1.f1(2);
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts14.C b/gcc/testsuite/g++.dg/contracts/contracts14.C
index 4e81155ba11..55208dbc0a1 100644
--- a/gcc/testsuite/g++.dg/contracts/contracts14.C
+++ b/gcc/testsuite/g++.dg/contracts/contracts14.C
@@ -5,16 +5,6 @@
#include <iostream>
#include <experimental/contract>
-int
-__on_contract_violation (bool continue_,
- int line_number,
- const char * file_name,
- const char * function_name,
- const char * comment,
- const char * assertion_level,
- const char * assertion_role,
- int continuation_mode);
-
void handle_contract_violation(const std::experimental::contract_violation &violation) {
std::cerr << "custom std::handle_contract_violation called:"
<< " " << violation.line_number()
@@ -29,11 +19,6 @@ int fun() {
return 0;
}
-int fun2() {
- __on_contract_violation(true, 1, "T1", "T2", "T3", "T4", "T5", 1);
- return 1;
-}
-
int fun3() {
fun();
return 2;
@@ -47,12 +32,6 @@ int main(int, char**) {
std::cerr << "synth caught direct: " << ex << std::endl;
}
- try {
- fun2();
- } catch(int &ex) {
- std::cerr << "caught lib direct: " << ex << std::endl;
- }
-
try {
fun();
} catch(int &ex) {
@@ -69,13 +48,11 @@ int main(int, char**) {
return 0;
}
-// { dg-output "custom std::handle_contract_violation called: 45 .*/contracts14.C(\n|\r\n|\r)*" }
-// { dg-output "synth caught direct: -45(\n|\r\n|\r)*" }
-// { dg-output "custom std::handle_contract_violation called: 1 T1(\n|\r\n|\r)*" }
-// { dg-output "caught lib direct: -1(\n|\r\n|\r)*" }
-// { dg-output "custom std::handle_contract_violation called: 28 .*/contracts14.C(\n|\r\n|\r)*" }
-// { dg-output "synth caught indirect: -28(\n|\r\n|\r)*" }
-// { dg-output "custom std::handle_contract_violation called: 28 .*/contracts14.C(\n|\r\n|\r)*" }
-// { dg-output "synth caught double indirect: -28(\n|\r\n|\r)*" }
+// { 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" }
diff --git a/gcc/testsuite/g++.dg/contracts/contracts15.C b/gcc/testsuite/g++.dg/contracts/contracts15.C
index af3e539e1ce..35bb5223a70 100644
--- a/gcc/testsuite/g++.dg/contracts/contracts15.C
+++ b/gcc/testsuite/g++.dg/contracts/contracts15.C
@@ -29,11 +29,6 @@ int fun() noexcept {
return 0;
}
-int fun2() {
- __on_contract_violation(true, 1, "T1", "T2", "T3", "T4", "T5", 1);
- return 1;
-}
-
int fun3() {
fun();
return 2;
@@ -47,12 +42,6 @@ int main(int, char**) {
std::cerr << "synth caught direct: " << ex << std::endl;
}
- try {
- fun2();
- } catch(int &ex) {
- std::cerr << "caught lib direct: " << ex << std::endl;
- }
-
try {
fun();
} catch(int &ex) {
@@ -69,10 +58,8 @@ int main(int, char**) {
return 0;
}
-// { dg-output "custom std::handle_contract_violation called: 45 .*/contracts15.C(\n|\r\n|\r)*" }
-// { dg-output "synth caught direct: -45(\n|\r\n|\r)*" }
-// { dg-output "custom std::handle_contract_violation called: 1 T1(\n|\r\n|\r)*" }
-// { dg-output "caught lib direct: -1(\n|\r\n|\r)*" }
+// { dg-output "custom std::handle_contract_violation called: 40 .*/contracts15.C(\n|\r\n|\r)*" }
+// { dg-output "synth caught direct: -40(\n|\r\n|\r)*" }
// { dg-output "custom std::handle_contract_violation called: 28 .*/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" }
diff --git a/libstdc++-v3/src/experimental/contract.cc b/libstdc++-v3/src/experimental/contract.cc
index ddb1df47cb5..f1b6eb3a58a 100644
--- a/libstdc++-v3/src/experimental/contract.cc
+++ b/libstdc++-v3/src/experimental/contract.cc
@@ -1,5 +1,5 @@
// -*- C++ -*- std::experimental::contract_violation and friends
-// Copyright (C) 1994-2018 Free Software Foundation, Inc.
+// Copyright (C) 1994-2022 Free Software Foundation, Inc.
//
// This file is part of GCC.
//
@@ -24,7 +24,6 @@
#include <experimental/contract>
#include <iostream>
-#include <cstdlib>
__attribute__ ((weak)) void
handle_contract_violation (const std::experimental::contract_violation &violation)
@@ -39,30 +38,3 @@ handle_contract_violation (const std::experimental::contract_violation &violatio
<< " " << (int)violation.continuation_mode()
<< std::endl;
}
-
-// We take POD types here to make synthesis easier
-int
-__on_contract_violation (bool continue_,
- int line_number,
- const char *file_name,
- const char *function_name,
- const char *comment,
- const char *assertion_level,
- const char *assertion_role,
- int continuation_mode)
-{
- using cvmc = std::experimental::contract_violation_continuation_mode;
- std::experimental::contract_violation violation (line_number,
- file_name,
- function_name,
- comment,
- assertion_level,
- assertion_role,
- static_cast<cvmc>(continuation_mode));
- handle_contract_violation (violation);
-
- if (!continue_)
- std::abort ();
-
- return 0;
-}
diff --git a/libstdc++-v3/include/experimental/contract b/libstdc++-v3/include/experimental/contract
index dc58a38a343..fb03f116847 100644
--- a/libstdc++-v3/include/experimental/contract
+++ b/libstdc++-v3/include/experimental/contract
@@ -24,7 +24,6 @@
// <http://www.gnu.org/licenses/>.
// FIXME string_view vs. freestanding
-// FIXME remove on_contract_violation?
/** @file contract
* This is a Standard C++ Library header.
@@ -59,13 +58,8 @@ namespace experimental
const char* _M_comment;
const char* _M_level;
const char* _M_role;
- contract_violation_continuation_mode _M_continue;
+ signed char _M_continue;
public:
- contract_violation (int __l, const char* __f, const char* __fn,
- const char* __c, const char* __lv, const char *__r,
- contract_violation_continuation_mode __m)
- : _M_line(__l), _M_file(__f), _M_function(__fn), _M_comment(__c),
- _M_level(__lv), _M_role(__r), _M_continue(__m) { }
// From N4820
uint_least32_t line_number() const noexcept { return _M_line; }
string_view file_name() const noexcept { return _M_file; }
@@ -75,7 +69,7 @@ namespace experimental
// From P1332
string_view assertion_role() const noexcept { return _M_role; }
contract_violation_continuation_mode continuation_mode() const noexcept
- { return _M_continue; }
+ { return static_cast<contract_violation_continuation_mode>(_M_continue); }
};
} // namespace experimental
@@ -83,9 +77,8 @@ namespace experimental
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
- //void handle_contract_violation (const contract_violation &);
-
- //int on_contract_violation (bool, int, const char *, const char *, const char *, const char *, const char *, int);
+// To override the contract violation handler, define
+//void ::handle_contract_violation (const std::experimental::contract_violation &);
#endif // C++17
#endif // _GLIBCXX_CONTRACT
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2022-11-01 11:43 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-01 11:43 [gcc/devel/c++-contracts] c++: build contract_violation object directly Jason Merrill
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).