This PR points out that when we're invoking a non-static member function
on a null instance during constant evaluation, we should reject.
cxx_eval_call_expression calls cxx_bind_parameters_in_call which
evaluates function arguments, but it won't detect problems like these.

Well, ok, so use integer_zerop to detect a null 'this'.  This also
detects member calls on a variable whose lifetime has ended, because
check_return_expr creates an artificial nullptr:
10195       else if (!processing_template_decl
10196                && maybe_warn_about_returning_address_of_local (retval, 
loc)
10197                && INDIRECT_TYPE_P (valtype))
10198         retval = build2 (COMPOUND_EXPR, TREE_TYPE (retval), retval,
10199                          build_zero_cst (TREE_TYPE (retval)));
It would be great if we could somehow distinguish between those two
cases, but experiments with setting TREE_THIS_VOLATILE on the zero
didn't work, so I left it be.

But by the same token, we should detect out-of-bounds accesses.  For
this I'm (ab)using eval_and_check_array_index so that I don't have
to reimplement bounds checking yet again.  But this only works for
ARRAY_REFs, so won't detect

  X x;
  (&x)[0].foo(); // ok
  (&x)[1].foo(); // bad

so I've added a special handling of POINTER_PLUS_EXPRs.

While here, we should also detect using an inactive union member.  For
that, I'm using cxx_eval_component_reference.

Does this approach seem sensible?

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

gcc/cp/ChangeLog:

        PR c++/97230
        * constexpr.c (eval_and_check_array_index): Forward declare.
        (cxx_eval_component_reference): Likewise.
        (cxx_eval_call_expression): Verify the 'this' pointer for
        non-static member functions.

gcc/testsuite/ChangeLog:

        PR c++/97230
        * g++.dg/cpp0x/constexpr-member-fn1.C: New test.
---
 gcc/cp/constexpr.c                            | 72 ++++++++++++++++++-
 .../g++.dg/cpp0x/constexpr-member-fn1.C       | 44 ++++++++++++
 2 files changed, 115 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-member-fn1.C

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index a118f8a810b..f62f37ce384 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -2181,6 +2181,11 @@ cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, 
tree thunk_fndecl,
                                       non_constant_p, overflow_p);
 }
 
+static tree eval_and_check_array_index (const constexpr_ctx *, tree, bool,
+                                       bool *, bool *);
+static tree cxx_eval_component_reference (const constexpr_ctx *, tree,
+                                         bool, bool *, bool *);
+
 /* Subroutine of cxx_eval_constant_expression.
    Evaluate the call expression tree T in the context of OLD_CALL expression
    evaluation.  */
@@ -2467,6 +2472,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree 
t,
   if (*non_constant_p)
     return t;
 
+  tree result = NULL_TREE;
   depth_ok = push_cx_call_context (t);
 
   /* Remember the object we are constructing.  */
@@ -2496,8 +2502,72 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree 
t,
            new_obj = NULL_TREE;
        }
     }
