.. Copyright (C) 2014-2024 Free Software Foundation, Inc.
   Originally contributed by David Malcolm <dmalcolm@redhat.com>

   This is free software: you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see
   <https://www.gnu.org/licenses/>.

.. default-domain:: cpp

Expressions
===========

Rvalues
-------
.. class:: gccjit::rvalue

A :class:`gccjit::rvalue` is an expression that can be computed.  It is a
subclass of :class:`gccjit::object`, and is a thin wrapper around
:c:expr:`gcc_jit_rvalue *` from the C API.

It can be simple, e.g.:

  * an integer value e.g. `0` or `42`
  * a string literal e.g. `"Hello world"`
  * a variable e.g. `i`.  These are also lvalues (see below).

or compound e.g.:

  * a unary expression e.g. `!cond`
  * a binary expression e.g. `(a + b)`
  * a function call e.g. `get_distance (&player_ship, &target)`
  * etc.

Every rvalue has an associated type, and the API will check to ensure
that types match up correctly (otherwise the context will emit an error).

.. function:: gccjit::type gccjit::rvalue::get_type ()

  Get the type of this rvalue.


Simple expressions
******************

.. function:: gccjit::rvalue \
              gccjit::context::new_rvalue (gccjit::type numeric_type, \
                                           int value) const

   Given a numeric type (integer or floating point), build an rvalue for
   the given constant :expr:`int` value.

.. function:: gccjit::rvalue \
              gccjit::context::new_rvalue (gccjit::type numeric_type, \
                                           long value) const

   Given a numeric type (integer or floating point), build an rvalue for
   the given constant :expr:`long` value.

.. function::  gccjit::rvalue \
               gccjit::context::zero (gccjit::type numeric_type) const

   Given a numeric type (integer or floating point), get the rvalue for
   zero.  Essentially this is just a shortcut for:

   .. code-block:: c++

      ctxt.new_rvalue (numeric_type, 0)

.. function::  gccjit::rvalue \
               gccjit::context::one (gccjit::type numeric_type) const

   Given a numeric type (integer or floating point), get the rvalue for
   one.  Essentially this is just a shortcut for:

   .. code-block:: c++

      ctxt.new_rvalue (numeric_type, 1)

.. function::  gccjit::rvalue \
               gccjit::context::new_rvalue (gccjit::type numeric_type, \
                                            double value) const

   Given a numeric type (integer or floating point), build an rvalue for
   the given constant :expr:`double` value.

.. function:: gccjit::rvalue \
              gccjit::context::new_rvalue (gccjit::type pointer_type, \
                                           void *value) const

   Given a pointer type, build an rvalue for the given address.

.. function:: gccjit::rvalue \
              gccjit::context::new_rvalue (const std::string &value) const

   Generate an rvalue of type :c:data:`GCC_JIT_TYPE_CONST_CHAR_PTR` for
   the given string.  This is akin to a string literal.

Vector expressions
******************

.. function:: gccjit::rvalue \
	      gccjit::context::new_rvalue (gccjit::type vector_type, \
	                                   std::vector<gccjit::rvalue> elements) const

   Given a vector type, and a vector of scalar rvalue elements, generate a
   vector rvalue.

   The number of elements needs to match that of the vector type.

Unary Operations
****************

.. function:: gccjit::rvalue  \
              gccjit::context::new_unary_op (enum gcc_jit_unary_op, \
                                             gccjit::type result_type, \
                                             gccjit::rvalue rvalue, \
                                             gccjit::location loc)

   Build a unary operation out of an input rvalue.

   Parameter ``loc`` is optional.

   This is a thin wrapper around the C API's
   :c:func:`gcc_jit_context_new_unary_op` and the available unary
   operations are documented there.

There are shorter ways to spell the various specific kinds of unary
operation:

.. function:: gccjit::rvalue \
              gccjit::context::new_minus (gccjit::type result_type, \
                                          gccjit::rvalue a, \
                                          gccjit::location loc)

   Negate an arithmetic value; for example:

   .. code-block:: c++

      gccjit::rvalue negpi = ctxt.new_minus (t_double, pi);

   builds the equivalent of this C expression:

   .. code-block:: c

      -pi

