Skip to content

Commit

Permalink
Merge pull request gcc-mirror#21 from iains/contracts-investigation-o…
Browse files Browse the repository at this point in the history
…f-wrapper-obj-returns

c++, contracts: Handle object returns in the virtual fn wrapper.
  • Loading branch information
villevoutilainen authored Sep 24, 2024
2 parents b86f8ef + e218011 commit 64516ad
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 34 deletions.
29 changes: 13 additions & 16 deletions gcc/cp/call.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5119,10 +5119,7 @@ build_new_function_call (tree fn, vec<tree, va_gc> **args,
result = error_mark_node;
}
else
{
result = build_over_call (cand, LOOKUP_NORMAL, complain);
result = maybe_contract_wrap_new_method_call(cand->fn, result);
}
result = build_over_call (cand, LOOKUP_NORMAL, complain);

if (flag_coroutines
&& result
Expand Down Expand Up @@ -5257,8 +5254,6 @@ build_operator_new_call (tree fnname, vec<tree, va_gc> **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. */
Expand Down Expand Up @@ -5431,8 +5426,6 @@ build_op_call (tree obj, vec<tree, va_gc> **args, tsubst_flags_t complain)
`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
{
Expand Down Expand Up @@ -7298,7 +7291,6 @@ 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))
Expand Down Expand Up @@ -7661,7 +7653,6 @@ 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 ();
Expand Down Expand Up @@ -8635,7 +8626,6 @@ 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:
Expand Down Expand Up @@ -10597,6 +10587,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
&& DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL)
maybe_warn_class_memaccess (input_location, fn, args);

tree orig_fn = fn;
if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0)
{
tree t;
Expand Down Expand Up @@ -10630,7 +10621,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
ADDR_EXPR_DENOTES_CALL_P (fn) = true;
}

tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag);
tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag, orig_fn);
if (call == error_mark_node)
return call;
if (cand->flags & LOOKUP_LIST_INIT_CTOR)
Expand Down Expand Up @@ -11094,7 +11085,9 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
SET_EXPR_LOCATION (fn, loc);

fndecl = get_callee_fndecl (fn);
if (!orig_fndecl)
if (!fndecl && orig_fndecl)
fndecl = orig_fndecl;
else if (!orig_fndecl)
orig_fndecl = fndecl;

/* Check that arguments to builtin functions match the expectations. */
Expand Down Expand Up @@ -11135,8 +11128,11 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
}
}

/* When we have contracts enabled, and they are P2900-style then we might
wrap a virtual method call with caller-side checking. */

if (VOID_TYPE_P (TREE_TYPE (fn)))
return fn;
return maybe_contract_wrap_new_method_call (fndecl, fn);

/* 5.2.2/11: If a function call is a prvalue of object type: if the
function call is either the operand of a decltype-specifier or the
Expand All @@ -11148,13 +11144,16 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
fn = require_complete_type (fn, complain);
if (fn == error_mark_node)
return error_mark_node;
fn = maybe_contract_wrap_new_method_call (fndecl, fn);

if (MAYBE_CLASS_TYPE_P (TREE_TYPE (fn)))
{
fn = build_cplus_new (TREE_TYPE (fn), fn, complain);
maybe_warn_parm_abi (TREE_TYPE (fn), loc);
}
}
else
fn = maybe_contract_wrap_new_method_call (fndecl, fn);
return convert_from_reference (fn);
}

Expand Down Expand Up @@ -11889,8 +11888,6 @@ build_new_method_call (tree instance, tree fns, vec<tree, va_gc> **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);
}
}
}
Expand Down
35 changes: 19 additions & 16 deletions gcc/cp/contracts.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2419,12 +2419,12 @@ build_contract_wrapper_function (tree fndecl)
/* 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;
}
/* Build our result decl. */
tree resdecl = build_decl (loc, RESULT_DECL, 0, wrapper_return_type);
DECL_CONTEXT (resdecl) = wrapdecl;
DECL_ARTIFICIAL (resdecl) = 1;
DECL_IGNORED_P (resdecl) = 1;
DECL_RESULT (wrapdecl) = resdecl;

/* Copy the function parameters. */
tree last = DECL_ARGUMENTS (wrapdecl) = copy_decl (DECL_ARGUMENTS (fndecl));
Expand Down Expand Up @@ -2503,13 +2503,19 @@ set_contract_wrapper_function (tree decl, tree wrapper)
tree
maybe_contract_wrap_new_method_call (tree fndecl, tree call)
{
/* We can be called from build_cxx_call without a known callee... */
if (!fndecl)
return call;

if (fndecl == error_mark_node) return fndecl;
if (fndecl == error_mark_node || call == error_mark_node)
return error_mark_node;

/* We only need to wrap the function if it's a virtual function */
if (!flag_contracts_nonattr || !handle_contracts_p (fndecl)
if (!flag_contracts_nonattr
|| !handle_contracts_p (fndecl)
|| !DECL_IOBJ_MEMBER_FUNCTION_P (fndecl)
|| !DECL_VIRTUAL_P (fndecl))
return call;
return call;

bool do_pre = has_active_preconditions (fndecl);
bool do_post = has_active_postconditions (fndecl);
Expand Down Expand Up @@ -2579,13 +2585,10 @@ bool define_contract_wrapper_func(const tree& fndecl, const tree& wrapdecl, void
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_return_stmt (call);
else
finish_return_stmt (NULL_TREE);

finish_compound_stmt (compound_stmt);
finish_function_body (body);
expand_or_defer_fn (finish_function (/*inline_p=*/false));
Expand Down
2 changes: 0 additions & 2 deletions gcc/testsuite/g++.dg/contracts/cpp26/virtual_func.C
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ int fooBase(Base& b)
return b.f(1);
}



int main(int, char**)
{
Base b;
Expand Down
100 changes: 100 additions & 0 deletions gcc/testsuite/g++.dg/contracts/cpp26/virtual_func1.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// 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 <cstdio>

struct T {
int x;
T (int _x) : x(_x) {}
~T () { x = -1; }
};

struct Base
{
virtual T f(const int a) pre (a > 5);
};

T Base::f(const int a)
{
return T (a);
}

// inherits original
struct Child0 : Base
{
};

struct Child1 : Base
{
virtual T f(const int a) pre (a > 14){ return T (a + 10); }
};


struct GChild1 : Child0
{
virtual T f(const int a) post(a > 6) { return T (a + 100); };
};

struct GChild2 : Child1
{
virtual T f(const int a) post(a > 30) { return T (a + 200); };
};

T fooBase(Base& b)
{
return b.f(1);
}

int main(int, char**)
{
Base b;
Child0 c0;
Child1 c1;
GChild1 g1;
GChild2 g2;
T q = b.f(3); // a > 5
printf("Base: %d\n", q.x );
printf("Child0: %d\n", c0.f(3).x); // a > 5
printf("Child1: %d\n", c1.f(7).x); // a > 14
printf("GChild1: %d\n", g1.f(3).x); // a > 6
printf("GChild2: %d\n", g2.f(7).x); // a > 30

printf("fooBase(Base): %d\n", fooBase(b).x); // a > 5
printf("fooBase(Child0): %d\n", fooBase(c0).x); // a > 5
printf("fooBase(Child1): %d\n", fooBase(c1).x); // a > 14
printf("fooBase(GChild1): %d\n", fooBase(g1).x); // a > 6
printf("fooBase(GChild2): %d\n", fooBase(g2).x); // 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)" }
Loading

0 comments on commit 64516ad

Please sign in to comment.