libgccjit: detect various kinds of errors relating to params and locals
authorDavid Malcolm <dmalcolm@redhat.com>
Mon, 12 Jan 2015 22:25:37 +0000 (22:25 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Mon, 12 Jan 2015 22:25:37 +0000 (22:25 +0000)
gcc/jit/ChangeLog:
* jit-recording.c (class gcc::jit::rvalue_usage_validator): New.
(gcc::jit::rvalue_usage_validator::rvalue_usage_validator): New
ctor.
(gcc::jit::rvalue_usage_validator::visit): New function.
(gcc::jit::recording::rvalue::verify_valid_within_stmt): New
function.
(gcc::jit::recording::rvalue::set_scope): New function.
(gcc::jit::recording::function::function): Call set_scope on each
param, issuing errors for any params that already have a function.
(gcc::jit::recording::block::add_eval): Return the new statement;
update the comment given that some error-checking now happens after
this returns.
(gcc::jit::recording::block::add_assignment): Likewise.
(gcc::jit::recording::block::add_assignment_op): Likewise.
(gcc::jit::recording::block::add_comment): Likewise.
(gcc::jit::recording::block::end_with_conditional): Likewise.
(gcc::jit::recording::block::end_with_jump): Likewise.
(gcc::jit::recording::block::end_with_return): Likewise.
(gcc::jit::recording::block::validate): Add a comment.
(gcc::jit::recording::unary_op::visit_children): New function.
(gcc::jit::recording::binary_op::visit_children): New function.
(gcc::jit::recording::comparison::visit_children): New function.
(gcc::jit::recording::cast::visit_children): New function.
(gcc::jit::recording::call::visit_children): New function.
(gcc::jit::recording::call_through_ptr::visit_children): New function.
(gcc::jit::recording::array_access::visit_children): New function.
(gcc::jit::recording::access_field_of_lvalue::visit_children): New
function.
(gcc::jit::recording::access_field_rvalue::visit_children): New
function.
(gcc::jit::recording::dereference_field_rvalue::visit_children):
New function.
(gcc::jit::recording::dereference_rvalue::visit_children): New
function.
(gcc::jit::recording::get_address_of_lvalue::visit_children): New
function.
* jit-recording.h: Within namespace gcc::jit::recording...
(class rvalue_visitor): New.
(rvalue::rvalue): Initialize m_scope.
(rvalue::get_loc): New accessor.
(rvalue::verify_valid_within_stmt): New function.
(rvalue::visit_children): New pure virtual function.
(rvalue::set_scope): New function.
(rvalue::get_scope): New function.
(rvalue::dyn_cast_param): New function.
(rvalue::m_scope): New field.
(param::visit_children): New empty function.
(param::dyn_cast_param): New function.
(function::get_loc): New function.
(block::add_eval): Return the new statement.
(block::add_assignment): Likewise.
(block::add_assignment_op): Likewise.
(block::add_comment): Likewise.
(block::end_with_conditional): Likewise.
(block::end_with_jump): Likewise.
(block::end_with_return): Likewise.
(global::visit_children): New function.
(memento_of_new_rvalue_from_const<HOST_TYPE>::visit_children):
New function.
(memento_of_new_string_literal::visit_children): New function.
(unary_op::visit_children): New function.
(binary_op::visit_children): New function.
(comparison::visit_children): New function.
(cast::visit_children): New function.
(call::visit_children): New function.
(call_through_ptr::visit_children): New function.
(array_access::visit_children): New function.
(access_field_of_lvalue::visit_children): New function.
(access_field_rvalue::visit_children): New function.
(dereference_field_rvalue::visit_children): New function.
(dereference_rvalue::visit_children): New function.
(get_address_of_lvalue::visit_children): New function.
(local::local): Call set_scope.
(local::visit_children): New function.
(statement::get_block): Make public.
* libgccjit.c (RETURN_VAL_IF_FAIL_PRINTF5): New macro.
(RETURN_NULL_IF_FAIL_PRINTF5): New macro.
(gcc_jit_context_new_function): Verify that each param has
not yet been used for creating another function.
(gcc_jit_block_add_eval): After creating the stmt, verify
that the rvalue expression tree is valid to use within it.
(gcc_jit_block_add_assignment): Likewise for the lvalue and
rvalue expression trees.
(gcc_jit_block_add_assignment_op): Likewise.
(gcc_jit_block_end_with_conditional): Likewise for the boolval
expression tree.
(gcc_jit_block_end_with_return): Likewise for the rvalue
expression tree.
(gcc_jit_block_end_with_void_return): Remove return of "void",
now that block::end_with_return is now non-void.

gcc/testsuite/ChangeLog:
* jit.dg/test-error-local-used-from-other-function.c: New test
case.
* jit.dg/test-error-param-reuse.c: New test case.
* jit.dg/test-error-param-sharing.c: New test case.
* jit.dg/test-error-param-used-from-other-function.c: New test
case.
* jit.dg/test-error-param-used-without-a-function.c: New test
case.

From-SVN: r219498

gcc/jit/ChangeLog
gcc/jit/jit-recording.c
gcc/jit/jit-recording.h
gcc/jit/libgccjit.c
gcc/testsuite/ChangeLog
gcc/testsuite/jit.dg/test-error-local-used-from-other-function.c [new file with mode: 0644]
gcc/testsuite/jit.dg/test-error-param-reuse.c [new file with mode: 0644]
gcc/testsuite/jit.dg/test-error-param-sharing.c [new file with mode: 0644]
gcc/testsuite/jit.dg/test-error-param-used-from-other-function.c [new file with mode: 0644]
gcc/testsuite/jit.dg/test-error-param-used-without-a-function.c [new file with mode: 0644]

index 1ccbdf986e278bad72ca91a917813f569b0ce2ff..dbc1dc692d0e4632bd0bc716d4d6e007ac5cfa14 100644 (file)
@@ -1,3 +1,96 @@
+2015-01-12  David Malcolm  <dmalcolm@redhat.com>
+
+       * jit-recording.c (class gcc::jit::rvalue_usage_validator): New.
+       (gcc::jit::rvalue_usage_validator::rvalue_usage_validator): New
+       ctor.
+       (gcc::jit::rvalue_usage_validator::visit): New function.
+       (gcc::jit::recording::rvalue::verify_valid_within_stmt): New
+       function.
+       (gcc::jit::recording::rvalue::set_scope): New function.
+       (gcc::jit::recording::function::function): Call set_scope on each
+       param, issuing errors for any params that already have a function.
+       (gcc::jit::recording::block::add_eval): Return the new statement;
+       update the comment given that some error-checking now happens after
+       this returns.
+       (gcc::jit::recording::block::add_assignment): Likewise.
+       (gcc::jit::recording::block::add_assignment_op): Likewise.
+       (gcc::jit::recording::block::add_comment): Likewise.
+       (gcc::jit::recording::block::end_with_conditional): Likewise.
+       (gcc::jit::recording::block::end_with_jump): Likewise.
+       (gcc::jit::recording::block::end_with_return): Likewise.
+       (gcc::jit::recording::block::validate): Add a comment.
+       (gcc::jit::recording::unary_op::visit_children): New function.
+       (gcc::jit::recording::binary_op::visit_children): New function.
+       (gcc::jit::recording::comparison::visit_children): New function.
+       (gcc::jit::recording::cast::visit_children): New function.
+       (gcc::jit::recording::call::visit_children): New function.
+       (gcc::jit::recording::call_through_ptr::visit_children): New function.
+       (gcc::jit::recording::array_access::visit_children): New function.
+       (gcc::jit::recording::access_field_of_lvalue::visit_children): New
+       function.
+       (gcc::jit::recording::access_field_rvalue::visit_children): New
+       function.
+       (gcc::jit::recording::dereference_field_rvalue::visit_children):
+       New function.
+       (gcc::jit::recording::dereference_rvalue::visit_children): New
+       function.
+       (gcc::jit::recording::get_address_of_lvalue::visit_children): New
+       function.
+       * jit-recording.h: Within namespace gcc::jit::recording...
+       (class rvalue_visitor): New.
+       (rvalue::rvalue): Initialize m_scope.
+       (rvalue::get_loc): New accessor.
+       (rvalue::verify_valid_within_stmt): New function.
+       (rvalue::visit_children): New pure virtual function.
+       (rvalue::set_scope): New function.
+       (rvalue::get_scope): New function.
+       (rvalue::dyn_cast_param): New function.
+       (rvalue::m_scope): New field.
+       (param::visit_children): New empty function.
+       (param::dyn_cast_param): New function.
+       (function::get_loc): New function.
+       (block::add_eval): Return the new statement.
+       (block::add_assignment): Likewise.
+       (block::add_assignment_op): Likewise.
+       (block::add_comment): Likewise.
+       (block::end_with_conditional): Likewise.
+       (block::end_with_jump): Likewise.
+       (block::end_with_return): Likewise.
+       (global::visit_children): New function.
+       (memento_of_new_rvalue_from_const<HOST_TYPE>::visit_children):
+       New function.
+       (memento_of_new_string_literal::visit_children): New function.
+       (unary_op::visit_children): New function.
+       (binary_op::visit_children): New function.
+       (comparison::visit_children): New function.
+       (cast::visit_children): New function.
+       (call::visit_children): New function.
+       (call_through_ptr::visit_children): New function.
+       (array_access::visit_children): New function.
+       (access_field_of_lvalue::visit_children): New function.
+       (access_field_rvalue::visit_children): New function.
+       (dereference_field_rvalue::visit_children): New function.
+       (dereference_rvalue::visit_children): New function.
+       (get_address_of_lvalue::visit_children): New function.
+       (local::local): Call set_scope.
+       (local::visit_children): New function.
+       (statement::get_block): Make public.
+       * libgccjit.c (RETURN_VAL_IF_FAIL_PRINTF5): New macro.
+       (RETURN_NULL_IF_FAIL_PRINTF5): New macro.
+       (gcc_jit_context_new_function): Verify that each param has
+       not yet been used for creating another function.
+       (gcc_jit_block_add_eval): After creating the stmt, verify
+       that the rvalue expression tree is valid to use within it.
+       (gcc_jit_block_add_assignment): Likewise for the lvalue and
+       rvalue expression trees.
+       (gcc_jit_block_add_assignment_op): Likewise.
+       (gcc_jit_block_end_with_conditional): Likewise for the boolval
+       expression tree.
+       (gcc_jit_block_end_with_return): Likewise for the rvalue
+       expression tree.
+       (gcc_jit_block_end_with_void_return): Remove return of "void",
+       now that block::end_with_return is now non-void.
+
 2015-01-12  David Malcolm  <dmalcolm@redhat.com>
 
        * jit-playback.c (gcc::jit::playback::context::read_dump_file):
index 20fd2d2f82b860f1ada6d4bffd114ae10fe5c177..ec247e5a70b07fc5451a300ffc97820fc80e34e5 100644 (file)
@@ -2034,6 +2034,115 @@ recording::rvalue::dereference (recording::location *loc)
   return result;
 }
 
