diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 83070b2f633ea..09c71116ad351 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -5121,6 +5121,7 @@ build_new_function_call (tree fn, vec **args, else { result = build_over_call (cand, LOOKUP_NORMAL, complain); + result = maybe_contract_wrap_new_method_call(cand->fn, result); } if (flag_coroutines @@ -5256,6 +5257,8 @@ build_operator_new_call (tree fnname, vec **args, /* Build the CALL_EXPR. */ tree ret = build_over_call (cand, LOOKUP_NORMAL, complain); + ret =maybe_contract_wrap_new_method_call(cand->fn, ret); + /* Set this flag for all callers of this function. In addition to new-expressions, this is called for allocating coroutine state; treat that as an implicit new-expression. */ @@ -5427,6 +5430,9 @@ build_op_call (tree obj, vec **args, tsubst_flags_t complain) which is operator() turns out to be a static member function, `a' is none-the-less evaluated. */ result = keep_unused_object_arg (result, obj, cand->fn); + + result = maybe_contract_wrap_new_method_call(cand->fn, result); + } else { @@ -7292,6 +7298,7 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags, "arguments"); } result = build_over_call (cand, LOOKUP_NORMAL, ocomplain); + result = maybe_contract_wrap_new_method_call(cand->fn, result); } if (trivial_fn_p (cand->fn) || DECL_IMMEDIATE_FUNCTION_P (cand->fn)) @@ -7654,6 +7661,7 @@ build_op_subscript (const op_location_t &loc, tree obj, which is operator[] turns out to be a static member function, `a' is none-the-less evaluated. */ result = keep_unused_object_arg (result, obj, cand->fn); + result = maybe_contract_wrap_new_method_call(cand->fn, result); } else gcc_unreachable (); @@ -8627,6 +8635,7 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum, TARGET_EXPR_LIST_INIT_P (expr) = true; } + expr = maybe_contract_wrap_new_method_call(cand->fn, expr); return expr; } case ck_identity: @@ -11523,6 +11532,7 @@ complain_about_no_candidates_for_method_call (tree instance, print_z_candidates (location_of (name), candidates); } + /* Build a call to "INSTANCE.FN (ARGS)". If FN_P is non-NULL, it will be set, upon return, to the function called. ARGS may be NULL. This may change ARGS. */ @@ -11879,6 +11889,8 @@ build_new_method_call (tree instance, tree fns, vec **args, "operator delete(~X(f()))" (rather than generating "t = f(), ~X(t), operator delete (t)"). */ call = build_nop (void_type_node, call); + + call = maybe_contract_wrap_new_method_call(cand->fn, call); } } } diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc index db13104355bb8..1307feada4148 100644 --- a/gcc/cp/contracts.cc +++ b/gcc/cp/contracts.cc @@ -1512,7 +1512,8 @@ handle_contracts_p (tree decl1) { return (flag_contracts && !processing_template_decl - && DECL_ABSTRACT_ORIGIN (decl1) == NULL_TREE + && (DECL_ABSTRACT_ORIGIN (decl1) == NULL_TREE + || DECL_IS_WRAPPER_FN_P(decl1)) && contract_any_active_p (DECL_CONTRACTS (decl1))); } @@ -2004,7 +2005,8 @@ start_function_contracts (tree decl1) /* Contracts may have just been added without a chance to parse them, though we still need the PRE_FN available to generate a call to it. */ - if (!DECL_PRE_FN (decl1)) + /* Do we already have declarations generated ? */ + if (!DECL_PRE_FN (decl1) && !DECL_POST_FN (decl1)) build_contract_function_decls (decl1); } @@ -2191,7 +2193,7 @@ finish_function_contracts (tree fndecl) if (post) { DECL_PENDING_INLINE_P (post) = false; - start_preparsed_function (post, + start_preparsed_function (post, DECL_ATTRIBUTES (post), flags); remap_and_emit_conditions (fndecl, post, POSTCONDITION_STMT); @@ -2201,6 +2203,12 @@ finish_function_contracts (tree fndecl) tree finished_post = finish_function (false); expand_or_defer_fn (finished_post); } + + /* Check if we need to update wrapper function contracts. */ + tree wrapdecl = get_contract_wrapper_function (fndecl); + if (wrapdecl){ + copy_and_remap_contracts (wrapdecl, fndecl); + } } @@ -2330,7 +2338,7 @@ duplicate_contracts (tree newdecl, tree olddecl) PARM_DECL in OVERRIDER. */ void -inherit_base_contracts (tree overrider, tree basefn) +copy_and_remap_contracts (tree overrider, tree basefn) { tree last = NULL_TREE, contract_attrs = NULL_TREE; for (tree a = DECL_CONTRACTS (basefn); @@ -2357,4 +2365,224 @@ inherit_base_contracts (tree overrider, tree basefn) set_decl_contracts (overrider, contract_attrs); } +/* Build a declaration for the contract wrapper of a caller FNDECL. */ + +static tree +build_contract_wrapper_function (tree fndecl) +{ + if (TREE_TYPE (fndecl) == error_mark_node) + return error_mark_node; + + /* Use mangled name so we can differentiate between different virtual + functions of the same name. */ + const char *name = IDENTIFIER_POINTER ((DECL_ASSEMBLER_NAME (fndecl))); + name = ACONCAT ((name, ".contract_wrapper", NULL)); + + location_t loc = DECL_SOURCE_LOCATION (fndecl); + + tree wrapper_return_type = TREE_TYPE (TREE_TYPE (fndecl)); + tree wrapper_args = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); + + tree wrapper_type = build_function_type ( wrapper_return_type, + wrapper_args); + + + /* this will create a member function because fndecl is a member function, + so we will need to adjust the type later */ + tree wrapdecl + = build_lang_decl_loc (loc, FUNCTION_DECL, get_identifier (name), wrapper_type); + + + DECL_CONTEXT (wrapdecl) = NULL_TREE; + DECL_SOURCE_LOCATION (wrapdecl) = loc; + /*the declaration was implicitly generated by the compiler */ + DECL_ARTIFICIAL (wrapdecl) = true; + /* no function body at present * */ + DECL_INITIAL (wrapdecl) = error_mark_node; + + + if (DECL_RESULT (fndecl)) + { + DECL_RESULT (wrapdecl) = copy_decl (DECL_RESULT (fndecl)); + DECL_CONTEXT (DECL_RESULT (wrapdecl)) = wrapdecl; + } + + /* Copy the function parameters. */ + tree last = DECL_ARGUMENTS (wrapdecl) = copy_decl (DECL_ARGUMENTS (fndecl)); + DECL_CONTEXT (last) = wrapdecl; + for (tree p = TREE_CHAIN (DECL_ARGUMENTS (fndecl)); p; p = TREE_CHAIN (p)) + { + if (VOID_TYPE_P (p)) + { + TREE_CHAIN (last) = void_list_node; + break; + } + last = TREE_CHAIN (last) = copy_decl (p); + DECL_CONTEXT (last) = wrapdecl; + } + + /* Copy selected attributes from the original function. */ + TREE_USED (wrapdecl) = TREE_USED (fndecl); + if (DECL_SECTION_NAME (fndecl)) + set_decl_section_name (wrapdecl, fndecl); + + /* Copy any alignment that the FE added. */ + if (DECL_ALIGN (fndecl)) + SET_DECL_ALIGN (wrapdecl, DECL_ALIGN (fndecl)); + /* Copy any alignment the user added. */ + DECL_USER_ALIGN (wrapdecl) = DECL_USER_ALIGN (fndecl); + + /* Apply attributes from the original fn. + TODO : we can probably skip this step if function needs + instantiating as it will be done as a part of instantiation. */ + copy_and_remap_contracts (wrapdecl, fndecl); + + DECL_ABSTRACT_ORIGIN (wrapdecl) = fndecl; + + /* Make this function internal */ + TREE_PUBLIC (wrapdecl) = false; + DECL_EXTERNAL (wrapdecl) = false; + DECL_WEAK (wrapdecl) = false; + + // below is copied from build_contract_condition_function + DECL_INTERFACE_KNOWN (wrapdecl) = true; + + /* Update various inline related declaration properties. */ + //DECL_DECLARED_INLINE_P (wrapdecl) = true; + DECL_DISREGARD_INLINE_LIMITS (wrapdecl) = true; + TREE_NO_WARNING (wrapdecl) = 1; + + return wrapdecl; +} + + +/* Map from FUNCTION_DECL to a FUNCTION_DECL for contract wrapper. */ +static GTY(()) hash_map *decl_wrapper_fn; + +/* Returns the wrapper funtion for D, or null if not set. */ + +tree +get_contract_wrapper_function (tree decl) +{ + hash_map_maybe_create (decl_wrapper_fn); + tree *result = decl_wrapper_fn->get (decl); + return result ? *result : NULL_TREE; +} + +/* Makes wrapper the precondition function for D. */ + +void +set_contract_wrapper_function (tree decl, tree wrapper) +{ + gcc_assert (wrapper); + hash_map_maybe_create (decl_wrapper_fn); + gcc_assert (!decl_wrapper_fn->get (decl)); + decl_wrapper_fn->put (decl, wrapper); +} + + +tree +maybe_contract_wrap_new_method_call (tree fndecl, tree call) +{ + + if (fndecl == error_mark_node) return fndecl; + + /* We only need to wrap the function if it's a virtual function */ + if (!flag_contracts_nonattr || !handle_contracts_p (fndecl) + || !DECL_VIRTUAL_P (fndecl)) + return call; + + bool do_pre = has_active_preconditions (fndecl); + bool do_post = has_active_postconditions (fndecl); + + /* We should not have reached here with nothing to do... */ + gcc_checking_assert (do_pre || do_post); + + /* build the declaration of the wrapper if we need to */ + tree wrapdecl = get_contract_wrapper_function (fndecl); + + if (!wrapdecl){ + wrapdecl = build_contract_wrapper_function (fndecl); + wrapdecl = pushdecl_top_level (wrapdecl); + set_contract_wrapper_function (fndecl, wrapdecl); + } + + unsigned nargs = call_expr_nargs (call); + vec *argwrap; + vec_alloc (argwrap, nargs); + + tree arg; + call_expr_arg_iterator iter; + FOR_EACH_CALL_EXPR_ARG (arg, iter, call) + { + argwrap->quick_push (arg); + } + + tree wrapcall = build_call_expr_loc_vec(DECL_SOURCE_LOCATION (wrapdecl), wrapdecl, argwrap); + + CALL_FROM_THUNK_P (wrapcall) = true; + return wrapcall; +} + + +bool define_contract_wrapper_func(const tree& fndecl, const tree& wrapdecl, void*) +{ + + start_preparsed_function (wrapdecl, /*DECL_ATTRIBUTES (wrapdecl)*/ NULL_TREE, SF_DEFAULT | SF_PRE_PARSED); + + tree body = begin_function_body (); + tree compound_stmt = begin_compound_stmt (BCS_FN_BODY); + + + vec * args = build_arg_list (wrapdecl); + tree return_type = TREE_TYPE (TREE_TYPE (fndecl)); + tree *class_ptr = args->begin(); + tree fn; + gcc_assert (class_ptr); + + tree t; + tree binfo = lookup_base (TREE_TYPE (TREE_TYPE (*class_ptr)), + DECL_CONTEXT (fndecl), + ba_any, NULL, tf_warning_or_error); + gcc_assert (binfo && binfo != error_mark_node); + + *class_ptr = build_base_path (PLUS_EXPR, *class_ptr, binfo, 1, + tf_warning_or_error); + if (TREE_SIDE_EFFECTS (*class_ptr)) + *class_ptr = save_expr (*class_ptr); + t = build_pointer_type (TREE_TYPE (fndecl)); + fn = build_vfn_ref (*class_ptr, DECL_VINDEX (fndecl)); + TREE_TYPE (fn) = t; + + + tree call = build_call_a (fn, + args->length (), + args->address ()); + + if (!VOID_TYPE_P (return_type)) + { + tree r = build_cplus_new(return_type, call, tf_warning_or_error); + finish_return_stmt (r); + } + else { + finish_return_stmt (NULL_TREE); + } + finish_compound_stmt (compound_stmt); + finish_function_body (body); + expand_or_defer_fn (finish_function (/*inline_p=*/false)); + return true; +} + +void emit_contract_wrapper_func() +{ + if (!decl_wrapper_fn || decl_wrapper_fn->is_empty ()) return; + + decl_wrapper_fn->traverse(NULL); + + decl_wrapper_fn->empty (); + +} + + + #include "gt-cp-contracts.h" diff --git a/gcc/cp/contracts.h b/gcc/cp/contracts.h index af4ab87aba500..df64c7aeda139 100644 --- a/gcc/cp/contracts.h +++ b/gcc/cp/contracts.h @@ -265,6 +265,11 @@ enum contract_matching_context #define DECL_POST_FN(NODE) \ (get_postcondition_function ((NODE))) +/* For a FUNCTION_DECL of a guarded function, this holds the function decl + where caller contract checks are emitted. */ +#define DECL_WRAPPER_FN(NODE) \ + (get_contract_wrapper_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) @@ -273,6 +278,12 @@ enum contract_matching_context #define DECL_IS_POST_FN_P(NODE) \ (DECL_ABSTRACT_ORIGIN (NODE) && DECL_POST_FN (DECL_ABSTRACT_ORIGIN (NODE)) == NODE) +/* True iff the FUNCTION_DECL is the caller contract wrapper function + for a guarded function. */ +#define DECL_IS_WRAPPER_FN_P(NODE) \ + (DECL_ABSTRACT_ORIGIN (NODE) && DECL_WRAPPER_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); @@ -281,6 +292,7 @@ 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 tree get_contract_wrapper_function (tree); extern void duplicate_contracts (tree, tree); extern void match_deferred_contracts (tree); extern void defer_guarded_contract_match (tree, tree, tree); @@ -290,7 +302,7 @@ 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 void copy_and_remap_contracts (tree, tree); //extern tree apply_postcondition_to_return (tree); extern void start_function_contracts (tree); extern void maybe_apply_function_contracts (tree); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 4620da10de8d4..11da7a0460a88 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8789,6 +8789,8 @@ extern tree grok_contract (tree, tree, tree, cp_expr, location_t); extern tree finish_contract_condition (cp_expr); extern tree constify_contract_access (tree); extern tree view_as_const (tree); +extern tree maybe_contract_wrap_new_method_call (tree, tree); +extern void emit_contract_wrapper_func (); /* Return the first contract in ATTRS, or NULL_TREE if there are none. */ diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index 6d674684931cd..637770ff54c57 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -5205,6 +5205,9 @@ c_parse_final_cleanups (void) importer. */ continue; + if (flag_contracts_nonattr && flag_contracts) + emit_contract_wrapper_func(); + /* Write out virtual tables as required. Writing out the virtual table for a template class may cause the instantiation of members of that class. If we write out diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 63c047a29323f..a06c617912012 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -12807,7 +12807,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr, statement = cp_parser_transaction_cancel (parser); break; case RID_CONTASSERT: - if (flag_contracts_nonattr &&flag_contracts) + if (flag_contracts_nonattr && flag_contracts) { tree cont_assert = token->u.value; diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc index e117204afa52f..bbd7c35c732c4 100644 --- a/gcc/cp/search.cc +++ b/gcc/cp/search.cc @@ -2179,7 +2179,7 @@ check_final_overrider (tree overrider, tree basefn) { /* 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); + copy_and_remap_contracts (overrider, basefn); } else if (DECL_HAS_CONTRACTS_P (basefn) && DECL_HAS_CONTRACTS_P (overrider)) { diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/contracts-pre4.C b/gcc/testsuite/g++.dg/contracts/cpp26/contracts-pre4.C index 732d9b9499ac2..a2ca300b866be 100644 --- a/gcc/testsuite/g++.dg/contracts/cpp26/contracts-pre4.C +++ b/gcc/testsuite/g++.dg/contracts/cpp26/contracts-pre4.C @@ -78,14 +78,18 @@ int main(int, char**) return 0; } -// { dg-output "contract violation in function Base::f at .*.C:7: .*(\n|\r\n|\r)" } +// { dg-output "contract violation in function .*contract_wrapper at .*.C:7: a > 5.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function Base::f at .*.C:7: a > 5.*(\n|\r\n|\r)" } // { dg-output "Base: 13(\n|\r\n|\r)" } -// { dg-output "contract violation in function Base::f at .*.C:7: .*(\n|\r\n|\r)" } +// { dg-output "contract violation in function .*contract_wrapper at .*.C:7: a > 5.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function Base::f at .*.C:7: a > 5.*(\n|\r\n|\r)" } // { dg-output "Child0: 13(\n|\r\n|\r)" } -// { dg-output "contract violation in function Child1::f at .*.C:23: .*(\n|\r\n|\r)" } +// { dg-output "contract violation in function .*contract_wrapper at .*.C:23: a > 14.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function Child1::f at .*.C:23: a > 14.*(\n|\r\n|\r)" } // { dg-output "Child1: 27(\n|\r\n|\r)" } // { dg-output "Child2: 31(\n|\r\n|\r)" } -// { dg-output "contract violation in function Child3::f at .*.C:45: .*(\n|\r\n|\r)" } +// { dg-output "contract violation in function .*contract_wrapper at .*.C:45: a > 0.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function Child3::f at .*.C:45: a > 0.*(\n|\r\n|\r)" } // { dg-output "Child3: 40(\n|\r\n|\r)" } // { dg-output "Child3: 41(\n|\r\n|\r)" } // { dg-output "Child4: 50(\n|\r\n|\r)" } diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/virtual_func.C b/gcc/testsuite/g++.dg/contracts/cpp26/virtual_func.C new file mode 100644 index 0000000000000..4ea3ea3cca8f7 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/virtual_func.C @@ -0,0 +1,96 @@ +// test that contracts on overriding functions are found correctly +// { dg-do run } +// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on -fcontracts-nonattr" } +#include + +struct Base +{ + virtual int f(const int a) pre (a > 5); +}; + +int Base::f(const int a) +{ + return a; +} + +// inherits original +struct Child0 : Base +{ +}; + +struct Child1 : Base +{ + virtual int f(const int a) pre (a > 14){ return a + 10; } +}; + + +struct GChild1 : Child0 +{ + virtual int f(const int a) post(a > 6) { return a + 100; }; +}; + +struct GChild2 : Child1 +{ + virtual int f(const int a) post(a > 30) { return a + 200; }; +}; + + +int fooBase(Base& b) +{ + return b.f(1); +} + + + +int main(int, char**) +{ + Base b; + Child0 c0; + Child1 c1; + GChild1 g1; + GChild2 g2; + + printf("Base: %d\n", b.f(3)); // a > 5 + printf("Child0: %d\n", c0.f(3)); // a > 5 + printf("Child1: %d\n", c1.f(7)); // a > 14 + printf("GChild1: %d\n", g1.f(3)); // a > 6 + printf("GChild2: %d\n", g2.f(7)); // a > 30 + + printf("fooBase(Base): %d\n", fooBase(b)); // a > 5 + printf("fooBase(Child0): %d\n", fooBase(c0)); // a > 5 + printf("fooBase(Child1): %d\n", fooBase(c1)); // a > 14 + printf("fooBase(GChild1): %d\n", fooBase(g1)); // a > 6 + printf("fooBase(GChild2): %d\n", fooBase(g2)); // a > 30 + return 0; +} + +// { dg-output "contract violation in function .*contract_wrapper at .*: a > 5.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function Base::f at .*: a > 5.*(\n|\r\n|\r)" } +// { dg-output "Base: 3(\n|\r\n|\r)" } +// { dg-output "contract violation in function .*contract_wrapper at .*: a > 5.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function Base::f at .*: a > 5.*(\n|\r\n|\r)" } +// { dg-output "Child0: 3(\n|\r\n|\r)" } +// { dg-output "contract violation in function .*contract_wrapper at .*: a > 14.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function Child1::f at .*: a > 14.*(\n|\r\n|\r)" } +// { dg-output "Child1: 17(\n|\r\n|\r)" } +// { dg-output "contract violation in function GChild1::f at .*: a > 6.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function .*contract_wrapper at .*: a > 6.*(\n|\r\n|\r)" } +// { dg-output "GChild1: 103(\n|\r\n|\r)" } +// { dg-output "contract violation in function GChild2::f at .*: a > 30.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function .*contract_wrapper at .*: a > 30.*(\n|\r\n|\r)" } +// { dg-output "GChild2: 207(\n|\r\n|\r)" } +// { dg-output "contract violation in function .*contract_wrapper at .*: a > 5.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function Base::f at .*: a > 5.*(\n|\r\n|\r)" } +// { dg-output "fooBase.Base.: 1(\n|\r\n|\r)" } +// { dg-output "contract violation in function .*contract_wrapper at .*: a > 5.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function Base::f at .*: a > 5.*(\n|\r\n|\r)" } +// { dg-output "fooBase.Child0.: 1(\n|\r\n|\r)" } +// { dg-output "contract violation in function .*contract_wrapper at .*: a > 5.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function Child1::f at .*: a > 14.*(\n|\r\n|\r)" } +// { dg-output "fooBase.Child1.: 11(\n|\r\n|\r)" } +// { dg-output "contract violation in function .*contract_wrapper at .*: a > 5.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function GChild1::f at .*: a > 6.*(\n|\r\n|\r)" } +// { dg-output "fooBase.GChild1.: 101(\n|\r\n|\r)" } +// { dg-output "contract violation in function .*contract_wrapper at .*: a > 5.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function GChild2::f at .*: a > 30.*(\n|\r\n|\r)" } +// { dg-output "fooBase.GChild2.: 201(\n|\r\n|\r)" } diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/virtual_func3.C b/gcc/testsuite/g++.dg/contracts/cpp26/virtual_func3.C new file mode 100644 index 0000000000000..8d842fa0c9096 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/virtual_func3.C @@ -0,0 +1,30 @@ +// { dg-do run } +// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr -fcontract-continuation-mode=on" } +template +struct Base +{ + virtual char f(const int a) pre(a > 5) post( i > 4 ){ return 'b';} +private: + int i = 2; +}; + +template +struct Child1 : Base +{ + virtual char f(const int a) pre(a > 3){ return 'c'; } +}; +template +void foo(Base& b){ + b.f(1); +} + + +int main() +{ + Child1 c; + foo(c); + +} +// { dg-output "contract violation in function .*contract_wrapper at .*: a > 5.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function Child1::f at .*: a > 3.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function .*contract_wrapper at .*: i > 4.*(\n|\r\n|\r)" }