.. function:: gccjit::rvalue \
              new_bitwise_negate (gccjit::type result_type, \
                                  gccjit::rvalue a, \
                                  gccjit::location loc)

   Bitwise negation of an integer value (one's complement); for example:

   .. code-block:: c++

      gccjit::rvalue mask = ctxt.new_bitwise_negate (t_int, a);

   builds the equivalent of this C expression:

   .. code-block:: c

      ~a

.. function:: gccjit::rvalue \
              new_logical_negate (gccjit::type result_type, \
                                  gccjit::rvalue a, \
                                  gccjit::location loc)

   Logical negation of an arithmetic or pointer value; for example:

   .. code-block:: c++

      gccjit::rvalue guard = ctxt.new_logical_negate (t_bool, cond);

   builds the equivalent of this C expression:

   .. code-block:: c

      !cond


The most concise way to spell them is with overloaded operators:

.. function:: gccjit::rvalue operator- (gccjit::rvalue a)

   .. code-block:: c++

     gccjit::rvalue negpi = -pi;


.. function:: gccjit::rvalue operator~ (gccjit::rvalue a)

   .. code-block:: c++

      gccjit::rvalue mask = ~a;

.. function:: gccjit::rvalue operator! (gccjit::rvalue a)

   .. code-block:: c++

      gccjit::rvalue guard = !cond;


Binary Operations
*****************

.. function:: gccjit::rvalue\
              gccjit::context::new_binary_op (enum gcc_jit_binary_op, \
                                              gccjit::type result_type, \
                                              gccjit::rvalue a, \
                                              gccjit::rvalue b, \
                                              gccjit::location loc)

   Build a binary operation out of two constituent rvalues.

   Parameter ``loc`` is optional.

   This is a thin wrapper around the C API's
   :c:func:`gcc_jit_context_new_binary_op` and the available binary
   operations are documented there.

There are shorter ways to spell the various specific kinds of binary
operation:

.. function:: gccjit::rvalue \
              gccjit::context::new_plus (gccjit::type result_type, \
                                         gccjit::rvalue a, gccjit::rvalue b, \
                                         gccjit::location loc)
              gccjit::rvalue \
              gccjit::context::new_minus (gccjit::type result_type, \
                                          gccjit::rvalue a, gccjit::rvalue b, \
                                          gccjit::location loc)
              gccjit::rvalue \
              gccjit::context::new_mult (gccjit::type result_type, \
                                         gccjit::rvalue a, gccjit::rvalue b, \
                                         gccjit::location loc)
              gccjit::rvalue \
              gccjit::context::new_divide (gccjit::type result_type, \
                                           gccjit::rvalue a, gccjit::rvalue b, \
                                           gccjit::location loc)
              gccjit::rvalue \
              gccjit::context::new_modulo (gccjit::type result_type, \
                                           gccjit::rvalue a, gccjit::rvalue b, \
                                           gccjit::location loc)
              gccjit::rvalue \
              gccjit::context::new_bitwise_and (gccjit::type result_type, \
                                                gccjit::rvalue a, gccjit::rvalue b, \
                                                gccjit::location loc)
              gccjit::rvalue \
              gccjit::context::new_bitwise_xor (gccjit::type result_type, \
                                                gccjit::rvalue a, gccjit::rvalue b, \
                                                gccjit::location loc)
              gccjit::rvalue \
              gccjit::context::new_bitwise_or (gccjit::type result_type, \
                                               gccjit::rvalue a, gccjit::rvalue b, \
                                               gccjit::location loc)
              gccjit::rvalue \
              gccjit::context::new_logical_and (gccjit::type result_type, \
                                                gccjit::rvalue a, gccjit::rvalue b, \
                                                gccjit::location loc)
              gccjit::rvalue \
              gccjit::context::new_logical_or (gccjit::type result_type, \
                                               gccjit::rvalue a, gccjit::rvalue b, \
                                               gccjit::location loc)

The most concise way to spell them is with overloaded operators:

.. function:: gccjit::rvalue operator+ (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue sum = a + b;

.. function:: gccjit::rvalue operator- (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue diff = a - b;

.. function:: gccjit::rvalue operator* (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue prod = a * b;

.. function:: gccjit::rvalue operator/ (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue result = a / b;

.. function:: gccjit::rvalue operator% (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue mod = a % b;

.. function:: gccjit::rvalue operator& (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue x = a & b;

.. function:: gccjit::rvalue operator^ (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue x = a ^ b;

.. function:: gccjit::rvalue operator| (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue x = a | b;

.. function:: gccjit::rvalue operator&& (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue cond = a && b;

.. function:: gccjit::rvalue operator|| (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue cond = a || b;

These can of course be combined, giving a terse way to build compound
expressions:

   .. code-block:: c++

      gccjit::rvalue discriminant = (b * b) - (four * a * c);


Comparisons
***********

.. function:: gccjit::rvalue \
              gccjit::context::new_comparison (enum gcc_jit_comparison,\
                                               gccjit::rvalue a, \
                                               gccjit::rvalue b, \
                                               gccjit::location loc)

   Build a boolean rvalue out of the comparison of two other rvalues.

   Parameter ``loc`` is optional.

   This is a thin wrapper around the C API's
   :c:func:`gcc_jit_context_new_comparison` and the available kinds
   of comparison are documented there.

There are shorter ways to spell the various specific kinds of binary
operation:

.. function:: gccjit::rvalue \
              gccjit::context::new_eq (gccjit::rvalue a, gccjit::rvalue b, \
                                       gccjit::location loc)
              gccjit::rvalue \
              gccjit::context::new_ne (gccjit::rvalue a, gccjit::rvalue b, \
                                       gccjit::location loc)
              gccjit::rvalue \
              gccjit::context::new_lt (gccjit::rvalue a, gccjit::rvalue b, \
                                       gccjit::location loc)
              gccjit::rvalue \
              gccjit::context::new_le (gccjit::rvalue a, gccjit::rvalue b, \
                                       gccjit::location loc)
              gccjit::rvalue \
              gccjit::context::new_gt (gccjit::rvalue a, gccjit::rvalue b, \
                                       gccjit::location loc)
              gccjit::rvalue \
              gccjit::context::new_ge (gccjit::rvalue a, gccjit::rvalue b, \
                                       gccjit::location loc)

The most concise way to spell them is with overloaded operators:

.. function:: gccjit::rvalue \
              operator== (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue cond = (a == ctxt.zero (t_int));

.. function:: gccjit::rvalue \
              operator!= (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue cond = (i != j);

.. function:: gccjit::rvalue \
              operator< (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue cond = i < n;

.. function:: gccjit::rvalue \
              operator<= (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue cond = i <= n;

.. function:: gccjit::rvalue \
              operator> (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue cond = (ch > limit);

.. function:: gccjit::rvalue \
              operator>= (gccjit::rvalue a, gccjit::rvalue b)

   .. code-block:: c++

      gccjit::rvalue cond = (score >= ctxt.new_rvalue (t_int, 100));

.. TODO: beyond this point

Function calls
**************
.. function:: gcc_jit_rvalue *\
              gcc_jit_context_new_call (gcc_jit_context *ctxt,\
                                        gcc_jit_location *loc,\
                                        gcc_jit_function *func,\
                                        int numargs , gcc_jit_rvalue **args)

   Given a function and the given table of argument rvalues, construct a
   call to the function, with the result as an rvalue.

   .. note::

      :func:`gccjit::context::new_call` merely builds a
      :class:`gccjit::rvalue` i.e. an expression that can be evaluated,
      perhaps as part of a more complicated expression.
      The call *won't* happen unless you add a statement to a function
      that evaluates the expression.

      For example, if you want to call a function and discard the result
      (or to call a function with ``void`` return type), use
      :func:`gccjit::block::add_eval`:

      .. code-block:: c++

         /* Add "(void)printf (arg0, arg1);".  */
         block.add_eval (ctxt.new_call (printf_func, arg0, arg1));

Function pointers
*****************

.. function:: gccjit::rvalue \
	      gccjit::function::get_address (gccjit::location loc)

   Get the address of a function as an rvalue, of function pointer
   type.

Type-coercion
*************

.. function:: gccjit::rvalue \
              gccjit::context::new_cast (gccjit::rvalue rvalue,\
                                         gccjit::type type, \
                                         gccjit::location loc)

   Given an rvalue of T, construct another rvalue of another type.

   Currently only a limited set of conversions are possible:

     * int <-> float
     * int <-> bool
     * P*  <-> Q*, for pointer types P and Q

Lvalues
-------

.. class:: gccjit::lvalue

An lvalue is something that can of the *left*-hand side of an assignment:
a storage area (such as a variable).  It is a subclass of
:class:`gccjit::rvalue`, where the rvalue is computed by reading from the
storage area.

It iss a thin wrapper around :c:expr:`gcc_jit_lvalue *` from the C API.

.. function:: gccjit::rvalue \
              gccjit::lvalue::get_address (gccjit::location loc)

   Take the address of an lvalue; analogous to:

   .. code-block:: c

     &(EXPR)

   in C.

   Parameter "loc" is optional.

Global variables
****************

.. function:: gccjit::lvalue \
              gccjit::context::new_global (enum gcc_jit_global_kind,\
                                           gccjit::type type, \
                                           const char *name, \
                                           gccjit::location loc)

   Add a new global variable of the given type and name to the context.

   This is a thin wrapper around :c:func:`gcc_jit_context_new_global` from
   the C API; the "kind" parameter has the same meaning as there.

Working with pointers, structs and unions
-----------------------------------------

.. function:: gccjit::lvalue \
              gccjit::rvalue::dereference (gccjit::location loc)

   Given an rvalue of pointer type ``T *``, dereferencing the pointer,
   getting an lvalue of type ``T``.  Analogous to:

   .. code-block:: c++

     *(EXPR)

   in C.

   Parameter "loc" is optional.

If you don't need to specify the location, this can also be expressed using
an overloaded operator:

.. function:: gccjit::lvalue \
              gccjit::rvalue::operator* ()

   .. code-block:: c++

      gccjit::lvalue content = *ptr;

Field access is provided separately for both lvalues and rvalues:

.. function:: gccjit::lvalue \
              gccjit::lvalue::access_field (gccjit::field field, \
                                            gccjit::location loc)

   Given an lvalue of struct or union type, access the given field,
   getting an lvalue of the field's type.  Analogous to:

   .. code-block:: c++

      (EXPR).field = ...;

   in C.

.. function:: gccjit::rvalue \
              gccjit::rvalue::access_field (gccjit::field field, \
                                            gccjit::location loc)

   Given an rvalue of struct or union type, access the given field
   as an rvalue.  Analogous to:

   .. code-block:: c++

      (EXPR).field

   in C.

.. function:: gccjit::lvalue \
              gccjit::rvalue::dereference_field (gccjit::field field, \
                                                 gccjit::location loc)

   Given an rvalue of pointer type ``T *`` where T is of struct or union
   type, access the given field as an lvalue.  Analogous to:

   .. code-block:: c++

      (EXPR)->field

   in C, itself equivalent to ``(*EXPR).FIELD``.

.. function:: gccjit::lvalue \
              gccjit::context::new_array_access (gccjit::rvalue ptr, \
                                                 gccjit::rvalue index, \
                                                 gccjit::location loc)

   Given an rvalue of pointer type ``T *``, get at the element `T` at
   the given index, using standard C array indexing rules i.e. each
   increment of ``index`` corresponds to ``sizeof(T)`` bytes.
   Analogous to:

   .. code-block:: c++

      PTR[INDEX]

   in C (or, indeed, to ``PTR + INDEX``).

   Parameter "loc" is optional.

For array accesses where you don't need to specify a :class:`gccjit::location`,
two overloaded operators are available:

    gccjit::lvalue gccjit::rvalue::operator[] (gccjit::rvalue index)

    .. code-block:: c++

       gccjit::lvalue element = array[idx];

    gccjit::lvalue gccjit::rvalue::operator[] (int index)

    .. code-block:: c++

       gccjit::lvalue element = array[0];