+/* An rvalue visitor, for validating that every rvalue within an expression
+   trees within "STMT" has the correct scope (e.g. no access to locals
+   of a different function).  */
+
+class rvalue_usage_validator : public recording::rvalue_visitor
+{
+ public:
+  rvalue_usage_validator (const char *api_funcname,
+                         recording::context *ctxt,
+                         recording::statement *stmt);
+
+  void
+  visit (recording::rvalue *rvalue);
+
+ private:
+  const char *m_api_funcname;
+  recording::context *m_ctxt;
+  recording::statement *m_stmt;
+};
+
+/* The trivial constructor for rvalue_usage_validator.  */
+
+rvalue_usage_validator::rvalue_usage_validator (const char *api_funcname,
+                                               recording::context *ctxt,
+                                               recording::statement *stmt)
+  : m_api_funcname (api_funcname),
+    m_ctxt (ctxt),
+    m_stmt (stmt)
+{
+}
+
+/* Verify that the given rvalue is in the correct scope.  */
+
+void
+rvalue_usage_validator::visit (recording::rvalue *rvalue)
+{
+  gcc_assert (m_stmt->get_block ());
+  recording::function *stmt_scope = m_stmt->get_block ()->get_function ();
+
+  /* Most rvalues don't have a scope (only locals and params).  */
+  if (rvalue->get_scope ())
+    {
+      if (rvalue->get_scope () != stmt_scope)
+       m_ctxt->add_error
+         (rvalue->get_loc (),
+          "%s:"
+          " rvalue %s (type: %s)"
+          " has scope limited to function %s"
+          " but was used within function %s"
+          " (in statement: %s)",
+          m_api_funcname,
+          rvalue->get_debug_string (),
+          rvalue->get_type ()->get_debug_string (),
+          rvalue->get_scope ()->get_debug_string (),
+          stmt_scope->get_debug_string (),
+          m_stmt->get_debug_string ());
+    }
+  else
+    {
+      if (rvalue->dyn_cast_param ())
+       m_ctxt->add_error
+         (rvalue->get_loc (),
+          "%s:"
+          " param %s (type: %s)"
+          " was used within function %s"
+          " (in statement: %s)"
+          " but is not associated with any function",
+          m_api_funcname,
+          rvalue->get_debug_string (),
+          rvalue->get_type ()->get_debug_string (),
+          stmt_scope->get_debug_string (),
+          m_stmt->get_debug_string ());
+    }
+}
+
+/* Verify that it's valid to use this rvalue (and all expressions
+   in the tree below it) within the given statement.
+
+   For example, we must reject attempts to use a local from one
+   function within a different function here, or we'll get
+   an ICE deep inside toplev::main.  */
+
+void
+recording::rvalue::verify_valid_within_stmt (const char *api_funcname, statement *s)
+{
+  rvalue_usage_validator v (api_funcname,
+                           s->get_context (),
+                           s);
+
+  /* Verify that it's OK to use this rvalue within s.  */
+  v.visit (this);
+
+  /* Traverse the expression tree below "this", verifying all rvalues
+     within it.  */
+  visit_children (&v);
+}
+
+/* Set the scope of this rvalue to be the given function.  This can only
+   be done once on a given rvalue.  */
+
+void
+recording::rvalue::set_scope (function *scope)
+{
+  gcc_assert (scope);
+  gcc_assert (NULL == m_scope);
+  m_scope = scope;
+}
+
+
 /* The implementation of class gcc::jit::recording::lvalue.  */
 
 /* Create a recording::new_access_field_of_lvalue instance and add it to
@@ -2106,7 +2215,40 @@ recording::function::function (context *ctxt,
   m_blocks ()
 {
   for (int i = 0; i< num_params; i++)
-    m_params.safe_push (params[i]);
+    {
+      param *param = params[i];
+      gcc_assert (param);
+
+      /* Associate each param with this function.
+
+        Verify that the param doesn't already have a function.  */
+      if (param->get_scope ())
+       {
+         /* We've already rejected attempts to reuse a param between
+            different functions (within gcc_jit_context_new_function), so
+            if the param *does* already have a function, it must be being
+            reused within the params array for this function.  We must
+            produce an error for this reuse (blocking the compile), since
+            otherwise we'd have an ICE later on.  */
+         gcc_assert (this == param->get_scope ());
+         ctxt->add_error
+           (loc,
+            "gcc_jit_context_new_function:"
+            " parameter %s (type: %s)"
+            " is used more than once when creating function %s",
+            param->get_debug_string (),
+            param->get_type ()->get_debug_string (),
+            name->c_str ());
+       }
+      else
+       {
+         /* The normal, non-error case: associate this function with the
+            param.  */
+         param->set_scope (this);
+       }
+
+      m_params.safe_push (param);
+    }
 }
 
 /* Implementation of pure virtual hook recording::memento::replay_into
@@ -2366,26 +2508,25 @@ recording::function::make_debug_string ()
    the block's context's list of mementos, and to the block's
    list of statements.
 
-   Implements the post-error-checking part of
-   gcc_jit_block_add_eval.  */
+   Implements the heart of gcc_jit_block_add_eval.  */
 
