From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2122) id 35AEC3857831; Tue, 1 Nov 2022 11:43:21 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 35AEC3857831 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1667303001; bh=2P/jH9mrWT9yiAk9sSFtKRftN5VLPXLX56wCFOp9Fho=; h=From:To:Subject:Date:From; b=x6VpewX3qGwJAFv8JdrD+LcQKpqcTmM0I4wP0SOjEDv+yRYfMjXTB5GagZ6l42x7E lrbwSWncStQXSGx7d2ozptV0xw0xvOVpusj8mjsuF2HljB1ZvVGf/txP+dlXgic0rg 9qNt9XLq40q4n66qnhXaHuIg6334Y2RFnEJ3PA+Q= Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Jason Merrill To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc/devel/c++-contracts] c++: build contract_violation object directly X-Act-Checkin: gcc X-Git-Author: Jason Merrill X-Git-Refname: refs/heads/devel/c++-contracts X-Git-Oldrev: ce5faeb1f533e2cdd4b0f45f18770e0a26f05182 X-Git-Newrev: 6f047a4540277fe70d0fe73b2e2db24659093ec5 Message-Id: <20221101114321.35AEC3857831@sourceware.org> Date: Tue, 1 Nov 2022 11:43:21 +0000 (GMT) List-Id: https://gcc.gnu.org/g:6f047a4540277fe70d0fe73b2e2db24659093ec5 commit 6f047a4540277fe70d0fe73b2e2db24659093ec5 Author: Jason Merrill 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 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 . - 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 : + 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 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 #include -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 #include -#include __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(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 @@ // . // 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(_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