+   /* Verify that the object we're invoking the function on is sane.  */
+  else if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fun)
+          /* maybe_add_lambda_conv_op creates a null 'this' pointer.  */
+          && !LAMBDA_TYPE_P (CP_DECL_CONTEXT (fun)))
+    {
+      tree thisarg = TREE_VEC_ELT (new_call.bindings, 0);
+      if (integer_zerop (thisarg))
+       {
+         if (!ctx->quiet)
+           error_at (loc, "member call on null pointer is not allowed "
+                     "in a constant expression");
+         *non_constant_p = true;
+         result = error_mark_node;
+       }
+      else
+       {
+         STRIP_NOPS (thisarg);
+         if (TREE_CODE (thisarg) == ADDR_EXPR)
+           thisarg = TREE_OPERAND (thisarg, 0);
+         /* Detect out-of-bounds accesses.  */
+         if (TREE_CODE (thisarg) == ARRAY_REF)
+           {
+             eval_and_check_array_index (ctx, thisarg, /*allow_one_past*/false,
+                                         non_constant_p, overflow_p);
+             if (*non_constant_p)
+               result = error_mark_node;
+           }
+         /* Detect using an inactive member of a union.  */
+         else if (TREE_CODE (thisarg) == COMPONENT_REF)
+           {
+             cxx_eval_component_reference (ctx, thisarg, /*lval*/false,
+                                           non_constant_p, overflow_p);
+             if (*non_constant_p)
+               result = error_mark_node;
+           }
+         /* Detect other invalid accesses like
 
-  tree result = NULL_TREE;
+              X x;
+              (&x)[1].foo();
+
+            where we'll end up with &x p+ 1.  */
+         else if (TREE_CODE (thisarg) == POINTER_PLUS_EXPR)
+           {
+             tree op0 = TREE_OPERAND (thisarg, 0);
+             /* This shouldn't trigger if we're accessing a base class of
+                the object in question.  */
+             if (TREE_CODE (op0) == ADDR_EXPR
+                 && DECL_P (TREE_OPERAND (op0, 0)))
+               {
+                 if (!ctx->quiet)
+                   {
+                     tree op1 = TREE_OPERAND (thisarg, 1);
+                     if (integer_onep (op1))
+                       error_at (loc, "cannot dereference one-past-the-end "
+                                 "pointer in a constant expression");
+                     else
+                       error_at (loc, "cannot access element %qE of a "
+                                 "non-array object in a constant expression",
+                                 op1);
+                   }
+                 *non_constant_p = true;
+                 result = error_mark_node;
+               }
+           }
+       }
+    }
 
   constexpr_call *entry = NULL;
   if (depth_ok && !non_constant_args && ctx->strict)
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-member-fn1.C 
b/gcc/testsuite/g++.dg/cpp0x/constexpr-member-fn1.C
new file mode 100644
index 00000000000..8ba5b87286f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-member-fn1.C
@@ -0,0 +1,44 @@
+// PR c++/97230
+// { dg-do compile { target c++11 } }
+
+struct X {
+  constexpr int foo () const { return 1; }
+};
+
+constexpr X *eval () { return nullptr; }
+constexpr const X *dead (X tmp) { return &tmp; } // { dg-warning "address of 
local variable" }
+
+static constexpr X x;
+static constexpr X arr[3];
+
+constexpr const X *gimme () { return &x; }
+
+union U {
+  int i;
+  X x;
+};
+constexpr U u{42};
+
+void
+fn ()
+{
+  constexpr auto x1 = ((X *) nullptr)->foo(); // { dg-error "member call on 
null pointer" }
+  constexpr auto x2 = (eval())->foo(); // { dg-error "member call on null 
pointer" }
+  constexpr auto x3 = dead ({})->foo(); // { dg-error "member call" }
+  constexpr auto x4 = (&x)[0].foo();
+  constexpr auto x5 = (&x)[1].foo(); // { dg-error "cannot dereference 
one-past-the-end pointer" }
+  constexpr auto x6 = (&x)[2].foo(); // { dg-error "cannot access element .2. 
of a non-array object" }
+  constexpr auto x7 = (&x)[3].foo(); // { dg-error "cannot access element .3. 
of a non-array object" }
+  constexpr auto x8 = arr[2].foo();
+  constexpr auto x9 = arr[3].foo(); // { dg-error "outside the bounds" }
+  constexpr auto *p = &arr[0];
+  constexpr auto x10 = (*p).foo();
+  constexpr auto x11 = p->foo();
+  constexpr auto x12 = (*(p + 1)).foo();
+  constexpr auto x13 = (*(p + 3)).foo(); // { dg-error "outside the bounds" }
+  constexpr auto x14 = (p + 3)->foo(); // { dg-error "outside the bounds" }
+  constexpr auto x15 = gimme()->foo();
+  constexpr auto x16 = (gimme() + 1)->foo(); // { dg-error "cannot dereference 
one-past-the-end pointer" }
+  constexpr auto x17 = (gimme() + 2)->foo(); // { dg-error "cannot access 
element .2. of a non-array object" }
+  constexpr auto x18 = u.x.foo(); // { dg-error "accessing .U::x. member 
instead of initialized .U::i. member" }
+}

base-commit: 04b99da898a9817e72fedb4063589648b7961ac5
-- 
2.26.2

Reply via email to