-void
+recording::statement *
 recording::block::add_eval (recording::location *loc,
                            recording::rvalue *rvalue)
 {
   statement *result = new eval (this, loc, rvalue);
   m_ctxt->record (result);
   m_statements.safe_push (result);
+  return result;
 }
 
 /* Create a recording::assignment instance and add it to
    the block's context's list of mementos, and to the block's
    list of statements.
 
-   Implements the post-error-checking part of
-   gcc_jit_block_add_assignment.  */
+   Implements the heart of gcc_jit_block_add_assignment.  */
 
-void
+recording::statement *
 recording::block::add_assignment (recording::location *loc,
                                  recording::lvalue *lvalue,
                                  recording::rvalue *rvalue)
@@ -2393,16 +2534,16 @@ recording::block::add_assignment (recording::location *loc,
   statement *result = new assignment (this, loc, lvalue, rvalue);
   m_ctxt->record (result);
   m_statements.safe_push (result);
+  return result;
 }
 
 /* Create a recording::assignment_op instance and add it to
    the block's context's list of mementos, and to the block's
    list of statements.
 
-   Implements the post-error-checking part of
-   gcc_jit_block_add_assignment_op.  */
+   Implements the heart of gcc_jit_block_add_assignment_op.  */
 
-void
+recording::statement *
 recording::block::add_assignment_op (recording::location *loc,
                                     recording::lvalue *lvalue,
                                     enum gcc_jit_binary_op op,
@@ -2411,32 +2552,32 @@ recording::block::add_assignment_op (recording::location *loc,
   statement *result = new assignment_op (this, loc, lvalue, op, rvalue);
   m_ctxt->record (result);
   m_statements.safe_push (result);
+  return result;
 }
 
 /* Create a recording::comment instance and add it to
    the block's context's list of mementos, and to the block's
    list of statements.
 
-   Implements the post-error-checking part of
-   gcc_jit_block_add_comment.  */
+   Implements the heart of gcc_jit_block_add_comment.  */
 
-void
+recording::statement *
 recording::block::add_comment (recording::location *loc,
                               const char *text)
 {
   statement *result = new comment (this, loc, new_string (text));
   m_ctxt->record (result);
   m_statements.safe_push (result);
+  return result;
 }
 
 /* Create a recording::end_with_conditional instance and add it to
    the block's context's list of mementos, and to the block's
    list of statements.
 
-   Implements the post-error-checking part of
-   gcc_jit_block_end_with_conditional.  */
+   Implements the heart of gcc_jit_block_end_with_conditional.  */
 
-void
+recording::statement *
 recording::block::end_with_conditional (recording::location *loc,
                                        recording::rvalue *boolval,
                                        recording::block *on_true,
@@ -2446,16 +2587,16 @@ recording::block::end_with_conditional (recording::location *loc,
   m_ctxt->record (result);
   m_statements.safe_push (result);
   m_has_been_terminated = true;
+  return result;
 }
 
 /* Create a recording::end_with_jump instance and add it to
    the block's context's list of mementos, and to the block's
    list of statements.
 
-   Implements the post-error-checking part of
-   gcc_jit_block_end_with_jump.  */
+   Implements the heart of gcc_jit_block_end_with_jump.  */
 
-void
+recording::statement *
 recording::block::end_with_jump (recording::location *loc,
                                 recording::block *target)
 {
@@ -2463,6 +2604,7 @@ recording::block::end_with_jump (recording::location *loc,
   m_ctxt->record (result);
   m_statements.safe_push (result);
   m_has_been_terminated = true;
+  return result;
 }
 
 /* Create a recording::end_with_return instance and add it to
@@ -2473,7 +2615,7 @@ recording::block::end_with_jump (recording::location *loc,
    gcc_jit_block_end_with_return and
    gcc_jit_block_end_with_void_return.  */
 
-void
+recording::statement *
 recording::block::end_with_return (recording::location *loc,
                                   recording::rvalue *rvalue)
 {
@@ -2484,6 +2626,7 @@ recording::block::end_with_return (recording::location *loc,
   m_ctxt->record (result);
   m_statements.safe_push (result);
   m_has_been_terminated = true;
+  return result;
 }
 
 /* Override the default implementation of
@@ -2513,6 +2656,7 @@ recording::block::write_to_dump (dump &d)
 bool
 recording::block::validate ()
 {
+  /* Check for termination.  */
   if (!has_been_terminated ())
     {
       statement *stmt = get_last_statement ();
@@ -2856,6 +3000,14 @@ recording::unary_op::replay_into (replayer *r)
                                     m_a->playback_rvalue ()));
 }
 
+/* Implementation of pure virtual hook recording::rvalue::visit_children
+   for recording::unary_op.  */
+void
+recording::unary_op::visit_children (rvalue_visitor *v)
+{
+  v->visit (m_a);
+}
+
 /* Implementation of recording::memento::make_debug_string for
    unary ops.  */
 
@@ -2890,6 +3042,15 @@ recording::binary_op::replay_into (replayer *r)
                                      m_b->playback_rvalue ()));
 }
 
+/* Implementation of pure virtual hook recording::rvalue::visit_children
+   for recording::binary_op.  */
+void
+recording::binary_op::visit_children (rvalue_visitor *v)
+{
+  v->visit (m_a);
+  v->visit (m_b);
+}
+
 /* Implementation of recording::memento::make_debug_string for
    binary ops.  */
 
@@ -2955,6 +3116,16 @@ recording::comparison::replay_into (replayer *r)
                                       m_b->playback_rvalue ()));
 }
 
+/* Implementation of pure virtual hook recording::rvalue::visit_children
+   for recording::comparison.  */
+
+void
+recording::comparison::visit_children (rvalue_visitor *v)
+{
+  v->visit (m_a);
+  v->visit (m_b);
+}
+
 /* Implementation of pure virtual hook recording::memento::replay_into
    for recording::cast.  */
 
@@ -2966,6 +3137,14 @@ recording::cast::replay_into (replayer *r)
                                 get_type ()->playback_type ()));
 }
 
+/* Implementation of pure virtual hook recording::rvalue::visit_children
+   for recording::cast.  */
+void
+recording::cast::visit_children (rvalue_visitor *v)
+{
+  v->visit (m_rvalue);
+}
+
 /* Implementation of recording::memento::make_debug_string for
    casts.  */
 
@@ -3011,6 +3190,16 @@ recording::call::replay_into (replayer *r)
                                 &playback_args));
 }
 
+/* Implementation of pure virtual hook recording::rvalue::visit_children
+   for recording::call.  */
+
+void
+recording::call::visit_children (rvalue_visitor *v)
+{
+  for (unsigned i = 0; i< m_args.length (); i++)
+    v->visit (m_args[i]);
+}
+
 /* Implementation of recording::memento::make_debug_string for
    function calls.  */
 
@@ -3088,6 +3277,17 @@ recording::call_through_ptr::replay_into (replayer *r)
                                             &playback_args));
 }
 
+/* Implementation of pure virtual hook recording::rvalue::visit_children
+   for recording::call_through_ptr.  */
+
+void
+recording::call_through_ptr::visit_children (rvalue_visitor *v)
+{
+  v->visit (m_fn_ptr);
+  for (unsigned i = 0; i< m_args.length (); i++)
+    v->visit (m_args[i]);
+}
+
 /* Implementation of recording::memento::make_debug_string for
    calls through function ptrs.  */
 
@@ -3144,6 +3344,16 @@ recording::array_access::replay_into (replayer *r)
                         m_index->playback_rvalue ()));
 }
 
+/* Implementation of pure virtual hook recording::rvalue::visit_children
+   for recording::array_access.  */
+
+void
+recording::array_access::visit_children (rvalue_visitor *v)
+{
+  v->visit (m_ptr);
+  v->visit (m_index);
+}
+
 /* Implementation of recording::memento::make_debug_string for
    array accesses.  */
 
@@ -3171,6 +3381,15 @@ recording::access_field_of_lvalue::replay_into (replayer *r)
 
 }
 
+/* Implementation of pure virtual hook recording::rvalue::visit_children
+   for recording::access_field_of_lvalue.  */
+
+void
+recording::access_field_of_lvalue::visit_children (rvalue_visitor *v)
+{
+  v->visit (m_lvalue);
+}
+
 /* Implementation of recording::memento::make_debug_string for
    accessing a field of an lvalue.  */
 
@@ -3197,6 +3416,15 @@ recording::access_field_rvalue::replay_into (replayer *r)
                      m_field->playback_field ()));
 }
 
+/* Implementation of pure virtual hook recording::rvalue::visit_children
+   for recording::access_field_rvalue.  */
+
+void
+recording::access_field_rvalue::visit_children (rvalue_visitor *v)
+{
+  v->visit (m_rvalue);
+}
+
 /* Implementation of recording::memento::make_debug_string for
    accessing a field of an rvalue.  */
 
@@ -3224,6 +3452,15 @@ recording::dereference_field_rvalue::replay_into (replayer *r)
                         m_field->playback_field ()));
 }
 
+/* Implementation of pure virtual hook recording::rvalue::visit_children
+   for recording::dereference_field_rvalue.  */
+
+void
+recording::dereference_field_rvalue::visit_children (rvalue_visitor *v)
+{
+  v->visit (m_rvalue);
+}
+
 /* Implementation of recording::memento::make_debug_string for
    dereferencing a field of an rvalue.  */
 
@@ -3249,6 +3486,15 @@ recording::dereference_rvalue::replay_into (replayer *r)
       dereference (playback_location (r, m_loc)));
 }
 
+/* Implementation of pure virtual hook recording::rvalue::visit_children
+   for recording::dereference_rvalue.  */
+
+void
+recording::dereference_rvalue::visit_children (rvalue_visitor *v)
+{
+  v->visit (m_rvalue);
+}
+
 /* Implementation of recording::memento::make_debug_string for
    dereferencing an rvalue.  */
 
@@ -3273,6 +3519,15 @@ recording::get_address_of_lvalue::replay_into (replayer *r)
       get_address (playback_location (r, m_loc)));
 }
 
+/* Implementation of pure virtual hook recording::rvalue::visit_children
+   for recording::get_address_of_lvalue.  */
+
+void
+recording::get_address_of_lvalue::visit_children (rvalue_visitor *v)
+{
+  v->visit (m_lvalue);
+}
+
 /* Implementation of recording::memento::make_debug_string for
    getting the address of an lvalue.  */
 
index 43e99bae0fda88db011f3c83ba1dfdad806b9c44..812205c0a1bb7cdb48e9c5cba109e196e8cce055 100644 (file)
@@ -800,6 +800,18 @@ private:
   fields *m_fields;
 };
 
+/* An abstract base class for operations that visit all rvalues within an
+   expression tree.
+   Currently the only implementation is class rvalue_usage_validator within
+   jit-recording.c.  */
+
+class rvalue_visitor
+{
+ public:
+  virtual ~rvalue_visitor () {}
+  virtual void visit (rvalue *rvalue) = 0;
+};
+
 class rvalue : public memento
 {
 public:
@@ -808,11 +820,14 @@ public:
          type *type_)
   : memento (ctxt),
     m_loc (loc),
-    m_type (type_)
+    m_type (type_),
+    m_scope (NULL)
   {
     gcc_assert (type_);
   }
 
+  location * get_loc () const { return m_loc; }
+
   /* Get the recording::type of this rvalue.
 
      Implements the post-error-checking part of
@@ -835,9 +850,23 @@ public:
   lvalue *
   dereference (location *loc);
 
+  void
+  verify_valid_within_stmt (const char *api_funcname, statement *s);
+
+  virtual void visit_children (rvalue_visitor *v) = 0;
+
+  void set_scope (function *scope);
+  function *get_scope () const { return m_scope; }
+
+  /* Dynamic cast.  */
+  virtual param *dyn_cast_param () { return NULL; }
+
 protected:
   location *m_loc;
   type *m_type;
+
+ private:
+  function *m_scope; /* NULL for globals, non-NULL for locals/params */
 };
 
 class lvalue : public rvalue
@@ -881,12 +910,16 @@ public:
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *) {}
+
   playback::param *
   playback_param () const
   {
     return static_cast <playback::param *> (m_playback_obj);
   }
 
+  param *dyn_cast_param () { return this; }
+
 private:
   string * make_debug_string () { return m_name; }
 
@@ -925,6 +958,7 @@ public:
   block*
   new_block (const char *name);
 
+  location *get_loc () const { return m_loc; }
   type *get_return_type () const { return m_return_type; }
   string * get_name () const { return m_name; }
   const vec<param *> &get_params () const { return m_params; }
@@ -979,36 +1013,36 @@ public:
   bool has_been_terminated () { return m_has_been_terminated; }
   bool is_reachable () { return m_is_reachable; }
 
-  void
+  statement *
   add_eval (location *loc,
            rvalue *rvalue);
 
-  void
+  statement *
   add_assignment (location *loc,
                  lvalue *lvalue,
                  rvalue *rvalue);
 
-  void
+  statement *
   add_assignment_op (location *loc,
                     lvalue *lvalue,
                     enum gcc_jit_binary_op op,
                     rvalue *rvalue);
 
-  void
+  statement *
   add_comment (location *loc,
               const char *text);
 
-  void
+  statement *
   end_with_conditional (location *loc,
                        rvalue *boolval,
                        block *on_true,
                        block *on_false);
 
-  void
+  statement *
   end_with_jump (location *loc,
                 block *target);
 
-  void
+  statement *
   end_with_return (location *loc,
                   rvalue *rvalue);
 
@@ -1063,6 +1097,8 @@ public:
 
   void replay_into (replayer *);
 
+  void visit_children (rvalue_visitor *) {}
+
   void write_to_dump (dump &d);
 
 private:
@@ -1086,6 +1122,8 @@ public:
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *) {}
+
 private:
   string * make_debug_string ();
 
@@ -1104,6 +1142,8 @@ public:
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *) {}
+
 private:
   string * make_debug_string ();
 
@@ -1126,6 +1166,8 @@ public:
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *v);
+
 private:
   string * make_debug_string ();
 
@@ -1149,6 +1191,8 @@ public:
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *v);
+
 private:
   string * make_debug_string ();
 
@@ -1173,6 +1217,8 @@ public:
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *v);
+
 private:
   string * make_debug_string ();
 
@@ -1195,6 +1241,8 @@ public:
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *v);
+
 private:
   string * make_debug_string ();
 
@@ -1213,6 +1261,8 @@ public:
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *v);
+
 private:
   string * make_debug_string ();
 
@@ -1232,6 +1282,8 @@ public:
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *v);
+
 private:
   string * make_debug_string ();
 
@@ -1254,6 +1306,8 @@ public:
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *v);
+
 private:
   string * make_debug_string ();
 
@@ -1276,6 +1330,8 @@ public:
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *v);
+
 private:
   string * make_debug_string ();
 
@@ -1298,6 +1354,8 @@ public:
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *v);
+
 private:
   string * make_debug_string ();
 
@@ -1320,6 +1378,8 @@ public:
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *v);
+
 private:
   string * make_debug_string ();
 
@@ -1339,6 +1399,8 @@ public:
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *v);
+
 private:
   string * make_debug_string ();
 
@@ -1358,6 +1420,8 @@ public:
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *v);
+
 private:
   string * make_debug_string ();
 
@@ -1371,10 +1435,15 @@ public:
   local (function *func, location *loc, type *type_, string *name)
     : lvalue (func->m_ctxt, loc, type_),
     m_func (func),
-    m_name (name) {}
+    m_name (name)
+  {
+    set_scope (func);
+  }
 
   void replay_into (replayer *r);
 
+  void visit_children (rvalue_visitor *) {}
+
   void write_to_dump (dump &d);
 
 private:
@@ -1393,6 +1462,7 @@ public:
 
   void write_to_dump (dump &d);
 
+  block *get_block () const { return m_block; }
   location *get_loc () const { return m_loc; }
 
 protected:
@@ -1401,8 +1471,6 @@ protected:
     m_block (b),
     m_loc (loc) {}
 
-  block *get_block () const { return m_block; }
-
   playback::location *
   playback_location (replayer *r) const
   {
index d596d08cd3e060e3a8f8476c551741148bbf4474..ad8ee752d29f11c40b4e96e670f4416f850ba631 100644 (file)
@@ -172,6 +172,16 @@ struct gcc_jit_param : public gcc::jit::recording::param
       }                                                                \
   JIT_END_STMT
 
+#define RETURN_VAL_IF_FAIL_PRINTF5(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4) \
+  JIT_BEGIN_STMT                                                       \
+    if (!(TEST_EXPR))                                                  \
+      {                                                                \
+       jit_error ((CTXT), (LOC), "%s: " ERR_FMT,                               \
+                  __func__, (A0), (A1), (A2), (A3), (A4));     \
+       return (RETURN_EXPR);                                           \
+      }                                                                \
+  JIT_END_STMT
+
 #define RETURN_VAL_IF_FAIL_PRINTF6(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4, A5) \
   JIT_BEGIN_STMT                                                       \
     if (!(TEST_EXPR))                                                  \
@@ -197,6 +207,9 @@ struct gcc_jit_param : public gcc::jit::recording::param
 #define RETURN_NULL_IF_FAIL_PRINTF4(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3) \
   RETURN_VAL_IF_FAIL_PRINTF4 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0, A1, A2, A3)
 
+#define RETURN_NULL_IF_FAIL_PRINTF5(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4) \
+  RETURN_VAL_IF_FAIL_PRINTF5 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4)
+
 #define RETURN_NULL_IF_FAIL_PRINTF6(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4, A5) \
   RETURN_VAL_IF_FAIL_PRINTF6 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4, A5)
 
@@ -844,10 +857,23 @@ gcc_jit_context_new_function (gcc_jit_context *ctxt,
     ctxt, loc,
     "NULL params creating function %s", name);
   for (int i = 0; i < num_params; i++)
-    RETURN_NULL_IF_FAIL_PRINTF2 (
-      params[i],
-      ctxt, loc,
-      "NULL parameter %i creating function %s", i, name);
+    {
+      RETURN_NULL_IF_FAIL_PRINTF2 (
+       params[i],
+       ctxt, loc,
+       "NULL parameter %i creating function %s", i, name);
+      RETURN_NULL_IF_FAIL_PRINTF5 (
+       (NULL == params[i]->get_scope ()),
+       ctxt, loc,
+       "parameter %i \"%s\""
+       " (type: %s)"
+       " for function %s"
+       " was already used for function %s",
+       i, params[i]->get_debug_string (),
+       params[i]->get_type ()->get_debug_string (),
+       name,
+       params[i]->get_scope ()->get_debug_string ());
+    }
 
   return (gcc_jit_function*)
     ctxt->new_function (loc, kind, return_type, name,
@@ -1800,7 +1826,14 @@ gcc_jit_block_add_eval (gcc_jit_block *block,
   /* LOC can be NULL.  */
   RETURN_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
 
-  return block->add_eval (loc, rvalue);
+  gcc::jit::recording::statement *stmt = block->add_eval (loc, rvalue);
+
+  /* "stmt" should be good enough to be usable in error-messages,
+     but might still not be compilable; perform some more
+     error-checking here.  We do this here so that the error messages
+     can contain a stringified version of "stmt", whilst appearing
+     as close as possible to the point of failure.  */
+  rvalue->verify_valid_within_stmt (__func__, stmt);
 }
 
 /* Public entrypoint.  See description in libgccjit.h.
@@ -1832,7 +1865,15 @@ gcc_jit_block_add_assignment (gcc_jit_block *block,
     rvalue->get_debug_string (),
     rvalue->get_type ()->get_debug_string ());
 
-  return block->add_assignment (loc, lvalue, rvalue);
+  gcc::jit::recording::statement *stmt = block->add_assignment (loc, lvalue, rvalue);
+
+  /* "stmt" should be good enough to be usable in error-messages,
+     but might still not be compilable; perform some more
+     error-checking here.  We do this here so that the error messages
+     can contain a stringified version of "stmt", whilst appearing
+     as close as possible to the point of failure.  */
+  lvalue->verify_valid_within_stmt (__func__, stmt);
+  rvalue->verify_valid_within_stmt (__func__, stmt);
 }
 
 /* Public entrypoint.  See description in libgccjit.h.
@@ -1860,7 +1901,15 @@ gcc_jit_block_add_assignment_op (gcc_jit_block *block,
     op);
   RETURN_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
 
-  return block->add_assignment_op (loc, lvalue, op, rvalue);
+  gcc::jit::recording::statement *stmt = block->add_assignment_op (loc, lvalue, op, rvalue);
+
+  /* "stmt" should be good enough to be usable in error-messages,
+     but might still not be compilable; perform some more
+     error-checking here.  We do this here so that the error messages
+     can contain a stringified version of "stmt", whilst appearing
+     as close as possible to the point of failure.  */
+  lvalue->verify_valid_within_stmt (__func__, stmt);
+  rvalue->verify_valid_within_stmt (__func__, stmt);
 }
 
 /* Internal helper function for determining if rvalue BOOLVAL is of
@@ -1921,7 +1970,14 @@ gcc_jit_block_end_with_conditional (gcc_jit_block *block,
     on_false->get_debug_string (),
     on_false->get_function ()->get_debug_string ());
 
-  return block->end_with_conditional (loc, boolval, on_true, on_false);
+  gcc::jit::recording::statement *stmt = block->end_with_conditional (loc, boolval, on_true, on_false);
+
+  /* "stmt" should be good enough to be usable in error-messages,
+     but might still not be compilable; perform some more
+     error-checking here.  We do this here so that the error messages
+     can contain a stringified version of "stmt", whilst appearing
+     as close as possible to the point of failure.  */
+  boolval->verify_valid_within_stmt (__func__, stmt);
 }
 
 /* Public entrypoint.  See description in libgccjit.h.
@@ -2003,7 +2059,14 @@ gcc_jit_block_end_with_return (gcc_jit_block *block,
     func->get_debug_string (),
     func->get_return_type ()->get_debug_string ());
 
-  return block->end_with_return (loc, rvalue);
+  gcc::jit::recording::statement *stmt = block->end_with_return (loc, rvalue);
+
+  /* "stmt" should be good enough to be usable in error-messages,
+     but might still not be compilable; perform some more
+     error-checking here.  We do this here so that the error messages
+     can contain a stringified version of "stmt", whilst appearing
+     as close as possible to the point of failure.  */
+  rvalue->verify_valid_within_stmt (__func__, stmt);
 }
 
 /* Public entrypoint.  See description in libgccjit.h.
@@ -2029,7 +2092,7 @@ gcc_jit_block_end_with_void_return (gcc_jit_block *block,
     func->get_debug_string (),
     func->get_return_type ()->get_debug_string ());
 
-  return block->end_with_return (loc, NULL);
+  block->end_with_return (loc, NULL);
 }
 
 /**********************************************************************
index 03e7acf7957d862d1fa8b1f80cd2d3949adaff16..ba472c521b66dd2ed0fa87492fab2a08a5fa1b70 100644 (file)
@@ -1,3 +1,14 @@
+2015-01-12  David Malcolm  <dmalcolm@redhat.com>
+
+       * jit.dg/test-error-local-used-from-other-function.c: New test
+       case.
+       * jit.dg/test-error-param-reuse.c: New test case.
+       * jit.dg/test-error-param-sharing.c: New test case.
+       * jit.dg/test-error-param-used-from-other-function.c: New test
+       case.
+       * jit.dg/test-error-param-used-without-a-function.c: New test
+       case.
+
 2015-01-12  Jakub Jelinek  <jakub@redhat.com>
 
        PR tree-optimization/64563
diff --git a/gcc/testsuite/jit.dg/test-error-local-used-from-other-function.c b/gcc/testsuite/jit.dg/test-error-local-used-from-other-function.c
new file mode 100644 (file)
index 0000000..fac47c8
--- /dev/null
@@ -0,0 +1,70 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+     void
+     fn_one ()
+     {
+       int i;
+     }
+
+     int
+     fn_two ()
+     {
+       return i;
+     }
+
+     and verify that the API complains about the use of the local
+     from the other function.  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  gcc_jit_function *fn_one =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                 GCC_JIT_FUNCTION_EXPORTED,
+                                 void_type,
+                                 "fn_one",
+                                 0, NULL,
+                                 0);
+  gcc_jit_lvalue *local =
+    gcc_jit_function_new_local (fn_one, NULL, int_type, "i");
+
+  gcc_jit_function *fn_two =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  int_type,
+                                  "fn_two",
+                                  0, NULL,
+                                  0);
+
+  gcc_jit_block *block = gcc_jit_function_new_block (fn_two, NULL);
+  /* "return i;", using local i from the wrong function.  */
+  gcc_jit_block_end_with_return (block,
+                                NULL,
+                                gcc_jit_lvalue_as_rvalue (local));
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_VALUE (result, NULL);
+
+  /* Verify that the correct error message was emitted.  */
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+                     ("gcc_jit_block_end_with_return:"
+                      " rvalue i (type: int)"
+                      " has scope limited to function fn_one"
+                      " but was used within function fn_two"
+                      " (in statement: return i;)"));
+}
+
diff --git a/gcc/testsuite/jit.dg/test-error-param-reuse.c b/gcc/testsuite/jit.dg/test-error-param-reuse.c
new file mode 100644 (file)
index 0000000..32cb0c0
--- /dev/null
@@ -0,0 +1,48 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Verify that we get an error (rather than a crash)
+     if the client code reuses a gcc_jit_param * within
+     a function.  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Create a param.  */
+  gcc_jit_param *param =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "i");
+
+  /* Try to use it twice when creating "fn".  */
+  gcc_jit_param *params[2];
+  params[0] = param;
+  params[1] = param;
+
+  gcc_jit_context_new_function (ctxt, NULL,
+                               GCC_JIT_FUNCTION_IMPORTED,
+                               void_type,
+                               "fn",
+                               2, params,
+                               0);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_VALUE (result, NULL);
+
+  /* Verify that the correct error message was emitted.  */
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+                     ("gcc_jit_context_new_function:"
+                      " parameter i (type: int)"
+                      " is used more than once when creating function"
+                      " fn"))
+}
+
diff --git a/gcc/testsuite/jit.dg/test-error-param-sharing.c b/gcc/testsuite/jit.dg/test-error-param-sharing.c
new file mode 100644 (file)
index 0000000..036049c
--- /dev/null
@@ -0,0 +1,61 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  extern void
+  called_function (void *ptr);
+
+#ifdef __cplusplus
+}
+#endif
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Verify that we get an error (rather than a crash)
+     if the client code reuses a gcc_jit_param * for
+     two different functions.  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  /* Create a param.  */
+  gcc_jit_param *param =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "i");
+
+  /* Try to use it for two different functions. */
+  gcc_jit_context_new_function (ctxt, NULL,
+                               GCC_JIT_FUNCTION_EXPORTED,
+                               void_type,
+                               "fn_one",
+                               1, &param,
+                               0);
+  gcc_jit_context_new_function (ctxt, NULL,
+                               GCC_JIT_FUNCTION_EXPORTED,
+                               void_type,
+                               "fn_two",
+                               1, &param,
+                               0);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_VALUE (result, NULL);
+
+  /* Verify that the correct error message was emitted.  */
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+                     ("gcc_jit_context_new_function:"
+                      " parameter 0 \"i\" (type: int)"
+                      " for function fn_two"
+                      " was already used for function fn_one"))
+}
+
diff --git a/gcc/testsuite/jit.dg/test-error-param-used-from-other-function.c b/gcc/testsuite/jit.dg/test-error-param-used-from-other-function.c
new file mode 100644 (file)
index 0000000..84e898b
--- /dev/null
@@ -0,0 +1,74 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+     void
+     fn_one (int i)
+     {
+     }
+
+     int
+     fn_two ()
+     {
+       return i * 2;
+     }
+
+     and verify that the API complains about the use of the param
+     from the other function.  */
+  gcc_jit_type *void_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  gcc_jit_param *param =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "i");
+
+  gcc_jit_context_new_function (ctxt, NULL,
+                               GCC_JIT_FUNCTION_EXPORTED,
+                               void_type,
+                               "fn_one",
+                               1, &param,
+                               0);
+  gcc_jit_function *fn_two =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  int_type,
+                                  "fn_two",
+                                  0, NULL,
+                                  0);
+
+  gcc_jit_block *block = gcc_jit_function_new_block (fn_two, NULL);
+  /* "return i * 2;", using param i from the wrong function.  */
+  gcc_jit_block_end_with_return (
+    block,
+    NULL,
+    gcc_jit_context_new_binary_op (
+      ctxt, NULL,
+      GCC_JIT_BINARY_OP_MULT,
+      int_type,
+      gcc_jit_param_as_rvalue (param),
+      gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 2)));
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_VALUE (result, NULL);
+
+  /* Verify that the correct error message was emitted.  */
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+                     ("gcc_jit_block_end_with_return:"
+                      " rvalue i (type: int)"
+                      " has scope limited to function fn_one"
+                      " but was used within function fn_two"
+                      " (in statement: return i * (int)2;)"));
+}
+
diff --git a/gcc/testsuite/jit.dg/test-error-param-used-without-a-function.c b/gcc/testsuite/jit.dg/test-error-param-used-without-a-function.c
new file mode 100644 (file)
index 0000000..f369c6b
--- /dev/null
@@ -0,0 +1,56 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+  /* Let's try to inject the equivalent of:
+
+     int
+     test_fn ()
+     {
+       return i;
+     }
+
+     where "i" is a param that isn't associated with any function,
+     and verify that the API complains.  */
+
+  gcc_jit_type *int_type =
+    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+
+  gcc_jit_param *param =
+    gcc_jit_context_new_param (ctxt, NULL, int_type, "i");
+
+  gcc_jit_function *test_fn =
+    gcc_jit_context_new_function (ctxt, NULL,
+                                  GCC_JIT_FUNCTION_EXPORTED,
+                                  int_type,
+                                  "test_fn",
+                                  0, NULL,
+                                  0);
+
+  gcc_jit_block *block = gcc_jit_function_new_block (test_fn, NULL);
+  /* "return i;", using param i from the wrong function.  */
+  gcc_jit_block_end_with_return (block,
+                                NULL,
+                                gcc_jit_param_as_rvalue (param));
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+  CHECK_VALUE (result, NULL);
+
+  /* Verify that the correct error message was emitted.  */
+  CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+                     ("gcc_jit_block_end_with_return:"
+                      " param i (type: int)"
+                      " was used within function test_fn"
+                      " (in statement: return i;)"
+                      " but is not associated with any function"))
+}
+